Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic menu in WPF

Tags:

c#

wpf

How to add menu items to menu control (not contextmenu) in WPF from a database table with Bindings and Observable collections?. I have this menu:

<Menu HorizontalAlignment="Left" Height="27" VerticalAlignment="Top" Width="649">
    <MenuItem Header="_File">
       <MenuItem Header="_Exit" Command="{Binding ExitCommand}"/>
       </MenuItem>
    <MenuItem Header="_MyMenu">
       <MenuItem Header="_SubMenu1" Command="{Binding  SubMenu1Command}" />
       <MenuItem Header="_SubMenu2" Command="{Binding  SubMenu2Command}" />
    </MenuItem>
</Menu>

The "SubMenu1" and "_SuMenu2" are values from the database table:

codSubMenu | SubMenuColum | CommandColumn

1__________|SubMenu1_____|SubMenu1Command 2__________|SubMenu2_____|_SubMenu2Command

I need something this:

<Menu HorizontalAlignment="Left" Height="27" VerticalAlignment="Top" Width="649"
    ItemsSource="{Binding ObservableCollectionMenu}">
    <MenuItem Header="_File">
       <MenuItem Header="_Exit" Command="{Binding ExitCommand}"/>
    </MenuItem>
    <MenuItem Header="_MyMenu">
        <MenuItem Header="{Binding  ObservableCollectionMenu.SubMenuColumn}" Command="{Binding  ObservableCollectionMenu.CommandColumn}" />
    </MenuItem>
</Menu>

When I run the app the menu must show this when I press the options File and MyMenu:

File | MyMenu

Exit | SubMenu1

___| SubMenu2

like image 408
Ejrr1085 Avatar asked Oct 10 '13 14:10

Ejrr1085


2 Answers

Use the ItemsSource property of the Menu and the MenuItems (in a style) to bind your collections:

<Menu ItemsSource="{Binding YourCollection}" />

and

<Style TargetType="MenuItem">
    <Setter Property="Header" Value="{Binding Path=Name}" />
    <Setter Property="ItemsSource" Value="{Binding Path=Children}" />
</Style>

Edit: For command binding do the following:

  1. Add a setter like this to the template of the MenuItem:

    <Setter Property="Command" Value="{Binding Path=Command}" />
    
  2. Use this structure for a MenuItem view model:

    public class BindableMenuItem
    {
         public string Name { get; set; }
         public BindableMenuItem[] Children { get; set; }
         public ICommand Command { get; set; }
    }
    
  3. Add the root items to a collection of BindableMenuItems and bind this collection to the menu.

like image 97
cguedel Avatar answered Nov 20 '22 01:11

cguedel


This is how I solved it,

I created a MenuItem class (notice that it has a list of Items so you can build sub-menus):

public class MenuItem : ModelBase<MenuItem>
{
    private List<MenuItem> _Items;

    public MenuItem(string header, ICommand command)
    {
        Header = header;
        Command = command;
    }

    public MenuItem()
    {

    }

    public string Header { get; set; }

    public List<IMenuItem> Items
    {
        get { return _Items ?? (_Items = new List<IMenuItem>()); }
        set { _Items = value; }
    }

    public ICommand Command { get; set; }
    public string CommandName { get; set; }
    public object Icon { get; set; }
    public bool IsCheckable { get; set; }
    private bool _IsChecked;
    public bool IsChecked
    {
        get { return _IsChecked; }
        set
        {
            _IsChecked = value;
            NotifyPropertyChanged(m=>m.IsChecked);
        }
    }

    public bool Visible { get; set; }
    public bool IsSeparator { get; set; }
    public string InputGestureText { get; set; }
    public string ToolTip { get; set; }
    public int MenuHierarchyID { get; set; }
    public int ParentMenuHierarchyID { get; set; }
    public string IconPath { get; set; }
    public bool IsAdminOnly { get; set; }
    public object Context { get; set; }
    public IMenuItem Parent { get; set; }
    public int int_Sequence { get; set; }
    public int int_KeyIndex { get; set; }
}

And a View:

<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=MainMenu}">
    <Menu.ItemContainerStyle>
        <Style>
            <Setter Property="MenuItem.Header" Value="{Binding Path=Header}" />
            <Setter Property="MenuItem.ItemsSource" Value="{Binding Path=Items}" />
            <Setter Property="MenuItem.Icon" Value="{Binding Path=Icon}" />
            <Setter Property="MenuItem.IsCheckable" Value="{Binding Path=IsCheckable}" />
            <Setter Property="MenuItem.IsChecked" Value="{Binding Path=IsChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
            <Setter Property="MenuItem.Command" Value="{Binding Path=Command}" />
            <!--<Setter Property="MenuItem.CommandParameter" Value="{Binding Path=IsChecked}"/>-->
            <Setter Property="MenuItem.CommandParameter" Value="{Binding Path=.}"/>
            <Setter Property="MenuItem.InputGestureText" Value="{Binding Path=InputGestureText}"/>
            <Setter Property="MenuItem.ToolTip" Value="{Binding Path=ToolTip}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=IsSeparator}" Value="true">
                    <Setter Property="MenuItem.Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type MenuItem}">
                                <Separator Style="{DynamicResource {x:Static MenuItem.SeparatorStyleKey}}" />
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Menu.ItemContainerStyle>
</Menu>

Where MainMenu is an ObservableCollection property in my main ViewModel, which you can populate from your database.

    public ObservableCollection<MenuItem> MainMenu
    {
        get { return _MainMenu; }
        set
        {
            _MainMenu = value;
            NotifyPropertyChanged(x => x.MainMenu);
        }
    }
like image 30
Adolfo Perez Avatar answered Nov 19 '22 23:11

Adolfo Perez