Определите, если свойство загружено или установлено представлением

Я пытаюсь сделать "грязную" реализацию с 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 работает следующим образом:

  1. Вы обновляете модель
  2. Обратные вызовы изменения (так что вы устанавливаете _canBeDirty => true)
  3. VM замечает, что модель была изменена, и обновляет выставленные / связанные свойства.

В основном я подозреваю, что вы устанавливаете _canBeDirty => true слишком рано.

При условии, что ViewModelBase придерживается INotifyPropertyChanged подписаться на занятия INotifyPropertyChanged событие и установить там грязный флаг вместо подписки на отдельные события изменений.

По определению это должно произойти после того, как установлено любое значение.

Пример как

public MyViewModel(MyModel myModel, IMyService myService)
 {
    ...
    this.PropertyChanged += (sender, args) => 
                            {
                                if (_canGetDirty)
                                    _myService?.AskForSaving();
                            };
 }

Вы можете отсеять любую логику состояния гонки в режиме с args.PropertyName проверять.

Другие вопросы по тегам