C# 在嵌套用户控件上绑定嵌套验证规则

C# 在嵌套用户控件上绑定嵌套验证规则,c#,wpf,xaml,data-binding,textbox,C#,Wpf,Xaml,Data Binding,Textbox,这是我在这里的第一个问题,所以…我已经准备了很长时间,从来没有需要寻求帮助,因为我通常会找到我需要的,但我有一个艰难的时间与这个 我正在WPF中开发一个工具套件。我创建了几个用户控件,如下所示: 标签文本框(标签在左侧,而文本框在右侧) LabelTextBoxToggle(LabelTextBox在左侧,复选框在右侧) LabelTextBoxBrowseFile(LabelTextBox位于左侧,浏览文件按钮位于右侧) 我使用依赖属性来绑定我需要的所有属性,它们都可以正常工作。我最近遇到的问

这是我在这里的第一个问题,所以…我已经准备了很长时间,从来没有需要寻求帮助,因为我通常会找到我需要的,但我有一个艰难的时间与这个

我正在WPF中开发一个工具套件。我创建了几个用户控件,如下所示:

  • 标签文本框(标签在左侧,而文本框在右侧)
  • LabelTextBoxToggleLabelTextBox在左侧,复选框在右侧)
  • LabelTextBoxBrowseFileLabelTextBox位于左侧,浏览文件按钮位于右侧)
  • 我使用依赖属性来绑定我需要的所有属性,它们都可以正常工作。我最近遇到的问题是,当这些规则应用于LabelTextBoxToggleLabelTextBoxBrowseFile用户控件时,如何使验证规则在我在LabelTextBoxToggle中使用的基本文本框上正常工作,因为我必须绑定两个级别才能更新LabelTextBox中的控件。我可以运行验证规则,但是当发现错误时,我无法让TextBox控件相应地更新其背景色,就像我在LabelTextBox未嵌套在另一个用户控件中时所做的那样

    下面是我的代码:

    用于文本框的样式:

    <!-- 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>
    
    <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>
    
    #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( "" )
    );
    
    <!-- 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}" />
    
    <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>
    
    LabelTextBoxToggle.xaml:

    <!-- 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>
    
    <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>
    
    #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( "" )
    );
    
    <!-- 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}" />
    
    <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>
    
    
    

    MaterialBuilder.xaml:

    <!-- 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>
    
    <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>
    
    #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( "" )
    );
    
    <!-- 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}" />
    
    <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>
    
    
    \n
    
    我知道这可能是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);
      }
        }
      }
    }
    
    使用System.Windows;
    使用System.Windows.Controls;
    使用System.Windows.Data;
    命名空间CapMachina.Common.Controls
    {
    公共部分类FormField\u UC:UserControl
    {
    公共字符串头
    {
    get{return(string)GetValue(HeaderProperty);}
    set{SetValue(HeaderProperty,value);}
    }
    公共静态只读从属属性HeaderProperty=
    DependencyProperty.Register(“标头”、typeof(字符串)、typeof(FormField_UC));
    公共字符串文本
    {
    获取{return(string)GetValue(TextProperty);}
    set{SetValue(TextProperty,value);}
    }
    公共静态只读DependencyProperty TextProperty=
    DependencyProperty.Register(“文本”、typeof(字符串)、typeof(FormField_UC));
    公共字符串水印文本
    {
    get{return(string)GetValue(WaterMarkTextProperty);}
    set{SetValue(WaterMarkTextProperty,value);}
    }
    公共静态只读从属属性WaterMarkTextProperty=
    DependencyProperty.Register(“水印文本”、typeof(字符串)、typeof(FormField_UC));
    公共图书馆是只读的
    {
    获取{return(bool)GetValue(IsReadOnlyProperty);}
    set{SetValue(IsReadOnlyProperty,value);}
    }
    公共静态只读从属属性为只读属性=
    DependencyProperty.Register(“IsReadOnly”、typeof(bool)、typeof(FormField_UC)、new PropertyMetadata(true));
    公共样式ReadOnlyStyle{get;set;}
    公共样式DefaultStyle{get;set;}
    public FormField_UC()
    {
    ReadOnlyStyle=Application.Current.FindResource(“ReadOnlyTextBox”)作为样式;
    DefaultStyle=Application.Current.FindResource(“DefaultTextBox”)作为样式;
    初始化组件();
    }
    私有void MainTxtBx_TextChanged(对象发送方,textchangedventargs e)
    {
    if(string.IsNullOrEmpty(MainTxtBx.Text)和&IsReadOnly)
    可见性=可见性。已折叠;
    其他的
    可见性=可见性。可见;
    }
    已加载私有void MainTxtBx_(对象发送方,路由目标)
    {
    BindingExpression mainTxtBxBinding=BindingOperations.GetBindingExpression(MainTxtBx,TextBox.TextProperty);
    BindingExpression textBinding=BindingOperations.GetBindingExpression(此,TextProperty);
    如果(textBinding!=null&&mainTxtBxBinding!=null&&textBinding.ParentBinding!=null&&textBinding.ParentBinding.ValidationRules.Count>0&&mainTxtBxBinding.ParentBinding.ValidationRules.Count<1)
    {
    foreach(textBinding.ParentBinding.ValidationRules中的ValidationRule vRule)
    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>
    
    
    
    因为你不能在事后设置它们。所以不需要在使用行中设置它们