如何在 ViewModel 更改后正确更新模型?
How to correctly update the Model after the ViewModel has changed?
假设我们有一个 模型 (class Model
) 具有以下 属性.
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 的 class 的主要部分。
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 的 class 的主要部分:
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 的集合 属性 InputFileStr
中添加以下调用:
m_Model.InputFileName = value;
但是,这种更新模型的解决方案会导致明显的意外效果:
- 用户修改了视图。
- 自动修改 ViewModel。
- ViewModel 更新模型 (
m_Model.InputFileName = value;
)。
- 模型已更新...
- ...所以它通知 ViewModel 有关更改
以上行为是否正确?我希望如果 ViewModel 更新模型,则模型不必将相同的更改重新通知 ViewModel ... 作为替代解决方案,我想我会向模型添加 Update
方法:此方法应在不使用模型属性的情况下更新模型。
public void Update(string inputFileName) // this method does not notifies the ViewModel
{
m_InputFileName = inputFileName;
}
这个替代解决方案是正确的解决方案还是有更好的解决方案?
根据您的模型,您通常只需调用 "Save" 方法或类似方法。大多数模型(例如,数据库)不会 need/want 实时向它们提供所有更改。
所以一般来说,流程是:
- 用户调用 "save" 操作
- 视图模型将此作为命令接收
- 视图模型使用新数据对模型调用 "save" 操作
如果您的 DTO 对象在模型和视图模型之间共享,您甚至不需要担心同步问题。否则,这是同步它们的好时机。
类似地,在 模型 class 中使用 PropertyChanged
通常不是一个好主意。对于初学者来说,听起来一点也不有趣。相反,如果模型接收到新数据,则使用新数据向 VM 引发语义更清晰的事件。
tldr;基本上,不必太担心保持模型和视图模型同步。通常,模型根本不会保留当前状态的副本!即使是这样,也只需在视图模型准备好 "commit" 更改时更新它,并通过正常事件通知视图模型对模型的外部更改。
假设我们有一个 模型 (class Model
) 具有以下 属性.
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 的 class 的主要部分。
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 的 class 的主要部分:
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 的集合 属性 InputFileStr
中添加以下调用:
m_Model.InputFileName = value;
但是,这种更新模型的解决方案会导致明显的意外效果:
- 用户修改了视图。
- 自动修改 ViewModel。
- ViewModel 更新模型 (
m_Model.InputFileName = value;
)。 - 模型已更新...
- ...所以它通知 ViewModel 有关更改
以上行为是否正确?我希望如果 ViewModel 更新模型,则模型不必将相同的更改重新通知 ViewModel ... 作为替代解决方案,我想我会向模型添加 Update
方法:此方法应在不使用模型属性的情况下更新模型。
public void Update(string inputFileName) // this method does not notifies the ViewModel
{
m_InputFileName = inputFileName;
}
这个替代解决方案是正确的解决方案还是有更好的解决方案?
根据您的模型,您通常只需调用 "Save" 方法或类似方法。大多数模型(例如,数据库)不会 need/want 实时向它们提供所有更改。
所以一般来说,流程是:
- 用户调用 "save" 操作
- 视图模型将此作为命令接收
- 视图模型使用新数据对模型调用 "save" 操作
如果您的 DTO 对象在模型和视图模型之间共享,您甚至不需要担心同步问题。否则,这是同步它们的好时机。
类似地,在 模型 class 中使用 PropertyChanged
通常不是一个好主意。对于初学者来说,听起来一点也不有趣。相反,如果模型接收到新数据,则使用新数据向 VM 引发语义更清晰的事件。
tldr;基本上,不必太担心保持模型和视图模型同步。通常,模型根本不会保留当前状态的副本!即使是这样,也只需在视图模型准备好 "commit" 更改时更新它,并通过正常事件通知视图模型对模型的外部更改。