在WPF中捕获对绑定XML的更改的简单位置

在WPF中捕获对绑定XML的更改的简单位置,wpf,xml,mvvm,binding,Wpf,Xml,Mvvm,Binding,我已经将一个XML结构绑定到WPF表单,并将其全部设置为只需按一个按钮即可将更改提交到数据库 我正在寻找一种方法来检测用户是否更改了模型中的任何内容,以知道如何启用“保存”按钮,将表单设置为“脏”等。我更喜欢数据方面的内容,因为XAML视图绑定干净,基本上没有逻辑,因此ViewModel相当缺乏实质性,所以我想指出模型处于需要提交到数据库的状态 我考虑过的一个“黑客”是在加载时捕获XML,然后与DataContext的副本进行简单比较,以确定表单是否脏。如果XML包含在可以使用的XDocumen

我已经将一个XML结构绑定到WPF表单,并将其全部设置为只需按一个按钮即可将更改提交到数据库

我正在寻找一种方法来检测用户是否更改了模型中的任何内容,以知道如何启用“保存”按钮,将表单设置为“脏”等。我更喜欢数据方面的内容,因为XAML视图绑定干净,基本上没有逻辑,因此ViewModel相当缺乏实质性,所以我想指出模型处于需要提交到数据库的状态


我考虑过的一个“黑客”是在加载时捕获XML,然后与DataContext的副本进行简单比较,以确定表单是否脏。

如果XML包含在可以使用的XDocument中


XmlDocument类具有您可以注册的属性。

创建如下视图模型:

public class XmlDataViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

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

    private XmlDocument _XmlData;
    public XmlDocument XmlData
    {
        get { return _XmlData; }
        set
        {
            if (value != _XmlData)
            {
               if (_XmlData != null)
               {
                  // it's not likely you'll need this, but why take chances?
                  _XmlData.NodeChanged -= XmlData_NodeChanged;
               }
               _XmlData = value;
               _XmlData.NodeChanged += XmlData_NodeChanged;
               CanSave = false;
               Provider = new XmlDataProvider { Document = XmlData };
               OnPropertyChanged("Provider");
            }
        }
    }

    private void XmlData_NodeChanged(object sender, XmlNodeChangedEventArgs e)
    {
        CanSave = true;
    }

    public XmlDataProvider Provider { get; private set; }

    public RelayCommand SaveCommand
    {
        get
        {
            return new RelayCommand(x => Save(), x => CanSave);
        }
    }

    private bool CanSave { get; set; }

    private void Save() { }
}
<StackPanel DataContext="{Binding Provider}">
   <TextBox Text="{Binding XPath=/Data/Field1}"/>
   <TextBox Text="{Binding XPath=/Data/Field2}"/>
</StackPanel>
将视图的
DataContext
设置为此对象的实例,并像这样绑定控件:

public class XmlDataViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

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

    private XmlDocument _XmlData;
    public XmlDocument XmlData
    {
        get { return _XmlData; }
        set
        {
            if (value != _XmlData)
            {
               if (_XmlData != null)
               {
                  // it's not likely you'll need this, but why take chances?
                  _XmlData.NodeChanged -= XmlData_NodeChanged;
               }
               _XmlData = value;
               _XmlData.NodeChanged += XmlData_NodeChanged;
               CanSave = false;
               Provider = new XmlDataProvider { Document = XmlData };
               OnPropertyChanged("Provider");
            }
        }
    }

    private void XmlData_NodeChanged(object sender, XmlNodeChangedEventArgs e)
    {
        CanSave = true;
    }

    public XmlDataProvider Provider { get; private set; }

    public RelayCommand SaveCommand
    {
        get
        {
            return new RelayCommand(x => Save(), x => CanSave);
        }
    }

    private bool CanSave { get; set; }

    private void Save() { }
}
<StackPanel DataContext="{Binding Provider}">
   <TextBox Text="{Binding XPath=/Data/Field1}"/>
   <TextBox Text="{Binding XPath=/Data/Field2}"/>
</StackPanel>

将按钮(或任何内容)绑定到
SaveCommand

当您将
XmlData
属性设置为
XmlDocument
(您可以在绑定到
XmlDataViewModel
之前或之后执行此操作)时,它将创建一个新的
XmlDataProvider
并引发
属性更改
,因此视图的控件将全部刷新,数据将按照您的预期被推出。对绑定控件的更改将引发
NodeChanged
,这将设置
CanSave
,从而启用
SaveCommand

如果随后将
XmlData
设置为不同的
XmlDocument
,则绑定的控件将刷新,并且
SaveCommand
将被禁用,直到用户更改新
XmlDocument
中的数据。并且之前的
XmlDocument
NodeChanged
事件将被取消注册,以便可以释放该对象


我得说,这是一个非常复杂的行为,因为实际上只有很少的代码。谢谢你提出这个问题。

看起来就是这样。当我附加该事件时,您知道吗,因为目前似乎找不到在绑定后添加该事件的方法,例如,当表单最初加载时,该事件多次激发。我尝试了.Loaded(它是一个wpf树视图),但没有成功。通过创建一个特定的调用来加载XDocument,然后添加EventHandler,而不是直接从start/constructor加载xml。还是我遗漏了什么?不@Erno-我想我是那个遗漏了什么的人。在我看来,有东西在完成绘制之前触发了XMLNodeChanged事件。无法理解如何/为什么,但必须是一个绑定的东西。谢谢@Robert-我已经实现了这一点,而且它似乎工作得很好,尽管我的XAML视图中的某些东西必须在它绘制XML时更改我的XML,因为它在绘制时触发XMLNodeChanged事件。我认为这也是@Erno回答的问题。我尝试了他的方法,得到了类似的结果;到XmlData_NodeChanged事件处理程序,以便通知UI CanSave已更改并知道要更新,当然,我必须跟踪RelayCommand的实现(很简单,但如果有人需要的话。最终解决了问题。当绑定的组合框绑定到ItemSource中不存在的字段时,NodeChanged正在启动。在eventhandler中捕获并忽略该条件完成了此解决方案,非常感谢。您不必在
CanSave
上实现更改通知。
PropertyChanged
事件与启用/禁用命令无关。查看
CommandManager.RequerySuggested
ICommand.CanExecuteChanged