Sub Menu Item Command MVVM - wpf

<MenuItem Header="Flag(s)" ItemsSource="{Binding Path=LineItemFlags}" Command="{Binding AssignFollowupCommand}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding FlagName}">
</TextBlock>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
For the parent Item i mean menu item i have a command it's invoking i want to know how to setup command for Sub Menu item

Rather than setting the ItemTemplate, set the ItemContainerStyle. Give it a style that sets the Header and Command properties of the menu item.
<MenuItem Header="_Recent Files" ItemsSource="{Binding RecentFiles}">
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Header" Value="{Binding FileName}"/>
<Setter Property="MenuItem.Command" Value="{Binding Open}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
Full example and description on Code Project.

Related

Xceed extended WPF datagrid select row with right click

I am trying to add a context menu to an Xceed extended WPF datagrid. I am able to show the context menu and the commands fire from the menu, but right clicking on a row does not set it as the selected row and so the wrong record is used by the command.
Is there a way to change the way the selected item is set so that it can be updated by right click?
<xcdg:DataGridControl ItemsSource="{Binding Source={StaticResource RecordsForList}}" SelectedItem="{Binding SelectedRecord}">
<xcdg:DataGridControl.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding OpenCommand}" Header="Open" />
</ContextMenu>
</xcdg:DataGridControl.ContextMenu>
</xcdg:DataGridControl>
Instead of changing the way selecting an item works, you could pass the current item as a command parameter to the command if you set the ContextMenu property of each individual DataRow:
<xcdg:DataGridControl ItemsSource="{Binding Source={StaticResource RecordsForList}}" SelectedItem="{Binding SelectedRecord}">
<xcdg:DataGridControl.ItemContainerStyle>
<Style TargetType="xcdg:DataRow">
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=xcdg:DataGridControl}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="{Binding PlacementTarget.Tag.OpenCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding}" Header="Open" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</xcdg:DataGridControl.ItemContainerStyle>
</xcdg:DataGridControl>
The other option would be to write some code in the view that actually selects an item on right-click, e.g.:
<xcdg:DataGridControl ItemsSource="{Binding Source={StaticResource RecordsForList}}" SelectedItem="{Binding SelectedRecord}">
<xcdg:DataGridControl.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding OpenCommand}" Header="Open" />
</ContextMenu>
</xcdg:DataGridControl.ContextMenu>
<xcdg:DataGridControl.ItemContainerStyle>
<Style TargetType="xcdg:DataRow">
<EventSetter Event="PreviewMouseRightButtonDown" Handler="xgrid_PreviewMouseRightButtonDown" />
</Style>
</xcdg:DataGridControl.ItemContainerStyle>
</xcdg:DataGridControl>
private void xgrid_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Xceed.Wpf.DataGrid.DataRow row = sender as Xceed.Wpf.DataGrid.DataRow;
xgrid.CurrentItem = row.DataContext;
}

MenuItem.Header and MenuItem.Icon DataTemplate

I have a menu with the two following menu items:
<MenuItem Header="Item1">
<MenuItem.Icon>
<cc:Icon ImageSource="..." Size="22"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Item2">
<MenuItem.Icon>
<cc:Icon ImageSource="..." Size="22"/>
</MenuItem.Icon>
</MenuItem>
As you can see each Item has a name as the "Header" and an Icon (my own control) as an an "Icon".
Instead of writing them explicitly in the XAML I want to Bind the ItemsSource to a List<MenuItem> object in my ViewModel, like this:
ItemsSource="{Binding Path=SomeMenu}"
My MenuItem class has the fields "String Name" and ImageSource Icon which I want to bind to the Header and the Icon parts of my menu item exactly how I did here.
I thought the right way was to use a DataTemplate, like this:
<Menu.ItemTemplate>
<DataTemplate>
<!-- TODO: Bind the Header and Icon properties-->
<TextBlock Text="{Binding Path=Name}"/>
<!-- how to bind the icon? -->
</DataTemplate>
<Menu.ItemTemplate>
But I don't understand how to define the Header and the Icon templates separately.
How do I accomplish this, than?
update: I want the <cc:Icon> to appear whereever in the menuItem template there is a <contentPresenter Source="Icon"> and the textBox the appear whereever there is a <contentPresenter Source="Header">
i believe this is what you want:
<Menu ItemsSource="{Binding SomeMenu}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Icon">
<Setter.Value>
<cc:Icon Source="{Binding Icon}"></Image>
</Setter.Value>
</Setter>
<Setter Property="Header" Value="{Binding Name}"/>
</Style>
</Menu.ItemContainerStyle>
</Menu>
or did i fail miserably again? :)

Command Binding of Submenu item in Context Menu

I have a TreeView with an hierarchical Data Template (2 levels). I have a context menu created on the first level of the tree view with 3 levels. I want to bind a command of my view model to the second level of the context menu. Unfortunately I can only get it to work when using a command in my Model, which is not what I want to do... If possible, I would like to do it all in XAML.
I have tested the pure xaml solutions given here and here.
In the designer, the "Tag" is underlined blue saying "Cannot resolve Property Tag in data context of type System.Windows.UIElement" or, if I use "Parent.PlacementTarget....", the designer tells me that PlacementTarget cannot be resolved in the data context of type System.Windows.DependencyObject.
The code is compileable, but my command is never reached.
This is what I have:
In a ResourceDictionary:
<DataTemplate DataType="{x:Type viewModel:UpdateToolViewModel}">
<view:UpdateToolView/>
</DataTemplate>
<DataTemplate x:Key="ToolNameDataTemplate" DataType="{x:Type src:Element}">
<Grid>
<TextBlock Text="{Binding Path=NameProperty.Value}" FontSize="12" />
</Grid>
</DataTemplate>
<HierarchicalDataTemplate x:Key="ToolGroupsDataTemplate" ItemsSource="{Binding Elements}" DataType="{x:Type src:ElementGroup}" ItemTemplate="{StaticResource ToolNameDataTemplate}">
<TextBlock Text="{Binding Path=TextProperty.Value}" FontSize="14" FontWeight="Bold" Tag="{Binding ElementName=UpdateToolControl}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Tool" ItemContainerStyle="{StaticResource ToolGroupContextMenuToolsItemStyle}" >
<MenuItem.ItemsSource>
<CompositeCollection>
<MenuItem Header="Add New ..." Command="{Binding PlacementTarget.Tag.DataContext.AddToolCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}}" />
<CollectionContainer Collection="{Binding Source={StaticResource AddToolContextMenuSource}}"/>
</CompositeCollection>
</MenuItem.ItemsSource>
</MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</HierarchicalDataTemplate>
In the UserControl:
<UserControl x:Class="...UpdateToolView" ... Name="UpdateToolControl">
<TreeView Name="ToolTreeView" ItemsSource="{Binding AllElementsInGroups}"
ItemTemplate="{StaticResource ToolGroupsDataTemplate}"
ItemContainerStyle="{StaticResource ToolTreeViewItemStyle}"/>
</UserControl>
I am already on the verge of using the command in my model, calling a method in my view model. Not nice, but I just don't seem to get it to work differently.
I was just going on, trying to figure out how to add a command to the CollectionContainer Items, when I found this: CollectionContainer Binding (sorry, its german, but the xaml code is the relevant thing here)
Now I have added the command in the ItemContainerStyle of the MenuItem and suddenly the whole thing works (although "Tag" is still underlined blue in the designer):
<converter:ElementToToolTipConverter x:Key="ElementToToolTipConverter"/>
<Style x:Key="ToolGroupContextMenuToolsItemStyle" TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="ItemsSource" Value="{Binding Children}"/>
<Setter Property="ToolTip" Value="{Binding Element, Converter={StaticResource ElementToToolTipConverter}}"/>
<Setter Property="Command" Value="{Binding PlacementTarget.Tag.DataContext.AddToolCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
Sometimes, it already helps thinking about something else... :)

How to get the Selected Item in Context Menu

I have a context menu that has binding items and I want to set up a command and command parameter so I know which item was clicked on but I don't know how.
<MenuItem Command="{Binding Sync}"
Header="Synchronize"
ItemsSource="{Binding ItemsToSync}">
<MenuItem.Icon>
<Image Height="25" Source="Sync.png" />
</MenuItem.Icon>
</MenuItem>
You can try something like this:
In this example I have a listview and I can right click and delete a selected item. The Reason I'm using RelativeSource here is because when it comes to passing parameters in menuitems, most of the time at this level you can't reach the datacontext of the page. Hope this helps.
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" Command="{Binding Path=DeleteDescriptions}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}" Name="MenuItem1">
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
That did not help but I was able to create my own solution.
<MenuItem Header="Synchronize" ItemsSource="{Binding ItemsToSync}">
<MenuItem.Icon>
<Image Height="25" Source="Sync.png" />
</MenuItem.Icon>
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Header" Value="{Binding Name}" />
<Setter Property="MenuItem.IsChecked" Value="{Binding IsCurrent}" />
<Setter Property="MenuItem.Command" Value="PT:Commands.SyncFromContextMenu" />
<Setter Property="MenuItem.CommandParameter" Value="{Binding}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</MenuItem>
I did have to create a static class for the command.

MenuItem databinding

I've been puzzling over this for a day or so with no luck - I'm probably missing something obvious. Basically, I have a context menu with two items. One is statically declared, and bound to a command. The other has no command of its own, but binds to a collection of viewmodels. So visually the menu should look something like:
Delete
Add
Item 1
Item 2
Where the items vary depending on the thing the context menu was bound to. Originally I had something like this:
<ContextMenu x:Key="itemContextMenu">
<MenuItem Header="_Delete"
Command="{Binding DeleteCommand}" />
<MenuItem Header="_Add" DataContext=""
ItemsSource="{Binding AvailableTypes}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Path=ItemType.Name}"
Command="{Binding Path=AddItemCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}"
CommandParameter="{Binding}" />
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
Which works, but gives me the nested MenuItems others have experienced. Based on a couple threads here on stackoverflow, I then tried this:
<ContextMenu x:Key="itemContextMenu">
<MenuItem Header="_Delete"
Command="{Binding DeleteCommand}" />
<MenuItem Header="_Add"
ItemsSource="{Binding AvailableTypes}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header"
Value="{Binding Path=ItemType.Name}" />
<Setter Property="Command"
Value="{Binding Path=AddItemCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}" />
<Setter Property="CommandParameter"
Value="{Binding}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
However, when I do that my bindings all fail with errors like:
BindingExpression path error: 'ItemType' property not found on 'object' ''String'
BindingExpression path error: 'AddItemCommand' property not found on 'object' ''Grid'
To me that says that the DataContext is getting lost when I use the ItemContainerStyle. What am I missing?
edit:
I think I had some red herrings in here, so I've simplified the examples further to try and narrow down the problem.
Working but screwy layout-wise:
<ContextMenu x:Key="itemContextMenu">
<MenuItem Header="_Delete" />
<MenuItem Header="_Add" DataContext=""
ItemsSource="{Binding AvailableTypes}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Path=ItemType.Name}" />
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
Non-working with a BindingExpression error:
<ContextMenu x:Key="itemContextMenu">
<MenuItem Header="_Delete" />
<MenuItem Header="_Add"
ItemsSource="{Binding AvailableTypes}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header"
Value="{Binding Path=ItemType.Name}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
Apparently this is a bug in 3.5. I upgraded my project to 4.0, and now everything works as expected.

Resources