【问题标题】:How to correctly update the Model after the ViewModel has changed?ViewModel改变后如何正确更新Model?
【发布时间】:2015-05-21 23:56:57
【问题描述】:

假设我们有一个具有以下属性的 ModelModel 类)。

public string InputFileName
{
    get { return m_InputFileName; }
    set
    {
        m_InputFileName = value;
        RaiseNotifyPropertyChanged("InputFileName");
    }
}

上面的模型实现了INotifyPropertyChanged接口,所以我们也有下面的方法和下面的事件。下面的RaiseNotifyPropertyChanged 方法用于更新ViewModel

#region INotifyPropertyChanged Implementation

private void RaiseNotifyPropertyChanged(string property)
{
    var handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(property));
    }
}

public event PropertyChangedEventHandler PropertyChanged;

#endregion

以下是实现ViewModel的类的主要部分。

public class ViewModel : INotifyPropertyChanged
{
    #region Members

    private Model m_Model;

    private string m_InputFileStr;

    private readonly ICommand m_SubmitCommand;

    #endregion

    #region Constructors

    public ViewModel()
    {
        m_Model = new Model();
        m_Model.PropertyChanged += new PropertyChangedEventHandler(this.Model_PropertyChanged);

        m_InputFileStr = string.Empty;

        // ...
        // initialize m_SubmitCommand
    }

    #endregion

    // ...

    #region Properties

    public string InputFileStr
    {
        get { return m_InputFileStr; }
        set
        {
            if (value == m_InputFileStr) return;
            m_InputFileStr = value;
            OnPropertyChanged("InputFileStr");
            m_SubmitCommand.RaiseCanExecuteChanged();
        }
    }

    #endregion

    #region INotifyPropertyChanged Implementation

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion

    // This method is called when the model changes, so the Model notified the ViewModel.
    private void Model_PropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        if (args.PropertyName == "InputFileName")
        {
            InputFileStr = m_Model.InputFileName;
        }
        else if (args.PropertyName == "OutputFileName")
        {
            OutputFileStr = m_Model.OutputFileName;
        }
        else if (args.PropertyName == "ReportText")
        {
            ReportTextStr = m_Model.ReportText;
        }
    }
}

以下是实现View的类的主要部分:

MainWindow.xaml

<TextBox Name="inputfileTextBox"
         Text="{Binding Path=InputFileStr, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

<Button Name="submitButton"
        Content="Submit"
        Command="{Binding SubmitCommand}"/>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new ViewModel();
    }
}

上述实现工作正常:

  • View 和 ViewModel 正确地相互更新;
  • 模型正确更新了 ViewModel。

为了使 ViewModel 能够更新模型,我想我会在 ViewModel 的 set 属性 InputFileStr 中添加以下调用:

m_Model.InputFileName = value;

但是,这种更新模型的解决方案会导致明显的意外效果:

  1. 用户修改了视图。
  2. ViewModel 会自动修改。
  3. ViewModel 更新模型 (m_Model.InputFileName = value;)。
  4. 模型已更新...
  5. ...所以它会通知 ViewModel 有关更改

上述行为是否正确?我希望如果 ViewModel 更新了 Model,那么 Model 不必重新通知 ViewModel 相同的更改...作为替代解决方案,我想我会在 Model 中添加一个 Update 方法:此方法应该在不使用模型属性的情况下更新模型。

public void Update(string inputFileName)   // this method does not notifies the ViewModel
{
    m_InputFileName = inputFileName;
}

这种替代解决方案是正确的解决方案还是有更好的解决方案?

【问题讨论】:

    标签: c# .net wpf user-interface mvvm


    【解决方案1】:

    根据您的模型,您通常只需调用“保存”方法或类似方法。大多数模型(例如,数据库)不需要/不希望实时获得所有更改。

    所以一般来说,流程是:

    1. 用户调用“保存”操作
    2. 视图模型将此作为命令接收
    3. 视图模型使用新数据对模型调用“保存”操作

    如果您的 DTO 对象在模型和视图模型之间共享,您甚至不必担心同步问题。否则,这是同步它们的好时机。

    类似地,在 model 类中使用PropertyChanged 通常是个坏主意。对于初学者来说,听它一点也不好玩。相反,如果模型接收到新数据,则使用新数据向 VM 发出语义更清晰的事件。

    tldr;基本上,不要太担心保持模型和视图模型同步。很多时候,模型根本不会保留当前状态的副本!即使是这样,只要在视图模型准备好“提交”更改时更新它,并通过正常事件通知视图模型对模型的外部更改。

    【讨论】:

      猜你喜欢
      • 2012-05-06
      • 2021-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-21
      • 2020-09-20
      相关资源
      最近更新 更多