I have this style for a RadioButton on WPF
<Style x:Key="UserRadioButton" TargetType="RadioButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Grid>
<Border
x:Name="border"
Background="Transparent"
BorderBrush="Black"
BorderThickness="2"
CornerRadius="5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel>
<fa:IconImage MaxHeight="50" Icon="User" />
<TextBlock
Width="Auto"
Height="Auto"
FontSize="16"
Text="{TemplateBinding Content}" />
</StackPanel>
</ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="border" Property="Background" Value="LightBlue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the TextBlock Text it's not Binding to the content
I tried to use different properties to bind but it's not working
ContentPresenter
does not allow content to be defined using XAML content syntax. This is because the ContentPresenter
does not derive from ContentControl
. The ConTentControl
type is decorated with the ContentPropertyAttribute
attribute which is inherited to all inheritors. And ContentPresenter
does not apply the ContentPropertyAttribute
itself. Therefore, ContentPresenter
does not support XAML content syntax.
The ContentPresenter
is used inside the template of a ContentControl
to actually render the value of the ContentControl.Content
property. Because it supports templating, it can render any content.
If you override the Controltemplate
of a ContentControl
(e.g. RadioButton
) you usually you would embed a ContentPresenter
into the new layout.
In your case you are trying to replace the ContentPresenter
by adding a TextBlock
to render the content. This limits the reusability significantly as now the content can only be a string
. For this reason, the recommended way is to embed the ContentPresenter
into you extended layout. You always want to show the ContentControl.Content
value so you always would embed the ContentPresenter
.
Also don't set Border
attributes like Background
property locally as this eliminates customization by assignig local values on the templated parent (for example RadioButton Background="Red" />
won't work anymore).
Instead use TemplateBinding
to bind those properties to the owner of the ControlTemplate
and set the default values using Style.Setters
.
Recommended solution
Embed the ContentPresenter
into the custom layout:
<Style x:Key="UserRadioButton"
TargetType="RadioButton">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="BorderBrush"
Value="Black" />
<Setter Property="BorderThickness"
Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
CornerRadius="5">
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<fa:IconImage MaxHeight="50"
Icon="User" />
<!--
Automatically shows the value of the Content property .
and also inherits properties like FontSize, FontWeight etc.
from the templated parent control.
-->
<ContentPresenter />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
For the sake of completeness, below are different variants you can use if you want to replace the ContentPresenter
with your TextBlock
.
Note that non of those examples below are recommended solutions.
Example 1 (not recommended)
Set ContentPrersenter.Content
explicitly:
<ControlTemplate TargetType="RadioButton">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
CornerRadius="5">
<ContentPresenter>
<!--
The XAML content syntax is not supported by the ContentPresenter.
However, we can use the XAML property element syntax to set the Content:
-->
<ContentPresenter.Content>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<fa:IconImage MaxHeight="50"
Icon="User" />
<TextBlock Width="Auto"
Height="Auto"
FontSize="16"
Text="{TemplateBinding Content}" />
</StackPanel>
<ContentPresenter.Content>
</ContentPresenter>
</Border>
</ControlTemplate>
Example 2 (not recommended)
Simply drop the ContentPrersenter
from the layout as its content was already explicitly defined in example 1 what effectively makes the ContentPresenter
pointless:
<ControlTemplate TargetType="RadioButton">
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
CornerRadius="5">
<!--
Because the Content is presented using the TextBlock
the ContentPresenter has no more use and can be dropped from the layout.
-->
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<fa:IconImage MaxHeight="50"
Icon="User" />
<TextBlock Width="Auto"
Height="Auto"
FontSize="16"
Text="{TemplateBinding Content}" />
</StackPanel>
</Border>
</ControlTemplate>
Example 3 (recommended for some scenarios, for example when the original layout and interaction behavior, like mouse over state effects, should be preserved)
A more useful alternative is to define the value for ContentControl.Content
inline either directly on the element for example
<!--
This solution will preserve the original layout,
like the circle that highlights the toggle state,
and interaction behavior
-->
<RadioButton>
<RadioButton.Content>
<TextBlock Text="Some text" />
</RadioButton.Content>
</RadioButton>
or with the help of a Style
:
<!--
This solution will preserve the original layout,
like the circle that highlights the toggle state,
and interaction behavior
-->
<Style x:Key="UserRadioButton"
TargetType="RadioButton">
<Setter Property="Content">
<Setter.Value>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<fa:IconImage MaxHeight="50"
Icon="User" />
<!--
This example binds the the TextBlock.Text property
to the current DataContext of the styled control (RadioButton).
-->
<TextBlock Text="{Binding textPropertyOfDataContext}" />
</StackPanel>
</Setter.Value>
</Setter>
</Style>
Example 4 (recommended for some scenarios, for example when the original layout and interaction behavior, like mouse over state effects, should be preserved)
Another useful alternative is to define the value for ContentControl.Content
by defining a DataTemplate
for the ContentControl.ContentTemplate
property:
<!--
This solution will preserve the original layout,
like the circle that highlights the toggle state,
and interaction behavior
-->
<Style x:Key="UserRadioButton"
TargetType="RadioButton">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<fa:IconImage MaxHeight="50"
Icon="User" />
<!--
This example binds then TextBlock.Text property
to the current DataContext of the DataTemplate
which is the ContentControl.Content value.
-->
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>