R – Applying a single style to multiple controls (and tweaking each style)

controlsstyleswpf

I have a WPF application that is using a custom style. In it I have a set of buttons that each have a custom background image. For each button, I'm supplying a normal and a mouse down image. Is there a simple way to do this with a single style (and customize each button on a case by case basis)?

Currently I'm creating a new style for each button, and that can't be the best way to do this surely?

Best Solution

For a XAML only solution you could consider this:-

I'm assuming you are using a control template as part of your style something like the following:-

<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
        <Image x:Name="img" Style="{DynamicResource NormalImage}"/>
        <ControlTemplate.Triggers>
          <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Source" TargetName="img" Value="Images\DisabledImage.png"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

I've butchered the control template here and yours might be more sophisticated

Now if you add the following style in the same area as the above style then this will specify an image to use for the default case

<Style x:Key="NormalImage" TargetType="{x:Type Image}">
  <Setter Property="Source" Value="Images/DeafultImage.png"/>
</Style>

Further down in you XAML when you need to use the button you could do this:-

<Button Style="{DynamicResource MyButtonStyle}" >
  <Button.Resources>
    <Style x:Key="NormalImage" TargetType="{x:Type Image}">
      <Setter Property="Source" Value="Images/OverrideImage.png"/>
    </Style>
  </Button.Resources>
</Button>

It is not pretty but would still allow a master style and be slightly shorter and less duplication than using multiple styles.

The way it works is that the resource name "NormalImage" is first searched for in the button's resources (which are usually empty), then the parent container, all the way up to the page/window resources and beyond to the application resources. The first match wins, so in this case the locally defined style called "NormalImage" that references OverrideImage.png is selected ahead of the window/page/container level resource with the same name that references DefaultImage.png

To further reduce the amount of text

If all the buttons or most of the buttons will use this style then drop the x:Key="MyButtonStyle" from the style definition. That way WPF will use the style on all buttons in the scope without you needing to specify the style explicitly. You can then use the style="{x:null}" to explicitly NOT use the default style. Thus:-

<Window.Resources>
  <Style x:Key="NormalImage" TargetType="{x:Type Image}">
    <Setter Property="Source" Value="Images/DeafultImage.png"/>
  </Style>

  <Style TargetType="{x:Type Button}">
    <Setter Property="Template">
            ...
    </Setter>
  </Style>
</Window.Resources>

<Button >
  <Button.Resources>
    <Style x:Key="NormalImage" TargetType="{x:Type Image}">
      <Setter Property="Source" Value="Images/OverrideImage.png"/>
    </Style>
  </Button.Resources>
</Button>

<Button Style="{x:null}>Normal Button</Button>
Related Question