Определите, если свойство загружено или установлено представлением
Я пытаюсь сделать "грязную" реализацию с Catel.
У меня есть модель, с [Model]
собственность и несколько [ViewModelToModel]
сопоставлены с ним.
И я добавил логический член _canGetDirty
, что когда установлено true
позволяет свойствам viewmodel запрашивать сервис для сохранения.
Поэтому моя логика заключается в том, что если свойство модели изменяется, _canGetDirty
установлен в false
Таким образом, свойства viewmodel изменяются без загрязнения, и когда модель будет изменена, мы устанавливаем _canGetDirty
в true
заново.
Проблема в том, что PropertyChanged
Событие для свойства модели вызывается до изменения свойств viewmodel, следовательно _canGetDirty
всегда верно, и мой сервис вызывается для сохранения всякий раз, когда я загружаю новую модель.
Как обойти эту проблему?
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
}
Изменить: некоторые объяснения того, как Catel работает в этом контексте.
Изменения зарегистрированных свойств:
Мы регистрируем свойства, которые будут уведомлять обновления с RegisterProperty
:
public static readonly PropertyData Prop1Property = RegisterProperty("Prop1",
typeof(string), null, PropertyToSaveChanged);
Последний параметр - это функция обратного вызова, вызываемая при изменении зарегистрированного свойства.
Автоматическое обновление свойств модели:
Мы устанавливаем свойство в качестве модели:
[Model]
public MyModel MyModel
{
get { return GetValue<MyModel>(MyModelProperty); }
set
{
_canGetDirty = false;
SetValue(MyModelProperty, value);
}
}
Этот класс содержит несколько свойств (Prop1, Prop2, Prop3). Catel позволяет нам автоматически обновлять их из модели представления, сопоставляя их с ViewModelToModel:
[ViewModelToModel("MyModel")]
public string Prop1
{
get { return GetValue<string>(Prop1Property); }
set { SetValue(Prop1Property, value); }
}
2 ответа
Прежде всего, я рекомендую использовать Catel.Fody, который значительно упрощает регистрацию вашей собственности (и да, он также поддерживает обратные вызовы изменений;-)).
Почему модель внешне изменится? Когда модель изменяется (которая вводится в ваш ctor), она должна воссоздать новую ВМ, и, таким образом, вы можете начать с нового листа.
Возвращаясь к этой проблеме: вы проверяли, действительно ли вызывается ваш сеттер? Вполне возможно, что Catel внутренне напрямую вызывает SetValue (соответствует поведению свойств зависимости) вместо вызова оболочки в вашем vm. Catel работает следующим образом:
- Вы обновляете модель
- Обратные вызовы изменения (так что вы устанавливаете _canBeDirty => true)
- VM замечает, что модель была изменена, и обновляет выставленные / связанные свойства.
В основном я подозреваю, что вы устанавливаете _canBeDirty => true слишком рано.
При условии, что ViewModelBase
придерживается INotifyPropertyChanged
подписаться на занятия INotifyPropertyChanged
событие и установить там грязный флаг вместо подписки на отдельные события изменений.
По определению это должно произойти после того, как установлено любое значение.
Пример как
public MyViewModel(MyModel myModel, IMyService myService)
{
...
this.PropertyChanged += (sender, args) =>
{
if (_canGetDirty)
_myService?.AskForSaving();
};
}
Вы можете отсеять любую логику состояния гонки в режиме с args.PropertyName
проверять.