views:

210

answers:

2

I have a ControlTemplate which uses the same color in multiple elements. On certain triggers (e.g. OnMouseOver) I'd like to change that color. As far as I can see I have to define a setter for every element to change its color. Is there a way to reference a shared resource in the template that all contained elements can access, and which can be changed by a trigger, so I don't have to address each and every element?

Here's an (made up) example:

<ControlTemplate x:Key="myTemplate" TargetType="{x:Type Button}">
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Ellipse Fill="red" Grid.Column="0"/>
    <Ellipse Fill="red" Grid.Column="1"/>
    <ContentPresenter Grid.ColumnSpan="2" VerticalAlignment="Center"/>
 </Grid>
</ControlTemplate>

When the control is disabled, I want the ellipses to be grey, without setting both of them explicitly, e.g. I don't want to write

<Trigger Property="IsEnabled" Value="False">
  <Setter TargetName="_ellipse1" Property="Fill" Value="Grey"/>
  <Setter TargetName="_ellipse2" Property="Fill" Value="Grey"/>
</Trigger>

but set the color of both ellipses with just one setter.

+1  A: 

I think the best way to do this is with a value converter. Then you can avoid a messy trigger altogether. Here is your example, but with a converter added.

<Window.Resources>
  <local:EnabledToColorConverter x:Key="enabledToColorConverter"/>
  <ControlTemplate x:Key="myTemplate" TargetType="{x:Type Button}">
    <Grid>
      <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
      </Grid.ColumnDefinitions>
      <Ellipse Name="_ellipse1" Fill="{TemplateBinding IsEnabled, Converter={StaticResource enabledToColorConverter}}" Grid.Column="0"/>
      <Ellipse Name="_ellipse2" Fill="{TemplateBinding IsEnabled, Converter={StaticResource enabledToColorConverter}}" Grid.Column="1"/>
      <ContentPresenter Grid.ColumnSpan="2" VerticalAlignment="Center"/>
    </Grid>
  </ControlTemplate>
</Window.Resources>

<StackPanel>
  <Button Template="{StaticResource myTemplate}">Enabled Button</Button>
  <Button Template="{StaticResource myTemplate}" IsEnabled="False">Disabled Button</Button>
</StackPanel>

And here is the converter:

public class EnabledToColorConverter : IValueConverter
{
 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
 {
  bool isEnabled = (bool)value;
  return isEnabled ?
   Brushes.Red :
   Brushes.Gray;
 }

 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
 {
  throw new NotImplementedException();
 }
}
Charlie
It's pretty ugly solution, because the colors are hard-coded in the value converter (unfortunately, IValueConverter.Convert takes only one parameter), but that could do the trick. I'll try that.
Thomas Freudenberg
No, you could use ConverterParameter on the binding and pass different colors. Or you could define the colors statically and access them that way.
Charlie
+2  A: 

Place the trigger on a style for the ellipses (ellipsi?) instead of the button. IsEnabled will propagate down if you set IsEnabled = false on the Button.

<ControlTemplate x:Key="myTemplate" TargetType="{x:Type Button}">
    <ControlTemplate.Resources>
        <Style TargetType="{x:Type Ellipse}">
            <Setter Property="Fill" Value="Red" />
            <Style.Triggers>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Fill" Value="Gray" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </ControlTemplate.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Ellipse Grid.Column="0"/>
        <Ellipse Grid.Column="1"/>
        <ContentPresenter Grid.ColumnSpan="2" VerticalAlignment="Center"/>
    </Grid>
</ControlTemplate>
Bryce Kahle
That did the trick in the simplest way, thanks.(though I had to figure out that you must not define the color in ControlTemplate, but in the style exclusively.)
Thomas Freudenberg
Thank you - exactly the syntax I needed as well
Mark Pim