Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/heroku/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WPF MVVM-如何检测视图是否为;肮脏的;_C#_Wpf_Mvvm - Fatal编程技术网

C# WPF MVVM-如何检测视图是否为;肮脏的;

C# WPF MVVM-如何检测视图是否为;肮脏的;,c#,wpf,mvvm,C#,Wpf,Mvvm,如果视图中的任何字段已更改/更新,我当前需要通知我的应用程序用户 例如,如果用户更改视图上的日期字段,然后尝试关闭视图,应用程序将显示一条消息,要求用户继续并丢失更改或取消,以便他们可以单击“保存”按钮 问题是:如何检测视图中任何数据字段的更改 希望这比您之前提到的更有意义,在MVVM中,视图绑定到视图模型,而视图模型又绑定到模型 视图不能脏,因为它的更改会立即反映到视图模型中 如果希望更改仅在“确定”或“接受”时应用于模型, 将视图绑定到不将更改应用于模型的视图模型, 直到执行ApplyCom

如果视图中的任何字段已更改/更新,我当前需要通知我的应用程序用户

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

问题是:如何检测视图中任何数据字段的更改


希望这比您之前提到的更有意义,

在MVVM中,视图绑定到视图模型,而视图模型又绑定到模型

视图不能脏,因为它的更改会立即反映到视图模型中

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

(视图绑定到的命令由视图模型实现。)

示例-虚拟机:

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));
    }
}
示例-视图:

    <Label Content="{Binding MyText}" />

    <!-- You can translate the events to commands by using a suitable framework -->
    <!-- or use code behind to update a new dependency property as in this example -->
    <TextBox
        LostFocus="TextBox_LostFocus"
        GotFocus="TextBox_GotFocus"
        Text="{Binding Path=MyTextTemp, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
        />

示例-视图-代码隐藏:

    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

    /// <summary>
    /// Gets or Sets IsTextBoxFocused
    /// </summary>
    public bool IsTextBoxFocused
    {
        get
        {
            return (bool)this.GetValue(IsTextBoxFocusedProperty);
        }
        set
        {
            this.SetValue(IsTextBoxFocusedProperty, value);
        }
    }

    /// <summary>
    /// The backing DependencyProperty behind IsTextBoxFocused
    /// </summary>
    public static readonly DependencyProperty IsTextBoxFocusedProperty = DependencyProperty.Register(
      "IsTextBoxFocused", typeof(bool), typeof(MainWindow), new PropertyMetadata(default(bool)));

    #endregion
public主窗口()
{
初始化组件();
设置绑定(IsTextBoxFocusedProperty,
新装订
{
路径=新属性路径(“IsMyTextBeingEdited”),
模式=BindingMode.OneWayToSource,
});
}
私有void文本框\u LostFocus(对象发送方,RoutedEventArgs e)
{
IsTextBoxFocused=false;
}
私有无效文本框_GotFocus(对象发送方,路由目标)
{
IsTextBoxFocused=true;
}
#以区域为中心
/// 
///获取或设置IsTextBoxFocused
/// 
以公众为中心
{
得到
{
返回(bool)this.GetValue(IsTextBoxFocusedProperty);
}
设置
{
此.SetValue(IsTextBoxFocusedProperty,value);
}
}
/// 
///IsTextBoxFocused背后的支持从属属性
/// 
公共静态只读DependencyProperty IsTextBoxFocusedProperty=DependencyProperty.Register(
“IsTextBoxFocused”、typeof(bool)、typeof(main窗口)、新属性元数据(默认值(bool));
#端区

您可以采取的一种方法是利用和接口

如果创建了视图模型继承自(
ViewModelBase
)的抽象基类,该基类实现了
IChangeTracking
INotifyPropertyChanged
接口,则可以将视图模型基附加到属性更改通知(实际上表示视图模型已被修改)并将属性设置为true,以指示视图模型为“脏”

使用这种方法,您依赖于通过数据绑定发出的属性更改通知来跟踪更改,并在进行任何提交后重置更改跟踪

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

简单示例:

/// <summary>
/// Provides a base class for objects that support property change notification 
/// and querying for changes and resetting of the changed status.
/// </summary>
public abstract class ViewModelBase : IChangeTracking, INotifyPropertyChanged
{
    //========================================================
    //  Constructors
    //========================================================
    #region ViewModelBase()
    /// <summary>
    /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
    /// </summary>
    protected ViewModelBase()
    {
        this.PropertyChanged += new PropertyChangedEventHandler(OnNotifiedOfPropertyChanged);
    }
    #endregion

    //========================================================
    //  Private Methods
    //========================================================
    #region OnNotifiedOfPropertyChanged(object sender, PropertyChangedEventArgs e)
    /// <summary>
    /// Handles the <see cref="INotifyPropertyChanged.PropertyChanged"/> event for this object.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="PropertyChangedEventArgs"/> that contains the event data.</param>
    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
    /// <summary>
    /// Gets the object's changed status.
    /// </summary>
    /// <value>
    /// <see langword="true"/> if the object’s content has changed since the last call to <see cref="AcceptChanges()"/>; otherwise, <see langword="false"/>. 
    /// The initial value is <see langword="false"/>.
    /// </value>
    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()
    /// <summary>
    /// Resets the object’s state to unchanged by accepting the modifications.
    /// </summary>
    public void AcceptChanges()
    {
        this.IsChanged = false;
    }
    #endregion

    //========================================================
    //  INotifyPropertyChanged Implementation
    //========================================================
    #region PropertyChanged
    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion

    #region OnPropertyChanged(PropertyChangedEventArgs e)
    /// <summary>
    /// Raises the <see cref="INotifyPropertyChanged.PropertyChanged"/> event.
    /// </summary>
    /// <param name="e">A <see cref="PropertyChangedEventArgs"/> that provides data for the event.</param>
    protected void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
    #endregion

    #region OnPropertyChanged(string propertyName)
    /// <summary>
    /// Raises the <see cref="INotifyPropertyChanged.PropertyChanged"/> event for the specified <paramref name="propertyName"/>.
    /// </summary>
    /// <param name="propertyName">The <see cref="MemberInfo.Name"/> of the property whose value has changed.</param>
    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }
    #endregion

    #region OnPropertyChanged(params string[] propertyNames)
    /// <summary>
    /// Raises the <see cref="INotifyPropertyChanged.PropertyChanged"/> event for the specified <paramref name="propertyNames"/>.
    /// </summary>
    /// <param name="propertyNames">An <see cref="Array"/> of <see cref="String"/> objects that contains the names of the properties whose values have changed.</param>
    /// <exception cref="ArgumentNullException">The <paramref name="propertyNames"/> is a <see langword="null"/> reference (Nothing in Visual Basic).</exception>
    protected void OnPropertyChanged(params string[] propertyNames)
    {
        if (propertyNames == null)
        {
            throw new ArgumentNullException("propertyNames");
        }

        foreach (var propertyName in propertyNames)
        {
            this.OnPropertyChanged(propertyName);
        }
    }
    #endregion
}
//
///为支持属性更改通知的对象提供基类
///以及查询更改和重置更改状态。
/// 
公共抽象类ViewModelBase:IChangeTracking,INotifyPropertyChanged
{
//========================================================
//建设者
//========================================================
#区域ViewModelBase()
/// 
///初始化类的新实例。
/// 
受保护的ViewModelBase()
{
this.PropertyChanged+=新的PropertyChangedEventHandler(OnNotifiedDofPropertyChanged);
}
#端区
//========================================================
//私有方法
//========================================================
#区域OnNotifiedOffPropertyChanged(对象发送方,PropertyChangedEventArgs e)
/// 
///处理此对象的事件。
/// 
///事件的来源。
///包含事件数据的。
私有void onNotifiedOffPropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
如果(e!=null&&!String.Equals(e.PropertyName,“IsChanged”,StringComparison.Ordinal))
{
this.IsChanged=true;
}
}
#端区
//========================================================
//IChangeTracking实现
//========================================================
#地区变更
/// 
///获取对象的已更改状态。
/// 
/// 
///如果自上次调用以来对象的内容已更改,则为。
///初始值为。
/// 
公共场所被改变了
{
得到
{
锁定(_notifyingObjectIsChangedSyncRoot)
{
返回-通知对象已更改;
}
}
保护集
{
锁定(_notifyingObjectIsChangedSyncRoot)
{
如果(!Boolean.Equals(_notifyingObjectIsChanged,value))
{
_notifyingObjectIsChanged=值;
本条关于不动产变更(“IsChanged”);
}
}
}
}
私人机构通知目标;
私有只读对象_notifyingObjectIsChangedSyncRoot=新对象();
#端区
#地区变化()
/// 
///通过接受修改将对象的状态重置为“未更改”。
/// 
公共更改()
{
this.IsChanged=false;
}
#端区
//========================================================
//INotifyPropertyChanged实现
//========================================================
#注册