Split Button in WPF
One of the commonly requested controls in WPF is the split button... its pretty simple to implement it in WPF... heres my take on it with a slight twist... ;)
In the control template of the button, I have the following - the TextBlock and the Menu being the additions..
<ContentPresenter .../>
<TextBlock Grid.Row="0" Grid.Column="1" Text="|" Foreground="gray" />
<Menu Grid.Row="0" Grid.Column="2" Background="transparent" >
<MenuItem Header=">" Margin="0" Padding="0" Name="DropDownButton"/>
</Menu>
Thats it with the style .. I tend to shy away from manipulating styles .. :) ... so now for the menu I add items to the context menu of the button ...
<Button.ContextMenu>
<ContextMenu >
<MenuItem Header="_Save" Command="Save"/>
<MenuItem Header="_Open" Command="Open"/>
</ContextMenu>
</Button.ContextMenu>
Next on loading we need to open the context menu on clicking the Menu in the button.. so we do that with this code snippet
b.ContextMenu.Width = b.ActualWidth; // context menu should be the same width as button
b.ContextMenuOpening += new ContextMenuEventHandler(b_ContextMenuOpening);
DependencyObject d = VisualTreeHelper.GetChild(b, 0); //this gives the buttonChrome
MenuItem m = LogicalTreeHelper.FindLogicalNode(d, "DropDownButton") as MenuItem;
m.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(m_PreviewMouseLeftButtonUp);
Now that we have the handler simply set the IsOpen property to true and also set the placement of the ContextMenu at the bottom
b.ContextMenu.PlacementTarget = b;
b.ContextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
ContextMenuService.SetPlacement(b, System.Windows.Controls.Primitives.PlacementMode.Bottom);
b.ContextMenu.IsOpen = true;
The last part is ensuring that the context menu is not opened when you right click on the button or through the keyboard ContextMenu key. This is acheived in the ContextMenuOpening handler.
b.ContextMenu.IsOpen = false;
The complete project is attached ...
Comments
Anonymous
October 26, 2006
I am having problems with the <mwt:ButtonChrome> tag. I added this: xmlns:mwt="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Royale" to the top but I still get an error that says: The tag 'ButtonChrome' does not exist in XML namespace 'clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Royale'. Any thoughts?Anonymous
October 26, 2006
check to see that you have the PresentationFramework.Royale dll referenced in the project....Anonymous
October 28, 2006
Thanks a lot for the idea Lester. The ContextMenu method works well, but what if you want a "real" context menu too? Well, I had a play around and posted a blog entry with an adapted version of this control at, http://andyonwpf.blogspot.com/2006/10/dropdownbuttons-in-wpf.htmlAnonymous
October 29, 2006
yeah the above does assume that you would not require a normal context menu... as a button doesnt normally provide that and it is also counter intuitive...Anonymous
October 30, 2006
Very good point. I can't think of any time I've seen a button with a context menu. In fact, if a button had some form on menu associated with it, I'd expect a split-button anyway. Your solution also has the advantage that you don't even need to create a custom control, just a normal button with a context menu and your code in a Click handler in the Window class for example. This solution I feel is the best one if you are writing a UI and just want to add a split button to get the job done. In my case I'm trying to in the end write a control library. In this situation, who am I to tell the user of the control how they should be using it? In fact in WPF this becomes more of an issue - after all, the control user can style it to look nothing like a button any longer. Maybe even a situation where a drop-down and a context menu are applicable. In this case I prefer my more flexible implementation.