WPF - UserControl的样式验证错误

时间:2014-06-05 08:44:43

标签: c# wpf validation xaml user-controls

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

如果TextBox存在于与创建基础数据模型的绑定相同的UserControl中,则此方法可以正常使用。

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

我已经尝试了一些建议,例如让子控件实现INotifyDataError,但到目前为止我没有运气。这篇文章是同一个问题,我找不到使用它的解决方案,所以我希望有更多标签,这可能会引起更多关注和解决方案Show Validation Error in UserControl

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

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

提前感谢您的建议。

1 个答案:

答案 0 :(得分:0)

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

<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值是您的 UserControlUnitInput)的名称/类型,而不是标准UserControl,因为没有在其中声明LabelValue属性。在Visual Studio的输出窗口中,您可能会遇到类似于下面的错误,当您遇到数据绑定问题时,总是是您首先看到的错误。

  

System.Windows.Data错误:40:BindingExpression路径错误:'object'''UserControl'(HashCode = 55649279)'上找不到'Label'属性。 BindingExpression:路径=标签; ...

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