Search code examples
wpfstylescustom-controlssplit-button

How to style a SplitButton


Using the SplitButton from exceed's extended wpf toolkit, I would like to use a style I have, "blueButtonStyle".

Trying to apply the style directly doesn't work, since the button really isn't a button! So am wondering how to approach this.

Ideally there would be a button property on the split button that I could apply the blueButtonStyle to, but I don't see one.

How should I approach this?

Cheers,
Berryl

desired look (blueButtonStyle)

enter image description here

default look

enter image description here

current xaml definition with error (inapplicable style)

<toolkit:SplitButton DockPanel.Dock="Right" Content="{resx:Resx SplitButton_Add}" 
                     Height="25" Width="80" HorizontalAlignment="Right" Margin="15" 
                     Style="{StaticResource blueButtonStyle}"
                     >
    <toolkit:SplitButton.DropDownContent>
        <StackPanel Orientation="Vertical" >
            <StackPanel.Resources>
                <Style TargetType="{x:Type Button}" BasedOn="{StaticResource blueButtonStyle}">
                </Style>
            </StackPanel.Resources>
            <Button Content="Person" Command="{Binding AddNewPersonCommand}"/>
            <Button Content="Company" Command="{Binding AddNewOrganizationCommand}"/>
        </StackPanel>
    </toolkit:SplitButton.DropDownContent>
</toolkit:SplitButton>

SplitButton xaml

<!-- =============================================================================== -->
<!-- SplitButton                                                                     -->
<!-- =============================================================================== -->
<Style TargetType="{x:Type local:SplitButton}">
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="IsTabStop" Value="False" />
    <Setter Property="HorizontalContentAlignment" Value="Center" />
    <Setter Property="VerticalContentAlignment" Value="Center" />
    <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
    <Setter Property="Padding" Value="3" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:SplitButton}">
                <Grid x:Name="MainGrid" SnapsToDevicePixels="True">
                    <chrome:ButtonChrome x:Name="ControlChrome" Background="{TemplateBinding Background}" RenderEnabled="{TemplateBinding IsEnabled}" CornerRadius="2.75">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <Button x:Name="PART_ActionButton" Margin="0" 
                                    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" Padding="{TemplateBinding Padding}" >
                                <Button.Template>
                                    <ControlTemplate TargetType="Button">
                                        <ContentPresenter />
                                    </ControlTemplate>
                                </Button.Template>
                                <Grid>
                                    <chrome:ButtonChrome x:Name="ActionButtonChrome"
                                               CornerRadius="2.75, 0, 0, 2.75"                                                         
                                               RenderNormal="False"
                                               RenderEnabled="{TemplateBinding IsEnabled}"
                                                          RenderMouseOver="{Binding IsMouseOver, ElementName=PART_ActionButton}"
                                                          RenderPressed="{Binding IsPressed, ElementName=PART_ActionButton}">
                                        <ContentPresenter Name="ActionButtonContent" Margin="{TemplateBinding Padding}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="true" />
                                    </chrome:ButtonChrome>
                                </Grid>
                            </Button>
                            <ToggleButton x:Name="PART_ToggleButton"
                                  Grid.Column="1"
                                  IsTabStop="False"
                                  IsChecked="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                                  IsHitTestVisible="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource InverseBoolConverter}}">
                                <ToggleButton.Template>
                                    <ControlTemplate TargetType="ToggleButton">
                                        <ContentPresenter />
                                    </ControlTemplate>
                                </ToggleButton.Template>
                                <Grid>
                                    <chrome:ButtonChrome x:Name="ToggleButtonChrome"                                                         
                                               CornerRadius="0, 2.75, 2.75, 0"
                                               RenderNormal="False"
                                               RenderChecked="{TemplateBinding IsOpen}"
                                               RenderEnabled="{TemplateBinding IsEnabled}"
                                                          RenderMouseOver="{Binding IsMouseOver, ElementName=PART_ToggleButton}"
                                                          RenderPressed="{Binding IsPressed, ElementName=PART_ToggleButton}">
                                        <Grid x:Name="arrowGlyph" IsHitTestVisible="False" Margin="4,3,4,3">
                                            <Path Width="7" Height="4" Data="M 0,1 C0,1 0,0 0,0 0,0 3,0 3,0 3,0 3,1 3,1 3,1 4,1 4,1 4,1 4,0 4,0 4,0 7,0 7,0 7,0 7,1 7,1 7,1 6,1 6,1 6,1 6,2 6,2 6,2 5,2 5,2 5,2 5,3 5,3 5,3 4,3 4,3 4,3 4,4 4,4 4,4 3,4 3,4 3,4 3,3 3,3 3,3 2,3 2,3 2,3 2,2 2,2 2,2 1,2 1,2 1,2 1,1 1,1 1,1 0,1 0,1 z" Fill="#FF000000" />
                                        </Grid>
                                    </chrome:ButtonChrome>
                                </Grid>
                            </ToggleButton>
                        </Grid>
                    </chrome:ButtonChrome>

                    <Popup x:Name="PART_Popup" 
                     HorizontalOffset="1"
                     VerticalOffset="1"
                     AllowsTransparency="True"
                     StaysOpen="False"
                     Placement="Bottom"
                     Focusable="False"
                     IsOpen="{Binding IsChecked, ElementName=PART_ToggleButton}">
                        <!-- TODO: Create Popup Styles that can be reused on all popups in the toolkit-->
                        <Border BorderThickness="1" Background="{StaticResource PopupBackgroundBrush}" BorderBrush="{StaticResource ColorPickerDarkBorderBrush}">
                            <ContentPresenter Content="{TemplateBinding DropDownContent}" />
                        </Border>
                    </Popup>

                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

BlueButtonStyle

<!--BlueButtonStyle-->
<Style x:Key="BlueButtonStyle" TargetType="Button">
    <Setter Property="Padding" Value="20,0" />
    <Setter Property="Margin" Value="3" />
    <Setter Property="Height" Value="23" />
    <Setter Property="MinWidth" Value="75" />
    <Setter Property="Foreground" Value="White" />

    <Setter Property="FocusVisualStyle">
        <Setter.Value>
            <Style>
                <Setter Property="Control.Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Rectangle RadiusX="5" RadiusY="5" Stroke="Gold" StrokeThickness="1" />
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Grid x:Name="rootGrid" RenderTransformOrigin=".5,.5">

                    <Grid.Resources>
                        <SolidColorBrush x:Key="borderBrush" Color="Transparent"/>
                    </Grid.Resources>

                    <Grid.RenderTransform>
                        <TranslateTransform X="0" Y="0"/>
                    </Grid.RenderTransform>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <Rectangle Grid.RowSpan="2" x:Name="bottomBorder" RadiusX="5" RadiusY="5" 
                               Stroke="{StaticResource borderBrush}" StrokeThickness="2"
                               Fill="{StaticResource headerBrush}" />
                    <Rectangle x:Name="highlight" Margin="2.5" Fill="White" RadiusX="3" RadiusY="3">
                        <Rectangle.OpacityMask>
                            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                <GradientStop Color="#D0FFFFFF" Offset="0"/>
                                <GradientStop Color="#00FFFFFF" Offset="1"/>
                            </LinearGradientBrush>
                        </Rectangle.OpacityMask>
                    </Rectangle>
                    <ContentPresenter Grid.RowSpan="2"
                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 
                        Margin="{TemplateBinding Padding}"
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                        RecognizesAccessKey="True" RenderTransformOrigin="0.5,0.5" x:Name="contentPresenter" />
                </Grid>
                <ControlTemplate.Resources>
                    <Storyboard x:Key="buttonDown_Animation">
                        <DoubleAnimation Storyboard.TargetName="rootGrid" 
                                         Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"
                                         From="0" To="0" Duration="0:0:0" 
                                         />
                    </Storyboard>
                </ControlTemplate.Resources>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsFocused" Value="True">
                        <Setter TargetName="bottomBorder" Property="Stroke" Value="Black" />
                    </Trigger>

                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="bottomBorder" Property="Stroke" Value="Black" />
                    </Trigger>

                    <Trigger Property="IsEnabled" Value="False">
                        <Setter TargetName="bottomBorder" Property="Fill" Value="DarkGray" />
                        <Setter TargetName="bottomBorder" Property="Stroke" Value="Silver" />
                        <Setter TargetName="bottomBorder" Property="StrokeThickness" Value="4" />
                        <Setter TargetName="highlight" Property="Fill" Value="#20FFFFFF" />
                    </Trigger>

                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Solution

  • The first thing I would do is make sure that the BlueButtonStyle is actually applicable to this type of Control.

    Styles have a TargetType property that need to be set that defines the list of properties they can effect. Your style should look like this:

    <Style x:Key="blueButtonStyle" TargetType="{x:Type toolkit:SplitButton}">
        <!-- edit stuff here -->
    </Style>
    

    Now, assuming you've done that, but binding the background elements and whatnot in your style doesn't work, you may need to edit the ControlTemplate for the SplitButton to allow for that kind of styling. The ControlTemplate defines what Controls make up a given Control (let me rephrase: The ControlTemplate for this SplitButton defines what SubControls, taken together, make up this SplitButton). If the Controls in a ControlTemplate aren't set up to allow binding of properties like Background and Foreground then no Style will be able to change them.

    To get the ControlTemplate, you'll need some sort of decompilation tool. I use Blend for this purpose.

    Once in Blend, make a new project and add the SplitButton to the canvas. On the TreeView pane towards the bottom left, you should see the SplitButton appear. Right-Click it, navigate to "Edit Template...", and choose "Edit a Copy".

    This will create an exact duplicate of the ControlTemplate for that Control, which you can now use just like any other WPF resource.

    In that Template, you'll want to look for the Controls that make up the SplitButton that you want to style. On those controls, make sure the properties that you want to style are set as TemplateBindings. They should look something like this:

    <ControlTemplate x:Key="splitButtonTemplate" TargetType="{x:Type toolkit:SplitButton}">
        <!-- some data here.... -->
        <Button Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" />
    </ControlTemplate>
    

    As long as the properties you want to style are bound in this manner, as TemplateBindings, they'll pick up the changes wrought by your style.