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