如何从 "traditional" 过渡到响应式 MVVM

How to make the transition from "traditional" to reactive MVVM

我想过渡到反应式视图模型/模型。

到目前为止我已经使用了 3 个场景:

这是我的"current"方法:

class Model
{
    public string ValueA {get;set;}

    private string valueB;
    public event ValueBChangedEvent ValueBChanged;
    public string ValueB
    {
        get
        {
            return valueB;
        }
        set
        {
            valueB = value;
            ValueBChanged();
        }
    }
}

class ViewModel : INotifyPropertyChanged
{
    private Model model;

    public string ValueA
    {
        get 
        {
            return model.ValueA;
        }
        set
        {
            model.ValueA = value;
            OnPropertyChanged();
        }
    }

    ViewModel()
    {
        model.ValueBChanged += model_ValueBChanged;
    }

    private void model_ValueBChanged()
    {
        OnPropertyChanged("ValueB");
    }


    public string ValueB
    {
        get 
        {
            return model.ValueB;
        }
        set
        {
            model.ValueB = value;
            // no change notification since done via model
        }
    }   


    private string valueC;
    public string ValueC
    {
        get 
        {
            return valueC;
        }
        set
        {
            valueC = value;
            OnPropertyChanged();
        }
    }       
}

这就是我打算使用响应式扩展对它们进行建模的方式:

class ReactiveModel
{
    public string ValueA {get;set;}

    private ISubject<string> valueB = new Subject<string>();
    public ISubject<string> ValueB
    {
        get
        {
            return valueB;
        }
    }
}

class ReactiveViewModel : INotifyPropertyChanged
{
    private ReactiveModel model;

    public string ValueA
    {
        get 
        {
            return ???;
        }
        set
        {
            ???
        }
    }


    private ReactiveProperty<string> valueB = model.valueB.ToReactiveProperty();
    public Reactive<string> ValueB
    {
        get 
        {
            return valueB;
        }
        // no setter since access via ValueB.Value which is read-write
    }   


    private ISubject<string> _valueC = new Subject<string>();
    private ReactiveProperty<string> valueC = _valueC.ToReactiveProperty();
    public ReactiveProperty<string> ValueC
    {
        get 
        {
            return valueC;
        }
        // no setter since access via ValueC.Value which is read-write
    }       
}

总结:

如果我有 ValueA 和 ValueB 的解决方案,我会很高兴。

这是一个有点争议的话题,但我个人并不认为 属性 更改通知是特定于视图模型和视图的,因此我使用 B 但我也将 INPC 添加到模型中在我的数据层。这可以在 post-processing 构建步骤中使用 Fody 或通过使用 Castle Dynamic Proxy 之类的代理将模型包装在代理中来完成。我个人使用后者,尽管它需要与您的 ORM 集成,以免影响性能,即您不希望您的数据库代码加载模型对象,然后认为该对象已更改,因为您尝试使用代理更新它包装器(当您将 IList<> 转换为 ObservableCollection 时尤其如此)。

您目前的做法似乎没有多大意义。您正在实施 事件 以在 Model 发生变化时发出信号,以便 View Model 可以采取行动。然而只有视图模型应该改变模型,因此事件是完全没有必要的。

View Model 负责对 Model 进行更改,因此它应该 知道 何时执行更改,因为它是源所说的变化。


MVVM 方法是这样的:

public class MyModel
{
    public string MyValue { get; set; }

    ...
}

public class MyViewModel
{
    private MyModel _Model;

    public string MyModelValue
    {
        get { return _Model.MyValue; }
        set 
        {
            _Model.MyValue = value;
            //Notify property changed.
        }
    }

    ...
}

Model 没有责任通知 View 变化,ViewModel 有责任通知这些变化。 Model 应该 而不是 暴露给 View,而是 View Model 的属性]requires 应该公开。

这样想。

  • 用户在 View 上的 TextBox 中更改了 MyModelValue 属性。
  • View 将更改通知 ViewModel
  • ViewModel 更改 Model

INotifyPropertyChanged的唯一目的是当上述过程反转时,ViewModel需要告诉一个属性变了View

  • 调用 ViewModel 中的方法更新 MyModelValue
  • ViewModel 将更改通知 View
  • View更新了TextBox

总是遵循视图需要的 Model 属性的模式,相反,您可以看到 entire Model 被暴露给 View,但正如我之前多次说过的,MVVM 是一种 模式 ,不是法律。在 Model 中实施 INotifyPropertyChanged 是完全可以接受的。

ValueB:视图模型负责更新模型。 ReactiveProperty仅使用模型属性中的 IObservable 接口并从 ValueB 中读取值(不写入任何内容)。 ReactiveProperty 通过 Value 属性 视图更改。 ReactiveProperty 实现了 IObservable,您应该订阅更改以获得新值。

ValueA:我们可以在视图模型端创建一个ReactiveProperty订阅以将更改的值传播到模型。

这是解决方案的代码:

class ReactiveModel
{
    public string ValueA {get;set;}
    private readonly Subject<string> valueB = new Subject<string>();
    public IObservable<string> ValueB
    {
        get
        {
            return valueB;
        }
    }

    public void UpdateB(string newValue)
    {
        valueB.OnNext(newValue);
    }
}

class ReactiveViewModel
{
    private readonly ReactiveModel model;
    private readonly ReactiveProperty<string> valueA;
    private readonly ReactiveProperty<string> valueB;

    public ReactiveViewModel(ReactiveModel model)
    {
        this.model = model;

        valueA = new ReactiveProperty<string>(model.ValueA);
        valueA.Subscribe(x => model.ValueA = x);

        valueB = model.ValueB.ToReactiveProperty();
        valueB.Subscribe(model.UpdateB);
    }

    public IObservable<string> ValueA
    {
        get
        {
            return valueA;
        }
    }

    public ReactiveProperty<string> ValueB
    {
        get 
        {
            return valueB;
        }
    }
}

XAML 在这两种情况下都是:

<TextBox Text="{Binding ValueA.Value, UpdateSourceTrigger=PropertyChanged}"/>