2

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

我在视图模型中的验证没有任何问题——IDataErrorInfo已实现并且一切都很好。我遇到的问题是 UI 绑定错误,它们可能会将字母TextBox绑定到 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在外部 Shell 窗口的引导程序中调用一次,然后每次触发事件时以某种方式确定哪个 ViewModel 正在为该特定控件提供动力?

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

4

1 回答 1

4

您可能对此答案感兴趣:https ://stackoverflow.com/a/13335971/1094526 主要思想正是您所说的(订阅错误处理程序)。据我了解,问题是您无法从 ViewModel 访问控件,但这并不难解决

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

如果这将在多个 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 */ }
}

然后,通过子类化 Behavior 创建一个 Behavior。我们使用 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.Interactivity 命名空间的引用(来自 Expression Blend SDK):

<UserControl 
        ...
        xmlns:behavior="clr-namespace:MyApp.Behaviors"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        ...
>

2. 添加行为(与您的 UserControl 的内容处于同一级别:

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

前任:

<UserControl 
        ...
        xmlns:behavior="clr-namespace:MyApp.Behaviors"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        ...
>
    <i:Interaction.Behaviors>
       <behavior:NotifyDataErrorsBehavior/>
    </i:Interaction.Behaviors>
    <Grid>
       ...
    </Grid>
</UserControl>
于 2012-11-28T02:55:20.193 回答