我创建了一个自定义 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}" />
绑定
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。