I'm trying to make a "Dirty" implementation with Catel.
I have a viewmodel, with a [Model]
property and a few [ViewModelToModel]
mapped to it.
And I added a boolean member _canGetDirty
, that when set to true
allows viewmodel properties to prompt a service for saving.
So my logic is that if the model property changes, _canGetDirty
is set to false
, so the viewmodel properties change without getting dirty, and when the model is done changing we set _canGetDirty
to true
anew.
The problem is that the PropertyChanged
event for the model property is called before the viewmodel properties are changed, hence _canGetDirty
is always true, and my service is called for saving whenever I load a new model.
How to work around this issue?
public class MyViewModel : ViewModelBase
{
private IMyService _myService;
private bool _canGetDirty;
public MyViewModel(MyModel myModel, IMyService myService)
{
MyModel = myModel;
_myService = myService;
}
[Model]
public MyModel MyModel
{
get { return GetValue<MyModel>(MyModelProperty); }
set
{
_canGetDirty = false;
SetValue(MyModelProperty, value);
}
}
[ViewModelToModel("MyModel")]
public string Prop1
{
get { return GetValue<string>(Prop1Property); }
set { SetValue(Prop1Property, value); }
}
[ViewModelToModel("MyModel")]
public string Prop2
{
get { return GetValue<string>(Prop2Property); }
set { SetValue(Prop2Property, value); }
}
[ViewModelToModel("MyModel")]
public string Prop3Contains
{
get { return GetValue<string>(Prop3Property); }
set { SetValue(Prop3Property, value); }
}
#region Registering
public static readonly PropertyData Prop1Property = RegisterProperty("Prop1", typeof(string), null, PropertyToSaveChanged);
public static readonly PropertyData Prop2Property = RegisterProperty("Prop2", typeof(string), null, PropertyToSaveChanged);
public static readonly PropertyData Prop3Property = RegisterProperty("Prop3", typeof(string), null, PropertyToSaveChanged);
public static readonly PropertyData MyModelProperty = RegisterProperty("MyModel", typeof(MyModel), null, MyModelChanged);
#endregion
#region Property Changed Handlers
private static void MyModelChanged(object sender, PropertyChangedEventArgs e)
{
(sender as MyViewModel)._canGetDirty = true;
}
private static void PropertyToSaveChanged(object sender, PropertyChangedEventArgs e)
{
var vm = sender as MyViewModel;
if (vm._canGetDirty)
vm._myService.AskForSaving();
}
#endregion
}
Edit: some explanation on how Catel works in this context.
Registered properties changes:
We register properties that will notify updates with RegisterProperty
:
public static readonly PropertyData Prop1Property = RegisterProperty("Prop1",
typeof(string), null, PropertyToSaveChanged);
The last parameter is a callback function called when the registered property changes.
Automatic update of model's properties:
We set a property as a Model:
[Model]
public MyModel MyModel
{
get { return GetValue<MyModel>(MyModelProperty); }
set
{
_canGetDirty = false;
SetValue(MyModelProperty, value);
}
}
This class contains a few properties (Prop1, Prop2, Prop3). Catel allows us to automatically update them from the viewmodel by mapping them with ViewModelToModel:
[ViewModelToModel("MyModel")]
public string Prop1
{
get { return GetValue<string>(Prop1Property); }
set { SetValue(Prop1Property, value); }
}
Assuming that
ViewModelBase
adheres toINotifyPropertyChanged
subscribe to the classes'INotifyPropertyChanged
event and set the dirty flag there instead of subscribing to individual change events.By definition that should happen after any value is set.
Example as
You could weed out any race condition logic on the mode with the
args.PropertyName
check.