2

I am having trouble accessing a named element of a parent template style in WPF.

I have a custom control for a Button (written per this StackOverflow question):

CustomButton.xaml:

<ResourceDictionary ...>
<Style x:Key="ButtonCustomStyle" TargetType="{x:Type local:ImageButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ImageButton}">
                <Grid>
                    <Image Source="{TemplateBinding Image}" Stretch="Fill" x:Name="CurrentImage"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="CurrentImage" Property="Source" Value="{Binding ImageHover, RelativeSource={RelativeSource TemplatedParent}}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

MainWindow.xaml:

<Window ...>
<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="CustomButton.xaml"/>
        </ResourceDictionary.MergedDictionaries>
        <Style TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource ButtonCustomStyle}"/>
    </ResourceDictionary>
</Window.Resources>

<Grid>
    <local:ImageButton Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
</Grid>

ImageButton inherits Button, and Image and ImageHover are defined as dependency properties. This code works great and does what expected.

Now, I want to extend this template style for a single particular button, adding an additional Trigger for changing the image of the button. I am trying to do the following:

<Window...>
<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="CustomButton.xaml"/>
        </ResourceDictionary.MergedDictionaries>
        <Style TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource ButtonCustomStyle}"/>
        <Style TargetType="{x:Type local:ImageButton}"
               BasedOn="{StaticResource ButtonCustomStyle}"
               x:Key="Disableable">
            <Style.Triggers>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter TargetName="CurrentImage" Property="Source" Value="\Images\disabled.png"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </ResourceDictionary>
</Window.Resources>

<Grid>
    <local:ImageButton Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
    <local:ImageButton Style="{StaticResource Disableable}" Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
</Grid>

But Visual Studio underscores the new setter line and says 'The name "CurrentImage" is not recognized'.

Is there a way to alter the CurrentImage element?

kazarey
  • 814
  • 1
  • 15
  • 32

2 Answers2

2

I am having trouble accessing a named element of a parent template style in WPF.

This is not possible. You can't base a ControlTemplate on another ControlTemplate.

I am afraid you will have to re-define the entire ControlTemplate from scratch in the derived Style if you want the setter to be able find the CurrentImage element that is defined in the template of the base Style.

WPF: Is there a way to override part of a ControlTemplate without redefining the whole style?

But instead of using a ControlTemplate trigger, you should be able to use a Style trigger that sets the Image property of the control itself rather than setting the Source property of the Image element in the template:

<Style x:Key="ButtonCustomStyle" TargetType="{x:Type local:ImageButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ImageButton}">
                <Grid>
                    <Image Source="{TemplateBinding Image}" Stretch="Fill" x:Name="CurrentImage"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Image" Value="{Binding ImageHover, RelativeSource={RelativeSource Self}}"/>
        </Trigger>
    </Style.Triggers>
</Style>

<Style TargetType="{x:Type local:ImageButton}"
               BasedOn="{StaticResource ButtonCustomStyle}"
               x:Key="Disableable">
    <Style.Triggers>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Image" Value="\Images\disabled.png"/>
        </Trigger>
    </Style.Triggers>
</Style>
mm8
  • 163,881
  • 10
  • 57
  • 88
  • It's a tad off-topic, but when I comment out the `` part and insert the `` part, the image change on hovering stops working. When I comment out the `` and uncomment `` back, it starts working again. Could it be something with the `Image` property definition? It is written exactly after that SO question mentioned in my quesiton. – kazarey Aug 14 '17 at 15:00
  • Try to use RelativeSource Self as per my edited answer. – mm8 Aug 14 '17 at 15:01
  • No avail :( I copied your Style definition; removed everything extra from `Window.Resources`. The code with `` does not respond to mouse hovering; the code with `` does. – kazarey Aug 14 '17 at 15:10
  • How about ? – mm8 Aug 14 '17 at 15:13
  • No, this also didn't help. Well, I guess, its hard to do the debugging remotely. It may be something with my code, although this is strange that the ControlTemplate trigger works while the Style trigger doesn't. – kazarey Aug 14 '17 at 15:23
1

Unlike ControlTemplete trigger, Style trigger doesn't have access to template elements. since you exposed Image property, use it in Style trigger setter:

<Trigger Property="IsEnabled" Value="False">
    <Setter Property="Image" Value="\Images\disabled.png"/>
</Trigger>
ASh
  • 34,632
  • 9
  • 60
  • 82