WPF MVVM – 如何检测View是否“脏”

我目前要求通知我的应用程序用户是否在View上更改/更新了任何字段。

例如,如果用户更改了视图上的日期字段,然后尝试关闭视图,则应用程序将显示一条消息,要求用户继续并丢失更改或取消,以便他们可以单击“保存”按钮。

问题是:如何检测View中的任何数据字段?

希望这是有道理的,比你提前,问候,

您可以采用的一种方法是利用IChangeTracking和INotifyPropertyChanged接口。

如果您创建了一个抽象基类,您的视图模型inheritance自(ViewModelBase)实现IChangeTracking和INotifyPropertyChanged接口,您可以将视图模型基础附加到属性更改通知(实际上表明视图模型已被修改)和这会将IsChanged属性设置为true以指示视图模型为“脏”。

使用此方法,您依靠通过数据绑定的属性更改通知来跟踪更改,并在任何提交后重置更改跟踪。

在您描述的情况下,您可以处理视图的Unloaded或Closing事件以检查DataContext ; 如果DataContext实现IChangeTracking,您可以使用IsChanged属性来确定是否已进行任何未接受的更改。

简单的例子:

///  /// Provides a base class for objects that support property change notification /// and querying for changes and resetting of the changed status. ///  public abstract class ViewModelBase : IChangeTracking, INotifyPropertyChanged { //======================================================== // Constructors //======================================================== #region ViewModelBase() ///  /// Initializes a new instance of the  class. ///  protected ViewModelBase() { this.PropertyChanged += new PropertyChangedEventHandler(OnNotifiedOfPropertyChanged); } #endregion //======================================================== // Private Methods //======================================================== #region OnNotifiedOfPropertyChanged(object sender, PropertyChangedEventArgs e) ///  /// Handles the  event for this object. ///  /// The source of the event. /// A  that contains the event data. private void OnNotifiedOfPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e != null && !String.Equals(e.PropertyName, "IsChanged", StringComparison.Ordinal)) { this.IsChanged = true; } } #endregion //======================================================== // IChangeTracking Implementation //======================================================== #region IsChanged ///  /// Gets the object's changed status. ///  ///  ///  if the object's content has changed since the last call to ; otherwise, . /// The initial value is . ///  public bool IsChanged { get { lock (_notifyingObjectIsChangedSyncRoot) { return _notifyingObjectIsChanged; } } protected set { lock (_notifyingObjectIsChangedSyncRoot) { if (!Boolean.Equals(_notifyingObjectIsChanged, value)) { _notifyingObjectIsChanged = value; this.OnPropertyChanged("IsChanged"); } } } } private bool _notifyingObjectIsChanged; private readonly object _notifyingObjectIsChangedSyncRoot = new Object(); #endregion #region AcceptChanges() ///  /// Resets the object's state to unchanged by accepting the modifications. ///  public void AcceptChanges() { this.IsChanged = false; } #endregion //======================================================== // INotifyPropertyChanged Implementation //======================================================== #region PropertyChanged ///  /// Occurs when a property value changes. ///  public event PropertyChangedEventHandler PropertyChanged; #endregion #region OnPropertyChanged(PropertyChangedEventArgs e) ///  /// Raises the  event. ///  /// A  that provides data for the event. protected void OnPropertyChanged(PropertyChangedEventArgs e) { var handler = this.PropertyChanged; if (handler != null) { handler(this, e); } } #endregion #region OnPropertyChanged(string propertyName) ///  /// Raises the  event for the specified . ///  /// The  of the property whose value has changed. protected void OnPropertyChanged(string propertyName) { this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } #endregion #region OnPropertyChanged(params string[] propertyNames) ///  /// Raises the  event for the specified . ///  /// An  of  objects that contains the names of the properties whose values have changed. /// The  is a  reference (Nothing in Visual Basic). protected void OnPropertyChanged(params string[] propertyNames) { if (propertyNames == null) { throw new ArgumentNullException("propertyNames"); } foreach (var propertyName in propertyNames) { this.OnPropertyChanged(propertyName); } } #endregion } 

在MVVM中,View绑定到View-Model,而View-Model又绑定到Model。

视图不能脏,因为它的更改会立即反映到View-Model。

如果您希望仅在“确定”或“接受”时将更改应用于模型,
将视图绑定到不对模型应用更改的视图模型,
直到执行ApplyCommand或AcceptCommand(您定义并实现)。

(View绑定到的命令由View-Model实现。)

示例 – VM:

 public class MyVM : INotifyPropertyChanged { public string MyText { get { return _MyText; } set { if (value == _MyText) return; _MyText = value; NotifyPropertyChanged("MyText"); } } private string _MyText; public string MyTextTemp { get { return _MyTextTemp; } set { if (value == _MyTextTemp) return; _MyTextTemp = value; NotifyPropertyChanged("MyTextTemp"); NotifyPropertyChanged("IsTextDirty"); } } private string _MyTextTemp; public bool IsTextDirty { get { return MyText != MyTextTemp; } } public bool IsMyTextBeingEdited { get { return _IsMyTextBeingEdited; } set { if (value == _IsMyTextBeingEdited) return; _IsMyTextBeingEdited = value; if (!value) { MyText = MyTextTemp; } NotifyPropertyChanged("IsMyTextBeingEdited"); } } private bool _IsMyTextBeingEdited; public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } 

示例 – 查看:

      

示例 – 视图 – 后面的代码:

  public MainWindow() { InitializeComponent(); SetBinding(IsTextBoxFocusedProperty, new Binding { Path = new PropertyPath("IsMyTextBeingEdited"), Mode = BindingMode.OneWayToSource, }); } private void TextBox_LostFocus(object sender, RoutedEventArgs e) { IsTextBoxFocused = false; } private void TextBox_GotFocus(object sender, RoutedEventArgs e) { IsTextBoxFocused = true; } #region IsTextBoxFocused ///  /// Gets or Sets IsTextBoxFocused ///  public bool IsTextBoxFocused { get { return (bool)this.GetValue(IsTextBoxFocusedProperty); } set { this.SetValue(IsTextBoxFocusedProperty, value); } } ///  /// The backing DependencyProperty behind IsTextBoxFocused ///  public static readonly DependencyProperty IsTextBoxFocusedProperty = DependencyProperty.Register( "IsTextBoxFocused", typeof(bool), typeof(MainWindow), new PropertyMetadata(default(bool))); #endregion