繁体   English   中英

如何验证 UserControl 上的依赖项属性?

[英]How to validate a dependency property on a UserControl?

我的视图模型实现了IDataErrorInfo并包含一个经过验证的Message属性。

我创建了一个UserControl ,它的Text DependencyProperty绑定到Message 我的UserControl上有几个控件绑定到Text (因此显示Message )。

如何在我的UserControl中未直接绑定到Message的控件上显示验证错误?

一段时间后,我设法找到了一个我认为应该分享的解决方案,以防其他人发现它有用:

基本上我在我的Text DependencyProperty上添加了一个PropertyChangedCallback 在这个回调中,我获得了Text和视图模型上的属性之间的绑定,并检查它是否存在验证错误。 如果发现ValidationError ,我会检查UserControl中绑定到Text所有控件,并使用Validation.MarkInvalid为其绑定提供相同的错误。

编辑:

如果我将下面的代码放在按钮单击事件处理程序中,则复制这样的验证错误可以正常工作。 但是,如果代码位于TextPropertyChangedCallback ,则什么也不会发生。 有没有人有办法解决吗?

// 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>();
    }
}

那应该就够了。 验证在模型中完成,结果下拉到用户控件。 无需复制。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM