Reputation: 351
I'm new to WPF, and I'm trying to define a button style for the buttons in my app. I've added the following ResourceDictionary
and added it in the App's resources in App.xaml
:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ReciepeFinder">
<Style x:Key="NavButton" TargetType="Button">
<Setter Property="Width"
Value="120" />
<Setter Property="Height"
Value="120" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home.png" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home_selected.png" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Basically, on no hover, I want the button to show resources/images/home.png
and on hover, resources/images/home_selected.png
. When I try to define the ControlTemplate
as in the code above and I run the app, the button just doesn't appear. However, if I remove the ControlTemplate
tag entirely, the button is there with the proper Background
, but obviously no hover effect.
What am I doing wrong?
Bonus question: If I wanted to use binding so that I could just set the path for the no hover image and the on hover image in other buttons' attributes and have it behave the same way, how could i achieve that?
Upvotes: 0
Views: 818
Reputation: 149
Because when you are setting the ControlTemplate you basically have to redefine the way the control is displayed. And you have defined just the triggers without any content representation. The ContentPresenter control will help you with that. Just add some Border and Content presenter after the ControlTemplate.Triggers and ControlTemplate. This should do the trick:
</ControlTemplate.Triggers>
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
Upvotes: 1
Reputation: 22119
If you want to restyle the appearance of a control along with its visual states, you have to edit its ControlTemplate
. However, a control template contains definitions for mandatory parts of a control, as well as its appearance in different states and transitions between them. To make a ControlTemplate
work as expected, you have to implement all of these parts and states. You can find the information on required parts ans states for each built-in control on MSDN, e.g. for Button
.
Instead of creating your ControlTemplate
from scratch, you can copy the default template and adapt it, which is much easier. You can extract it with Blend or Visual Studio.
Your control template does not work, because it is essentially empty. There is nothing to display a background or any content. It does not contain any visuals or controls. I have created a sample control template for you that should fit your requirements.
<Style x:Key="NavButton" TargetType="{x:Type Button}">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home.png" />
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" SnapsToDevicePixels="true"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home_selected.png" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="border" Property="Opacity" Value="0.5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The Border
displays the image background. In general, you would put a ContentPresenter
inside the Border
, because it would show the content that you assigned to the Content
property of your Button
, but since you display images instead of the content I have omitted that.
Please note that there are TemplateBinding
s inside this template. These are used to bind the specified properties from the control that the template is applied on. In other words, if you set the Background
on a button directly or create Style
derived from NavButton
where you set it, this value would be used. If you just hardcoded the value in the control template, it would not change.
For your bonus question: When you use these TemplateBinding
s, setting the Background
directly or in a derived Style
will work as stated above, so you can reuse it. As there is no HoverBackground
property, you would either have to create a derived button and add a dependency property to that, create an attached property or use e.g. the unused Foreground
property for that, although that does not feel right, as it is a dirty workaround. This is an example for Foreground
, just because it is easy to test for you, but it works for the other options stated above, too.
<Setter Property="Foreground">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home_selected.png" />
</Setter.Value>
</Setter>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Foreground}"/>
</Trigger>
Please note, that for accessing a property of the templated parent in Setter
, you have to use a different binding syntax, that has the same effect as TemplateBinding
.
Upvotes: 1