How to define a Command for a ComboBox? - c#

I'm wondering if is possible bound a Command to the ComboBox, I've actually implemented the Command logic on a Menu, in this way:
<Menu HorizontalAlignment="Left" VerticalAlignment="Stretch">
<MenuItem Header="Theme" Width="100"
ItemContainerStyle="{StaticResource ThemeColorMenuItemStyle}"
ItemsSource="{Binding Themes, Mode=OneTime}" />
</Menu>
where the ItemContainerStyle have this structure:
<Style x:Key="AccentColorMenuItemStyle"
BasedOn="{StaticResource MetroMenuItem}" TargetType="{x:Type MenuItem}">
<Setter Property="CommandParameter" Value="{Binding }" />
<Setter Property="Command" Value="{Binding DataContext.ApplyAccentCommand,
RelativeSource={RelativeSource AncestorType=Window}}" />
<Setter Property="Header" Value="{Binding Name, Mode=OneWay}" />
<Setter Property="Icon" Value="{StaticResource AccentMenuIcon}" />
</Style>
and this is the command:
public ICommand ApplyAccentCommand { get; } = new SimpleCommand(o => ApplyAccent((Swatch)o));
private static void ApplyAccent(Swatch swatch)
{
new PaletteHelper().ReplaceAccentColor(swatch);
}
this MenuItem bound a Theme collection provided by MaterialDesignInXaml as Swatch model, that have this class:
public class Swatch
{
public Swatch(string name, IEnumerable<Hue> primaryHues, IEnumerable<Hue> accentHues);
public string Name { get; }
public Hue ExemplarHue { get; }
public Hue AccentExemplarHue { get; }
public IEnumerable<Hue> PrimaryHues { get; }
public IEnumerable<Hue> AccentHues { get; }
public bool IsAccented { get; }
public override string ToString();
}
so, returning to the question: is possible have this logic on a ComboBox? 'cause the MenuItem doesn't have the SelectedItem property, and I need this.

You can use Blend Behaviors and bind an event to a command. You will need to refer System.Windows.Interactivity namespace, which you can get by installing the Expression.Blend.Sdk NuGet package.
Once installed, add the following XAML namespace to your page:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
And then use it as follows:
<ComboBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding MyCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
The MvvmLight toolkit also offers more advanced version of InvokeCommandAction called EventToCommand that allows you to specify a EventArgsConverter as well to be able to get a specific value from the event's EventArgs instance.

Related

Binding a dynamic list to a column of ComboBoxes in a DataGrid only works until actually showing the list

I'm working on a WPF application according to the MVVM pattern and am facing a challenge that I abstracted in the code below.
The app contains a DataGrid with 2 ComboBox columns (each generated in a different manner). The aim is to have a ComboBox present only those items that have not yet been selected by the other ComboBoxes in the same column.
The comboboxes are Bound to an ObservableCollection of Professions. Each profession has a Boolean "Selectable", and a ComboBox should only show those entries with a value of "true".
The list contains:
Painter
Poet
Scientist
To simulate an interactive Command from XAML to the ViewModel, I placed a button that will set the Scientist to Selectable to "false".
App.xaml:
<Application x:Class="wpf_ComboBoxColumn.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
</Application>
MainWindow.xaml.cs:
using System.Windows;
namespace wpf_ComboBoxColumn
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
}
MainWindow.xaml:
<Window x:Class="wpf_ComboBoxColumn.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:wpf_ComboBoxColumn"
xmlns:viewModel="clr-namespace:wpf_ComboBoxColumn"
Title="Combobox Column Binding" Height="350" Width="460">
<Window.Resources>
<ResourceDictionary>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="ItemsSource"
Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Selectable}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding Selectable}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.DataContext>
<viewModel:MainViewModel />
</Grid.DataContext>
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridComboBoxColumn
Header="ComboBoxColumn"
SelectedValueBinding="{Binding Description, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedValuePath="Description"
DisplayMemberPath="Description"
>
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Professions}"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Professions}"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
<DataGridTemplateColumn Header="TemplateColumn">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Path=DataContext.Professions, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
DisplayMemberPath="Description"
SelectedValue="{Binding Profession, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="210,290,0,0" VerticalAlignment="Top" Width="75" Command="{Binding DebugCommand}"/>
</Grid>
</Window>
CustomCommand.cs (ICommand implementation):
using System;
using System.Windows.Input;
namespace wpf_ComboBoxColumn
{
public class CustomCommand: ICommand
{
private readonly Action<object> execute;
public CustomCommand(Action<object> execute)
{
this.execute = execute;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
}
MainViewModel.cs:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace wpf_ComboBoxColumn
{
public class NotifyUIBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Profession
{
public string Description { get; set; }
public Boolean Selectable { get; set; }
}
public class Person
{
public string Name { get; set; }
public string Profession { get; set; }
}
public class MainViewModel : NotifyUIBase
{
public ObservableCollection<Person> People { get; set; }
public ObservableCollection<Profession> Professions { get; set; }
public ICommand DebugCommand { get; set; }
public MainViewModel()
{
DebugCommand = new CustomCommand(Debug);
People = new ObservableCollection<Person>
{
new Person{Name="Tom", Profession="" },
new Person{Name= "Dick", Profession="" },
new Person{Name= "Harry", Profession="" }
};
Professions = new ObservableCollection<Profession>
{
new Profession{ Description="Painter", Selectable=true},
new Profession{ Description="Poet", Selectable=true},
new Profession{ Description="Scientist", Selectable=true},
};
}
private void Debug(object obj)
{
Professions[2].Selectable = false;
}
}
}
Now consider the following scenario (I'm still trying to figure out how to include screen shots):
Open the app: This will show a grid with 3 columns:
First column shows the names "Tom", "Dick" and "Harry".
Second column contains a ComboBox for each person. It requires multiple clicks to open.
Third column also contains a ComboBox for each person. This one is recognizable as such.
Choose "Scientist" for Tom
Click the button (to fake that we executed code that changed Profession.Selectable)
Click on the Combobox for Dick
This will indeed show the remaining Professions (without Scientist), for the rightmost column of ComboBoxes. The leftmost column will still show all options, so this one fails right away.
Click on the Combobox for Tom again
This will, even for the rightmost column of ComboBoxes, show all options again (or rather: still)!
It turns out that the list, once shown, is not dynamically updated. Until we click on it, it is (makes me think of Quantum Mechanics, but that's another story)
The question is: Is there a way to force a refresh of the ItemsSource? Preferrably, of course, respecting MVVM, but at this point, I'll go for any working solution, using either ComboBox-type.
Thanks!
You need to raise the PropertyChanged event on the Selectable property. You're binding to it, and then you're changing it, so if you want the view to change based on this property, it needs to raise PropertyChanged.

WPF command not working for submenu items in MVVM application

I have a menu which is built from a collection at runtime. This is all working as shown.
But if the menu contains child items (Child1, Child2 etc) the ReactiveCommand MenuCommand is never called.
If I remove all children from the menu so that the menu only contains parent items then MenuCommand is called. I am fairly new to WPF. I have re-created the problem in a sample app (code below). There are no visible binding errors in VS.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
public class Service
{
public Service(string menuHeading, string menuSubHeading)
{
MenuHeading = menuHeading;
MenuSubHeading = menuSubHeading;
}
public string MenuHeading { get; set; }
public string MenuSubHeading { get; set; }
}
public static class MenuBuilder
{
public static ReactiveList<MenuItem> Build(ReactiveList<Service> services)
{
ReactiveList<MenuItem> menuItems = new ReactiveList<MenuItem>();
foreach (var service in services)
{
AddOrUpdate(menuItems, service);
}
return menuItems;
}
private static void AddOrUpdate(ReactiveList<MenuItem> menu, Service service)
{
if (menu.Any((_ => _.Header.ToString() == service.MenuHeading)))
{
var item = menu.FirstOrDefault(x => x.Header.ToString() == service.MenuHeading);
item.Items.Add(new MenuItem() { Header = service.MenuSubHeading });
//if above line removed MenuCommand works
}
else
{
menu.Add(new MenuItem() { Header = service.MenuHeading });
var item = menu.FirstOrDefault(x => x.Header.ToString() == service.MenuHeading);
item.Items.Add(new MenuItem() { Header = service.MenuSubHeading });
//if above line removed MenuCommand works
}
}
}
public class MainWindowViewModel : ReactiveObject
{
public MainWindowViewModel()
{
MenuCommand = ReactiveCommand.Create<Object>(selectedItem => OnMenuItemSelected(selectedItem));
MenuCommand.Execute().Subscribe();
}
public ReactiveCommand<Object, Unit> MenuCommand { get; }
private ReactiveList<MenuItem> servicesMenu;
private ReactiveList<Service> Services = new ReactiveList<Service>()
{
new Service("Parent1", "Child1"),
new Service("Parent2", "Child1"),
new Service("Parent2", "Child2"),
};
public ReactiveList<MenuItem> ServicesMenu
{
get
{
if (servicesMenu == null)
{
servicesMenu = MenuBuilder.Build(Services);
return servicesMenu;
}
else
{
return servicesMenu;
}
}
}
private void OnMenuItemSelected(Object selectedItem)
{
//This method is only called when the menu does not contain any child items
}
}
<Grid>
<StackPanel Orientation="Vertical">
<Button Name="Button" Content="Button" Padding="5" HorizontalAlignment="Left"
Tag="{Binding RelativeSource={RelativeSource Self}, Path=DataContext}">
<Button.ContextMenu>
<ContextMenu x:Name="MainMenu" ItemsSource="{Binding ServicesMenu}"
DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding DataContext.MenuCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}}" />
<Setter Property="CommandParameter"
Value="{Binding RelativeSource={RelativeSource Self}}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</Grid>
Updated XAML after suggestions form Glenn
<Grid>
<StackPanel Orientation="Vertical">
<Button Name="Button" Content="Button" Padding="5" HorizontalAlignment="Left"
Tag="{Binding RelativeSource={RelativeSource Self}, Path=DataContext}">
<Button.ContextMenu>
<ContextMenu x:Name="MainMenu" ItemsSource="{Binding ServicesMenu}"
DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Command" Value="{Binding Command}" />
<!--<Setter Property="Command" Value="{Binding MenuCommand}" /> was also tried-->
<Setter Property="CommandParameter" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</Grid>
I suspect this is because child items placement target wouldn't be the Button like you expect, it would be the parent MenuItem.
One way I've gotten around this in the past is using MVVM approach for these type of menu items.
Create a Menu Item VM (you call them Service above) for your items (similar to what you already doing). In the VM have a Command property and pass in your command as part of it's constructor. Then you can just do {Binding MenuCommand} from your Item Container Style.
Also don't create the MenuItem's directly in your ViewModel, instead just bind direct to the Services. I would also recommend creating your sub-services as a ObservableCollection directly inside your Service, then in your item container set the ItemsSource property to bind to the sub-children of your Services.

Create Hierarchical ContextMenu dynamically MVVM

I would like to create dynamically Hierarchical ContextMenu from data in ViewModel.
In ViewMode, I defined ContextMenuAction:
public class ContextMenuAction : ViewModelBase
{
public string Header { get; set; }
public ICommand Action { get; set; }
public Brush Icon { get; set; }
public ObservableCollection<ContextMenuAction> SubActions { get; set; } = new ObservableCollection<ContextMenuAction>();
}
In View:
<ContextMenu ItemsSource="{Binding Path=PlacementTarget.Tag.Actions, RelativeSource={RelativeSource Self}}">
<ContextMenu.ItemTemplate >
<DataTemplate DataType="MenuItem">
<MenuItem/>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="ItemsSource" Value="{Binding SubActions}"/>
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Command" Value="{Binding Action}"/>
</Style>
</ContextMenu.ItemContainerStyle>
This is result, there no text in context menu.
I already check output window to check binding, all bindings work, there is no exception.
Please help me to find out the reason, thank in advance!
You should define a HierarchicalDataTemplate:
<ContextMenu ItemsSource="{Binding Path=PlacementTarget.Tag.Actions, RelativeSource={RelativeSource Self}}">
<ContextMenu.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ContextMenuAction}" ItemsSource="{Binding SubActions}">
<TextBlock Text="{Binding Header}" />
</HierarchicalDataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Action}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>

How do I add a command to items in dynamically generated ContextMenu

I have a context menu that is being populated from an ObservableCollection. I want the user to be able to click on any of those items, then a method is called passing the clicked item's text as a parameter.
I've started by following the answer to this question. However, I'm getting an error in my console output and my method is not being called.
System.Windows.Data Error: 40 : BindingExpression path error: 'FunctionToCall' property not found on 'object' ''MenuItem' (Name='myMenu')'. BindingExpression:Path=FunctionToCall; DataItem='MenuItem' (Name='myMenu'); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')
here is my xaml
<MenuItem Name="myMenu" Header="display text" ItemsSource="{Binding}" >
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding FunctionToCall, RelativeSource={RelativeSource AncestorType=MenuItem}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
And my view model code
RelayCommand _command;
public ICommand FunctionToCall
{
get
{
if (_command == null)
{
_command = new RelayCommand(p => this.InnerMethod(p));
}
return _command ;
}
}
public void InnerMethod(object parameter)
{
....
The other answer suggests playing around with adding one or two DataContexts to the Binding, I've tried this and I still get the same error although it says DataContext property cannot be found instead of FunctionToCall.
I found the definition of RelayCommand here.
The real problem is with your binding. Use the DataContext property of MenuItem to actually get to the ViewModel instance
<MenuItem Name="myMenu" Header="display text" ItemsSource="{Binding}" >
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.FunctionToCall, RelativeSource={RelativeSource AncestorType=MenuItem}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
MenuItem will get ViewModel as DataContext. So actually we want..
MenuItem.DataContext.FunctionToCall
Hopefully you don't need the different menu items to bind to different commands else you have to change your design a little.
As Per Your Comments:
You'll need a List<MenuItem> MenuItems to bind with ContextMenu ItemSource property as
public class MenuItem
{
public string Header { get; set; }
public ICommand Command { get; set; }
}
XAML:
<ContextMenu ItemsSource="{Binding MenuItems}" >
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" >
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Command" Value="{Binding Command}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
And add as many contextmenu item you want in your ViewModel AS YOU WANT.
This is how to do it.
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
xaml
<MenuItem Header="{Binding Item1}" Command="{Binding FunctionToCall}" CommandParameter="{Binding Header, RelativeSource={RelativeSource Self}}"/>
ViewModel
public class ViewModel
{
ICommand _cmd = new CustomCommand();
public ICommand FunctionToCall
{
get { return _cmd; }
set { _cmd = value; }
}
public string Item1 { get; set; }
public ViewModel() { Item1 = "1Header"; }
}
Command
public class CustomCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show(parameter.ToString());
}
}
So, in your case assuming you want to pass Header of MenuItem as parameter to your command, do following changes :
<Setter Property="Command" Value="{Binding FunctionToCall}"/>
<Setter Property="CommandParameter" Value="{Binding Header, RelativeSource={RelativeSource Self}}"/>

Dynamic menu in 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
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.
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);
}
}
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.

Categories

Resources