1

我正在尝试使用 Catel 进行“肮脏”的实施。
我有一个视图模型,有一个[Model]属性和一些[ViewModelToModel]映射到它。
我添加了一个布尔成员_canGetDirty,当设置为true允许视图模型属性提示服务进行保存时。

所以我的逻辑是,如果模型属性更改,_canGetDirty设置为false,因此视图模型属性更改而不会变脏,并且当模型完成更改时,我们设置_canGetDirtytrue新的。

问题是模型属性的PropertyChanged事件在视图模型属性更改之前被调用,因此_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); }
}
4

2 回答 2

1

首先,我建议使用 Catel.Fody,它可以大大简化您的属性注册(是的,它还支持更改回调;-))。

为什么模型会在外部发生变化?当模型发生变化(被注入到你的 ctor 中)时,它应该重新创建一个新的 VM,因此你可以从一个新的平板开始。

回到这个问题:你是否测试过你的 setter 是否真的被调用了?Catel 有可能在内部直接调用 SetValue(等于依赖属性的行为),而不是调用 vm 中的包装器。Catel 是这样工作的:

  1. 您更新模型
  2. 更改回调调用(因此您设置 _canBeDirty => true)
  3. vm 注意到模型已更改并更新暴露/链接属性。

我的怀疑基本上是您设置 _canBeDirty => true 太早了。

于 2017-01-07T06:34:03.090 回答
0

假设ViewModelBase坚持INotifyPropertyChanged订阅类的INotifyPropertyChanged事件并在那里设置脏标志而不是订阅单个更改事件。

根据定义,这应该在设置任何值之后发生。

示例为

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

您可以通过检查清除模式上的任何竞争条件逻辑args.PropertyName

于 2017-01-05T20:05:03.117 回答