Sunday, October 29, 2006

DropDownButtons in WPF

Whilst Windows Presentation Foundation (WPF) provides good support for menus, one omission is a drop-down button. By this I mean a button, that when clicked will result in a menu dropping down from the control.

So I started to write my own. Easy I thought, just style a MenuItem to look like a button. Well, this doesn't work as the MenuItem control will only work if it is a child of a Menu control. I could just wrap all my DropDownButtons in Menus, but that becomes a little messy wrapping everything in extra menus. I tried a number of other approaches, none of which worked as I would like.


This was several months ago. Recently however I was informed of a couple of related blog articles at Sheva's Techspace and Lester's piece on WPF. In particular I liked Lester's approach. This is to use the Button's ContextMenu to apply add a context menu, and then to use code to open this on a left mouse click. A simple solution that works well. But what if I wanted to have a real context-menu too?


Solution: Well, in fact ContextMenu doesn't need to be attached to a control to work. All you need to do is create a new ContextMenu object, add your MenuItems, and set IsOpen to true. So I put together the control as below.



public class DropDownButton : ToggleButton
{
// *** Dependency Properties ***


public static readonly DependencyProperty DropDownProperty = DependencyProperty.Register("DropDown", typeof(ContextMenu), typeof(DropDownButton), new UIPropertyMetadata(null));

// *** Constructors ***

public DropDownButton()
{
// Bind the ToogleButton.IsChecked property to the drop-down's IsOpen property

Binding binding = new Binding("DropDown.IsOpen");
binding.Source = this;
this.SetBinding(IsCheckedProperty, binding);
}

// *** Properties ***

public ContextMenu DropDown
{
get
{
return (ContextMenu)GetValue(DropDownProperty);
}
set
{
SetValue(DropDownProperty, value);
}
}

// *** Overridden Methods ***

protected override void OnClick()
{
if (DropDown != null)
{
// If there is a drop-down assigned to this button, then position and display it

DropDown.PlacementTarget = this;
DropDown.Placement = PlacementMode.Bottom;

DropDown.IsOpen = true;
}
}
}


This control derives directly from ToggleButton so we inherit the full behavior and styling of a button. We then provide a DropDown dependency property that takes a ContextMenu to show for the drop-down. In the OnClick event we simply position the menu under the control, and open it by setting IsOpen to true. Finally to tidy up, we bind the ToggleButton's IsChecked property to the drop-down menu's IsOpen property to ensure that these are synchronized.


To use this in your own code you simply use,



<ctrl:DropDownButton Content="Drop-Down">
<ctrl:DropDownButton.DropDown>
<ContextMenu>
<MenuItem Header="Item 1"/>
<MenuItem Header="Item 2"/>
<MenuItem Header="Item 3"/>
</ContextMenu>
</ctrl:DropDownButton.DropDown>
</ctrl:DropDownButton>



Technorati tags: , ,

Post a Comment