如何将实现 IDataErrorInfo 的视图模型绑定到 UserControl 并传播验证错误?

发布于 2024-11-04 00:40:36 字数 3449 浏览 0 评论 0原文

我编写了一个 UserControl,它公开了一些依赖属性来修改控件的布局(即,将其视为通用类型安全编辑器控件 - 所以我的数据类型是日期(通过日期选择器编辑)、枚举(编辑通过组合框)和数字(通过文本框编辑)我还公开了 3 个编辑器控件的值作为依赖属性,以便

用户控件中的控件都绑定到公开的依赖属性 。获取它们的值(必要时使用适当的转换器)。

该控件形成绑定到视图模型的较大 UI 的一小部分 - 要编辑的值、自定义控件的数据类型标志和可能的有效值列表都已绑定。 我的问题是这样的:我在视图模型上

实现了 IDataErrorInfo 并将自定义用户控件中的控件绑定设置为具有 ValidatesOnDataErrors=True, NotifyOnValidationError=True ,但验证是不显示。

这是我的用户控件的 XAML(除了依赖属性声明之外没有其他代码隐藏逻辑):

<UserControl.Resources>
    <!-- Converters -->
    <local:ValidationBooleanToImageConverter x:Key="ValidationBooleanToImageConverter"/>
    <local:ValidationErrorToStringConverter x:Key="ValidationErrorToStringConverter"/>
    <local:DataTypeToVisibilityConverter DataType="Date" x:Key="DateDataTypeToVisibilityConverter"/>
    <local:DataTypeToVisibilityConverter DataType="Numeric" x:Key="NumericDataTypeToVisibilityConverter"/>
    <local:DataTypeToVisibilityConverter DataType="Enumeration" x:Key="EnumerationDataTypeToVisibilityConverter"/>
    <local:DateToStringConverter x:Key="DateToStringConverter"/>
    <!-- Styles -->
    <Style TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors).CurrentItem, Converter={StaticResource ResourceKey=ValidationErrorToStringConverter}}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</UserControl.Resources>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition Width="16"/>
    </Grid.ColumnDefinitions>
    <toolkit:DatePicker Height="25" Margin="0,0,5,0" Visibility="{Binding Path=DataType, ElementName=UserControl, Converter={StaticResource ResourceKey=DateDataTypeToVisibilityConverter}}" SelectedDate="{Binding Path=Value, ElementName=UserControl, Converter={StaticResource ResourceKey=DateToStringConverter}}"/>
    <ComboBox Height="25" Margin="0,0,5,0" Visibility="{Binding Path=DataType, ElementName=UserControl, Converter={StaticResource ResourceKey=EnumerationDataTypeToVisibilityConverter}}" SelectedItem="{Binding Path=Value, ElementName=UserControl, Mode=TwoWay}" ItemsSource="{Binding Path=Items, ElementName=UserControl}" />
    <TextBox x:Name="_txtValue" Height="25" Margin="0,0,5,0" Visibility="{Binding Path=DataType, ElementName=UserControl, Converter={StaticResource ResourceKey=NumericDataTypeToVisibilityConverter}}" Text="{Binding Path=Value, ElementName=UserControl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"/>
    <Image Grid.Column="2" Grid.Row="0" Source="{Binding ElementName=_txtValue, Path=(Validation.HasError), Converter={StaticResource ResourceKey=ValidationBooleanToImageConverter} }" ToolTip="{Binding ElementName=_txtValue, Path=ToolTip}" Width="16" Height="16"/>
</Grid>

...并且来自较大视图中的用户控件引用是...

<control:ValueEditorControl DataType="{Binding Path=ContextualSelectedTagDataType}" Items="{Binding Path=ContextualSelectedTagItems}" Value="{Binding Path=ContextualSelectedTagDataObjectValue, Mode=TwoWay}" Height="25" VerticalAlignment="Top"/>

任何人都可以为我指出正确的方向吗?

I've written a UserControl that exposes a few dependency properties to modify the layout of the control (i.e. think of it as a generic type-safe editor control - so my data types are dates (edited through a date picker), enumerations (edited through a combobox) and a numeric (edited through a textbox). I've also exposed the value of the 3 editor controls as a dependency property so that it can be databound.

The controls within the usercontrol all bind to the exposed dependency properties to get their values (with appropriate converters where necessary).

This control forms a tiny piece of a larger UI which binds to a viewmodel - the value to be edited, the datatype flag for the custom control and a list of possible valid values are all bound to an object in the viewmodel.

My problem is this: I've implemented IDataErrorInfo on the viewmodel and set the controls' bindings within the custom usercontrol to have ValidatesOnDataErrors=True, NotifyOnValidationError=True but the validation is not displaying.

This is the XAML for my usercontrol (there is no other code-behind logic beyond the dependency property declarations):

<UserControl.Resources>
    <!-- Converters -->
    <local:ValidationBooleanToImageConverter x:Key="ValidationBooleanToImageConverter"/>
    <local:ValidationErrorToStringConverter x:Key="ValidationErrorToStringConverter"/>
    <local:DataTypeToVisibilityConverter DataType="Date" x:Key="DateDataTypeToVisibilityConverter"/>
    <local:DataTypeToVisibilityConverter DataType="Numeric" x:Key="NumericDataTypeToVisibilityConverter"/>
    <local:DataTypeToVisibilityConverter DataType="Enumeration" x:Key="EnumerationDataTypeToVisibilityConverter"/>
    <local:DateToStringConverter x:Key="DateToStringConverter"/>
    <!-- Styles -->
    <Style TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors).CurrentItem, Converter={StaticResource ResourceKey=ValidationErrorToStringConverter}}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</UserControl.Resources>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition Width="16"/>
    </Grid.ColumnDefinitions>
    <toolkit:DatePicker Height="25" Margin="0,0,5,0" Visibility="{Binding Path=DataType, ElementName=UserControl, Converter={StaticResource ResourceKey=DateDataTypeToVisibilityConverter}}" SelectedDate="{Binding Path=Value, ElementName=UserControl, Converter={StaticResource ResourceKey=DateToStringConverter}}"/>
    <ComboBox Height="25" Margin="0,0,5,0" Visibility="{Binding Path=DataType, ElementName=UserControl, Converter={StaticResource ResourceKey=EnumerationDataTypeToVisibilityConverter}}" SelectedItem="{Binding Path=Value, ElementName=UserControl, Mode=TwoWay}" ItemsSource="{Binding Path=Items, ElementName=UserControl}" />
    <TextBox x:Name="_txtValue" Height="25" Margin="0,0,5,0" Visibility="{Binding Path=DataType, ElementName=UserControl, Converter={StaticResource ResourceKey=NumericDataTypeToVisibilityConverter}}" Text="{Binding Path=Value, ElementName=UserControl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"/>
    <Image Grid.Column="2" Grid.Row="0" Source="{Binding ElementName=_txtValue, Path=(Validation.HasError), Converter={StaticResource ResourceKey=ValidationBooleanToImageConverter} }" ToolTip="{Binding ElementName=_txtValue, Path=ToolTip}" Width="16" Height="16"/>
</Grid>

...and the usercontrol reference from within the larger View is...

<control:ValueEditorControl DataType="{Binding Path=ContextualSelectedTagDataType}" Items="{Binding Path=ContextualSelectedTagItems}" Value="{Binding Path=ContextualSelectedTagDataObjectValue, Mode=TwoWay}" Height="25" VerticalAlignment="Top"/>

Can anyone point me in the right direction?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

枕花眠 2024-11-11 00:40:36

您的虚拟机是否实现了 INotifyPropertyChanged?即使您实现了 IDataErrorInfo,如果 WPF 没有收到有关 VM 更改的通知,那么它也不会绑定到这些更改。

话虽这么说,我会将您的工具提示设置器更改为:

<Setter Property="ToolTip"
            Value="{Binding RelativeSource={RelativeSource Self}, 
                   Path=(Validation.Errors)[0].ErrorContent}"/>

如果您想要整个样式,我会推荐这个:

<Style TargetType="{x:Type TextBox}">
    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <AdornedElementPlaceholder Name="controlWithError" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="Background" Value="LightPink"/>
            <Setter Property="ToolTip"
            Value="{Binding RelativeSource={RelativeSource Self}, 
                   Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>

Does your VM implement INotifyPropertyChanged? Even if you implement IDataErrorInfo if WPF isn't notified of changes to the VM then it won't bind to those changes.

That being said I would change your ToolTip setter to this:

<Setter Property="ToolTip"
            Value="{Binding RelativeSource={RelativeSource Self}, 
                   Path=(Validation.Errors)[0].ErrorContent}"/>

If you want the entire Style I would recommend this:

<Style TargetType="{x:Type TextBox}">
    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <AdornedElementPlaceholder Name="controlWithError" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="Background" Value="LightPink"/>
            <Setter Property="ToolTip"
            Value="{Binding RelativeSource={RelativeSource Self}, 
                   Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文