Validation.ErrorTemplate 的 Wpf 动态资源查找
Wpf dynamic resource lookup for Validation.ErrorTemplate
在我的 App.xaml 中,我为 Validation.ErrorTemplate
定义了一个资源,它依赖于动态 BorderBrush
资源。我打算在我拥有的每个 window 中以及 window.
内的不同块中定义唯一的 BorderBrush
<!--validation error template-->
<ControlTemplate x:Key="NonValid">
<Border BorderBrush="{DynamicResource BorderBrush}" BorderThickness="2" Margin="5">
<AdornedElementPlaceholder x:Name="ui"/>
</Border>
</ControlTemplate>
还有这个来演示我的问题(也有动态刷资源)
<!--test template-->
<ControlTemplate x:Key="ButtonRes" TargetType="Button">
<Border BorderBrush="{DynamicResource BorderBrush}" BorderThickness="2" Background="Khaki">
<ContentPresenter />
</Border>
</ControlTemplate>
现在window,在我使用这些模板的地方,可以解析普通模板的画笔资源,但不能解析Validation.ErrorTemplate
!
<Window x:Class="MyApp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test" Height="300" Width="300">
<Window.Resources>
<!-- window overrides resource-->
<SolidColorBrush x:Key="BorderBrush" Color="Blue"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- button can see window resource-->
<Button Template="{StaticResource ButtonRes}"/>
<Grid Grid.Row="1">
<Grid.Resources>
<!-- grid overrides resource-->
<SolidColorBrush x:Key="BorderBrush" Color="Red"/>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- button can see grid resource-->
<Button Template="{StaticResource ButtonRes}"/>
<!-- errorTemplate CAN SEE window resource-->
<!-- errorTemplate CAN NOT SEE grid resource-->
<TextBox Grid.Row="1" VerticalAlignment="Center" Text="{Binding Name}"
Validation.ErrorTemplate="{StaticResource NonValid}"/>
</Grid>
</Grid>
</Window>
我应该怎么做才能在 TextBox 周围设置 RED 边框?
您所看到的行为完全没有问题。背后的原因:
Validation.ErrorTemplate
位于 window 的 adorner layer
中,它位于 window 中所有其他控件之上。这就是为什么它无法查看在网格级别定义的资源并使用 window 资源解析引用。
如果您想动态解析它,唯一可能的解决方案是在 window 资源中声明它或使用静态分配。
我实现了一个绑定转换器,它可以从 Validation.ErrorTemplate 中找到资源。它需要一个 FrameworkElement 实例(显示 ErrorTemplate 的元素)和资源键:
public class ResourceProviderConverter : IValueConverter
{
/// <summary>
/// Returns requested resource from element visual tree
/// </summary>
/// <param name="value">Should contain FrameworkElement which has access to resource</param>
/// <param name="targetType"></param>
/// <param name="parameter">Resource key</param>
/// <param name="culture"></param>
/// <returns>Resource value if resource was found</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter != null && (value is FrameworkElement element))
{
var result = element.TryFindResource(parameter);
if (result != null)
return result;
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
修改后的错误模板:
<local:ResourceProviderConverter x:Key="ResourceProvider"/>
<ControlTemplate x:Key="NonValid">
<Border BorderBrush="{Binding ElementName=ui, Path=AdornedElement,
Converter={StaticResource ResourceProvider},
ConverterParameter='BorderBrush',
FallbackValue={x:Static Brushes.Purple}}"
BorderThickness="2" Margin="5">
<AdornedElementPlaceholder x:Name="ui"/>
</Border>
</ControlTemplate>
每次显示 ErrorTemplate 时都会触发绑定并因此触发转换器。因此可以更新资源并查看 ErrorTemplate 中的更改。但与 DynamicResource 不同的是,它不会立即发生。通过绑定解析的静态资源允许按实例自定义。
在我的 App.xaml 中,我为 Validation.ErrorTemplate
定义了一个资源,它依赖于动态 BorderBrush
资源。我打算在我拥有的每个 window 中以及 window.
BorderBrush
<!--validation error template-->
<ControlTemplate x:Key="NonValid">
<Border BorderBrush="{DynamicResource BorderBrush}" BorderThickness="2" Margin="5">
<AdornedElementPlaceholder x:Name="ui"/>
</Border>
</ControlTemplate>
还有这个来演示我的问题(也有动态刷资源)
<!--test template-->
<ControlTemplate x:Key="ButtonRes" TargetType="Button">
<Border BorderBrush="{DynamicResource BorderBrush}" BorderThickness="2" Background="Khaki">
<ContentPresenter />
</Border>
</ControlTemplate>
现在window,在我使用这些模板的地方,可以解析普通模板的画笔资源,但不能解析Validation.ErrorTemplate
!
<Window x:Class="MyApp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test" Height="300" Width="300">
<Window.Resources>
<!-- window overrides resource-->
<SolidColorBrush x:Key="BorderBrush" Color="Blue"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- button can see window resource-->
<Button Template="{StaticResource ButtonRes}"/>
<Grid Grid.Row="1">
<Grid.Resources>
<!-- grid overrides resource-->
<SolidColorBrush x:Key="BorderBrush" Color="Red"/>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- button can see grid resource-->
<Button Template="{StaticResource ButtonRes}"/>
<!-- errorTemplate CAN SEE window resource-->
<!-- errorTemplate CAN NOT SEE grid resource-->
<TextBox Grid.Row="1" VerticalAlignment="Center" Text="{Binding Name}"
Validation.ErrorTemplate="{StaticResource NonValid}"/>
</Grid>
</Grid>
</Window>
我应该怎么做才能在 TextBox 周围设置 RED 边框?
您所看到的行为完全没有问题。背后的原因:
Validation.ErrorTemplate
位于 window 的 adorner layer
中,它位于 window 中所有其他控件之上。这就是为什么它无法查看在网格级别定义的资源并使用 window 资源解析引用。
如果您想动态解析它,唯一可能的解决方案是在 window 资源中声明它或使用静态分配。
我实现了一个绑定转换器,它可以从 Validation.ErrorTemplate 中找到资源。它需要一个 FrameworkElement 实例(显示 ErrorTemplate 的元素)和资源键:
public class ResourceProviderConverter : IValueConverter
{
/// <summary>
/// Returns requested resource from element visual tree
/// </summary>
/// <param name="value">Should contain FrameworkElement which has access to resource</param>
/// <param name="targetType"></param>
/// <param name="parameter">Resource key</param>
/// <param name="culture"></param>
/// <returns>Resource value if resource was found</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter != null && (value is FrameworkElement element))
{
var result = element.TryFindResource(parameter);
if (result != null)
return result;
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
修改后的错误模板:
<local:ResourceProviderConverter x:Key="ResourceProvider"/>
<ControlTemplate x:Key="NonValid">
<Border BorderBrush="{Binding ElementName=ui, Path=AdornedElement,
Converter={StaticResource ResourceProvider},
ConverterParameter='BorderBrush',
FallbackValue={x:Static Brushes.Purple}}"
BorderThickness="2" Margin="5">
<AdornedElementPlaceholder x:Name="ui"/>
</Border>
</ControlTemplate>
每次显示 ErrorTemplate 时都会触发绑定并因此触发转换器。因此可以更新资源并查看 ErrorTemplate 中的更改。但与 DynamicResource 不同的是,它不会立即发生。通过绑定解析的静态资源允许按实例自定义。