简体   繁体   中英

Wpf dynamic resource lookup for Validation.ErrorTemplate

in my App.xaml I defined a resource for Validation.ErrorTemplate , which depends on dynamic BorderBrush resource. I intend to define unique BorderBrush in each window I have and also within different blocks inside window.

<!--validation error template-->
<ControlTemplate x:Key="NonValid">
    <Border BorderBrush="{DynamicResource BorderBrush}" BorderThickness="2" Margin="5">
        <AdornedElementPlaceholder x:Name="ui"/>
    </Border>
</ControlTemplate>

and this one to demonstrate my problem (also with dynamic brush resource)

<!--test template-->
<ControlTemplate x:Key="ButtonRes" TargetType="Button">
    <Border BorderBrush="{DynamicResource BorderBrush}" BorderThickness="2" Background="Khaki">
        <ContentPresenter />
    </Border>
</ControlTemplate>

and now window, where I use these templates, can resolve brush resource for normal template, but not for 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>

what should I do to get RED border around TextBox?

Behavior you are seeing is perfectly fine. Reasoning behind it:

Validation.ErrorTemplate is placed in adorner layer of window which is placed above all other controls in the window. That's why it's not able to see resource defined at Grid level and resolve reference with window resource.

In case you want to get it resolved dynamically, only possible solution is to declare it in window resources OR use static assignment.

I have implemented a binding converter which can find resources from Validation.ErrorTemplate. It needs an instance of FrameworkElement (element which shows ErrorTemplate) and resource key:

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();
    }
}

modified ErrorTemplate:

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

Binding and therefore converter are triggered every time when ErrorTemplate is shown. So it is possible to update resources and see changes in ErrorTemplate. But unlike DynamicResource it doesn't happen immediately. Still resource which is resolved by binding allows customization per instance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM