Wpf MVVM中的UI绑定验证

Wpf MVVM中的UI绑定验证,wpf,mvvm,validation,Wpf,Mvvm,Validation,我正在使用DataTemplates将一些代码转换为更合适的MVVM实现,并且在某些类型的UI验证方面遇到了问题 我对视图模型中的验证没有问题——IDataErrorInfo已经实现,一切都很好。我遇到的问题是UI绑定错误,他们可能会将字母放入绑定到int的文本框中 以前,我使用: System.Windows.Controls.Validation.AddErrorHandler(userControl, handler) 。。。并记录添加和删除的错误数,以了解表单的所有数据是否正常 但是

我正在使用DataTemplates将一些代码转换为更合适的MVVM实现,并且在某些类型的UI验证方面遇到了问题

我对视图模型中的验证没有问题——IDataErrorInfo已经实现,一切都很好。我遇到的问题是UI绑定错误,他们可能会将字母放入绑定到int的文本框中

以前,我使用:

System.Windows.Controls.Validation.AddErrorHandler(userControl, handler) 
。。。并记录添加和删除的错误数,以了解表单的所有数据是否正常

但是现在我正在做MVVM,我没有权限访问userControl来设置这个处理程序。所以我真的没有办法开始

是否有某种全局DataTemplateApplied事件处理程序可供我执行以下操作:

void OnDataTemplateApplied(object data, Control template)
{
  if (data is MyViewModelBase)
  {
    Validation.AddErrorHandler(template, handler);
  }
}
或者,我可以在引导程序中为外壳窗口调用AddErrorHandler一次,然后每次触发事件时,以某种方式找出哪个ViewModel为该特定控件供电


我知道有些人喜欢将所有VM字段设置为字符串,并在VM中进行大量类型转换——由于各种原因,这对于我们的系统来说是不现实的。

您可能对以下答案感兴趣: 主要思想正是您所说的订阅错误处理程序。据我所知,问题是您无法从ViewModel访问控件,但这并不难解决

在我正在进行的一个项目中,我从ViewModel中公开了两个方法:AddUIError和RemoveUIError。我在视图中创建了一个事件处理程序,在那里我将DataContext转换为ViewModel的类型,并根据发生的情况调用AddUIError或RemoveUIError。 我使用DataTemplates将视图与ViewModel关联,因此当应用该模板时,DataContext将自动设置为ViewModel。如果需要,可以将ViewModel存储在视图中的私有字段中,并在每次DataContext发生更改时更新引用

如果要在多个ViewModel中执行此操作,则可以将AddUIError和RemoveUIError两个方法都放在ViewModelBase之类的类中,并将ValidationError事件处理移动到一个行为中,并在每个视图中使用它

有关行为部分的详细信息: Behavior类是Expression Blend SDK的一部分,因此如果您希望按照这种方式操作,则需要它

例如,在不创建派生类的情况下,将一些公共功能附加到许多组件上的行为非常有用

首先,我们需要在名为ViewModelBase的类中定义AddUIError和RemoveUIError,该类当然是所有其他ViewModel的基类:

class ViewModelBase {
   public void AddUIError(...) {/* Details ommitted */ }
   public void RemoveUIError(...) {/* Details ommitted */ }
}
然后,通过子类化行为来创建行为。我们使用FrameworkElement作为模板参数,因此此行为可以附加到任何FrameworkElement或派生类实例:

class NotifyDataErrorsBehavior : Behavior<FrameworkElement>
{
    // Called when the the Behavior is attached
    protected override void OnAttached()
    {
        base.OnAttached();
        // Initialize the handler for the Validation Error Event
        _handler = new RoutedEventHandler(OnValidationRaised);
        // Add the handler to the event from the element which is attaching this behavior
        AssociatedObject.AddHandler(System.Windows.Controls.Validation.ErrorEvent, _handler);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        // Remove the event handler from the associated object
        AssociatedObject.RemoveHandler(System.Windows.Controls.Validation.ErrorEvent, _handler);
    }

    private RoutedEventHandler _handler = null;


    private void OnValidationRaised(object sender, RoutedEventArgs e)
    {
        var args = (System.Windows.Controls.ValidationErrorEventArgs)e;

        ViewModelBase viewModel = AssociatedObject.DataContext as ViewModelBase;
        if (viewModel != null)
        {
            // You can add only Exception validation errors if you want..

            if (args.Action == ValidationErrorEventAction.Added)
                viewModel.AddUIValidationError(...);
            else if (args.Action == ValidationErrorEventAction.Removed)
                viewModel.RemoveUIValidationError(...);
            else
                throw new NotSupportedException("ValidationErrorEventAction has changed");
        }
    }
}
最后,在XAML中使用它: 1.添加对NotifyDataErrorsBehavior所在命名空间的引用,以及对Expression Blend SDK中System.Windows.Interactive命名空间的引用:

<UserControl 
        ...
        xmlns:behavior="clr-namespace:MyApp.Behaviors"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        ...
>
二,。添加与UserControl内容相同级别的行为:

<i:Interaction.Behaviors>
    <behavior:NotifyDataErrorsBehavior/>
</i:Interaction.Behaviors>
例:


您可能对以下答案感兴趣: 主要思想正是您所说的订阅错误处理程序。据我所知,问题是您无法从ViewModel访问控件,但这并不难解决

在我正在进行的一个项目中,我从ViewModel中公开了两个方法:AddUIError和RemoveUIError。我在视图中创建了一个事件处理程序,在那里我将DataContext转换为ViewModel的类型,并根据发生的情况调用AddUIError或RemoveUIError。 我使用DataTemplates将视图与ViewModel关联,因此当应用该模板时,DataContext将自动设置为ViewModel。如果需要,可以将ViewModel存储在视图中的私有字段中,并在每次DataContext发生更改时更新引用

如果要在多个ViewModel中执行此操作,则可以将AddUIError和RemoveUIError两个方法都放在ViewModelBase之类的类中,并将ValidationError事件处理移动到一个行为中,并在每个视图中使用它

有关行为部分的详细信息: Behavior类是Expression Blend SDK的一部分,因此如果您希望按照这种方式操作,则需要它

例如,在不创建派生类的情况下,将一些公共功能附加到许多组件上的行为非常有用

首先,我们需要在名为ViewModelBase的类中定义AddUIError和RemoveUIError,该类当然是所有其他ViewModel的基类:

class ViewModelBase {
   public void AddUIError(...) {/* Details ommitted */ }
   public void RemoveUIError(...) {/* Details ommitted */ }
}
然后,通过子类化行为来创建行为。我们使用FrameworkElement作为模板参数,因此此行为可以附加到任何FrameworkElement或派生类实例:

class NotifyDataErrorsBehavior : Behavior<FrameworkElement>
{
    // Called when the the Behavior is attached
    protected override void OnAttached()
    {
        base.OnAttached();
        // Initialize the handler for the Validation Error Event
        _handler = new RoutedEventHandler(OnValidationRaised);
        // Add the handler to the event from the element which is attaching this behavior
        AssociatedObject.AddHandler(System.Windows.Controls.Validation.ErrorEvent, _handler);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        // Remove the event handler from the associated object
        AssociatedObject.RemoveHandler(System.Windows.Controls.Validation.ErrorEvent, _handler);
    }

    private RoutedEventHandler _handler = null;


    private void OnValidationRaised(object sender, RoutedEventArgs e)
    {
        var args = (System.Windows.Controls.ValidationErrorEventArgs)e;

        ViewModelBase viewModel = AssociatedObject.DataContext as ViewModelBase;
        if (viewModel != null)
        {
            // You can add only Exception validation errors if you want..

            if (args.Action == ValidationErrorEventAction.Added)
                viewModel.AddUIValidationError(...);
            else if (args.Action == ValidationErrorEventAction.Removed)
                viewModel.RemoveUIValidationError(...);
            else
                throw new NotSupportedException("ValidationErrorEventAction has changed");
        }
    }
}
最后,在XAML中使用它: 1.添加对NotifyDataErrorsBehavior所在命名空间的引用,以及对System.Windows的引用 .Expression Blend SDK中的交互性命名空间:

<UserControl 
        ...
        xmlns:behavior="clr-namespace:MyApp.Behaviors"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        ...
>
二,。添加与UserControl内容相同级别的行为:

<i:Interaction.Behaviors>
    <behavior:NotifyDataErrorsBehavior/>
</i:Interaction.Behaviors>
例:


关于将ValidationError事件处理移动到行为,您能提供更多的信息吗?行为在我现在的知识中有点像黑洞,但听起来这就是我想要的。我有几十个视图,并且正在寻找一段全局代码,而不是将样板文件复制并粘贴到我创建的每个视图中。Visual Studio Designer使将每个view UserControl的继承更改为公共基类有点麻烦,但这是我的备份计划。编辑此文章以添加该行为的示例。是的,我使用行为也是出于同样的原因。我不想把我所有的xaml文件都改成一个新的类,我发现这些行为非常容易使用,而且在这种情况下似乎工作得很好。你能提供一些关于将ValidationError事件处理移到行为的更多信息吗?行为在我现在的知识中有点像黑洞,但听起来这就是我想要的。我有几十个视图,并且正在寻找一段全局代码,而不是将样板文件复制并粘贴到我创建的每个视图中。Visual Studio Designer使将每个view UserControl的继承更改为公共基类有点麻烦,但这是我的备份计划。编辑此文章以添加该行为的示例。是的,我使用行为也是出于同样的原因。我不想把我所有的xaml文件都改成一个新的类,我发现这些行为非常容易使用,而且在这种情况下似乎工作得很好