WPF - UserControl 的样式验证错误 [英] WPF - Style validation error of UserControl

查看:23
本文介绍了WPF - UserControl 的样式验证错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用 DataAnnotations 和 INotifyDataError 在我的应用程序中实现了验证,并且可以成功显示何时发生错误以及错误是什么.我希望更改默认错误模板以设置输入值的文本框的样式.

I have implemented validation within my application using DataAnnotations and INotifyDataError and can successfully show when an error occurs and what the error is. I wish to change the default error template to style the textbox in which the value was entered.

如果 TextBox 与创建到底层数据模型的绑定存在于同一个 UserControl 中,这对我来说是正确的.

This works correctly for me if the TextBox exists in the same UserControl as where the binding to the underlying data model is created.

但是我有很多输入,因此决定提取一个 UserControl 来封装标签和文本框.问题是,完成此操作后,我无法再获取文本框来指示错误,我得到了整个控件周围的默认红色框.

However I have many inputs and have therefore decided to extract a UserControl to encapsulate the label and textbox. The problem is that having done this I can no longer get the textbox to indicate the error I get the default the red box around the entire control.

我尝试了一些建议,例如让子控件实现 INotifyDataError,但到目前为止我还没有运气.这篇文章是同样的问题,我找不到使用它的解决方案,所以我希望有更多标签,这可能会引起更多关注和解决方案 在用户控件中显示验证错误

I have tried a couple of suggestions such as having the child control implement INotifyDataError but I have had no luck so far. This post is the same issue and I could not find a solution using it so I'm hoping with more tags this may draw more attention and a solution Show Validation Error in UserControl

这是我制作的一个显示问题的小样本.如果输入的年龄无效,则顶部条目将 TextBox 样式设置为红色,但底部用户控件仅在其周围有一个红色框.

Here is a small sample I have produced that shows the problem. The top entry styles the TextBox to red if an invalid age is entered but the bottom user control simply has a red box around it.

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:ValidatorTest" mc:Ignorable="d" x:Class="ValidatorTest.MainWindow"
    Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
    <ResourceDictionary>

        <CollectionViewSource x:Key="customerViewSource" d:DesignSource="{d:DesignInstance {x:Type local:Customer}, CreateList=True}"/>

        <Style TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true" >
                    <Setter Property="Foreground" Value="Red"/>
                    <Setter Property="Background" Value="MistyRose"/>
                    <Setter Property="BorderBrush" Value="Red"/>
                    <Setter Property="BorderThickness" Value="1.0"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </ResourceDictionary>
</Window.Resources>
<Grid DataContext="{StaticResource customerViewSource}">
    <Grid x:Name="grid1" HorizontalAlignment="Left" Margin="19,27,0,0" VerticalAlignment="Top">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Orientation="Horizontal">
        <Label Content="Age:" HorizontalAlignment="Left" Margin="3"  VerticalAlignment="Center"/>
        <TextBox x:Name="ageTextBox" HorizontalAlignment="Left" Height="23" Margin="3"
                 Text="{Binding Age, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" 
                 VerticalAlignment="Center" 
                 Width="120"
                 />
        </StackPanel>
        <local:UnitInput Grid.Row="1"
                         Label="Age"
                         Value="{Binding Age, Mode=TwoWay, ValidatesOnExceptions=True}"/>
    </Grid>
</Grid>

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        CollectionViewSource customerViewSource = ((CollectionViewSource)(this.FindResource("customerViewSource")));
        customerViewSource.Source = new [] { new Customer{
        Age = 26}};
    }
}



using System.ComponentModel.DataAnnotations;

public class Customer : ModelBase
{
    private double? age;

    [Range(21, 55)]
    [Required(ErrorMessage = "Age is required.")]
    public double? Age
    {
        get
        {
            return this.age;
        }
        set
        {
            this.ValidateProperty(() => this.Age, value);

            if (!double.Equals(value, this.age))
            {
                this.age = value;

                this.RaisePropertyChanged(() => this.Age);
            }
        }
    }
}

<UserControl x:Class="ValidatorTest.UnitInput"
         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" >
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <TextBlock Grid.Column="0" HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Label}" VerticalAlignment="Center" Width="100"/>
    <TextBox Grid.Column="1" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="{Binding Value, Mode=TwoWay}" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="80" Margin="5"/>
</Grid>

/// <summary>
/// Interaction logic for UnitInput.xaml
/// </summary>
public partial class UnitInput : UserControl
{
    public UnitInput()
    {
        this.InitializeComponent();
    }

    public string Label
    {
        get
        {
            return (string)GetValue(LabelProperty);
        }
        set
        {
            SetValue(LabelProperty, value);
        }
    }

    // Using a DependencyProperty as the backing store for Label.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LabelProperty =
        DependencyProperty.Register("Label", typeof(string), typeof(UnitInput), new PropertyMetadata("Label"));

    public string Value
    {
        get
        {
            return (string)GetValue(ValueProperty);
        }
        set
        {
            SetValue(ValueProperty, value);
        }
    }

    // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(string), typeof(UnitInput), new PropertyMetadata(null));
}

预先感谢您的建议.

推荐答案

我没有时间在新项目中设置您的所有代码,但我确实注意到了一个潜在问题.尝试更改 UnitInput 控件中的 Binding:

I haven't got time to setup all of your code in a new project, but I did notice a potential problem. Try changing your Bindings in your UnitInput control:

<UserControl x:Class="ValidatorTest.UnitInput"
         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" >
    <Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UnitInput}}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0" HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Label}" VerticalAlignment="Center" Width="100"/>
        <TextBox Grid.Column="1" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="{Binding Value, Mode=TwoWay}" HorizontalContentAlignment="Right" VerticalAlignment="Top" Width="80" Margin="5"/>
    </Grid>
</UserControl>

区别在于这一行:

<Grid DataContext="{Binding RelativeSource={RelativeSource 
    AncestorType={x:Type UnitInput}}}">

要使用的正确 AncestorType 值是 您的 UserControl (UnitInput) 的名称/类型,而不是标准的 UserControl 因为没有在其中声明 LabelValue 属性.您可能会在 Visual Studio 的输出窗口中遇到一个错误,类似于下面的错误,当您遇到数据绑定问题时,它应该总是成为您查看的第一个位置.

The correct AncestorType value to use is the name/type of your UserControl (UnitInput), not the standard UserControl because that has no Label or Value properties declared in it. You would have had an error, similar to the one below, in the Output Window in Visual Studio, which should always be the first place that you look when you have data binding problems.

System.Windows.Data 错误:40:BindingExpression 路径错误:在对象"用户控件"(HashCode=55649279)上找不到标签"属性.BindingExpression:Path=Label;...

System.Windows.Data Error: 40 : BindingExpression path error: 'Label' property not found on 'object' ''UserControl' (HashCode=55649279)'. BindingExpression:Path=Label;...

请注意,您可能还有其他错误……这只是我看到的第一个错误.

Note that you may have further errors... this was just the first one that I saw.

这篇关于WPF - UserControl 的样式验证错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆