簡體   English   中英

在嵌套的用戶控件上綁定嵌套的驗證規則

[英]Binding nested validation rules on nested User Controls

這是我在這里的第一個問題...我已經准備好很長時間了,不需要尋求幫助,因為我通常會找到所需的東西,但是與此同時我卻很難過...

我正在使用WPF中的工具套件。 我創建了一些用戶控件,如下所示:

  1. LabelTextBox (標簽在左側, TextBox在右側)
  2. LabelTextBoxToggle (左側為LabelTextBox ,右側為Checkbox
  3. LabelTextBoxBrowseFile (左側為LabelTextBox ,右側為“瀏覽文件” 按鈕

我使用依賴項屬性來綁定我需要的所有屬性,並且它們都可以正常工作。 我最近遇到的問題是,當將這些規則應用於LabelTextBoxToggleLabelTextBoxBrowseFile 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中)

任何幫助或建議當然都非常受歡迎! 謝謝你的時間!

您的問題類似於我的。 我還創建了包含文本塊(作為標簽)和文本框(作為輸入)的自定義控件。 目標是對帶有簡單標簽的數據輸入進行通用控制。 問題是驗證。 我也很容易地綁定和驗證數據,但是在控件內部的指定文本框中顯示模板錯誤,這就是問題所在,如果我正確理解,您也會遇到同樣的問題。 所以我的解決方案是:

<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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM