如何在UserControl上验证依赖属性?

3
我的视图模型实现了 IDataErrorInfo 接口,并包含一个经过验证的 Message 属性。
我创建了一个 UserControl,其中包含一个 Text DependencyProperty,该属性绑定到 Message。我的 UserControl 上有几个控件,它们绑定到 Text(从而显示 Message)。
如何在未直接绑定到 Message 的 UserControl 控件上显示验证错误?

这可以通过使UserControl实现IDataErrorInfo以及视图模型来实现。有更好的解决方案吗? - Coder1095
2个回答

2
经过一段时间的研究,我终于找到了一个解决方案,我认为我应该分享一下,以防其他人也会发现它的有用性:
基本上,我在我的Text DependencyProperty上添加了一个PropertyChangedCallback。在这个回调中,我获取Text和ViewModel属性之间的绑定,并检查其是否存在验证错误。如果找到ValidationError,我将遍历所有绑定到Text的UserControl中的控件,并使用Validation.MarkInvalid给它们的绑定相同的错误。
编辑: 如果我将下面的代码放在按钮单击事件处理程序中,则可以正常工作。但是,如果将代码放在Text的PropertyChangedCallback中,则什么都不会发生。有没有人有解决方案?
// 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);
}

你有没有解决问题?我遇到了非常类似的问题,让我感到非常沮丧。 - Dude0001

0
这是我想出的解决方案,它允许具有依赖属性的将其绑定到的视图模型的验证“包装”起来。
首先,我按照此帖子中的模式创建了所需的层次结构。
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>();
    }
}

应该足够了。验证在模型中完成,结果被拉到用户控件中。不需要重复。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接