[英]Binding nested validation rules on nested User Controls
這是我在這里的第一個問題...我已經准備好很長時間了,不需要尋求幫助,因為我通常會找到所需的東西,但是與此同時我卻很難過...
我正在使用WPF中的工具套件。 我創建了一些用戶控件,如下所示:
我使用依賴項屬性來綁定我需要的所有屬性,並且它們都可以正常工作。 我最近遇到的問題是,當將這些規則應用於LabelTextBoxToggle和LabelTextBoxBrowseFile UserControls時,我要在ValidationRules上正確使用在LabelTextBox上使用的基本TextBox ,因為我必須向下綁定2個級別才能更新LabelTextBox中的控件。 我可以運行驗證規則,但是當發現錯誤時,我無法讓TextBox控件相應地更新其背景顏色,就像當LabelTextBox沒有嵌套在另一個用戶控件中時一樣。
因此,這是下面的代碼:
用於TextBox的樣式:
<!-- TextBox Default Style, Supports Validation Rules -->
<Style TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="{StaticResource TextBoxBGDefault}" />
<Style.Triggers>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Background" Value="{StaticResource TextBoxBGHasFocus}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{StaticResource TextBoxBGHasFocus}" />
</Trigger>
<DataTrigger Binding="{Binding Path=(Validation.HasError)}" Value="true">
<Setter Property="Background" Value="{StaticResource TextBoxBGHasError}" />
<Setter Property="BorderBrush" Value="Firebrick" />
<Setter Property="BorderThickness" Value="1.5" />
<Setter Property="ToolTipService.InitialShowDelay" Value="2" />
<Setter Property="ToolTip" Value="{Binding Path=(Validation.Errors)[0].ErrorContent}" />
</DataTrigger>
</Style.Triggers>
</Style>
LabelTextBox.xaml:
<Grid x:Name="LayoutRoot" DataContext="{Binding ElementName=ControlRoot, Mode=OneWay, ValidatesOnDataErrors=True}">
<Grid.RowDefinitions>
<RowDefinition Height="24" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label
x:Name="NameLabel"
Width="{Binding Path=LabelWidth, Converter={StaticResource WidthToAutoConverter}}"
Margin="0"
HorizontalAlignment="{Binding Path=HorizontalContentAlignment}"
HorizontalContentAlignment="{Binding Path=LabelHAlign, Converter={StaticResource valueToStringConverter}}"
VerticalContentAlignment="Center"
Content="{Binding Path=LabelContent}"
Padding="10,2,5,2" />
<TextBox
x:Name="ValueTextBox"
Grid.Column="1"
KeyDown="TextBox_KeyDown_Enter"
Padding="5,0"
Text="{Binding TextBoxContent, Mode=TwoWay}"
TextChanged="TextBox_TextChanged" VerticalContentAlignment="Center" Height="22" VerticalAlignment="Center" />
<TextBlock
x:Name="ErrorMsgTextBlock"
Grid.Row="1"
Grid.Column="1"
Margin="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Style="{DynamicResource ValidationErrorLabel}"
Text="{Binding Path=(Validation.Errors)[0].ErrorContent, ElementName=ControlRoot}"
Visibility="{Binding Path=(Validation.HasError), Converter={StaticResource BooleanToVisibilityConverter}, ElementName=ControlRoot, Mode=OneWay}" TextWrapping="Wrap" />
</Grid>
LabelTextBoxBaseClass:
#region TextBox Dependency Properties
public string TextBoxContent
{
get { return (string)GetValue( TextBoxContentProperty ); }
set { SetValue( TextBoxContentProperty, value ); }
}
public static readonly DependencyProperty TextBoxContentProperty =
DependencyProperty.Register( "TextBoxContent"
, typeof( string )
, typeof( LabelTextBoxBaseClass ), new PropertyMetadata( "" )
);
LabelTextBoxToggle.xaml:
<!-- This is the nested UserControl -->
<local:LabelTextBox
x:Name="LTBControl"
Margin="0"
VerticalContentAlignment="Center"
IsEnabled="{Binding Path=IsChecked, ElementName=ToggleCheckBox}"
LabelContent="{Binding Path=LabelContent}"
LabelHAlign="{Binding Path=LabelHAlign}"
LabelWidth="{Binding Path=LabelWidth}"
RaiseEnterKeyDownEvent="{Binding RaiseEnterKeyDownEvent, Mode=TwoWay}"
RaiseTextChangedEvent="{Binding RaiseTextChangedEvent, Mode=TwoWay}"
TextBoxContent="{Binding Path=TextBoxContent, Mode=TwoWay}" />
<CheckBox
x:Name="ToggleCheckBox"
Grid.Column="1"
Margin="5,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Click="ToggleCheckBox_Click"
IsChecked="{Binding CheckBoxChecked, Mode=TwoWay}" />
MaterialBuilder.xaml:
<UserControl.Resources>
<BindingGroup x:Key="SRBindingGroup" Name="PropertiesBindingGroup">
<BindingGroup.ValidationRules>
<local:AddMaterialRule ValidationStep="ConvertedProposedValue" />
</BindingGroup.ValidationRules>
</BindingGroup>
<srvalidators:StringNullOrEmptyValidationRule x:Key="stringNullOrEmptyValidationRule" ErrorMessage="Custom Dir cannot be null!" />
<srconverters:ListToStringConverter x:Key="ListToStringConverter" />
<srconverters:ListToStringConverter x:Key="listToStringConverter" />
<sys:String x:Key="newLine">\n</sys:String>
</UserControl.Resources>
<StackPanel x:Name="spSetup">
<!-- This contains a nested UserControl (LabelTextBox), and I can't get its TextBox background to change color, I just get the red border around the whole control on Validation Errors. -->
<srcontrols:LabelTextBoxBrowseFile
x:Name="ltbMaterialBlueprint"
Height="Auto"
Margin="0,5"
LabelContent="Material Blueprint:"
LabelWidth="120"
LostFocus="ltbMaterialBlueprint_UpdateUI"
OnButtonClick="ltbMaterialBlueprint_UpdateUI"
OnTextBoxEnterKeyDown="ltbMaterialBlueprint_UpdateUI"
TextBoxContent="{Binding MaterialBlueprintFilePath, Mode=TwoWay}">
<srcontrols:LabelTextBoxBrowseFile.TextBoxContent>
<Binding
Mode="TwoWay"
Path="CustomDirName"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<srvalidators:StringNullOrEmptyValidationRule ErrorMessage="Custom Dir cannot be empty!" />
</Binding.ValidationRules>
</Binding>
</srcontrols:LabelTextBoxBrowseFile.TextBoxContent>
</srcontrols:LabelTextBoxBrowseFile>
<!-- Here I use the base LabelTextBox control by itself and everything works as intended. The TextBox's background color changes to red on Validation Errors. -->
<srcontrols:LabelTextBox
x:Name="ltbMaterialName"
Margin="0,5,10,5"
LabelContent="Name:"
LabelWidth="60"
OnTextBoxTextChange="ltbMaterialName_Validate"
RaiseEnterKeyDownEvent="True"
RaiseTextChangedEvent="True">
<!-- Set-up the TextBox Content to use the ValidationRule by passing this GroupBox's BindingGroup resource as a parameter -->
<srcontrols:LabelTextBox.TextBoxContent>
<Binding
Mode="TwoWay"
Path="MaterialName"
UpdateSourceTrigger="Explicit"
ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<local:AddMaterialRule
BGroup="{StaticResource SRBindingGroup}"
CheckForDuplicates="True"
CheckForEmptyName="True"
IsMaterialName="True"
ValidationStep="ConvertedProposedValue" />
</Binding.ValidationRules>
</Binding>
</srcontrols:LabelTextBox.TextBoxContent>
</srcontrols:LabelTextBox>
</StackPanel>
我知道這可能是DataContext問題,但是與其他控件和依賴項屬性不同,當發現“驗證錯誤”時,我無法弄清楚如何使基本UserControl ui元素更新其外觀。 這是我的意思的一些圖片:
工作文本框(此處使用LabelTextBox控件):
損壞的文本框(此處使用的LabelTextBoxToggle控件,帶有嵌套的LabelTextBox):
任何幫助或建議當然都非常受歡迎! 謝謝你的時間!
您的問題類似於我的。 我還創建了包含文本塊(作為標簽)和文本框(作為輸入)的自定義控件。 目標是對帶有簡單標簽的數據輸入進行通用控制。 問題是驗證。 我也很容易地綁定和驗證數據,但是在控件內部的指定文本框中顯示模板錯誤,這就是問題所在,如果我正確理解,您也會遇到同樣的問題。 所以我的解決方案是:
<UserControl x:Class="CapMachina.Common.Controls.FormField_UC" x:Name="FormFieldCtrl"
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"
xmlns:local="clr-namespace:CapMachina.Common.Controls"
xmlns:Converters="clr-namespace:CapMachina.Common.Converters"
xmlns:metro="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<Converters:ConditionalValueConverter x:Key="conditionalValueConverter" />
<Converters:NullableObjectToVisibilityConverter x:Key="nullableObjectToVisibilityConverter" />
</UserControl.Resources>
<StackPanel>
<TextBlock FontWeight="Bold" Text="{Binding Header, ElementName=FormFieldCtrl}" Margin="1" />
<TextBox x:Name="MainTxtBx" metro:TextBoxHelper.Watermark="{Binding WaterMarkText, ElementName=FormFieldCtrl}" TextWrapping="Wrap"
Text="{Binding Text, ElementName=FormFieldCtrl, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}"
Margin="1" IsReadOnly="{Binding IsReadOnly, ElementName=FormFieldCtrl}" TextChanged="MainTxtBx_TextChanged" Loaded="MainTxtBx_Loaded">
<TextBox.Style>
<MultiBinding Converter="{StaticResource conditionalValueConverter}">
<Binding Path="IsReadOnly" ElementName="FormFieldCtrl" />
<Binding Path="ReadOnlyStyle" ElementName="FormFieldCtrl" />
<Binding Path="DefaultStyle" ElementName="FormFieldCtrl" />
</MultiBinding>
</TextBox.Style>
</TextBox>
</StackPanel>
</UserControl>
后面的代碼:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace CapMachina.Common.Controls
{
public partial class FormField_UC : UserControl
{
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(FormField_UC));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(FormField_UC));
public string WaterMarkText
{
get { return (string)GetValue(WaterMarkTextProperty); }
set { SetValue(WaterMarkTextProperty, value); }
}
public static readonly DependencyProperty WaterMarkTextProperty =
DependencyProperty.Register("WaterMarkText", typeof(string), typeof(FormField_UC));
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(FormField_UC), new PropertyMetadata(true));
public Style ReadOnlyStyle { get; set; }
public Style DefaultStyle { get; set; }
public FormField_UC()
{
ReadOnlyStyle = Application.Current.FindResource("ReadOnlyTextBox") as Style;
DefaultStyle = Application.Current.FindResource("DefaultTextBox") as Style;
InitializeComponent();
}
private void MainTxtBx_TextChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrEmpty(MainTxtBx.Text) && IsReadOnly)
Visibility = Visibility.Collapsed;
else
Visibility = Visibility.Visible;
}
private void MainTxtBx_Loaded(object sender, RoutedEventArgs e)
{
BindingExpression mainTxtBxBinding = BindingOperations.GetBindingExpression(MainTxtBx, TextBox.TextProperty);
BindingExpression textBinding = BindingOperations.GetBindingExpression(this, TextProperty);
if (textBinding != null && mainTxtBxBinding != null && textBinding.ParentBinding != null && textBinding.ParentBinding.ValidationRules.Count > 0 && mainTxtBxBinding.ParentBinding.ValidationRules.Count < 1)
{
foreach (ValidationRule vRule in textBinding.ParentBinding.ValidationRules)
mainTxtBxBinding.ParentBinding.ValidationRules.Add(vRule);
}
}
}
}
用法:
<Controls:FormField_UC Header="First name" IsReadOnly="False" HorizontalAlignment="Left" VerticalAlignment="Top">
<Controls:FormField_UC.Text>
<Binding Path="Person.FirstName" Mode="TwoWay">
<Binding.ValidationRules>
<VDRules:NamesValidationRule InventoryPattern="{StaticResource NamesRegex}">
<VDRules:NamesValidationRule.Attributes>
<Validation:ValidationAttributes IsRequired="True" />
</VDRules:NamesValidationRule.Attributes>
</VDRules:NamesValidationRule>
</Binding.ValidationRules>
</Binding>
</Controls:FormField_UC.Text>
</Controls:FormField_UC>
創建所有綁定后,我所做的就是將驗證規則復制到嵌套文本框中。 使用后無法修改綁定,但可以向其中添加驗證規則:)
在自定義控件中設置某些屬性非常重要,例如:
<UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True>
因為您以后不能設置它們。 因此,不需要在使用情況行中設置它們。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.