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>
No comments:
Post a Comment