Can't bind IsChecked on RadioButton with custom ControlTemplate - c#

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.

Related

how to change button background in UWP

I want to change button background by clicking the button. But, I don't want to change background using click event.
I have tried in WPF and achieved it using below code:
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Background"
Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Border CornerRadius="3"
BorderBrush="DarkGray"
BorderThickness="1"
Background="{TemplateBinding Background}" />
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers >
<Trigger Property="IsFocused"
Value="True">
<Setter Property="BorderThickness"
Value="2"/>
<Setter Property="FontWeight"
Value="Bold"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Button Content="All"
Height="20"
Width="50"
Margin="2"
Style="{StaticResource ButtonStyle}" />
Can anyone please suggest how to achieve the same functionality in UWP?
I want to change button background by clicking the button. But, I don't want to change background using click event.
For your requirement, You could realize it via use XamlBehaviors. For example:
<Button>
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Click">
<Core:ChangePropertyAction PropertyName="Background">
<Core:ChangePropertyAction.Value>
<SolidColorBrush Color="Red"/>
</Core:ChangePropertyAction.Value>
</Core:ChangePropertyAction>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
It is open source model and host all behaviors code on GitHub will allow new features and fixes to be addressed more quickly.

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?

Replace part of default template in WPF

is there any "best practice" way to replace a part of the default template. The current use case is a treeview. As default, the treeview has this small triangle shapes to expand and collapse.
I know how to replace these if I replace the whole control template, as shown in the code below. I am not sure if there is a way to "keep all default, just change XY". Its not a style, I basically need to replace a part of an existing control template.
To illustrate, take a look at the following XAML. The first smaller block is the relevant XAML I want to be able to adapt.
The bigger second and third part are basically a copy of the default templates, only to administer the "changed" part from the beginning.
Is there a better way to do this, saving the long and confusing XAML in the second half?
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid
Width="15"
Height="13"
Background="Transparent">
<Path x:Name="ExpandPath"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="1,1,1,1"
Fill="Black"
Data="M 4 0 L 8 4 L 4 8 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="Data"
TargetName="ExpandPath"
Value="M 0 4 L 8 4 L 4 8 Z"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19"
Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander"
Style="{StaticResource ExpandCollapseToggleStyle}"
IsChecked="{Binding Path=IsExpanded,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<Border Name="Bd"
Grid.Column="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ContentPresenter x:Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Border>
<ItemsPresenter x:Name="ItemsHost"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded"
Value="false">
<Setter TargetName="ItemsHost"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems"
Value="false">
<Setter TargetName="Expander"
Property="Visibility"
Value="Hidden"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader"
Value="false"/>
<Condition Property="Width"
Value="Auto"/>
</MultiTrigger.Conditions>
<Setter TargetName="PART_Header"
Property="MinWidth"
Value="75"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader"
Value="false"/>
<Condition Property="Height"
Value="Auto"/>
</MultiTrigger.Conditions>
<Setter TargetName="PART_Header"
Property="MinHeight"
Value="19"/>
</MultiTrigger>
<Trigger Property="IsSelected"
Value="true">
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected"
Value="true"/>
<Condition Property="IsSelectionActive"
Value="false"/>
</MultiTrigger.Conditions>
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
Unfortunately, I think you have to replace the entire template:
From MSDN: http://msdn.microsoft.com/en-us/library/aa970773.aspx
Controls in Windows Presentation
Foundation (WPF) have a
ControlTemplate that contains the
visual tree of that control. You can
change the structure and appearance of
a control by modifying the
ControlTemplate of that control. There
is no way to replace only part of the
visual tree of a control; to change
the visual tree of a control you must
set the Template property of the
control to its new and complete
ControlTemplate.
Actually there is a way (sort of). You can create your own custom control and override the OnApplyTemplate function to change the style dynamically.
For example, create a custom control like so (I am doing this in silverlight but it's all the same I presume):
namespace SilverlightClassLibrary1
{
public class MyButton: Button
{
public string BackgroundColor { get; set; }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (BackgroundColor != null)
{
Rectangle r = this.GetTemplateChild("BackgroundGradient") as Rectangle;
if (r != null)
{
r.Fill = new SolidColorBrush(Color.FromArgb(255,
Convert.ToByte(BackgroundColor.Substring(1,2),16),
Convert.ToByte(BackgroundColor.Substring(3,2),16),
Convert.ToByte(BackgroundColor.Substring(5,2),16)));
}
}
}
}
}
The interesting part is the GetTemplateChild method, that's looking for a Rectangle control named "BackgroundGradient". (BTW, it's easier if you define custom controls in a separate project, so create a new "Silverlight class library" project if you haven't already done so and put it into that project.)
Then add a new resource dictionary file and override the control template and make sure you have a rectangle named "BackgroundGradient". In this case we're using the standard button control template that I've cut down a bit:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:SilverlightClassLibrary1;assembly=SilverlightClassLibrary1">
<Style TargetType="custom:MyButton">
<Setter Property="Background" Value="#FF1F3B53"/>
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="Padding" Value="3"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFA3AEB9" Offset="0"/>
<GradientStop Color="#FF8399A9" Offset="0.375"/>
<GradientStop Color="#FF718597" Offset="0.375"/>
<GradientStop Color="#FF617584" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Border x:Name="Background" CornerRadius="3" Background="White" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
<Grid Background="{TemplateBinding Background}" Margin="1">
<Border Opacity="0" x:Name="BackgroundAnimation" Background="#FF448DCA" />
<Rectangle x:Name="BackgroundGradient" Fill="White" >
</Rectangle>
</Grid>
</Border>
<ContentPresenter
x:Name="contentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"/>
<Rectangle x:Name="DisabledVisualElement" RadiusX="3" RadiusY="3" Fill="#FFFFFFFF" Opacity="0" IsHitTestVisible="false" />
<Rectangle x:Name="FocusVisualElement" RadiusX="2" RadiusY="2" Margin="1" Stroke="#FF6DBDD1" StrokeThickness="1" Opacity="0" IsHitTestVisible="false" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
So you can now declare a button control and override a style if you'd like:
<UserControl x:Class="SilverlightApplication1.MainPage"
...
xmlns:custom="clr-namespace:SilverlightClassLibrary1;assembly=SilverlightClassLibrary1">
<custom:MyButton>Normal Button 1</custom:MyButton>
<custom:MyButton>Normal Button 2</custom:MyButton>
<custom:MyButton BackgroundColor="#8888cc">Customized Background</custom:MyButton>
I presume you could get get even more clever and pass through a resource name or a style name and load it dynamically.
You then need to include your resource file as part of your application:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SilverlightApplication1.App"
>
<Application.Resources>
<ResourceDictionary >
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
and you'll see your custom property changes in your XAML designer.
I would redesign your template to be a Custom Control, using ContentPresenters as placeholders for the elements that will change.
To use these placeholders you must link them to dependency properties.
Check this post to see how this is done
http://www.codeproject.com/Articles/82464/How-to-Embed-Arbitrary-Content-in-a-WPF-Control
"Using Custom Control" explains my approach
2020 Update
I came across this question on my search for an answer and this is how I did it:
To override the default control templates: use Microsoft Blend > Via the Designer right click on the control you wish to override > Edit Template > Edit Current or Edit a Copy (If you're using the same control multiple times like in my case a Menu Item) save the template in a Resource Dictionary and use it as a Dynamic Resource on the controls you wish to override. and of course, edit the template the way you want.

Adding a custom dependency property to a Control Template in XAML

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>

Categories