I am trying (and failing) to change the colour of the circle's border on a radio button when the mouse is hovered over the control, within WPF. My WPF for the Style is as follows:
<Style TargetType="RadioButton"
x:Key="RadioButtonStyling"
BasedOn="{StaticResource {x:Type RadioButton}}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
</Style.Triggers>
</Style
I am then calling this on the radio buttons as follows:
<RadioButton Style="{StaticResource RadioButtonStyling}" ... />
As it stands, no styling is applied to the outline on the circle, and it remains to be the default blue colour (out of the box Windows-esque blue). See the image below
The problem is the template for the radiobutton already has a mouseover trigger in it.
This sets the borderbrush on the border element by name and will therefore over-ride the value your trigger sets the border on the control to.
Here's a modified working version of the win 10 template which sets the circle red on mouse over:
<ControlTemplate x:Key="RadioButtonControlTemplate1" TargetType="{x:Type RadioButton}">
<Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="radioButtonBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="100" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1,1,2,1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid x:Name="markGrid" Margin="2">
<Ellipse x:Name="optionMark" Fill="#FF212121" MinWidth="6" MinHeight="6" Opacity="0"/>
</Grid>
</Border>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="1" ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="True">
<Setter Property="FocusVisualStyle">
<Setter.Value>
<Style>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="14,0,0,0" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="Padding" Value="4,-1,0,0"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" TargetName="radioButtonBorder" Value="#FFE6E6E6"/>
<Setter Property="BorderBrush" TargetName="radioButtonBorder" Value="#FFBCBCBC"/>
<Setter Property="Fill" TargetName="optionMark" Value="#FF707070"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="radioButtonBorder" Value="#FFF3F9FF"/>
<Setter Property="BorderBrush" TargetName="radioButtonBorder" Value="Red"/>
<Setter Property="Fill" TargetName="optionMark" Value="#FF212121"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" TargetName="radioButtonBorder" Value="#FFD9ECFF"/>
<Setter Property="BorderBrush" TargetName="radioButtonBorder" Value="#FF3C77DD"/>
<Setter Property="Fill" TargetName="optionMark" Value="#FF212121"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Opacity" TargetName="optionMark" Value="1"/>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter Property="Opacity" TargetName="optionMark" Value="0.56"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
The critical part is this trigger:
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="radioButtonBorder" Value="#FFF3F9FF"/>
<Setter Property="BorderBrush" TargetName="radioButtonBorder" Value="Red"/>
<Setter Property="Fill" TargetName="optionMark" Value="#FF212121"/>
</Trigger>
Where I've changed the original value to "Red"
I also moved that trigger down in the order of triggers so it's after isenabled.
These are the only changes I made to the default win10 template I extracted.
Each control in WPF has various states like inactive, mouse-over, pressed or disabled. I you want to modify certain states, simple setters on a style will not work, because there are already triggers defined in the control template that will override yours.
Therefore, you need to create a custom control template. You can use tools like Visual Studio or Blend that can automatically extract the default control templates that you can edit. After extraction, you will get one or more styles and a list of brushes like below.
<SolidColorBrush x:Key="RadioButton.Static.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="RadioButton.Static.Border" Color="#FF707070"/>
<SolidColorBrush x:Key="RadioButton.Static.Glyph" Color="#FF212121"/>
<SolidColorBrush x:Key="RadioButton.MouseOver.Background" Color="#FFF3F9FF"/>
<SolidColorBrush x:Key="RadioButton.MouseOver.Border" Color="#FF5593FF"/>
<SolidColorBrush x:Key="RadioButton.MouseOver.Glyph" Color="#FF212121"/>
<!-- ...and so on. -->
<Style x:Key="OptionMarkFocusVisual">
<!-- ...style used for displaying focus. -->
</Style>
<Style x:Key="RadioButtonStyle" TargetType="{x:Type RadioButton}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{StaticResource RadioButton.Static.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource RadioButton.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<!-- ...control template to display the radio button -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In your case you just have to overwrite the border brush for the mouse-over state.
<SolidColorBrush x:Key="RadioButton.MouseOver.Border" Color="Red"/>
Then you apply the changed style to your radio button.
<RadioButton Style="{DynamicResource RadioButtonStyle}"/>
Related
I'd like the background of my CheckBox to change colour depending on if it matches a pre-defined bool (not just if it's Checked or Unchecked). The problem is that this looks poor if you do it to the CheckBox alone, so I've wrapped the CheckBox in a Grid and set the background of the Grid instead. My issue now is that I want to pull out this style so I can reuse it for my other Checkbox
Here is my XAML:
<Grid Margin="5 10 0 0">
<CheckBox Name="cbJimbo" Content="JIMBO" FontSize="12"
IsChecked="{Binding MyObject.Jimbo}"
Style="{StaticResource CheckBoxStyle}"/>
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbJimbo, Path=Background}" Value="Yellow">
<Setter Property="Background" Value="Yellow" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
What is the best way to refactor this?
In the interest of completeness (Although not entirely relevant) here is the CheckBox style, which determines if the CheckBox matches the default value and then sets the CheckBox background in a too subtle fashion. Also relevant as my Grid is currently bound to the background of this Checkbox:
<Style x:Key="ValidationCheckBox" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="placeholder"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)/ErrorContent.ErrorType}" Value="{x:Static wrapper:ErrorTypeEnum.Default}">
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
</Style.Triggers>
</Style>
Making Your Style Reusable
If you want create a separate style for the Grid, the element name binding is problematic, since the same name might already exist in this namescope. An alternative, although not very robust is to explicitly specify the child that the CheckBox is via index. It is advisible to trigger on the real property that triggers the CheckBox, too, so you do not have to change the brush to in both style. It also makes the reason for the color change apparent.
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Children[0].(CheckBox.Background)}" Value="Yellow">
<Setter Property="Background" Value="Yellow" />
</DataTrigger>
</Style.Triggers>
</Style>
Changing the Control Template
As you might know, the visual appearance and states of a control are defined in a ControlTemplate. Instead of building a Grid around the CheckBox, you could extract its default styleand control template and adapt it, so that it has the same backgroud as the check mark box. There is already a Grid as template root, but its background is set to Transparent. If we replace that with a TemplateBinding to the Background property, it looks as expected.
<Grid x:Name="templateRoot" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
This is the whole and adapted control template for CheckBox.
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="OptionMarkFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="14,0,0,0" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="OptionMark.Static.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="OptionMark.Static.Border" Color="#FF707070"/>
<SolidColorBrush x:Key="OptionMark.Static.Glyph" Color="#FF212121"/>
<SolidColorBrush x:Key="OptionMark.MouseOver.Background" Color="#FFF3F9FF"/>
<SolidColorBrush x:Key="OptionMark.MouseOver.Border" Color="#FF5593FF"/>
<SolidColorBrush x:Key="OptionMark.MouseOver.Glyph" Color="#FF212121"/>
<SolidColorBrush x:Key="OptionMark.Pressed.Background" Color="#FFD9ECFF"/>
<SolidColorBrush x:Key="OptionMark.Pressed.Border" Color="#FF3C77DD"/>
<SolidColorBrush x:Key="OptionMark.Pressed.Glyph" Color="#FF212121"/>
<SolidColorBrush x:Key="OptionMark.Disabled.Background" Color="#FFE6E6E6"/>
<SolidColorBrush x:Key="OptionMark.Disabled.Border" Color="#FFBCBCBC"/>
<SolidColorBrush x:Key="OptionMark.Disabled.Glyph" Color="#FF707070"/>
<Style x:Key="CheckBoxStyle" TargetType="{x:Type CheckBox}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{StaticResource OptionMark.Static.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource OptionMark.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Grid x:Name="templateRoot" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="checkBoxBorder" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid x:Name="markGrid">
<Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{StaticResource OptionMark.Static.Glyph}" Margin="1" Opacity="0" Stretch="None"/>
<Rectangle x:Name="indeterminateMark" Fill="{StaticResource OptionMark.Static.Glyph}" Margin="2" Opacity="0"/>
</Grid>
</Border>
<ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="true">
<Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual}"/>
<Setter Property="Padding" Value="4,-1,0,0"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.MouseOver.Border}"/>
<Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.MouseOver.Glyph}"/>
<Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.MouseOver.Glyph}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Disabled.Border}"/>
<Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.Disabled.Glyph}"/>
<Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.Disabled.Glyph}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Pressed.Border}"/>
<Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.Pressed.Glyph}"/>
<Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.Pressed.Glyph}"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Opacity" TargetName="optionMark" Value="1"/>
<Setter Property="Opacity" TargetName="indeterminateMark" Value="0"/>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter Property="Opacity" TargetName="optionMark" Value="0"/>
<Setter Property="Opacity" TargetName="indeterminateMark" Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Creating Custom Controls
If you intend to use the same control hierarchy in the same way in multiple places, the original style will not save you from lots of code duplication that makes your code less maintainable. In such scenarios, consider creating a UserControl which encapsulates the control as single, reusable control instead.
The simplest way to create a control in WPF is to derive from UserControl. When you build a control that inherits from UserControl, you add existing components to the UserControl, name the components, and reference event handlers in WPF.
If built correctly, a UserControl can take advantage of the benefits of rich content, styles, and triggers. However, if your control inherits from UserControl, people who use your control will not be able to use a DataTemplate or ControlTemplate to customize its appearance.
If your control is not only a composition of other controls, but a fully-fledged new control with custom behavior and control template as well as theming support, you would create a custom control instead. For more information and differences refer to Control authoring overview.
I am new to XAML and WPF and so far I found a lot of good explanations on SO for different things I needed during my work on the project. I come from the web area where I have a lot of experience making graphic elements layouts for web pages using CSS. What really frustrates me is that I have no idea how make some custom nice looking UI in a WPF page, when I get a mock file (as attached).
In particular to this file, I need to place buttons which have a border on the left and an additional square on the right. The color of both depends on the button state. If the button is pressed, the color of the left border and right square should be green (as depicted in the screenshot).
Is there any way similar or at least near to CSS that I coud use to achieve this?
Your general layout suggests that you have a collection of states that you want ot be represented as a list of buttons. These buttons should be togglable and display green when toggled. In this case, an ItemsControl would be suitable, since you do not want or need selection, but interaction with buttons.
For the buttons itself, there is a lot more to do. The visual representation and state are defined in a control template. You can either create a new one from scratch or copy the default control template and adapt it. Here I have created a control template for ToggleButton that reflects your mockup.
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Button.Static.Border" Color="Black"/>
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="LawnGreen"/>
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="Black"/>
<SolidColorBrush x:Key="Button.Pressed.Background" Color="DarkGreen"/>
<SolidColorBrush x:Key="Button.Pressed.Border" Color="Black"/>
<SolidColorBrush x:Key="Button.IsChecked.Background" Color="Green"/>
<SolidColorBrush x:Key="Button.IsChecked.Border" Color="Black"/>
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
<Style x:Key="ToggleButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Rectangle x:Name="Lr" Grid.Column="0" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}" Width="20" Height="40"/>
<Border x:Name="Bd" Grid.Column="1" Background="Transparent" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<Rectangle x:Name="Rr" Grid.Column="2" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}" Width="40" Height="40" Margin="40, 0, 0, 0"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="Lr" Value="{StaticResource Button.MouseOver.Background}"/>
<Setter Property="Fill" TargetName="Rr" Value="{StaticResource Button.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Button.MouseOver.Border}"/>
<Setter Property="Stroke" TargetName="Lr" Value="{StaticResource Button.MouseOver.Border}"/>
<Setter Property="Stroke" TargetName="Rr" Value="{StaticResource Button.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Fill" TargetName="Lr" Value="{StaticResource Button.Pressed.Background}"/>
<Setter Property="Fill" TargetName="Rr" Value="{StaticResource Button.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Button.Pressed.Border}"/>
<Setter Property="Stroke" TargetName="Lr" Value="{StaticResource Button.Pressed.Border}"/>
<Setter Property="Stroke" TargetName="Rr" Value="{StaticResource Button.Pressed.Border}"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Fill" TargetName="Lr" Value="{StaticResource Button.IsChecked.Background}"/>
<Setter Property="Fill" TargetName="Rr" Value="{StaticResource Button.IsChecked.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Button.IsChecked.Border}"/>
<Setter Property="Stroke" TargetName="Lr" Value="{StaticResource Button.IsChecked.Border}"/>
<Setter Property="Stroke" TargetName="Rr" Value="{StaticResource Button.IsChecked.Border}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" TargetName="Lr" Value="{StaticResource Button.Disabled.Background}"/>
<Setter Property="Fill" TargetName="Rr" Value="{StaticResource Button.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Button.Disabled.Border}"/>
<Setter Property="Stroke" TargetName="Lr" Value="{StaticResource Button.Disabled.Border}"/>
<Setter Property="Stroke" TargetName="Rr" Value="{StaticResource Button.Disabled.Border}"/>
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In essence, the control template consists of a Grid that host two rectangles for the state color, and a ContentPresenter in between that shows the content. There are triggers for the different states, that set the color of the rectangles accordingly. The toggled state is active when IsChecked is true, IsPressed is active as you are pressing the mouse button on the button.
For more information on how to create custom control templates, please refer to the documentation.
How to create a template for a control (WPF.NET)
Now you need to create an ItemsControl, bind your collection and add a DataTemplate. The data template defines how the data items are represented, see Data Templating Overview.
<DockPanel>
<Border DockPanel.Dock="Top"
BorderBrush="Black"
BorderThickness="1"
Margin="0, 0, 0, 40">
<TextBlock Text="States"
TextAlignment="Center"/>
</Border>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding StringItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Content="{Binding}"
Style="{StaticResource ToggleButtonStyle}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
That is it and this is what the result looks like:
I want to change the default color of the button for IsMouseOver and IsPressed, but something is wrong with my code. It's not applying, buttons still have their default style.
This is my code:
<Style x:Key="PanelButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="Purple" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#479DE0"/>
</Trigger>
</Style.Triggers>
</Style>
And on the button...
<Button Style="{StaticResource PanelButtonStyle}" .../>
The IsMouseOver and IsPressed brushes are hardcoded into the default ControlTemplate for the Button.
You could copy the default template into your XAML markup by right-clicking on the Button element in design mode in Visual Studio or in Blend and choose Edit Template->Edit a Copy.
You would then edit the template as per your requirements:
<Style x:Key="PanelButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Resources>
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="#479DE0"/>
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
<SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
</Style.Resources>
<Setter Property="Background" Value="Purple" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Just adding triggers to the Style without modifying the template won't work because of how the default template is defined.
Button has already triggers to change the visual state defined internally.
You have to override the default template to override the default triggers (See: Styles and templates):
<Style x:Key="PanelButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="Purple" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#479DE0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I've just put a perfectly normal default button in a WPF window, expecting it to have some normal button states. But it's only reacting to mouse over and not mouse down. I only need it to have two states so I can do without the over state. How do I get it to react to the mouse button being pressed instead?
Here is my button:
<Button Width="380"
Height="80"
FontSize="30"
FontFamily="VAG Rounded"
Background="#337A05"
Foreground="White"
Margin="0,15"
Content="BACK TO GAMES" />
You can right-click on the Button in design mode in Visual Studio and choose Edit Template->Edit a Copy and then modify the default control template as per your requirements by for example remove the IsMouseOver trigger and change the value of the Background property setter in the IsPressed trigger:
<Window...>
<Window.Resources>
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
<SolidColorBrush x:Key="Button.Pressed.Background" Color="Red"/>
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<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" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<!--<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
</Trigger>-->
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Width="380"
Height="80"
FontSize="30"
FontFamily="VAG Rounded"
Background="#337A05"
Foreground="White"
Margin="0,15"
Content="BACK TO GAMES"
Style="{StaticResource ButtonStyle1}"/>
</Grid>
</Window>
And no, there is no property that you can simply set to do this :) You have to override the template.
You have to change it's style / template. You'll need to get acquainted with ControlTemplates and VisualStates. Here's a tutorial.
I'm trying to change the background colour of a button's style in xaml on hover
This is my current approach, but it doesn't work. The default hover colour is being used
<Style x:Key="AtStyle" TargetType="Button">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="{StaticResource AtBlue}" />
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="White" />
<Setter Property="Padding" Value="12,6" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="FontSize" Value="18.667" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Red" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
I've seen other solutions that say that you need to override the Control template to achieve this, but those solutions also require you define the border and the content as well which seems unnecessary. What is the minimal approach to defining a hover state in Xaml?
I have created simple style based button based on your requirement,
XAML
<Style TargetType="Button">
<Setter Property="Background"
Value="Blue" />
<Setter Property="HorizontalAlignment"
Value="Center" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="Foreground"
Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="bg"
Background="{TemplateBinding Background}"
BorderThickness="2"
BorderBrush="White">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="Red"
TargetName="bg" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Let's look at the template of the default button style for Aero theme:
<ControlTemplate TargetType="{x:Type ButtonBase}">
<theme:ButtonChrome Name="Chrome"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
RenderDefaulted="{TemplateBinding Button.IsDefaulted}"
RenderMouseOver="{TemplateBinding IsMouseOver}"
RenderPressed="{TemplateBinding IsPressed}"
SnapsToDevicePixels="True">
<ContentPresenter
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
RecognizesAccessKey="True"/>
</theme:ButtonChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter TargetName="Chrome" Property="RenderDefaulted" Value="True"/>
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter TargetName="Chrome" Property="RenderPressed" Value="True"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="#ADADAD"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
As you can see, the mouse-over and pressed colors are defined as bindings on the ButtonChrome's properties RenderMouseOver and RenderPressed. The way ButtonChrome is designed, they take priority over any values from the Background property. Therefore, unfortunately, the only way to override background color of a clicked or highlighted button is to override its template.