带有MVVM和DataAnnotations的WPF,UserControl中的验证错误

tho*_*ill 6 c# validation wpf mvvm

我有一个UserControl,将在我们正在开发的应用程序中重用.我们正在使用基于MVVMLight的框架.

为简单起见,假设用户控件只包含一个文本框,并公开一个名为"Quantity"的依赖项属性.用户控件上的文本框数据绑定到依赖项属性"Quantity".

在视图上使用用户控件时,usercontrol的"Quantity"依赖项属性将数据绑定到ViewModel中的属性.(此ViewModel是我们通过MVVMLight ViewModelLocator查看的datacontext).

这一切都很棒!绑定工作,属性设置像我期望的那样.一切都很好,直到验证.

我们使用DataAnnotations来装饰我们的ViewModel属性.ViewModel包含INotifyDataErrorInfo的自定义实现.我们为大多数输入控件实现了自定义样式,以在控件周围显示红色边框,并在控件旁边显示一条消息,显示验证错误消息.所有这些在正常情况下都很有效(例如,View上的Textbox绑定到视图模型中的属性).

当我尝试使用此用户控件的相同方法时,我最终得到的是整个用户控件周围的红色边框,并且实际文本框上没有错误指示.似乎存在错误的事实正在UI中反映出来,但它只是没有让它成为我想要的控件.

我已经在stackoverflow上搜索了这个问题,对于那些带有解决方案的问题,似乎没有一个适用于我的情况.

我的第一个猜测是,因为实际的文本框直接绑定到依赖项属性本身而不是我的视图模型上的属性,所以没有正确通知生成的错误.有没有办法通过usercontrol传播viewmodel中生成的错误,然后传递到文本框?

你能给予的任何帮助或建议都会很棒,谢谢.

这是UserControl xaml.

<UserControl x:Class="SampleProject.UserControls.SampleControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d"  x:Name="sampleControl"
         d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="LayoutRoot" DataContext="{Binding ElementName=sampleControl}">
        <TextBox Text="{Binding Path=Quantity, ValidatesOnDataErrors=True}" Width="100" Height="30" />
</Grid>
</UserControl>
Run Code Online (Sandbox Code Playgroud)

UserControl代码背后.

public partial class SampleControl : UserControl
{
    public SampleControl()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty QuantityProperty = 
         DependencyProperty.Register("Quantity", typeof(int?), typeof(SampleControl), 
    new FrameworkPropertyMetadata{DefaultValue=null, BindsTwoWayByDefault = true});

    public int? Quantity
    {
        get { return (int?)GetValue(QuantityProperty); }
        set { SetValue(QuantityProperty, value); }
    }
}
Run Code Online (Sandbox Code Playgroud)

用于视图.

<userControls:SampleControl Grid.Row="1" Quantity="{Binding Path=Quantity, ValidatesOnDataErrors=True}" Height="60" Width="300"/>
Run Code Online (Sandbox Code Playgroud)

ViewModel属性.

[Required(ErrorMessage = "Is Required")]
[Range(5, 10, ErrorMessage = "Must be greater than 5")]
public int? Quantity
{
    get { return _quantity; }
    set { Set(() => Quantity, ref _quantity, value); }
}
private int? _quantity;
Run Code Online (Sandbox Code Playgroud)

(*注意,setter中的Set方法只是基本视图模型中的一个辅助方法,用于设置支持属性并为其引发PropertyChanged事件.)

She*_*dan 0

DataContext尝试从 中删除UserControl. 不用设置它,而是使用 aBind直接从TextBox到实际属性RelativeSource Binding

<TextBox Text="{Binding Quantity, RelativeSource={RelativeSource Mode=FindAncestor, 
    AncestorType={x:Type YourControlNamespace:SampleControl, 
    ValidatesOnDataErrors=True}}}" Width="100" Height="30" />
Run Code Online (Sandbox Code Playgroud)

更新>>>

如果做不到这一点,只要绑定到此属性的视图模型始终具有要绑定的同名属性,您就可以Binding通过父视图模型进行搜索,DataContext如下所示:

<TextBox Text="{Binding Quantity, RelativeSource={RelativeSource Mode=FindAncestor, 
    AncestorLevel=2, ValidatesOnDataErrors=True}}}" Width="100" Height="30" />
Run Code Online (Sandbox Code Playgroud)

您需要将 更改2为 具有正确的父元素数量,然后TextBox才能访问正确的属性。例如,使用级别意味着框架将尝试在s 父控件的父控件中2查找名为Quantityto的属性。尽管我相信像s 这样的“隐藏”元素不包含在父元素中,但实现这一点比较棘手。BindDataContextTextBoxAncestorLevelGrid