我非常精通 WPF,但对实现 UI 验证的领域很陌生,我需要确保某些值已准备好保存到数据库。在我非常大的应用程序中,我有很多不同类型的验证,其中包括简单的单个(TextBox 需要一个值或最少字符)、(必须选择项目)、(必须选择至少一个选项)等等。我已经使用 INotifyDataErrorInfo 实现了验证,并且效果非常好,除了一些我正在绕圈子并需要一些指导的问题。事实上,这可能更多的是样式问题。以下只是我的问题之一,但如果我解决了这个问题,那么它可能会解决其他问题,所以我现在会坚持这个:我有一组单选按钮控件,其中一个必须由用户选择,但我不希望默认选择任何一个,因此被迫做出选择。在他们选择一个之前,需要在堆栈面板中的单选按钮周围显示一个红色边框。因为这是我想在我有一组控件的几个地方做的事情,所以我认为创建一个名为 ErrorBorderControl 的边框控件会很好,它使用属性管理数据错误,然后将项目弹出到该控件中。我在这个名为 ValidationObject 的对象类型的控件上创建了一个 DependecyProperty,它只需要一个可以测试以查看是否有错误的属性。这完美地工作,并且在未选择时显示红色边框,而不是在选择时显示。到目前为止很棒。然而,ErrorBorderControl 中定义的 ControlTemplate 会渗入 UI 中所有其他基于边框的控件,包括 RadioButton 控件周围的边框。我经常使用样式并了解范围,但这很奇怪。以下是我所做的,虽然作为第一次尝试非常基本:
用户控制:
<UserControl
x:Class="Itec.Common.Wpf.CustomControls.Controls.ErrorBorderControl"
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">
<!--
Provides a border around grouped controls that needs error feedback
-->
<Border>
<!-- Style -->
<Border.Style>
<Style
TargetType="{x:Type Border}"
x:Name="TheTemplate">
<!-- Error template definition -->
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<!-- Adorner for the error visual -->
<AdornedElementPlaceholder>
<!-- Simple border around the control -->
<Border
BorderBrush="#ec7063"
BorderThickness="1"/>
</AdornedElementPlaceholder>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Border.Style>
</Border>
</UserControl>
代码背后:
/// <summary>
/// Interaction logic for ErrorBorderControl.xaml
/// </summary>
public partial class ErrorBorderControl : UserControl
{
#region Dependency Properties
/// <summary>
/// The validation object property
/// </summary>
public static readonly DependencyProperty ValidationObjectProperty =
DependencyProperty.Register("ValidationObject", typeof(object), typeof(ErrorBorderControl),
new FrameworkPropertyMetadata(OnValidationObjectChanged));
#endregion Dependency Properties
#region Ctors
/// <summary>
/// Initializes a new instance of the <see cref="ErrorBorderControl"/> class.
/// </summary>
public ErrorBorderControl()
{
InitializeComponent();
}
#endregion Ctors
#region Public Properties
/// <summary>
/// Gets or sets the validation object.
/// </summary>
/// <value>The validation object.</value>
public object ValidationObject
{
get { return (object)GetValue(ValidationObjectProperty); }
set { SetCurrentValue(ValidationObjectProperty, value); }
}
#endregion Public Properties
#region Private Methods
/// <summary>
/// Raises when the ValidationObject property value changes
/// </summary>
/// <param name="d">The d.</param>
/// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private static void OnValidationObjectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ErrorBorderControl)d).ValidationObject = e.NewValue;
}
#endregion Private Methods
}
执行:
<!-- Owner type -->
<itc:ErrorBorderControl
Grid.Row="1"
ValidationObject="{Binding Path=RecordType, ValidatesOnNotifyDataErrors=True}">
<StackPanel
Orientation="Horizontal">
<!-- Owner -->
<itc:ItecRadioButton
Content="{DynamicResource Language.SingleLine.Owner}"
Margin="0,4,4,4"
IsChecked="{Binding Path=RecordType, Converter={itc:EnumToBooleanConverter EnumValue={x:Static recordOwners:RecordOwnerRecordType.Owner}}}"/>
<!-- FAO -->
<itc:ItecRadioButton
Content="{DynamicResource Language.SingleLine.FAO}"
Margin="0,4,4,4"
IsChecked="{Binding Path=RecordType, Converter={itc:EnumToBooleanConverter EnumValue={x:Static recordOwners:RecordOwnerRecordType.Fao}}}"/>
<!-- Account Manager -->
<itc:ItecRadioButton
Content="{DynamicResource Language.SingleLine.Account_Manager}"
Margin="0,4,4,4"
IsChecked="{Binding Path=RecordType, Converter={itc:EnumToBooleanConverter EnumValue={x:Static recordOwners:RecordOwnerRecordType.AccountManager}}}"/>
<!-- Watcher -->
<itc:ItecRadioButton
Content="{DynamicResource Language.SingleLine.Watcher}"
Margin="0,4,4,4"
IsChecked="{Binding Path=RecordType, Converter={itc:EnumToBooleanConverter EnumValue={x:Static recordOwners:RecordOwnerRecordType.Watcher}}}"/>
</StackPanel>
</itc:ErrorBorderControl>
输出:
请注意,看起来模板虽然在用户控件中定义,但正在影响其他控件中的其他边框控件。看看我何时做出选择:
红色的控件不参与验证。另一个控件中的控件模板如何影响所有边框?我只是不明白。我需要做的是定义一个模板,我可以将其应用于我希望它仅应用于的控件,并且能够重复使用它。