Search code examples
wpfstylesradio-buttontogglebutton

WPF RadioButton/ToggleButton styling


I would like to mimic a style for a group of ToggleButtons as in the below image. Only one of the buttons can be "Checked" at any time.

enter image description here

My question is related to styling:

  • I'd like to have rounded corners on the leftmost button and the rightmost button as in the image but if there is a button between (like in the image), that should not have rounded corners. Sometimes there might only be two buttons to toggle.
  • I need style for different states: "Normal/Unchecked", "Mouseover", "Pressed" and "Checked" at the minimum.

The current control I am using for this is done like this:

<StackPanel Orientation="Horizontal" >
    <RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="All" Padding="12,8,12,8" GroupName="View"  />
    <RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Geolocated" Padding="12,8,12,8" GroupName="View" />
    <RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Non Geolocated" Padding="12,8,12,8" GroupName="View" />
</StackPanel>

In the StackPanel Resources I am trying to set Style for ToggleButton but I'm pretty lost how to achieve the result as in the image above.


Solution

  • This may not be the easiest/best approach, but I took a stab at knocking up some ControlTemplates using Kaxaml, to produce something that looks like this:

    Button Preview

    You could store these templates in a ResourceDictionary and apply them when required, or use them if you were building your button list on the fly.

    I actually created three slightly different styles, one for the left and right buttons, and one for the middle (you may be able to simplify this with extending/inheriting styles). Some repeated code omitted.

    <Grid>
        <Grid.Resources>
            <!-- Brushes for colours/backgrounds -->
            <SolidColorBrush x:Key="FontBrush" Color="#DDDDDD"/>
    
            <LinearGradientBrush x:Key="BgBrush1" StartPoint="0,0" EndPoint="0,1">
                <GradientStop Offset="0" Color="#888888"/>
                <GradientStop Offset="1" Color="#222222"/>
            </LinearGradientBrush>
    
            <SolidColorBrush x:Key="BorderBrush1" Color="#333333"/>
            <LinearGradientBrush x:Key="CheckedBrush" StartPoint="0,0" EndPoint="0,1">
                <GradientStop Offset="0" Color="#555555"/>
                <GradientStop Offset="1" Color="#111111"/>
            </LinearGradientBrush>
    
            <!-- Left Button Template -->
            <ControlTemplate x:Key="ToggleButtonLeft" TargetType="{x:Type ToggleButton}">
                <Border
                    Name="Border"
                    Background="{StaticResource BgBrush1}"
                    BorderBrush="{StaticResource BorderBrush1}"
                    BorderThickness="1"
                    CornerRadius="5,0,0,5">
                    <ContentPresenter
                        HorizontalAlignment="Center"
                        Margin="{TemplateBinding Padding}"
                        VerticalAlignment="Center"
                        Content="{TemplateBinding Content}"
                        TextBlock.FontWeight="Bold"
                        TextBlock.Foreground="{StaticResource FontBrush}"/>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="ToggleButton.IsMouseOver" Value="true">
                        <Setter TargetName="Border" Property="Background" Value="#808080"/>
                    </Trigger>
                    <Trigger Property="IsChecked" Value="true">
                        <Setter TargetName="Border" Property="Background" Value="{StaticResource CheckedBrush}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
    
            <!-- Middle Button(s) Template -->
            <ControlTemplate x:Key="ToggleButtonMid" TargetType="{x:Type ToggleButton}">
                <Border
                    Name="Border"
                    Background="{StaticResource BgBrush1}"
                    BorderBrush="{StaticResource BorderBrush1}"
                    BorderThickness="0,1,0,1"
                    CornerRadius="0" />
            <!-- Other code identical to Left Button Template -->       
            </ControlTemplate>
    
            <!-- Right Button Template -->
            <ControlTemplate x:Key="ToggleButtonRight" TargetType="{x:Type ToggleButton}">
                <Border
                    Name="Border"
                    Background="{StaticResource BgBrush1}"
                    BorderBrush="{StaticResource BorderBrush1}"
                    BorderThickness="1"
                    CornerRadius="0, 5, 5, 0" />
            <!-- Other code identical to Left Button Template -->  
            </ControlTemplate>
        </Grid.Resources>
    
        <!-- Example Usage -->
        <Grid Background="#555555">
            <StackPanel Height="25" Orientation="Horizontal" Margin="5">
                <RadioButton Content="All" GroupName="View" Padding="2" Template="{DynamicResource ToggleButtonLeft}"/>
                <RadioButton Content="Geolocated" GroupName="View" Padding="2" Template="{DynamicResource ToggleButtonMid}"/>
                <RadioButton Content="Non Geolocated" GroupName="View" Padding="2" Template="{DynamicResource ToggleButtonRight}"/>
            </StackPanel>
        </Grid>
    </Grid>
    

    You would have to add additional Triggers etc. for the IsPressed state, and any others required (e.g IsEnabled).