Search code examples
c#wpf

Style to make RadioButtons look like ToggleButtons, but recolored?


I've made a custom radioButton style to make my buttons look like ToggleButtons. They work, but my triggers don't seem to apply to the buttons. My code:

<Style TargetType="RadioButton">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="RadioButton">
                        <ToggleButton Content="{TemplateBinding Content}"
                              IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}}">
                            <ToggleButton.Style>
                                <Style TargetType="ToggleButton">
                                    <Setter Property="Background" Value="#FF161717"/>
                                    <Setter Property="Foreground" Value="LightGreen"/>
                                    <Setter Property="BorderBrush" Value="#FF161717"/>
                                    <Style.Triggers>
                                        <Trigger Property="IsChecked" Value="True">
                                            <Setter Property="Background" Value="Yellow"/>
                                        </Trigger>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter Property="Background" Value="Cyan"/>
                                        </Trigger>
                                    </Style.Triggers>
                                </Style>
                            </ToggleButton.Style>
                        </ToggleButton>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

The styling works, but the trigger colors don't get applied.

EDIT: Found a solution: Image Source="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}"


Solution

  • You must not wrap a ToggleButton into a RadioButton. That's wrong. Apart from the overhead it will only introduce annoying layout issues like focus handling or event handling etc. - and, like in your case, layout issue like visual states or triggers don't modify the layout as expected.
    Instead override the default ControlTemplate and adjust the layout to adapt to the look and feel of a ToggleButton. The simplest case is to copy the ControlTemplate of the ToggleButton.

    You can find example styles and templates at Microsoft Docs: Control Styles and Templates or extract them using the XAML designer of Visual Studio or Blend.

    A restyled RadioButton that looks like a ToggleButton could look as follows:

    <Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
      <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="RadioButton">
            <Border x:Name="border"
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    SnapsToDevicePixels="true">
              <ContentPresenter x:Name="contentPresenter"
                                Focusable="False"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                Margin="{TemplateBinding Padding}"
                                RecognizesAccessKey="True"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
            </Border>
            <ControlTemplate.Triggers>
              <Trigger Property="Button.IsDefaulted"
                        Value="true">
                <Setter Property="BorderBrush"
                        TargetName="border"
                        Value="{x:Static SystemColors.HighlightBrush}" />
              </Trigger>
              <Trigger Property="IsMouseOver"
                        Value="true">
                <Setter Property="Background"
                        TargetName="border"
                        Value="{x:Static Brushes.CornflowerBlue}" />
                <Setter Property="BorderBrush"
                        TargetName="border"
                        Value="{x:Static Brushes.CornflowerBlue}" />
              </Trigger>
              <Trigger Property="IsChecked"
                        Value="true">
                <Setter Property="Background"
                        TargetName="border"
                        Value="{x:Static Brushes.DarkBlue}" />
                <Setter Property="BorderBrush"
                        TargetName="border"
                        Value="{x:Static Brushes.Black}" />
              </Trigger>
              <Trigger Property="IsEnabled"
                        Value="false">
                <Setter Property="Background"
                        TargetName="border"
                        Value="{x:Static Brushes.Gray}" />
                <Setter Property="BorderBrush"
                        TargetName="border"
                        Value="{x:Static Brushes.DarkGray}" />
                <Setter Property="TextElement.Foreground"
                        TargetName="contentPresenter"
                        Value="{x:Static Brushes.DarkGray}" />
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>