I have a chromeless button in a WPF application that uses the XAML style below:
However, there are 2 things that I’m trying to do with it.
- I want the displayed image to be one of two images, depending on a binding property from the datacontext of the template the button is in. The property is a boolean. Show one image if False, the other if True.
- I would like the image shown to change when the mouse hovers over the button and to be the image that would be shown when above bound property is True.
I’ve tried several things, but nothing really seems to work. I tried a converter with the bound value like this:
<x:ArrayExtension x:Key="ThumbsDown" Type="BitmapImage">
<BitmapImage UriSource="/Elpis;component/Images/thumbDown.png" />
<BitmapImage UriSource="/Elpis;component/Images/thumbBan.png" />
</x:ArrayExtension>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var data = (object[])parameter;
if (data.Length != 2 || value == null || value.GetType() != typeof(bool))
return data[0];
if(!((bool)value))
return data[0];
else
return data[1];
}
<Button Name="btnThumbDown" Grid.Column="1" Style="{StaticResource NoChromeButton}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Background="Transparent"
Click="btnThumbDown_Click">
<Image Width="32" Height="32" Margin="2"
Source="{Binding Banned,
Converter={StaticResource binaryChooser},
ConverterParameter={StaticResource ThumbsDown}}"/>
</Button>
But this causes 2 problems. Nothing I do for the hover image works anymore and the designer throws an exception.
The opacity trigger from below can go, as I really just want the hover now.
Any thoughts on how to make this properly work?
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="NoChromeButton" TargetType="{x:Type ButtonBase}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Grid x:Name="Chrome" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.75"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Opacity" Value="1.0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
I would solve this by adding an
Imagecontrol to theControlTemplateof the button, and then create aMultiDataTriggerwith one condition forIsMouseOverand one condition for the boolean property that you want to bind to. The trigger then sets the source of the image when active.Below is a style that accomplishes this. I’ve assumed that the button has a DataContext which contains the boolean property, and that the boolean property is called MyBoolean.
You might have to switch which image should be displayed when the trigger is active, if I misunderstod the requirements.
The trick here is to use a
MultiDataTriggerso that the two conditions you mention can be combined. Usually you would probably use aTriggerinstead of aDataTriggerwhen binding toIsMouseOverof a control. But since you need aDataTriggerfor the boolean property theIsMouseOverbinding can be written as aDataTriggerby using theElementNameproperty of the binding. By doing that you make is possible to use a MultiDataTrigger to combine the two.UPDATE:
To add support for customizing the images used, and also which property to bind to, for each button instance I would subclass the
Buttonclass and add a couple ofDependencyProperties.This class could then be used in the following way:
The control template could then be modified to look like this:
The major changes here is that the source for the image is set to the values of the dependency properties instead of hard coded URIs, and that the
MultiDataTriggerhas been changed into aMultiTriggerwhich binds to the dependency properties. Previously the path to the boolean property was also hard coded, but now that is configurable by changing the binding for theIsActiveproperty when creating the button, as shown in the example above.