c# - WPF-Error de validación de estilo de UserControl
validation xaml (1)
No tengo tiempo para configurar todo tu código en un nuevo proyecto, pero noté un problema potencial. Intenta cambiar tus UnitInput
en tu control 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>
La diferencia está en esta línea:
<Grid DataContext="{Binding RelativeSource={RelativeSource
AncestorType={x:Type UnitInput}}}">
El valor correcto de AncestorType
para usar es el nombre / tipo de UserControl
( UnitInput
), no el UserControl
estándar porque no tiene propiedades de Label
o de Value
declaradas en él. Habría tenido un error, similar al que se muestra a continuación, en la Ventana de resultados en Visual Studio, que siempre debe ser el primer lugar en el que se mire cuando tenga problemas de enlace de datos.
Error de System.Windows.Data: 40: Error de ruta BindingExpression: la propiedad ''Label'' no se encuentra en ''object'' '''' UserControl ''(HashCode = 55649279)''. BindingExpression: Path = Label; ...
Tenga en cuenta que puede tener más errores ... esta fue solo la primera que vi.
Implementé la validación dentro de mi aplicación usando DataAnnotations e INotifyDataError y puedo mostrar correctamente cuándo ocurre un error y cuál es el error. Deseo cambiar la plantilla de error predeterminada para darle un estilo al cuadro de texto en el que se ingresó el valor.
Esto funciona correctamente si el TextBox existe en el mismo UserControl que donde se crea el enlace al modelo de datos subyacente.
Sin embargo, tengo muchas entradas y, por lo tanto, he decidido extraer un UserControl para encapsular la etiqueta y el cuadro de texto. El problema es que al haber hecho esto ya no puedo obtener el cuadro de texto para indicar el error. Obtengo el recuadro rojo por defecto en todo el control.
He intentado un par de sugerencias como hacer que el control infantil implemente INotifyDataError pero hasta ahora no he tenido suerte. Esta publicación es la misma y no pude encontrar una solución para usarla, así que espero que con más etiquetas, esto atraiga más atención y una solución Show Validation Error in UserControl
Aquí hay una pequeña muestra que he producido que muestra el problema. La entrada superior le da un color rojo al TextBox si se ingresa una edad no válida, pero el control de usuario inferior simplemente tiene un recuadro rojo alrededor.
<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));
}
Gracias por adelantado por tus sugerencias.