WPF Toolbar Dropdown

Häufig möchte man zur Gruppierung in der Toolbar einen Dropdown-Button anzeigen, auf dessen Klick sich ein Menü mit weiteren Einträgen öffnet. WPF bietet dafür keine vorgefertigte Lösung, die kann man sich jedoch recht leicht selber bauen.

Das Prinzip

Das Prinzip ist einfach: Man legt für den Button ein Kontextmenü fest und zeigt dieses auch dann an, wenn der Button normal angeklickt wird.

Für diesen Zweck verwenden wir einen ToggleButton, damit der Button als aktiv angezeigt wird, solange das Menü angezeigt wird. Das Kontextmenü des Buttons missbrauchen wir als das Menü, das beim Klick angezeigt wird. Beim Check des Buttons (also beim ersten Klick) öffnen wir das Kontextmenü und platzieren es unter den Button, sobald sich das Kontextmenü schließt, setzen wir den Button wieder nicht aktiv.

Die Umsetzung

<ToolBarTray>
    <ToolBar>
        <ToggleButton Checked="DropdownButton_Checked" MouseRightButtonUp="DropdownButton_MouseRightButtonUp"> 
            <ToggleButton.Content>
                <StackPanel Orientation="Horizontal">
                    <!-- Image Source="mysrc.png" /-->
                    <TextBlock Text="Dropdown" />
                    <Path Margin="4" Width="5" Fill="Black" Stretch="Uniform" HorizontalAlignment="Right" VerticalAlignment="Center" Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z" />
                </StackPanel>
            </ToggleButton.Content>
            <ToggleButton.ContextMenu>
                <ContextMenu Closed="ContextMenu_Closed">
                    <MenuItem Header="Item 1" />
                    <MenuItem Header="Item 2" />
                </ContextMenu>
            </ToggleButton.ContextMenu>
        </ToggleButton>
    </ToolBar>
</ToolBarTray>
private void DropdownButton_Checked(object sender, RoutedEventArgs e)
{
    var menu = (sender as ToggleButton).ContextMenu;
    menu.PlacementTarget = sender as ToggleButton;
    menu.Placement = PlacementMode.Bottom;
    menu.IsOpen = true;
}

private void ContextMenu_Closed(object sender, RoutedEventArgs e)
{
    ((sender as ContextMenu).PlacementTarget as ToggleButton).IsChecked = false;
}

private void DropdownButton_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
    e.Handled = true;
}

Die Ereignishandler im Code-Behind sind vom sender abhängig und können daher auch für mehrere Dropdown-Buttons verwendet werden. Der MouseRightButtonUp-Ereignishandler dient nur dazu, damit das Kontextmenü nicht bei Rechtsklick auf den Button angezeigt wird. Wen das nicht stört, der kann das auch weglassen. Einziger Nachteil an dieser Methode ist, dass man kein eigenes Kontextmenü für den Rechtsklick setzen kann.

Geht auch als Klasse

Das ganze kann man sich natürlich auch in eigene Klasse packen und dann mit einer eigenen Property für das Dropdown-Menü versehen. Damit ist das Problem mit dem Kontextmenü behoben und der XAML-Code wird auch einfacher.

<ext:DropdownButton Content="Dropdown">
    <ext:DropdownButton.DropdownMenu>
        <ContextMenu>
            <MenuItem Header="Item 1" />
            <MenuItem Header="Item 2" />
        </ContextMenu>
    </ext:DropdownButton.DropdownMenu>
    <ext:DropdownButton.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Context Item 1" />
            <MenuItem Header="Context Item 2" />
        </ContextMenu>
    </ext:DropdownButton.ContextMenu>
</ext:DropdownButton>