I've read a couple of posts that handles the same issue, but I don't find a solution.
What I want to achieve is, that the WPF checkbox changes its background and borderbrush colors on mouse hover. The colors shouldn't be hardcoded in XAML. Background and borderbrush properties should be bound to properties in a POCO class.
This is, what I've done:
first, I created a POCO class, that derives from WPF CheckBox. I created a dependency property called MouseOverBorderBrush:
Public Class efCheckBox
Inherits CheckBox
Implements INotifyPropertyChanged
Public Shared ReadOnly MouseOverBorderBrushProperty As DependencyProperty =
DependencyProperty.Register("MouseOverBorderBrush",
GetType(Brush), GetType(efCheckBox))
Public Property MouseOverBorderBrush As Brush
Get
Return CType(GetValue(MouseOverBorderBrushProperty), Brush)
End Get
Set(ByVal value As Brush)
PropertyHasChanged(MouseOverBorderBrushProperty, value)
End Set
End Property
Private Sub PropertyHasChanged(dp As DependencyProperty, value As Object, <System.Runtime.CompilerServices.CallerMemberName> Optional p As String = Nothing)
SetValue(dp, value)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(p))
End Sub
Second, I created a ResourceDictionary called "efComboBox". I did this by cloning the template of the original WPF CheckBox, so the following code is - with two exceptions - the original CheckBox definition:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:emmbeeFramework"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
<SolidColorBrush x:Key="CheckBoxFillNormal" Color="#F4F4F4"/>
<SolidColorBrush x:Key="CheckBoxStroke" Color="#8E8F8F"/>
<Style x:Key="EmptyCheckBoxFocusVisual" TargetType="{x:Type local:efCheckBox}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="1" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CheckRadioFocusVisual">
<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>
<Style x:Key="efCheckBoxStyle" TargetType="{x:Type local:efCheckBox}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="{StaticResource CheckBoxFillNormal}"/>
<Setter Property="BorderBrush" Value="{StaticResource CheckBoxStroke}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource EmptyCheckBoxFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:efCheckBox}">
<BulletDecorator x:Name="bullet" Background="Transparent" SnapsToDevicePixels="true">
<BulletDecorator.Bullet>
<Themes:BulletChrome BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" IsChecked="{TemplateBinding IsChecked}" RenderPressed="{TemplateBinding IsPressed}"/>
</BulletDecorator.Bullet>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="true">
<Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}"/>
<Setter Property="Padding" Value="4,0,0,0"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="{Binding local:efCheckBox.MouseOverBorderBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The two exceptions:
I've chanted the "TargetType" from "CheckBox" to"efCheckBox"
I've added a trigger "IsMouseOver" near the end of the XAML code
This binding is not working:
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="{Binding local:efCheckBox.MouseOverBorderBrush}"/>
</Trigger>
I also tried, but it doesn't work:
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:efCheckBox}}, Path=MouseOverBorderBrush}"/>
If I hardcode the brush, it works:
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="Red"/>
</Trigger>
<Setter Property="BorderBrush" Value="Red">
Could someone please help me?
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 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 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}"/>
I have created a MenuBarButton.cs CustomControl in which I inherit from the Button class to add an image and some text to the button like in the picture shown below.
Here's my control class:
public class MenuBarButton : Button
{
public static readonly DependencyProperty ItemNameProperty =
DependencyProperty.Register(
"ItemName", // Property name
typeof(string), // Property type
typeof(MenuBarButton) // Property owner
);
public string ItemName
{
get { return (string)GetValue(ItemNameProperty); }
set { SetValue(ItemNameProperty, value); }
}
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register(
"Source", // Property name
typeof(ImageSource), // Property type
typeof(MenuBarButton) // Property owner
);
public ImageSource Source
{
get { return (ImageSource)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
static MenuBarButton()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(MenuBarButton),
new FrameworkPropertyMetadata(typeof(MenuBarButton))
);
}
}
And the Style is where I'm stuck a bit. So far I have this:
<Style TargetType="{x:Type local:MenuBarButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MenuBarButton}">
<Button>
<Grid HorizontalAlignment="Center">
<Image Source="{TemplateBinding Source}" />
<TextBlock Text="{TemplateBinding ItemName}" />
</Grid>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But ideally I'd like to define a custom shape for the button while keeping the rest of the functionalities such as hovering over and pressing the button. I really wanted to do this with a custom control cause I got tired of having to create a buttons with an Image and a TextBlock inside over and over.
Do I have to define a separate style for the Button's shape explicitly or is there a way I can do this inside of the MenuBarButtonStyle dictionary?
You could copy the default template for the Button and then edit as per your requirements. The easiest way to do this would be add a <Button /> element to a XAML source file in Visual Studio, switch to design mode, right-click on the Button and choose Edit Template and then Edit a copy ....
You could then for example replace the Border element in the default template with an Ellipse.
Below is an example:
<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="#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 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="Width" Value="200" />
<Setter Property="Height" Value="200" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<Ellipse x:Name="border" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}" Fill="{TemplateBinding Background}" SnapsToDevicePixels="true"
Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"/>
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="Stroke" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
<Setter Property="Stroke" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Fill" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
<Setter Property="Stroke" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
<Setter Property="Stroke" 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>
Usage:
<Button Style="{StaticResource ButtonStyle1}" Content="Button..." />
Appearance:
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.