Using MVVM style pattern in silverlight/WP7 application - wpf

I'm writing an application where I'm attempting to use an MVVM style architecture to handle my data binding (although I'm not using a MVVM specific library, such as MVVM Light). I've got a class which stores all of the information that my application requires, and then each of the screens is assigned a view model to its DataContext, which simply selects the values required for the specific screen, formatting the data if necessary.
As an example, the main data store looks something like this:
class DataStore {
int a, b, c;
string d;
DateTime e;
}
And then the view model allocated to a specific screen, which only uses several of the properties, is something like
class MainScreenViewModel {
public int data1 { get { return App.DataStore.a * App.DataStore.c } }
public int data2 { get { return App.DataStore.e.Day } }
}
This seems to work fine, when the page loads the data bindings are populated as they should be. However, they do not update automatically when the page loads. I've implemented INotifyPropertyChanged on the DataStore, but it seems that the change event doesn't bubble through to be reflected in the view model. I'm sure I'm going about this a really bad way, so if anyone could help point me in the right direction I'd be very grateful. I've read a stack of guides online, but I seem to be confusing myself more and more!

You have to implement INotifyPropertyChanged and raise PropertyChanged on your VM. In order to do this you will have to listen for DataStore.PropertyChanged. Sample:
class MainScreenViewModel {
public int data1 { get { return App.DataStore.a * App.DataStore.c } }
public int data2 { get { return App.DataStore.e.Day } }
public MainScreenViewModel()
{
App.DataStore.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == "a" || e.PropertyName == "c")
RaisePropertyChanged("data1");
if (e.PropertyName == "e")
RaisePropertyChanged("data2");
};
}
private void RaisePropertyChanged(string propertyName)
{
// raise it
}
}
The only part not covered here is the scenario when e.Day will change in DataStore.
Your approach itself is not the bad and is definitely good enough to start with.

You're binding to the MainScreenViewModel class, so it is that class that needs to implement INotifyPropertyChanged for the UI to get updated when the underlying data gets updated.
You could either move the logic into MainScreenViewModel and raise property change notification there, or handle the PropertyChanged event on DataStore in MainScreenViewModel and raise property changed notification for the appropriate properties.

Related

How do I get the Model instance from inside its wrapping ViewModel?

Short version:
If I have ViewModel, containing its Model object and exposing its properties, how do I get the model "back" after it has been edited? If the Model-inside-ViewModel is public, it violates encapsulation, and if it is private, I cannot get it (right?).
Longer version:
I am implementing a part of an application which displays collections of objects. Let's say the objects are of type Gizmo, which is declared in the Model layer, and simply holds properties and handle its own serialization/deserialization.
In the Model layer, I have a Repository<T> class, which I use to handle collections of MasterGizmo and DetailGizmo. One of the properties of this repository class is an IEnumerable<T> Items { get; } where T will be some of the Gizmo subtype.
Now since Gizmo doesn't implement INPC, I have created the following classes in ViewModel layer:
GizmoViewModel, which wraps every public property of a Gizmo so that setting any property raises PropertyChanged accordingly;
[**] RepositoryViewModel<T>, which has an ObservableCollection<GizmoViewModel> whose CollectionChanged is listened to by a method that handles Adds, Removes and Updates to the repository.
Notice that the Model layer has a "Repository of Models", while the ViewModel layer has a "ViewModel with an ObservableCollection of ViewModels".
The doubt is related to the [**] part above. My RepositoryViewModel.CollectionChangedHandler method is as follows:
void CollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var added in e.NewItems)
{
var gvm = added as GizmoViewModel;
if (gvm != null)
{
//// IS ANY OF THE ALTERNATIVES BELOW THE RIGHT ONE?
// Gizmo g = gvm.RetrieveModel(); ?? proper getter ??
// Gizmo g = GetModelFromViewModel(gvm); ?? external getter ??
// Gizmo g = gvm.Model; ?? public model property ??
_gizmo_repository.Add(g);
}
}
break;
....
Besides that, if anyone can detect any MVVM smell here, I'll be happy to know.
We can deal with our Models even outside the View and ViewModel layers, so leaving the model publicly accessible from ViewModel is I believe acceptable.
Let say you are creating the Models in "DataLayer" you can pass the instance of the Model to the ViewModel. To illustrate my point:
///Models ////////////////////////////
public interface IGizmo{}
public class Gizmo:IGizmo{}
public class SuperGizmo : IGizmo {}
public class SuperDuperGizmo : IGizmo { }
//////////////////////////////////////
public interface IGizmoViewModel<out T>
{
T GetModel();
}
public abstract class GizmoViewModelBase : IGizmoViewModel<IGizmo>
{
protected GizmoViewModelBase(IGizmo model)
{
_Model = model;
}
private readonly IGizmo _Model;
public IGizmo GetModel()
{
return _Model;
}
}
public class GizmoViewModel : GizmoViewModelBase
{
public GizmoViewModel(Gizmo model)
: base(model) { }
}
public class SuperDuperGizmoViewModel : GizmoViewModelBase
{
public SuperDuperGizmoViewModel(SuperDuperGizmo model)
: base(model){}
}
Your repository of Models will be updated on whatever updates it get from the ViewModel as long as you passed the same instance. So there is no need to have a repository of ViewModels to get the updates.
Reading your code, I think there is something of a mixup regarding your ViewModel and Model separation.
So, as I understand it, when your ObservableCollection of GizmoViewModel's changes, you are trying to add the Gizmo instance of the new item back to your Model?
I would approach this differently. You should create your Gizmo instances inside your Model layer, and when you do this you should add it to the Repository.
Otherwise, you haven't provided enough information - or rather, you have provided too much but it is the wrong sort of information. You need to describe the situation in which you want to do this, where these GizmoViewModels are created, etc.
From what I can see here, your GizmoViewModel has a dependency to your Repository<T>, so why not pass in the repository when you create your view model?
public class GizmoViewModel
{
private IRepository<Gizmo> _Repo;
//Underlying model (Doesn't implement INotifyPropertyChanged)
private Gizmo _Model;
//Wrapping properties
public int MyProperty
{
get { return _Model.Property; }
set
{
_Model.Property = value;
NotifyOfPropertyChange();
}
}
...
public GizmoViewModel(IRepository<Gizmo> repo)
{
_Repo = repo;
}
public void AddToRepo()
{
_Repo.Add(_Model);
}
...
It would be even better if these methods are inside the RepositoryViewModel base class. You can really go crazy with inheritance here. Perhaps something like this:
var gvm = added as IRepositoryViewModel;
if (gvm != null)
gvm.AddToRepo();
You can then simply call AddToRepo when you need to add the view model's underlying model to the repository.
Perhaps not the most elegant solution, however if encapsulation is what's worrying you, then you need to ensure that your dependencies are properly managed.
"If the Model-inside-ViewModel is public, it violates encapsulation"
Your assertion above is completely wrong and is killing your code.
By setting the Model property in ViewModel as private, you are forced to repeat your self ( code smells ), as you will need to define in your ViewModel, the same properties as you did for your Model, effectively transforming it into a Model class that mimics the Model it is supposed to expose to the View.
In MVVM the ViewModel role is to provide the View with all the presentation data and logic that it needs and for sure the Model is fundamental part of this data, by hidding it from the View you are killing MVVM.

Update a wpf label periodically

I am new to WPF and C# im trying to understand how can I update a UI element from a BL class (to keep a seperation between the logic and the UI) the bl gets periodic updates from a c++ network component and should update the form once a new argument comes in (I read on the msdn website but I want to see some concrete examples to make sure I got it right)
Because of your gets periodic updates from a c++ network component comment, I am assuming that you already have a system to update your property. I would expose that property from your business class in a view model class, a class with public properties and ICommand functions designed specifically to supply all of the required data to a view, or UserControl.
To be honest, I wouldn't have that (or any) functionality in a business class (depending what you mean by business class)... I'd personally put it straight into the view model, or have a manager/service class that exposed it.
If you insist on keeping it where it is, you'll have to implement either an event or a delegate in your business class so that users of that class can be alerted as to when the value changes. Then you could simply attach a handler to your event/delegate from the view model class and easily update the exposed property whenever the actual property changes.
So it would go a little something like this... in your business class (I am assuming that your value is an int, but you can change this if it is incorrect... the principal is the same):
public delegate void FieldUpdate(int value);
public FieldUpdate OnFieldUpdate { get; set; }
...
private int field;
public int Field
{
get { return field; }
set
{
if (value != field)
{
field = value;
if (OnFieldUpdate != null) OnFieldUpdate(field);
}
}
}
Then in your view model:
private YourBusinessClass instance = new YourBusinessClass();
public YourBusinessClass Instance
{
get { return instance; }
set { instance = value; NotifyPropertyChanged("Instance"); }
}
Attach a handler:
instance.OnFieldUpdate += OnBusinessClassFieldUpdate;
...
public void OnBusinessClassFieldUpdate(int value)
{
Instance = value;
}
Now whenever the field is updated in the business class, the view model (and data bound UI controls) will automatically update via the delegate.

WPF: Binding TreeView in MVVM way step by step tutorial

See the next post. This original one question content has been removed, as doesn't have any sense. Briefly, I asked how to bind XML (which I generated by mistake while parsing DLL assembly) to TreeView using XmlDataProvider in MVVM way. But later I understood that this approach was wrong, and I switched to generation of data entity model (just write classes which represent all the entities I would like to expose in the tree) instead of XML.
So, the result in the next post. Currently from time to time I update this "article", so F5, and
Enjoy reading!
Introduction
The right way I had found reading this article
It's a long story, most of you just can skip it :) But those, who want to understand the problem and solution, must read this all !
I'm QA, and some time ago had become responsible for Automation of the product I clicks. Fortunately, this automaton takes place not in some Testing Tool, but in Visual Studio, so it is maximally close to development.
For our automation we use a framework which consist of MbUnit (Gallio as runner) and of MINT (addition to MbUnit, which is written by the customer we work with). MbUnit gives us Test Fixtures and Tests, and MINT adds additional smaller layer -- Actions inside tests. Example. Fixture is called 'FilteringFixture'. It consist of amount of tests like 'TestingFilteringById', or 'TestingFilteringWithSpecialChars', etc. Each test consist of actions, which are atomic unit of our test. Example of actions are - 'Open app (parameter)', 'OpenFilterDialog', etc.
We already have a lot of tests, which contain a lot of actions, it's a mess. They use internal API of the product we QA. Also, we start investigation a new Automation approach - UI automation via Microsoft UI Automation (sorry for tautology). So the necessity of some "exporter", or "reporter" tool became severe for managers.
Some time ago I have got a task to develop some application, which can parse a DLL (which contains all the fixtures, tests and actions), and export its structure in the human readable format (TXT, HTML, CSV, XML, any other). But, right after that, I went to vacation (2 weeks).
It happens so, that my girlfriend went to her family until vacation (she also got it), and I remained at home so alone. Thinking what me to do all this time (2 weeks), I remember about that "write exporter tool task" and how long I have been planning to start learning WPF. So, I decided to make my task during vacation, and also dress a application to WPF. At that time I heard something about MVVM, and I decided to implement it using pure MVVM.
DLL which can parse DLL with fixrtures etc had been written rather fast (~1-2 days). After that I had started with WPF, and this article will show you how it ended.
I have spent a major part of my vacation (almost 8 days!), trying to sorted it out in my head and code, and finally, it is done (almost). My girlfriend would not believe what I was doing all this time, but I have a proof!
Sharing my solution step by step in pseudo code, to help others avoid similar problems. This answer is more looks like tutorial =) (Really?). If you are interested what were the most complicated things while learning WPF from scratch, I would say -- make it all really MVVM and f*g TreeView binding!
If you want an archived file with solution, I can give it a bit later, just when I have made a decision, that it is worth of that. One limitation, I'm not sure I may share the MINT.dll, which brings Actions, as it has been developed by the customer of our company. But I can just remove it, and share the application, which can display information about Fixtures and Tests only, but not about actions.
Boastful words. With just a little C# / WinForms / HTML background and no practice I have been able to implement this version of the application in almost 1 week (and write this article). So, impossible is possible! Just take a vacation like me, and spend it to WPF learning!
Step by step tutorial (w/o attached files yet)
Short repetition of the task:
Some time ago I have got a task to develop an application, which can parse a DLL (which contains test fixtures, test methods and actions - units of our unit testing based automation framework), and export its structure in the human readable format (TXT, HTML, CSV, XML, any other). I decided to implement it using WPF and pure MVVM (both were absolutely new things for me). The 2 most difficult problems for me became MVVM approach itself, and then MVVM binding to TreeView control. I skip the part about MVVM division, it's a theme for separate article. The steps below are about binding to TreeView in MVVM way.
Not so important: Create DLL which can open DLL with unit tests and finds fixtures, test methods and actions (more smaller level of unit test, written in our company) using reflection. If you are interested in how it had been done, look here: Parsing function / method content using Reflection
DLL: Separated classes are created for both fixtures, tests and actions (data model, entity model?).We'll use them for binding. You should think by yourself, what will be an entity model for your tree. Main idea - each level of tree should be exposed by appropriate class, with those properties, which help you to represent the model in the tree (and, ideally, will take right place in your MVVM, as model or part of the model). In my case, I was interested in entity name, list of children and ordinal number. Ordinal number is a number, which represents order of an entity in the code inside DLL. It helps me show ordinal number in the TreeView, still not sure it's right approach, but it works!
public class MintFixutre : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
private readonly List<MintTest> _tests = new List<MintTest>();
public MintFixutre(string fixtureName, int ordinalNumber)
{
_name = fixtureName;
if (ordinalNumber <= 0)
throw new ArgumentException("Ordinal number must begin from 1");
_ordinalNumber = ordinalNumber;
}
public List<MintTest> Tests
{
get { return _tests; }
}
public string Name { get { return _name; }}
public bool IsParent { get { return true; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
public class MintTest : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
private readonly List<MintAction> _actions = new List<MintAction>();
public MintTest(string testName, int ordinalNumber)
{
if (string.IsNullOrWhiteSpace(testName))
throw new ArgumentException("Test name cannot be null or space filled");
_name = testName;
if (ordinalNumber <= 0)
throw new ArgumentException("OrdinalNumber must begin from 1");
_ordinalNumber = ordinalNumber;
}
public List<MintAction> Actions
{
get { return _actions; }
}
public string Name { get { return _name; } }
public bool IsParent { get { return true; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
public class MintAction : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
public MintAction(string actionName, int ordinalNumber)
{
_name = actionName;
if (ordinalNumber <= 0)
throw new ArgumentException("Ordinal numbers must begins from 1");
_ordinalNumber = ordinalNumber;
}
public string Name { get { return _name; } }
public bool IsParent { get { return false; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
BTW, I also created an interface below, which implement all the entities. Such interface can help you in the future. Still not sure, should I was also add there Childrens property of List<IMintEntity> type, or something like that?
public interface IMintEntity
{
string Name { get; }
bool IsParent { get; }
int OrdinalNumber { get; }
}
DLL - building data model: DLL has a method which opens DLL with unit tests and enumerating data. During enumeration, it builds a data model like below. Real method example is given, reflection core + Mono.Reflection.dll are used, don't be confused with complexity. All that you need - look how the method fills _fixtures list with entities.
private void ParseDllToEntityModel()
{
_fixutres = new List<MintFixutre>();
// enumerating Fixtures
int f = 1;
foreach (Type fixture in AssemblyTests.GetTypes().Where(t => t.GetCustomAttributes(typeof(TestFixtureAttribute), false).Length > 0))
{
var tempFixture = new MintFixutre(fixture.Name, f);
// enumerating Test Methods
int t = 1;
foreach (var testMethod in fixture.GetMethods().Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Length > 0))
{
// filtering Actions
var instructions = testMethod.GetInstructions().Where(
i => i.OpCode.Name.Equals("newobj") && ((ConstructorInfo)i.Operand).DeclaringType.IsSubclassOf(typeof(BaseAction))).ToList();
var tempTest = new MintTest(testMethod.Name, t);
// enumerating Actions
for ( int a = 1; a <= instructions.Count; a++ )
{
Instruction action = instructions[a-1];
string actionName = (action.Operand as ConstructorInfo).DeclaringType.Name;
var tempAction = new MintAction(actionName, a);
tempTest.Actions.Add(tempAction);
}
tempFixture.Tests.Add(tempTest);
t++;
}
_fixutres.Add(tempFixture);
f++;
}
}
DLL: Public property Fixtures of the List<MintFixutre> type is created to return just created data model ( List of Fixtures, which contain lists of tests, which contains lists of Actions ). This will be our binding source for TreeView.
public List<MintFixutre> Fixtures
{
get { return _fixtures; }
}
ViewModel of MainWindow (with TreeView inside): Contains object / class from DLL which can parse unit tests DLLs. Also exposes Fixtures public property from the DLL of List<MintFixutre> type. We will bind to it from XAML of MainWindow. Something like that (simplified):
var _exporter = MySuperDllReaderExporterClass ();
// public property of ViewModel for TreeView, which returns property from #4
public List<MintFixture> Fixtures { get { return _exporter.Fixtures; }}
// Initializing exporter class, ParseDllToEntityModel() is called inside getter
// (from step #3). Cool, we have entity model for binding.
_exporter.PathToDll = #"open file dialog can help";
// Notifying all those how are bound to the Fixtures property, there are work for them, TreeView, are u listening?
// will be faced later in this article, anticipating events
OnPropertyChanged("Fixtures");
XAML of MainWindow - Setup data templates: Inside a Grid, which contains TreeView, we create <Grid.Resources> section, which contains a set of templates for our TreeViewItems. HierarchicalDataTemplate (Fixtures and Tests) is used for those who have child items, and DataTemplate is used for "leaf" items (Actions). For each template, we specify which its Content (text, TreeViewItem image, etc.), ItemsSource (in case of this item has children, e.g. for Fixtures it is {Binding Path=Tests}), and ItemTemplate (again, only in case this item has children, here we set linkage between templates - FixtureTemplate uses TestTemplate for its children, TestTemplate uses ActionTemplate for its children, Action template does not use anything, it is a leaf!). IMPORTANT: Don't forget, that in order to "link" "one" template to "another", the "another" template must be defined in XAML above the "one"! (just enumerating my own blunders :) )
XAML - TreeView linkage: We setup TreeView with: linking with data model from ViewModel (remember public property?) and with just prepared templates, which represent content, appearance, data sources and nesting of tree items! One more important note. Don't define your ViewModel as "static" resource inside XAML, like <Window.Resources><MyViewModel x:Key="DontUseMeForThat" /></Window.Resources>. If you do so, then you won't be able to notify it on property changed. Why? Static resource is static resource, it initializes ones, and after that remains immutable. I might be wrong here, but it was one of my blunders. So for TreeView use ItemsSource="{Binding Fixtures}" instead of ItemsSource="{StaticResource myStaticViewModel}"
ViewModel - ViewModelBase - Property Changed: Almost all. Stop! When user opens an application, then initially TreeView is empty of course, as user hasn't opened any DLL yet! We must wait until user opens a DLL, and only then perform binding. It is done via OnPropertyChanged event. To make life easier, all my ViewModels are inherited from ViewModelBase, which right exposes this functionality to all my ViewModel.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, args);
}
}
XAML - OnPropertyChanged and commanding. User clicks a button to opens DLL which contains unit tests data. As we using MVVM, then click is handled via commanding. At the end of the OpenDllExecuted handler OnPropertyChanged("Fixtures") is executed, notifying the Tree, that the property, to which it is bind to has been changed, and that now is time to refresh itself. RelayCommand helper class can be taken for example from there). BTW, as I know, there are some helper libraries and toolkits exist Something like that happens in the XAML:
And ViewModel - Commanding
private ICommand _openDllCommand;
//...
public ICommand OpenDllCommand
{
get { return _openDllCommand ?? (_openDllCommand = new RelayCommand(OpenDllExecuted, OpenDllCanExecute)); }
}
//...
// decides, when the <OpenDll> button is enabled or not
private bool OpenDllCanExecute(object obj)
{
return true; // always true for Open DLL button
}
//...
// in fact, handler
private void OpenDllExecuted(object obj)
{
var openDlg = new OpenFileDialog { ... };
_pathToDll = openDlg.FileName;
_exporter.PathToDll = _pathToDll;
// Notifying TreeView via binding that the property <Fixtures> has been changed,
// thereby forcing the tree to refresh itself
OnPropertyChanged("Fixtures");
}
Final UI (but not final for me, a lot of things should be done!). Extended WPF toolkit was used somewhere: http://wpftoolkit.codeplex.com/

Using a Non-Anemic Domain Model with Wpf MVVM

I am implementing a WPF based application using MVVMfor the UI.
I have a ViewModel that wraps each editable Model that can be edited. The VM contains all the logic for handling error notifications, "is dirty" management and so forth ..
This design supports well CRUD schenarios for simple domain Model objects that are anemic, that is, do not contain any logic.
Now, I am facing a more tricky problem cause I have a domain Model that contains logic and that logic can change the internal state of the domain Model.
Do someone have already faced this scenario ? If so, do you have some advices to handle this correctly ?
Riana
Here is how I usually deal with it:
The ViewModel layer is made of types that belong to this layer, meaning I don't ever directly use my business objects inside of a ViewModel. I map my business objects to ViewModel objects that may or may not be the exact same shape minus the behaviors. It can be argued that this violates Don't Repeat Yourself, but doing so allows you to adhere to the Single Responsibility Principle. In my opinion, SRP should usually trump DRY. The ViewModel exists to serve the view, and the model exists to serve business rules / behavior.
I create a facade/service layer that takes and returns ViewModels as arguments, but maps the ViewModels to-and-from their corresponding business object versions. This, way the non-anemic objects won't impose non view logic on the ViewModel
The dependencies would look like this:
ViewModel <--> Facade/ServiceLayer --> Business Objects
I think it is important to keep this in mind if you want to unleash the full potential of MVVM: The ViewModel is the model/abstraction of the view, not the model presented to the view.
Try using Command pattern. Your screen should be design not to edit an entity but to perform an action (command) on an entity. If you follow that principle when designing your screens, your ViewModel will have properties that should be mapped to a command object. Then, the command will be send to an (remote) facade of the domain model.
ViewModels for displaying the data could be mapped directly to the database (bypassing the domain model altogether) so that you don't need to put nasty getters in the domain model classes.
If the domain model is non-anemic, you will need to use events to communicate internal changes in the Model back to the ViewModel. That way you don't have to worry about keeping track of what operations could potentially make your VM out-of-sync with the model.
Here's a simple example:
First, a sample model:
public class NonAnemicModel
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
return;
_name = value;
OnNameChanged(EventArgs.Empty);
}
}
public event EventHandler NameChanged;
protected virtual void OnNameChanged(EventArgs e)
{
if (NameChanged != null)
NameChanged(this, e);
}
public void PerformNameCalculation(int chars)
{
//example of a complex logic that inadvertently changes the name
this.Name = new String('Z', chars); //makes a name of Z's
}
}
And here's a sample ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
private NonAnemicModel _model;
public NonAnemicModel Model
{
get { return _model; }
set
{
_model = value;
_model.NameChanged += (sender, args) => NotifyPropertyChanged("UserName");
}
}
public string UserName
{
get { return this.Model.Name; }
set { this.Model.Name = value; }
}
//this command would call out to the PerformNameCalculation method on the Model.
public ICommand PerformNameCalculation { get; private set; }
}
Notice that the PropertyChanged event is raised when the Name on the model changes. That way, regardless of whether the UserName setter was used, or the PerformNameCalculation command was used, the ViewModel stays in sync. The big downside to this is that you have to add many more events to your Model, but I've found that having these events in place is usually very helpful in the long run. Just be careful about memory leaks with events!

EntityFramework EntityState and databinding along with INotifyPropertyChanged

I have a WPF view that displays a Shipment entity. I have a textblock that contains an asterisk which will alert a user that the record is changed but unsaved. I originally hoped to bind the visibility of this (with converter) to the Shipment.EntityState property.
If value = EntityState.Modified Then
Return Visibility.Visible
Else
Return Visibility.Collapsed
End If
The property gets updated just fine, but the view is ignorant of the change. What I need to know is, how can I get the UI to receive notification of the property change. If this cannot be done, is there a good way of writing my own IsDirty property that handles editing retractions (i.e. if I change the value of a property, then change it back to it's original it does not get counted as an edit, and state remains Unchanged).
Any help, as always, will be greatly appreciated.
Cory
After struggling with the same problem for a little bit, here is a solution that is working for me.
Lets say I have an entity called Trip that was generated by EF, I just needed to extend the class by means of partial class as showed below. The RaiseEntityStateChanged method is useful when you need to force a refresh of the EntytyState property, for example after calling the context's SaveChanges method.
partial class Trip
{
bool _forced = false;
System.Data.EntityState _lastState;
public Trip()
{
_lastState = EntityState;
this.PropertyChanged += (s, e) =>
{
if (_lastState != this.EntityState && e.PropertyName != "EntityState" || _forced)
{
_forced = false;
OnPropertyChanged("EntityState");
}
_lastState = this.EntityState;
};
}
public virtual void RaiseEntityStateChanged()
{
_forced = true;
OnPropertyChanged("EntityState");
}
}
I don't see a way to create a XAML binding on an existing property to do what you are trying to do. But you could write your own IsDirty property, based on the EntityState; you could update this value by subscribing to the PropertyChanged event raised by the base EntityObject. Of course, you'll need to also raise a PropertyChanged event for IsDirty (so that the GUI is notified) and ignore this event in your handler (to prevent infinite recursion).
Edit: added the following after question by OP:
This is how I see it, in order to answer the comment.
In the shipment class, one can add:
public bool IsDirty { get { return EntityState == EntityState.Modified; } }
public Shipment() {
...
PropertyChanged += OnShipmentChanged;
}
private void OnShipmentChanged(object sender, PropertyChangedEventArgs pcea) {
if (pcea.PropertyName != "IsDirty") { // prevent recursion
OnPropertyChanged("IsDirty"); // notifies binding listener that the state has changed
}
}
During the night, I thought of another way, which is to create a multi-binding on each Shipment property (which would replace this whole notion of an IsDirty property and would actually answer the original question). This could make sense if there are just a couple of Shipment properties. I'd say if there are more than 3, we should forget about this idea.

Resources