我正在使用DataTemplate
将一些代码转换为更合适的MVVM实现,并且遇到某些类型的UI验证问题。
我在View模型中验证没有问题 - 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);
}
}
或者,也许我可以在引导程序中为外部Shell窗口调用AddErrorHandler
一次,然后每次触发事件时都会弄清楚哪个ViewModel正在为该特定控件供电?
我知道有些人喜欢在VM中创建所有VM字段字符串并进行大量的类型转换 - 由于各种原因,这对我们的系统来说并不现实。
答案 0 :(得分:3)
您可能对此答案感兴趣: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并在每个视图中使用它。
有关行为部分的更多信息: 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>