我的视图模型实现IDataErrorInfo
并包含一个Message
经过验证的属性。
我创建了UserControl
一个Text
DependencyProperty
绑定到Message
. my 上有几个控件UserControl
绑定到Text
(因此显示Message
)。
如何在我UserControl
的未Message
直接绑定的控件上显示验证错误?
我的视图模型实现IDataErrorInfo
并包含一个Message
经过验证的属性。
我创建了UserControl
一个Text
DependencyProperty
绑定到Message
. my 上有几个控件UserControl
绑定到Text
(因此显示Message
)。
如何在我UserControl
的未Message
直接绑定的控件上显示验证错误?
很长一段时间后,我设法找到了一个我认为应该分享的解决方案,以防其他人发现它有用:
基本上我PropertyChangedCallback
在我的Text
DependencyProperty
. 在这个回调中,我获得了Text
视图模型上的属性和属性之间的绑定,并检查它是否存在验证错误。如果ValidationError
找到 a ,我会检查UserControl
绑定到的所有控件Text
,并使用Validation.MarkInvalid
.
编辑:
如果我将下面的代码放在按钮单击事件处理程序中,则复制这样的验证错误可以正常工作。但是,如果代码在PropertyChangedCallback
forText
那么什么都不会发生。有没有人有办法解决吗?
// Get the binding from the Text property to the view model.
BindingExpression textBindingExpression = BindingOperations.GetBindingExpression(this,
MyUserControl.TextProperty);
// If there is a validation error, then give it to the control bindings.
if (textBindingExpression != null && textBindingExpression.ValidationError != null) {
Validation.MarkInvalid(this.MyTextBox.GetBindingExpression(TextBox.TextProperty),
textBindingExpression.ValidationError);
Validation.MarkInvalid(this.MyTextBlock.GetBindingExpression(TextBlock.TextProperty),
textBindingExpression.ValidationError);
}
这是我提出的解决方案,它允许UserControl
具有依赖属性的“包装”来自它绑定的视图模型的验证。
首先,我按照这篇文章中的模式创建了所需的DataContext
层次结构。
XAML:
<!-- Some boilerplate attributes snipped -->
<UserControl x:Class="App.Views.UserControls.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:App.Views.UserControls"
Validation.ErrorTemplate="{x:Null}">
<Grid x:Name="LayoutRoot"
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MyUserControl}}">
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</UserControl>
这样,控件的 DataContext 就是从父级继承的视图模型,也就是验证完成的地方。然后在控件的根子元素上将其重写为控件本身,这允许绑定到代码隐藏中的依赖属性。另请注意,控件ErrorTemplate
已被取消 - 这是为了防止出现默认的红色框。
现在可以很简单地从控件的代码中访问继承的视图模型:
private INotifyDataErrorInfo ViewModelErrors => DataContext as INotifyDataErrorInfo;
现在INotifyDataErrorInfo
在用户控件中实现并包装视图模型:
public bool HasErrors => ViewModelErrors.HasErrors;
public IEnumerable GetErrors(string propertyName)
{
return ViewModelErrors.GetErrors(propertyName);
}
当您需要知道您的控件依赖属性绑定到哪个模型属性时,棘手的部分就来了。如果您可以按名称查找已注册的依赖项属性并询问绑定,这将更容易,但我找不到没有反射的方法。因此,我使用了PropertyChangedCallback
依赖属性来手动构建映射列表。回调的参数包含所有必需的信息。
// Maps User Control properties to their View Model properties.
private readonly Dictionary<string, string> _propertyMappings = new Dictionary<string, string>();
// This should work for any property.
private static void OnDependencyPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var userControl = (MyUserControl)d;
var dependencyPropertyName = e.Property.Name;
// Create this mapping one time only.
if (!userControl._propertyMappings.ContainsKey(dependencyPropertyName))
{
// Get the binding from the property to the view model.
var binding = BindingOperations.GetBindingExpression(d, e.Property);
if (binding != null)
{
// Create a mapping of user control property to view model property.
// This will let us look up the error from the view model.
var boundPropertyName = binding.ResolvedSourcePropertyName;
userControl._propertyMappings[dependencyPropertyName] = boundPropertyName;
}
}
}
然后将其合并到GetErrors
:
public IEnumerable GetErrors(string propertyName)
{
if (ViewModelErrors != null && _propertyMappings.ContainsKey(propertyName))
{
return ViewModelErrors.GetErrors(_propertyMappings[propertyName]);
}
else
{
return Enumerable.Empty<string>();
}
}
这应该足够了。验证在模型中完成,并将结果下拉到用户控件。无需复制。