0
votes

I'd like to define a WPF ControlTemplate for a "color swatch" button. Essentially, I want to be able to use the template like this:

<Button Template="{StaticResource SwatchButtonTemplate}" Background="{Binding ActiveColor}">

Where ActiveColor is a Color property in the data context. I've been able to achieve that with this template:

<ControlTemplate x:Key="SwatchButtonTemplate" TargetType="{x:Type Button}">
    <Border Name="OuterBorder" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{StaticResource BorderBrush}" Background="{TemplateBinding Background}">
        <Border Name="InnerBorder" BorderThickness="0" BorderBrush="White" Background="{TemplateBinding Background}">
            <ContentPresenter/>
        </Border>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter TargetName="OuterBorder" Property="BorderThickness" Value="1"/>
            <Setter TargetName="InnerBorder" Property="BorderThickness" Value="1"/>
            <Setter TargetName="OuterBorder" Property="BorderBrush" Value="{StaticResource MouseOverAccentBrush}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

But now what I'd like to do is use a special brush for the button background if the color is transparent (e.g. display a red "X" in the background if the color is transparent).

I can't seem to figure out how to set up a trigger in the template that checks the background color, and if it is transparent, use a different brush for the Background property. I've tried adding a DataTrigger to the template:

    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            ...
        </Trigger>
        <DataTrigger Binding="{TemplateBinding Background}" Value="#00000000">
            <Setter TargetName="InnerBorder" Property="Background" Value="{StaticResource transparent_swatch_brush}"/>
        </DataTrigger>
    </ControlTemplate.Triggers>

But I get a XamlParseException when using this.

Edit: The full usage of this template is in a list of swatch buttons, and the source of the list is bound to an ObservableCollection<Brush> called ColorList populated with SolidColorBrushes (one of which is transparent)

<ItemsControl ItemsSource="{Binding ColorList}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button x:Name="button" Background="{Binding}"
                    Template="{StaticResource SwatchButtonTemplate}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Height="20" Rows="1" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

And the full template definition:

<ControlTemplate x:Key="SwatchButtonTemplate" TargetType="{x:Type Button}">
    <Border Name="OuterBorder" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{StaticResource BorderBrush}" Background="{TemplateBinding Background}">
        <Border Name="InnerBorder" BorderThickness="0" BorderBrush="White" Background="{TemplateBinding Background}">
            <ContentPresenter/>
        </Border>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter TargetName="OuterBorder" Property="BorderThickness" Value="1"/>
            <Setter TargetName="InnerBorder" Property="BorderThickness" Value="1"/>
            <Setter TargetName="OuterBorder" Property="BorderBrush" Value="{StaticResource MouseOverAccentBrush}"/>
        </Trigger>
        <DataTrigger Binding="{Binding Background, RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="#00ffffff">
            <Setter TargetName="InnerBorder" Property="Background" Value="{StaticResource transparent_swatch_brush}"/>
        </DataTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
2
TemplateBinding is pretty limited. When it gives you a SadTromboneException, as it so often does, always try Binding="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}"15ee8f99-57ff-4f92-890c-b56153
@EdPlunkett I just tried that, but then I get this error: BindingExpression path error: 'Background' property not found on 'object' ''ContentPresenter' (Name='')'. ... Is this due to how my template is structured?tdenniston
It's due to the fact that ContentPresenter doesn't have a property named Background, but something, somewhere is trying to set that property anyhow. I'd have to see the full template in its current state to know how it's trying to set Background on a ContentPresenter. If the TargetName is still InnerBorder, and InnerBorder is still the name of a Border, then maybe you're doing that somewhere else.15ee8f99-57ff-4f92-890c-b56153
@EdPlunkett I've added the full template definition as well as the usage of it.tdenniston
The problem is probably elsewhere. A usercontrol is overkill for this.15ee8f99-57ff-4f92-890c-b56153

2 Answers

0
votes

Thanks to @EdPlunkett for the suggestion to avoid "{TemplateBinding ...}" in the data trigger. The solution (XAML only) is to use a "Self" relative source:

<ControlTemplate x:Key="SwatchButtonTemplate" TargetType="{x:Type Button}">
    <Border Name="OuterBorder" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{StaticResource BorderBrush}" Background="{TemplateBinding Background}">
        <Border Name="InnerBorder" BorderThickness="0" BorderBrush="White" Background="{TemplateBinding Background}">
            <ContentPresenter/>
        </Border>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter TargetName="InnerBorder" Property="BorderThickness" Value="1" />
        </Trigger>
        <DataTrigger Binding="{Binding Path=Background.Color, RelativeSource={RelativeSource Self}}" Value="#00ffffff">
            <Setter TargetName="InnerBorder" Property="Background" Value="{StaticResource transparent_swatch_brush}" />
        </DataTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

(also note that Colors.Transparent is #00ffffff, not #00000000 as I thought).

0
votes

you can fix it with a binding to the anchestor of type Button like this:

<ControlTemplate.Triggers>
    <Trigger Property="IsMouseOver" Value="True">
        ...
    </Trigger>
    <DataTrigger Binding="{Binding Path=Background, RelativeSource={RelativeSource FindAncestor, AncestorType=Button}}" Value="#00000000">
        <Setter TargetName="InnerBorder" Property="Background" Value="{StaticResource transparent_swatch_brush}"/>
    </DataTrigger>

this will use the Background property from your Button