Adding a custom dependency property to a Control Template in XAML - c#

I have managed to get further with my read only check box after a bit of a break and now have the functionality I want in a reasonably elegant form. The problem is I have used a bit of a hack to make it work, although this is not a disaster it would be nice to do it better.
To recap: I want a regular looking checkbox that does not self check when it is clicked, instead the click event triggers a background worker that later on causes a variable to be updated. This variable is bound to checkbox.ischecked and it is then updated with the new value.
I would like to use a control template based on the idea here:
A read-only CheckBox in C# WPF
I have modified this and stripped out stuff I thought I didn't need (perhaps unwisely) and ended up with:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
<!-- -->
<Style x:Key="ReadOnlyCheckBoxStyle" TargetType="{x:Type CheckBox}" >
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<BulletDecorator SnapsToDevicePixels="true" Background="Transparent">
<BulletDecorator.Bullet>
<Microsoft_Windows_Themes:BulletChrome Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
RenderMouseOver="{TemplateBinding IsMouseOver}"
IsChecked="{TemplateBinding Tag}">
</Microsoft_Windows_Themes:BulletChrome>
</BulletDecorator.Bullet>
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True" />
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This checkbox works as described above and I call it like this:
<CheckBox x:Name="uiComboBox" Content="Does not set the backing property, but responds to it."
Style="{StaticResource ReadOnlyCheckBoxStyle}" Tag="{Binding MyBoolean}" Click="uiComboBox_Click"/>
The hack I made was to use the 'Tag' DependencyProperty to carry the data binding into the control template. This bypasses whatever mechanism is normally causing the checkbox to self check. To revert to a normal acting checkbox just change binding to Tag to a binding to IsChecked and inside the BulletDecorator set the TemplateBinding to IsChecked instead of Tag.
So I guess my questions are:
Have I got the wrong end of the stick? Is there a place where I can override whatever mechanism causes the box to self check? Perhaps in ControlTemplate Triggers?
Is it a good idea to go around eliminating any spare XAML that I think is just being brought in from the default CheckBox or should I try and keep a complete replacement for all styles?
If what I am doing is not too crazy, can I add a dependency property in XAML so that I don't have to use the Tag property?
It also occurs to me that perhaps what I really want is a button control that looks like a checkbox, maybe an invisible button with the usual animated checkbox on top which I bind data to the graphic of. Any thoughts on that plan would also be very welcome.
Thanks very much
Ed

I managed to sort out this problem and my ReadOnlyCheckBox idea, in the end I created a custom control based around Button and then applied a style to make it look like a CheckBox. I added my own IsChecked property that does not get set when the user clicks but is bound to the data so the displayed check only appears when the data changes.
C#:
public class ReadOnlyCheckBoxControl : System.Windows.Controls.Button
{
public static DependencyProperty IsCheckedProperty;
public ReadOnlyCheckBoxControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ReadOnlyCheckBoxControl), new FrameworkPropertyMetadata(typeof(ReadOnlyCheckBoxControl)));
}
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
static ReadOnlyCheckBoxControl()
{
IsCheckedProperty = DependencyProperty.Register("IsChecked", typeof(bool), typeof(ReadOnlyCheckBoxControl));
}
}
XAML:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:y="clr-namespace:ReadOnlyCheckBoxControlNS;assembly="
xmlns:Microsoft_Windows_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">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle SnapsToDevicePixels="true"
Margin="1"
Stroke="Black"
StrokeDashArray="1 2"
StrokeThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CheckRadioFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle SnapsToDevicePixels="true"
Margin="14,0,0,0"
Stroke="Black"
StrokeDashArray="1 2"
StrokeThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type y:ReadOnlyCheckBoxControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type y:ReadOnlyCheckBoxControl}">
<BulletDecorator SnapsToDevicePixels="true" Background="Transparent">
<BulletDecorator.Bullet>
<Microsoft_Windows_Themes:BulletChrome Background="{StaticResource CheckBoxFillNormal}"
BorderBrush="{StaticResource CheckBoxStroke}"
RenderMouseOver="{TemplateBinding IsMouseOver}"
IsChecked="{TemplateBinding IsChecked}">
</Microsoft_Windows_Themes:BulletChrome>
</BulletDecorator.Bullet>
<ContentPresenter SnapsToDevicePixels="True"
HorizontalAlignment="Left"
Margin="4,0,0,0"
VerticalAlignment="Center"
RecognizesAccessKey="True" />
</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="{StaticResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

How can I remove hover effect on a Button in WPF [duplicate]

This question already has answers here:
How to remove default mouse-over effect on WPF buttons?
(8 answers)
Closed 1 year ago.
I have a button in Visual Studio WPF and when I hover over it, you can see it gets lighter.
https://i.stack.imgur.com/l9kJh.png
How can I remove this? I tried looking up the solution but I'm a beginner so I don't understand how to implement given solutions.
You have to override the ControlTemplate. When you override it you have to re-implement the behavior (visual states) that is triggered by user interactions e.g., pressed, mouse over, disabled.
Only implement the triggers you need and leave the ones you want to avoid. In your case simply don't implement the mouse over visual state trigger:
App.xaml
<ControlTemplate x:Key="NoMouseOverButtonTemplate"
TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
<!-- Add only required visual state triggers -->
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Background"
Value="{x:Static SystemColors.ControlLightBrush}" />
<Setter Property="Foreground"
Value="{x:Static SystemColors.GrayTextBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Usage
<Button Template="{StaticResource NoMouseOverButtonTemplate}" />
To know the required elements contained in the ControlTemplate that are mandatory for the templated control to perform as expected check the Microsoft Docs: Control Styles and Templates page (in your case the Button Styles and Templates page) and check for named parts as some controls require certain elements to carry a certain name in order to be identified.
You can also use the default template provided there as a starting point to design or customize controls.
Need to set it up so the Background color would be the same color for "IsMouseOver" Trigger as is for default.
Same as this solution just keeping the colors the same
Change color of Button when Mouse is over
<Button Width="50" Height="50" Content="Hi" Click="ButtonBase_OnClick" >
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightGray"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

C# WPF Dynamic Button STYLE that has different pictures based on the x:name or x:key they have

I would like to make a game like Shakes & Fidgets. I got stuck at the Main menu, where I already overcomplicated stuff as I always do. I made a grid layout, where I will put the buttons, but every button is a picture. I use ImageBrush for every button's picture I want to create.
I would like to create ONE style for every button so they change their backgrounds based on the x:Name or x:Key they have. So a Button with x:Name or x:Key "PlayGame" would find it's as the PlayGame.png, PlayGame_Hover, PlayGame_OnClick where "PlayGame" is a variable.
In other words I would like to have a style that can filter the x:name, or x:key of a button, and uses it as a variable later on so I can do this: {StaticResource VARIABLENAME}
The Code I have now is:
<ImageBrush x:Key="PlayGame">
<ImageBrush.ImageSource>
<BitmapImage UriSource="./Pictures/PlayGameButton.png"/>
</ImageBrush.ImageSource>
</ImageBrush>
<ImageBrush x:Key="PlayGame_Hover">
<ImageBrush.ImageSource>
<BitmapImage UriSource="./Pictures/PlayGameButton_Hover.png"/>
</ImageBrush.ImageSource>
</ImageBrush>
<ImageBrush x:Key="PlayGame_OnClick">
<ImageBrush.ImageSource>
<BitmapImage UriSource="./Pictures/PlayGameButton_OnClick.png"/>
</ImageBrush.ImageSource>
</ImageBrush>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{StaticResource PlayGame}" />
<Setter Property="FontSize" Value="15" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border CornerRadius="4" Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource PlayGame_Hover}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{StaticResource PlayGame_OnClick}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I found a not very effective solution, but not the one I actually want
You can give the Style an
x:Key="styleKey"
and you have to give the button this part:
Style="{StaticResource styleKey}"
This way you will have to make a style for every different button you want to have, but it will work, and you will be happy about it if efficency is not key.:D

UWP CustomControl Styles don't inherit from default styles

I'm creating a custom control (not UserControl) and I put my default style in generic.xaml. Since I'm basing my control on CheckBox, and I'm not really doing anything interesting other than adding some extra logic on the backend and maybe overriding a single property I would like to base my styles on the default styles.
Previously, I would be able to do something like
<Style TargetType="local:MyCheckBox" BasedOn="CheckBox">
<Setter Property="..." Value="..." />
</Style>
And this would use the default style and override my single property. Is this possible in UWP apps or do I need to copy the entire built-in style and change my single property?
My class is defined like this:
public class MyCheckBox : CheckBox
{
public MyCheckBox()
{
this.DefaultStyleKey = typeof(MyCheckBox);
}
// etc.
}
<Style TargetType="local:MyCheckBox" BasedOn="CheckBox">
<Setter Property="..." Value="..." />
</Style>
And this would use the default style and override my single property. Is this possible in UWP apps or do I need to copy the entire built-in style and change my single property?
By my side, if I use BasedOn="CheckBox" and place a MyCheckBox in the MainPage, this error will occur:
The name "MyCheckBox" does not exist in the namespace "xxxx".
Invalid value for property 'BasedOn':...
Cannot assign text value 'CheckBox' into property 'BasedOn' of type 'Style'.
If you want to use BasedOn style here, you can refer to Use based-on styles. According to this doc, you will need to use StaticResource here.
But I don't think here only using BasedOn style can solve the problem. Yes you are right, CustomControl style doesn't inherit from default styles, as you can see, the generic.xaml includes a default style, in this style there are only a ControlTemplate and a Border, it's empty.
Since I'm basing my control on CheckBox, and I'm not really doing anything interesting other than adding some extra logic on the backend and maybe overriding a single property I would like to base my styles on the default styles.
If I understand your request correctly, since the customcontrol's template is fast empty, so you are also right, you will need to copy the entire built-in style. For example, you can modify the template the same as the CheckBox's ControlTemplate:
<Style x:Key="CheckBoxStyle" TargetType="CheckBox">
<Setter Property="Background" Value="Blue" />
<Setter Property="Foreground" Value="Red" />
<Setter Property="Padding" Value="8,5,0,0" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="MinWidth" Value="120" />
<Setter Property="MinHeight" Value="32" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
</Style>
<Style TargetType="local:MyCheckBox" BasedOn="{StaticResource CheckBoxStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyCheckBox">
<Grid BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Height="32" VerticalAlignment="Top">
<Rectangle x:Name="NormalRectangle" Fill="Transparent" Height="20" Stroke="{ThemeResource SystemControlForegroundBaseMediumHighBrush}" StrokeThickness="{ThemeResource CheckBoxBorderThemeThickness}" UseLayoutRounding="False" Width="20" />
<FontIcon x:Name="CheckGlyph" Foreground="{ThemeResource SystemControlHighlightAltChromeWhiteBrush}" FontSize="20" FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="" Opacity="0" />
</Grid>
<ContentPresenter x:Name="ContentPresenter" AutomationProperties.AccessibilityView="Raw" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" Grid.Column="1" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" TextWrapping="Wrap" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But I think you can also modify the template of your customcontrol like this:
<Style x:Key="CheckBoxStyle1" TargetType="CheckBox">
<Setter Property="Foreground" Value="Red" />
</Style>
<Style TargetType="local:MyCheckBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyCheckBox">
<CheckBox Content="222" Style="{StaticResource CheckBoxStyle1}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This will make your CustomControl getting the default style of CheckBox, so which single property do you want to change?

RichTextBox FocusVisualStyle not working

I have a RichTextBox that has some content. I want to hide the scrollbar and remove the border when the user moves the mouse over it.
I have added FocusVisualStyle="{x:Null}" which I read in this link: Remove focus rectangle on a UserControl
See below:
<RichTextBox HorizontalAlignment="Left" Height="105" Margin="48,42,0,0" VerticalAlignment="Top" Width="614" Background="Transparent" IsReadOnly="True" ScrollViewer.VerticalScrollBarVisibility="Hidden" BorderBrush="Transparent" ScrollViewer.CanContentScroll="False" FocusVisualStyle="{x:Null}">
</FlowDocument>
blah blah
</FlowDocument>
</RichTextBox>
But wheneever the user moves the mouse over it I still see a white border appear and I can scroll up and down.
Why is this??
That's not FocusVisual you are seeing instead its MouseOver effect which is applied by default for RichTextBox.
You need to override default template to remove that effect which is applied via trigger (in case UIElement.IsMouseOver value is true).
Create a style and provide your own template ( In case you want it to be applied for all RichTextBoxes in your app put this in app resources or window resources wherever it fits in your code. Otherwise you can declare it inline just for your RichTextbox):
<Style TargetType="RichTextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBoxBase">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
Name="border"
SnapsToDevicePixels="True">
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
Name="PART_ContentHost"
Focusable="False" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter Property="UIElement.Opacity" TargetName="border"
Value="0.56"/>
</Trigger>
<!--<Trigger Property="UIElement.IsMouseOver" Value="True">
<Setter Property="Border.BorderBrush" TargetName="border">
<Setter.Value>
<SolidColorBrush>#FF7EB4EA</SolidColorBrush>
</Setter.Value>
</Setter>
</Trigger>-->
<Trigger Property="UIElement.IsKeyboardFocused" Value="True">
<Setter Property="Border.BorderBrush" TargetName="border">
<Setter.Value>
<SolidColorBrush>#FF569DE5</SolidColorBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Notice the trigger i have commented in the above default template.

Can't bind IsChecked on RadioButton with custom ControlTemplate

I have been using a custom RadioButton control with a ToggleButton as the control template. Here's what the xaml looks like:
<RadioButton.Template>
<ControlTemplate>
<ToggleButton x:Name="tb" IsChecked="{Binding IsChecked, Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
Content="{TemplateBinding RadioButton.Content}"
PreviewMouseDown="tb_PreviewMouseDown">
</ToggleButton>
</ControlTemplate>
</RadioButton.Template>
It's been working well, except when I try to either programatically set a button's IsChecked property, or make a binding with it. Then the button that should be checked is visually unresponsive - it doesn't appear to be pressed, and the Aero mouse over effect does not appear. The Clicked event handler still works, and the IsChecked property of both the RadioButton and the ControlTemplate's toggle button are true when I examine their values. Amy I doing something wrong with the binding? Any ideas?
Here's an example of how I use it in the application:
<local:RadioToggleButton Content="1Hr" GroupName="Interval" x:Name="oneHrBtn"
IsChecked="{BindingPath=oneHrBtnIsChecked, Mode=TwoWay}" Margin="2 5 3 5"
IsEnabled="{Binding Path=oneHrBtnIsEnabled, Mode=TwoWay}"/>
What you have is very strange. The RadioButton class derives from ToggleButton. So effectively you put a button in a button. Are you simply trying to make the RadioButton look like a ToggleButton? If so, why don't you use ToggleButton directly?
If you want to make the RadioButton look like a ToggleButton so you can use the GroupName feature, then you'd have to copy the ToggleButton control template and use that (not embed a ToggleButton in the control template).
You can get the default templates from here. Then search for the ToggleButton style and copy it's ControlTemplate.
EDIT:
The following example shows how this can be done. You just need to add a reference to PresentationFramework.Aero.
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<LinearGradientBrush x:Key="ButtonNormalBackground" StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#F3F3F3" Offset="0" />
<GradientStop Color="#EBEBEB" Offset="0.5" />
<GradientStop Color="#DDDDDD" Offset="0.5" />
<GradientStop Color="#CDCDCD" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF707070" />
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" StrokeThickness="1" Stroke="Black" StrokeDashArray="1 2" SnapsToDevicePixels="true" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type RadioButton}" TargetType="{x:Type RadioButton}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}" />
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}" />
<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}" />
<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 RadioButton}">
<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}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</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>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<RadioButton GroupName="TestGroup">Option 1</RadioButton>
<RadioButton GroupName="TestGroup">Option 2</RadioButton>
</StackPanel>
</Window>
If all you want is a RadioButton which looks like a ToggleButton, you can actually implicitly refer to ToggleButton's style as a static resource by its type:
<RadioButton Style="{StaticResource {x:Type ToggleButton}}" />
This seems to work because RadioButton is descended from ToggleButton. So you can't, for example, use {StaticResource {x:Type ComboBox}}.
I'm not able to track down any documentation for using an x:Type as a resource for Style; I'd be interested to see it, if anyone out there knows where to look.
So the issue with my custom RadioToggleButton control was being caused by something very weird indeed. I will describe my solution below, not because I expect anyone else to run into this particular problem, but just as an example of a solution that seems unconnected to the problem.
There was a binding on IsEnabled property of the GroupBox containing the button group. This binding seemed to work fine, enabling and disabling all the internal controls when appropriate. But as soon as I removed this binding, the problem I described above disappeared. This is not ideal, but I decided that I had spent too much time on this issue, so I bound the IsEnabled properties of the individual controls to the same property that the GroupBox had been bound to, and now at least I have the behavior I wanted.

Categories