Reputation: 1071
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
Upvotes: 8
Views: 20559
Reputation: 1112
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:
Add a setter like this to the template of the MenuItem:
<Setter Property="Command" Value="{Binding Path=Command}" />
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; }
}
Add the root items to a collection of BindableMenuItems
and bind this collection to the menu.
Upvotes: 10
Reputation: 1071
I don't have a quick solution in XAML. I needed get submenus items from database, according specific profiles, some users had all items others only 2 or 3 items. The unique way is create the menu in XAML with disabled items, pass the menu reference to ViewModel(if is MVVM App) and compare with the ObservableCollection, only the items equals are enabled:
<menu horizontalalignment="Left" height="27" verticalalignment="Top" width="649" name="menu1">
<menuitem header="_File">
<menuitem header="_Exit" command="{Binding ExitCommand}" />
</menuitem>
<menuitem header="_MyMenu">
<menuitem header="_SubMenu1" command="{Binding Command1}" isenabled="False" />
<menuitem header="_SubMenu2" command="{Binding Command2}" isenabled="False" />
</menuitem>
</menu>
ViewModel:
for (int i = 0; i < ObservableCollectionMenu.Count; i++)
{
for (int j = 0; j < ((MenuItem)menu1.Items[1]).Items.Count; j++)
{
if (((MenuItem)((MenuItem)menu1.Items[1]).Items[j]).Header.ToString().Equals(ObservableCollectionMenu[i].SubMenuColumn))
{
((MenuItem)((MenuItem)menu1.Items[1]).Items[j]).IsEnabled = true;
break;
}
}
}
Thanks to all who answered my question, stackoverflow has better help that codeproject.
Upvotes: 0
Reputation: 2874
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);
}
}
Upvotes: 4