Showing posts with label INotifyPropertyChanged. Show all posts
Showing posts with label INotifyPropertyChanged. Show all posts

Tuesday, April 5, 2016

WPF/MVVM - tracking when an object has changed


The slow path along the WPF/MVVM learning curve continues.  Unfortunately it seems to make some things that should be simple very difficult.  One of those things is tracking when an object has changed.  This is needed to enable /disable save buttons, or ask the user "Are you Sure?" if he or she navigates away from a record with unsaved changes.

I implemented an IsDirty flag to do this.  The problem I ran into is that the PropertyChanged event fires when by UserControl is being initialized but AFTER the view model's constructor code finishes. So, the IsDirty flag is always true when the UserControl is first loaded, even though the user has not changed anything.

I had a very hard time figuring it out and was amazed how little information there was about the problem.  Surely I'm not the only one that's encountered this.  I probably just didn't know the right things to Google. Here's my solution.  Feel free to comment if you know a better way.

I implemented an IsDirty flag in my view model.  There are plenty of examples of how to do this.  Then I added a command to reset the flag to false when the view has finished rendering.  In other words, this causes the view model to ignore any PropertyChanged events that fire before the view is rendered.

Simplified version of my View Model class:
 // Property that the controls on the view are bound to  
 private Entity boundData;  
 public Entity BoundData   
 {   
   get  
   {  
    return boundData;  
   }  
   set   
   {  
      boundData = value;  
     NotifyPropertyChange("BoundData");  
   }  
 }  
 // Command that the view will use to tell the view model when it's finished rendering  
 public ICommand OnLoadedCommand { get; private set; }  
 // Constructor  
 public void MyUserControlViewModel()  
 {  
   // Get data from a data service, which in turn queries an Entity Framework ObjectContext  
   BoundData = myDataService.GetData();   
   // wire up the PropertyChanged event handler  
   BoundData.PropertyChanged += BoundData_PropertyChanged;  
   // wire up the OnLoadedCommand  
   OnLoadedCommand = new DelegateCommand(OnLoaded);  
   // Reset the IsDirty flag. But does not do any good because PropertyChanged events  
   // fire after this code executes, but before the UserControl has rendered.  
   IsDirty = false;  
 }  
 void BoundData_PropertyChanged(object sender, PropertyChangedEventArgs e)  
 {  
   IsDirty = true;  
 }  
 public void OnLoaded()  
 {  
  // Ignore any PropertyChanged events that fire before the UserControl is rendered.   
   IsDirty = false;   
 }  
 // implementation of the IsDirty flag  
 private bool _isDirty = false;  
 public bool IsDirty  
 {  
   get  
   {  
     return _isDirty;  
   }  
   set  
   {  
     _isDirty = value;  
     NotifyPropertyChange("IsDirty");  
   }  
 }  


Here is the command that fires when the view has finished rendering
 <UserControl x:Class="MyUserControl"  
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
      xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">  
 <i:Interaction.Triggers>  
   <i:EventTrigger EventName="Loaded">  
     <i:InvokeCommandAction Command="{Binding Path=OnLoadedCommand}"/>  
   </i:EventTrigger>  
 </i:Interaction.Triggers>  

Wednesday, March 9, 2016

WPF/MVVM - The ObservableCollection strikes back

Currently I am leading a project to rewrite one of my organization’s major internal apps. I got to choose the architecture and decided on WPF/XAML, the MVVM pattern, and Entity Framework 6. All three are new to me and have pretty significant learning curves. Welcome to the new world. Well, new world for me anyway and apparently old world for everyone else since 2009.

Pretty early on I fell into what I now know is a common WPF “trap for new players”. When you display a list box or gridview of items in an ObservableCollection, changes to items within the ObservableCollection don’t fire its INotifyPropertyChanged event. Fortunately this problem is well known and solutions are easy to find. I ended up using this one-http://www.codeproject.com/Articles/660423/ObservableCollection-notification-on-member-change

Unfortunately this only solved half of the problem for me. I’m using Entity Framework in Database First Mode because I have to use an existing SQL Server database. Therefore my ObservableCollections don’t implement INotifyPropertyChanged, even though, after implementing the above solution, the ObservableCollection is listening for it.

After lots of Googling and testing I ended up using PropertyChanged.Fody to fix this. It implements INotifyPropertyChanged at compile time into whatever model objects you specify- http://www.codeproject.com/Tips/853383/Adding-INotifyPropertyChanged-to-Entity-Framework

I hope someone will find this helpful.