使用 INotifyDataErrorInfo 进行 WPF 文本框验证

问题描述 投票:0回答:1

我创建了一个自定义 TextBox 控件,它覆盖了 TextBox。我一切都工作得很好,而且我还使用 OnTextChanged 的重写进行了验证。

我现在遇到的问题是 ViewModel 中的实现已更改,因此现在通过单击表单末尾的“保存”按钮(使用 INotifyDataErrorInfo)进行验证,而不是通过按键进行验证

我的问题是,如何在我的 CustomControl 中对正在更新的验证错误做出反应,因为我想根据验证结果重新着色我的文本框边框。


将控件添加到窗口的 Xaml 代码:

<ctrl:AppTextBox ButtonCommand="{Binding AddNewWordCommand}"
                 ButtonLabel="+"
                 Label="New Word"
                 Text="{Binding NewWord, 
                                UpdateSourceTrigger=PropertyChanged, 
                                ValidatesOnDataErrors=True}"
                 x:Name="txtNewWord" />

我的 CustomControl (AppTextBox) 的 Xaml 代码:

  <Style TargetType="{x:Type ctrl:AppTextBox}"
         BasedOn="{StaticResource {x:Type TextBox}}">

    <Setter Property="Background" Value="Transparent" />
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="FontFamily" Value="{DynamicResource Inter400}" />
    <Setter Property="FontSize" Value="{DynamicResource Font_Medium}" />
    <Setter Property="Foreground" Value="{DynamicResource Theme_Foreground}" />
    <Setter Property="Margin" Value="20,8" />
    <Setter Property="TextWrapping" Value="Wrap" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />

    <Setter Property="Template">

      <Setter.Value>

        <ControlTemplate TargetType="{x:Type ctrl:AppTextBox}">

          <StackPanel Orientation="Vertical">

            <Border Background="{TemplateBinding Background}"
                    BorderBrush="{DynamicResource TextBox_Border_Focus}" 
                    BorderThickness="1"
                    CornerRadius="{DynamicResource Control_CornerRadius}"
                    Cursor="IBeam"
                    Focusable="False"
                    MinHeight="{DynamicResource TextBox_Height}"
                    Padding="10,2,5,2"
                    x:Name="bdrOutline" >

              <Grid>

                <Grid.ColumnDefinitions>
                  <ColumnDefinition Width="*" />
                  <ColumnDefinition Width="Auto" />
                  <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>

                <StackPanel Background="Transparent"
                            Grid.Column="0"
                            Orientation="Vertical"
                            VerticalAlignment="Center"
                            x:Name="stpLayout" >

                  <Label Background="Transparent"
                         Content="{TemplateBinding Label}"
                         Focusable="False"
                         FontFamily="{DynamicResource Inter600}"
                         FontSize="12"
                         Foreground="{DynamicResource TextBox_Label_Focus}"
                         IsTabStop="False"
                         VerticalAlignment="Center"
                         Visibility="{TemplateBinding LabelVisibility}"
                         x:Name="lblHeading" />

                  <ScrollViewer Background="Transparent"
                                BorderBrush="Transparent"
                                Cursor="IBeam"
                                Foreground="{DynamicResource Theme_Foreground}" 
                                HorizontalAlignment="Stretch"
                                Margin="3,0,0,0"
                                VerticalAlignment="Center"
                                VerticalScrollBarVisibility="Auto"
                                Visibility="Visible"
                                x:Name="PART_ContentHost" >

                    <i:Interaction.Triggers>
                      <i:KeyTrigger Key="Return">
                        <i:InvokeCommandAction Command="{Binding ButtonCommand}" />
                      </i:KeyTrigger>
                    </i:Interaction.Triggers>

                  </ScrollViewer>

                </StackPanel>

                <ContentControl Focusable="False"
                                Grid.Column="1"
                                HorizontalAlignment="Center"
                                Margin="5,0"
                                Style="{DynamicResource ErrorIcon_Canvas}"
                                VerticalAlignment="Center"
                                Visibility="Collapsed"
                                x:Name="cvsError" />

                <Button Command="{TemplateBinding ButtonCommand}"
                        Content="+"
                        Cursor="Hand"
                        Grid.Column="2"            
                        Margin="5,0"
                        Padding="10,7"
                        Style="{DynamicResource FilledSquareButton_Base}"
                        VerticalAlignment="Center"
                        Visibility="Visible"
                        x:Name="btnAction" />

              </Grid>

            </Border>

            <!-- List Errors Here -->
            <ListBox ItemContainerStyle="{DynamicResource ErrorMessagesListBoxItem}"
                     ItemsSource="{Binding RelativeSource={x:Static RelativeSource.Self},
                                           Path=(Validation.Errors)/ErrorContent}" 
                     Style="{DynamicResource ErrorMessagesListBox}" />

          </StackPanel>

          <ControlTemplate.Triggers>

            <Trigger Property="ButtonIsActive" Value="False">
              <Setter TargetName="btnAction" Property="Visibility" Value="Collapsed" />
            </Trigger>

            <Trigger Property="HasBorder" Value="False">
              <Setter TargetName="bdrOutline" Property="BorderBrush" Value="Transparent" />
            </Trigger>

            <Trigger Property="Validation.HasError" Value="True">
              <Setter TargetName="lblHeading" Property="Foreground" Value="{DynamicResource Theme_ErrorBrush}" />
            </Trigger>

            <Trigger Property="Layout" Value="Inline">
              <Setter TargetName="lblHeading" Property="VerticalAlignment" Value="Top" />
              <Setter TargetName="PART_ContentHost" Property="Margin" Value="40,5,0,0" />
              <Setter TargetName="stpLayout" Property="Orientation" Value="Horizontal" />
            </Trigger>

            <MultiTrigger>
              <MultiTrigger.Conditions>
                <Condition Property="HasBorder" Value="True" />
                <Condition Property="Validation.HasError" Value="True" />
              </MultiTrigger.Conditions>
              <Setter TargetName="bdrOutline" Property="BorderBrush" Value="{DynamicResource Theme_ErrorBrush}" />
            </MultiTrigger>

            <MultiTrigger>
              <MultiTrigger.Conditions>
                <Condition Property="TextFillState" Value="Empty" />
                <Condition Property="IsKeyboardFocusWithin" Value="False" />
                <Condition Property="Validation.HasError" Value="False" />
              </MultiTrigger.Conditions>
              <Setter TargetName="bdrOutline" Property="BorderBrush" Value="{DynamicResource TextBox_Border_Empty}" />
              <Setter TargetName="lblHeading" Property="Foreground" Value="{DynamicResource TextBox_Label_Empty}" />
              <Setter TargetName="PART_ContentHost" Property="Visibility" Value="Collapsed" />
            </MultiTrigger>

            <MultiTrigger>
              <MultiTrigger.Conditions>
                <Condition Property="TextFillState" Value="Empty" />
                <Condition Property="IsMouseOver" Value="True" />
                <Condition Property="IsKeyboardFocusWithin" Value="False" />
                <Condition Property="Validation.HasError" Value="False" />
              </MultiTrigger.Conditions>
              <Setter TargetName="bdrOutline" Property="BorderBrush" Value="{DynamicResource TextBox_Border_Empty_Hover}" />
              <Setter TargetName="lblHeading" Property="Foreground" Value="{DynamicResource TextBox_Label_Empty_Hover}" />
            </MultiTrigger>

            <MultiTrigger>
              <MultiTrigger.Conditions>
                <Condition Property="TextFillState" Value="Filled" />
                <Condition Property="IsMouseOver" Value="False" />
                <Condition Property="IsKeyboardFocusWithin" Value="False" />
                <Condition Property="Validation.HasError" Value="False" />
              </MultiTrigger.Conditions>
              <Setter TargetName="bdrOutline" Property="BorderBrush" Value="{DynamicResource TextBox_Border_Filled}" />
              <Setter TargetName="lblHeading" Property="Foreground" Value="{DynamicResource TextBox_Label_Filled}" />
            </MultiTrigger>

            <MultiTrigger>
              <MultiTrigger.Conditions>
                <Condition Property="TextFillState" Value="Filled" />
                <Condition Property="IsMouseOver" Value="True" />
                <Condition Property="Validation.HasError" Value="False" />
              </MultiTrigger.Conditions>
              <Setter TargetName="bdrOutline" Property="BorderBrush" Value="{DynamicResource TextBox_Border_Filled_Hover}" />
              <Setter TargetName="lblHeading" Property="Foreground" Value="{DynamicResource TextBox_Label_Filled_Hover}" />
            </MultiTrigger>


            <MultiTrigger>
              <MultiTrigger.Conditions>
                <Condition Property="IsKeyboardFocusWithin" Value="True" />
                <Condition Property="Validation.HasError" Value="False" />
              </MultiTrigger.Conditions>
              <Setter TargetName="bdrOutline" Property="BorderBrush" Value="{DynamicResource TextBox_Border_Focus}" />
              <Setter TargetName="lblHeading" Property="Foreground" Value="{DynamicResource TextBox_Label_Focus}" />
            </MultiTrigger>

          </ControlTemplate.Triggers>

        </ControlTemplate>

      </Setter.Value>

    </Setter>

  </Style>

</ResourceDictionary>


更新:

我已经设法在我的用户控件中进行一些验证:

我可以通过检查来设置红色边框和标签

<Condition Property="Validation.HasError" Value="True" />

但是我无法显示控件内的错误,而是必须向窗口添加一个列表框,这不是我想要做的事情:

<ListBox ItemContainerStyle="{DynamicResource ErrorMessagesListBoxItem}"
         ItemsSource="{Binding ElementName=txtEntry, 
                               Path=(Validation.Errors)}" 
         Style="{DynamicResource ErrorMessagesListBox}" />

我尝试将此代码添加到我的自定义控件中,但由于某种原因它不起作用:

<ListBox ItemContainerStyle="{DynamicResource ErrorMessagesListBoxItem}"
         ItemsSource="{Binding RelativeSource={x:Static RelativeSource.Self}, 
                               Path=(Validation.Errors)}" 
         Style="{DynamicResource ErrorMessagesListBox}" />

这也不是:

<ListBox ItemContainerStyle="{DynamicResource ErrorMessagesListBoxItem}"
         ItemsSource="{Binding ElementName=PART_ContentHost,
                               Path=(Validation.Errors)}" 
         Style="{DynamicResource ErrorMessagesListBox}" />
wpf validation textbox inotifydataerrorinfo
1个回答
1
投票

绑定

RelativeSource.Self
引用定义
Binding
的当前元素。但是您位于
ControlTemplate
内部,并且想要读取
ControlTemplate
所应用的专利类型的附加属性。因此,您必须使用
RelativeSource.TemplatedParent
。并且因为您希望将
ListBox
绑定到错误 collection 而不是当前错误 item,所以您不能将
ListBox.ItemsSource
绑定到当前项目的
ValidationError.ErrorContent
属性。

Binding
内的
ListBox
上定义
ControlTemplate
如下:

<!-- List Errors Here -->
<ListBox ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Validation.Errors)}" 
         DisplayMemberName="ErrorContent"
         Foreground="Red" />

我强烈建议使用分配给

ControlTemplate
附加属性的错误
Validation.ErrorTemplate
,而不是将错误反馈视觉效果和逻辑直接添加到控件默认
ControlTemplate
的可视化树中。您可以遵循并扩展此示例:如何添加验证以查看模型属性或如何实现 INotifyDataErrorInfo

© www.soinside.com 2019 - 2024. All rights reserved.