Click here to Skip to main content
Click here to Skip to main content

A WPF SplitButton

By , 20 Oct 2007
 
Screenshot - SplitButton_a.jpg

Introduction

As the Windows Presentation Foundation (WPF) does not have a split button built-in, I decided to have a go at writing one. As I am fairly new to this Framework, I wanted something simple to start with, and this is what I came up with.

Although it would be possible to create a split button using Styles and Control Templates, I wanted to support the Windows Themes so a custom control seemed like the way to go.

The Control derives from System.Windows.Controls.Button.

Using the Code

To add a split button to a window, reference the assembly, then add an XML namespace to the Windows XAML file as shown below:

xmlns:m="clr-namespace:Wpf.Controls;assembly=Wpf.SplitButton"

The XAML below adds a split button to a window and shows how to add MenuItems that will be displayed by the context menu:

<m:SplitButton Content="Split Button" Placement="Bottom">
    <MenuItem Header="MenuItem 1"/> 
    <MenuItem Header="MenuItem 2"> 
        <MenuItem Header="MenuItem 1"/> 
        <MenuItem Header="MenuItem 2"/> 
    </MenuItem>
</m:SplitButton

The control assembly has a style defined for each of the Windows themes. To draw the control, I've used the ButtonChrome classes from the PresentationFramework DLLs.
The demo project has copies of these styles in the DemoStyles folder so that I could display each theme in the demo window using the x:Key attribute.

I've also added a Style that looks like a Button in Windows Explorer running on Windows Vista. To use this style, you have to set the Style property of the SplitButton explicitly using the following syntax:

Style="{DynamicResource {x:Static m:SplitButtonResources.VistaSplitButtonStyleKey}}" 

Screenshot - SplitButton_vista.jpg

Vista Styled Button with an Icon

Points of Interest

As shown above, the Button includes an Icon property.

To place the context menu, use the Placement, PlacementRectangle, HorizontalOffset and VerticalOffset properties. These are dependency properties defined by the ContextMenuService class, using the AddOwner method. I've added callbacks to each of these properties where I can set the equivalent property on the base Button's internal context menu.

The Button has two modes as defined by the Mode property, Split, which is the default and Dropdown. In Split mode, the control has two parts, the Button part which acts just like a normal button firing the Click event, and a dropdown button which displays the context menu when clicked. In Dropdown mode, clicking anywhere on the button displays the context menu.

Issues

This control was developed using the Beta2 release of Visual Studio 2008. Unfortunately the demo project does not display in the cider designer (for me anyway). This may be a bug in Visual Studio as it displays fine in Expression Blend and compiles and runs without problems.

History

24-Sept-2007: Article posted

02-Oct-2007: Article updated

  • As was discussed in the Icon Thread in the comments below, I've removed the Icon property from the SplitButton as it simply wasn't needed. The demo now includes the preferred method of adding an icon to the button as described by Josh.

09-Oct-2007: Article updated

  • Fixed the error in the demo

20-Oct-2007: Article updated

  • Bug fixes

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

alrh
Software Developer
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionadd child programatically in mvvmmembermtaboy19 May '12 - 4:08 
Very Good.
How do I add child MenuItems programatically in mvvm?
QuestionNice controlmemberMike Hankey17 Aug '11 - 9:05 
Don't wander in the WPF arena very often but was looking for just such a control.
Thanks
A girl phoned me and said, 'Come on over. There's nobody home.' I went over. Nobody was home! Rodney Dangerfield

GeneralMy vote of 5memberMember 477029225 May '11 - 3:34 
Good article - very useful bit of code.
GeneralWow!memberMember 473418720 Jan '11 - 22:42 
This is great control. Took only a short time to convert to Visual Studio 2010 and integrate the SplitButton in a test project.
Thanks a lot!
Best regards Anders.
GeneralMy vote of 5memberMember 473418720 Jan '11 - 22:39 
Really easy to use and implementation is great.
GeneralNot displaying correctlymemberEspiritu1 Oct '10 - 3:55 
Hi:
 
There's something strange with this control. The demo works as described, but when I use it in other project following the instructions, the button displays only a blank rectangle in design mode and when running doesn't show at all. I am using VS 2010, migrated the project to Framework 4.0.
 
The strange thing is that I inserted the demo project in my new project and there the splitbutton is shown correctly.
GeneralRe: Not displaying correctlymemberRoeni15 Jun '11 - 20:28 
sounds like a problem with the styles in the xaml-files, sure you have inserted them into your project corretly?
I'm also using VS2010 with .Net4, no Problems at all
QuestionHow to add style besides VistaSplitButtonStylememberkairu726 Mar '10 - 16:47 
hi,
i found out that i can only add VistaSplitButtonStyle as style,
 
how bout the other, since i need it to match my other normal button
 
thanks
Generalexcellent articlememberstg6095 Mar '10 - 18:00 
excellent article
General2 databinding related fixesmemberMartin Pozor27 Feb '10 - 11:38 
I have made 2 fixes related to databinding:
 
If menu items are data bound (like here[^]), one issue is that context menu placement properties are not applied (because ContextMenu instance is not created as part of EnsureContextMenuIsValid method).
 
Modified EnsureContextMenuIsValid method whis fixes this:
 
private void EnsureContextMenuIsValid()
{
    if (this.ContextMenu == null)
    {
        this.ContextMenu = new ContextMenu();
    }
 
    this.ContextMenu.PlacementTarget = this;
    this.ContextMenu.Placement = Placement;
 
    this.ContextMenu.Opened -= ((sender, routedEventArgs) => IsContextMenuOpen = true);
    this.ContextMenu.Closed -= ((sender, routedEventArgs) => IsContextMenuOpen = false); 
    this.ContextMenu.Opened += ((sender, routedEventArgs) => IsContextMenuOpen = true);
    this.ContextMenu.Closed += ((sender, routedEventArgs) => IsContextMenuOpen = false);            
}
 

2nd issue is which occurs also with databinding. if (!ContextMenu.HasItems)check in OnDropDown method was evaluated to false when items were data bound (but only in some scenarios) and menu was not shown. So I have removed this check.
Martin Pozor (pozi)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 20 Oct 2007
Article Copyright 2007 by alrh
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid