Einfaches WPF-Popup-Menu, das sich mit einem Button öffnen lässt

Ein typische WPF-Benutzeroberfläche benötigt oft mit Button öffenbare Menüs. Obwohl sich viele Lösungen im Internet befinden, sind sie kompliziert und erfordern, dass man ein neues Steuerelement erstellt. Das ist aber unnötig. WPF enthält schon ein Menü Bedienelement, das hierarchische Daten perfekt darstellen kann. Es muss nur richtig konfiguriert werden, damit das Wurzelelement des Menüs als Button dargestellt wird.

WPF Menü-Button

Erstens muss man die ViewModel-Klasse erstellen. TestMenuItemViewModel enthält nicht nur die Title, IconSource und Command Eigenschaften, sondern auch eine Liste von Kindelementen des Menüpunktes.

public class TestMenuItemViewModel
{
    public string IconSource { get; set; }
    public string Title { get; set; }
    public IEnumerable<TestMenuItemViewModel> Items { get; set; }
    public ICommand Command { get; set; }
}

Die ViewModel-Klasse enthält ein einziges Wurzelelement, das Hauptmenü, das unter dem Namen TestMenu zum DataContext eingefügt wird.

public TestMenuItemViewModel TestMenu { get; } = new TestMenuItemViewModel
    {
        Title = "Menu",
        IconSource = "image1.png",
        Items = new[]
        {
            new TestMenuItemViewModel { Title = "Submenu 1" },
            new TestMenuItemViewModel
            {
                Title = "Submenu 2",
                Items = new[]
                {   
                    new TestMenuItemViewModel{ Title = "Submenu 2-1", IconSource = @"image3.png" },
                    new TestMenuItemViewModel{ Title = "Submenu 2-2" }
                }
            },
            null,
            new TestMenuItemViewModel{ Title = "Submenu 3", IconSource = "image2.png" }
        }
    }

Der Xaml-Code des Menüs basiert auf der Modifizierung vom ItemContatinerStyle. Der Hauptmenüpunkt ist ein MenuItem mit einer eigenen Anlage von Steuerelementen, die einen Button ausmachen. Die Untermenüpunkte sind auch MenuItems, deren Style innerhalb vom Hauptmenü gesetzt wird und deshalb bleibt der Style des Hauptmenüs unberührt. Wenn ein TestMenuItem null ist, ein Separator, statt eines Menüpunktes wird eingeführt. Der Separator wird durch einen DataTrigger erstellt, der das ganze Styletemplate überschreibt.

<Menu DataContext="{Binding TestMenu}">
    <Menu.Resources>
        <Image x:Key="MenuIcon" x:Shared="False" Source="{Binding IconSource}" />
    </Menu.Resources>
    <MenuItem ItemsSource="{Binding Items}">
        <MenuItem.ItemContainerStyle>
            <Style TargetType="{x:Type MenuItem}">
                <Setter Property="Header" Value="{Binding Title}" />
                <Setter Property="ItemsSource" Value="{Binding Items}" />
                <Setter Property="Icon" Value="{StaticResource MenuIcon}"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding}" Value="{x:Null}">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate>
                                    <Separator Style="{StaticResource {x:Static MenuItem.SeparatorStyleKey}}" />
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </MenuItem.ItemContainerStyle>
        <MenuItem.Header>
            <StackPanel>
                <Image Source="{Binding IconSource}"></Image>
                <TextBlock Text="{Binding Title}"></TextBlock>
            </StackPanel>
        </MenuItem.Header>
    </MenuItem>
</Menu>

Wenn man verschiedene TestMenuItem-Type unterstützen, und je nach Typ die Darstellung ändern will, geben die DataTriggers auch dafür eine Lösung. Den Trigger muss man mit dem Datentyp verbinden und der Styletemplate dem Typ entsprechend gestalten.