Menu item won't show context menu - c#

I have a button that when clicked, displays a ContextMenu. In this ContextMenu, I have MenuItems. If the MenuItem is left clicked, it should execute a command. All this behavior works properly as of now with the following code:
<Button.ContextMenu>
<ContextMenu>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding StartContextMenuCommand}" />
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}, Path=Header}" />
</Style>
</ContextMenu.ItemContainerStyle>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.1}"/>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.2}"/>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.3}"/>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.4}"/>
</ContextMenu>
</Button.ContextMenu>
Now I want each menu item to have a context menu with one MenuItem. Since left clicking invokes a Command on a MenuItem, I want the right click behavior to display the following ContextMenu:
<ContextMenu>
<MenuItem Header="Set Default"></MenuItem>
</ContextMenu>
I tried putting this as a setter in the ItemContainerStyle. I also tried putting it as a ContextMenu of a MenuItem, but neither of my attempts, among others have worked. I'm sure I could come up with a hacky way to do this, but I want to keep it clean and simple.

Maybe i do not understand your question (my english is not... anything special):D But this should work if you just want multilevel contextmenu:
<ContextMenu>
<MenuItem Header="Top Level 1">
<MenuItem Header="Sub Level" />
<MenuItem Header="Sub Level" />
</MenuItem>
<MenuItem Header="Top Level 2">
<MenuItem Header="Sub Level" />
<MenuItem Header="Sub Level" />
</MenuItem>
</ContextMenu>

Here's my current fix to this issue, but I don't like it at all. I would like to avoid code behind as much as possible.
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding StartCommand}" />
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}, Path=Header}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu StaysOpen="True">
<MenuItem Header="Set As Default"/>
</ContextMenu>
</Setter.Value>
</Setter>
<EventSetter Event="PreviewMouseRightButtonUp" Handler="MenuItem_Click"/>
</Style>
</ContextMenu.ItemContainerStyle>
And the code behind:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
ButtonContextMenu.StaysOpen = true;
(sender as MenuItem).ContextMenu.IsOpen = true;
}

Related

WPF : Remove context menu items based on condition

I have a context menu in WPF with following items:
<ContextMenu x:Key="MyContextMenu">
<MenuItem Header="{x:Static localization:Resources.MyContext_Command1}" Command="{Binding Command1}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command2}" Command="{Binding Command2}" />
<Separator />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command3}" Command="{Binding Command3}" Visibility="{Binding IsItemActive, Converter={converters:BooleanToVisibilityConverter}}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command4}" Command="{Binding Command4}" Visibility="{Binding IsItemActive, Converter={converters:BooleanToVisibilityConverter}}" />
<Separator Visibility="{Binding IsItemActive, Converter={converters:BooleanToVisibilityConverter}}"/>
</ContextMenu>
With the above code these menu items(Command3 and Command4) are appearing grey(Disabled) when IsItemActive = false and appear black(Enabled) when IsItemActive = true. But i want my Menuitems(Command3 and Command4) and also Seperator to Disappear / Appear from context menu based on "IsItemActive".How can i achieve this ?
This should do the trick. But I cannot get my bindings to work now so it's not tested. You will have to give a name to the element that you are applying this in order to make the binding work. For my case, it's a window.
Create a dependency property for IsItemActive only then you can bind.
Tip : type propdp and double tab in Visual Studio to use code snippet.
<Window x:Class.....
x:Name="mainwindow"
...>
Then in the <Window.Resources> define the styles
<Style x:Key="MenuItemStyle" TargetType="MenuItem">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=mainwindow,Path=IsItemActive}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=mainwindow,Path=IsItemActive}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
Duplicate the style and change TargetType="Separator" Then apply the style on your context menu items and separator.
<ContextMenu x:Key="MyContextMenu">
<MenuItem Header="{x:Static localization:Resources.MyContext_Command1}" Command="{Binding Command1}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command2}" Command="{Binding Command2}" />
<Separator />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command3}" Command="{Binding Command3}" Style="{StaticResource MenuItemStyle}" />
<MenuItem Header="{x:Static localization:Resources.MyContext_Command4}" Command="{Binding Command4}" Style="{StaticResource MenuItemStyle}" />
<Separator Style="{StaticResource SeparatorStyle}"/>
</ContextMenu>

DataContext binding on ContextMenu

I have a DataGrid with a ContextMenu, I'm trying to figure out how to properly bind the context.
So far I have read that the context menu its ouside the visual tree, and therefore the DataContext is different. With that in mind the provided solution is to use the property Tag, but I still cannot make it work:
<UserControl>
<!--#region DataGrid-->
<DataGrid ItemsSource="{Binding Model.Collection}"
Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}, Path=DataContext}">
<!--#region Resources-->
<DataGrid.Resources>
<!--#region DataGridCell-->
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Open Details"
Command="{Binding DataContext.OpenRowDetailsCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding DataContext.SelectedIndex, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
<!--#endregion DataGridCell-->
</DataGrid.Resources>
<!--#endregion Resources-->
<!--#region DataGridColumns-->
<DataGrid.Columns>
<DataGridTextColumn Header="Filename"
Binding="{Binding FileInfo.Name}"
Width="Auto" />
</DataGrid.Columns>
<!--#endregion DataGridColumns-->
</DataGrid>
<!--#endregion DataGrid-->
The DataContext of the UserControl is working fine as I have other commands which uses the DataContext that way.
Anyone sees any error or has any other approach?
Thanks in advance.
This should work provided that the OpenRowDetailsCommand and SelectedIndex properties are defined in the view model class of the parent UserControl:
<Style TargetType="DataGridCell">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Open Details"
Command="{Binding PlacementTarget.Tag.DataContext.OpenRowDetailsCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding PlacementTarget.Tag.DataContext.SelectedIndex, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
I like to use a BindingProxy for this (as described in this SO answer)
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),typeof(BindingProxy));
}
Use it like this:
<DataGrid ItemsSource="{Binding Model.Collection}">
<DataGrid.Resources>
<local:BindingProxy x:Key="VMProxy" Data="{Binding}" />
<local:BindingProxy x:Key="DataGridProxy" Data="{Binding RelativeSource={RelativeSource Self}}" />
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Open Details"
Command="{Binding Data.OpenRowDetailsCommand, Source={StaticResource VMProxy}}"
CommandParameter="{Binding Data.SelectedIndex, Source={StaticResource DataGridProxy}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>

ContextMenu inside ContextMenu in WPF

I'm trying to get the following behavior:
When I right-click on My button it should open a window with buttons. When I right-click Button 1.2, I want to open another window with another kind of buttons.
NOTE: The style of Button 2.x is different than on the Button 1.x,
I've tried to make this work using ContextMenus, but when I right click on Button 1.x nothing happens. Is it impossible to use nested ContextMenus? Are there any other possibilities?
Here is an example:
<Button Content="Hello">
<Button.ContextMenu>
<ContextMenu>
<MenuItem/>
<ContextMenu.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Template">
<Setter.Value>
<ControlTemplate>
<Button Content="Level 1">
<Button.ContextMenu>
<ContextMenu>
<MenuItem />
<ContextMenu.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Template">
<Setter.Value>
<ControlTemplate>
<Button Content="Level 2" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
Much easier to use nested MenuItems
<Button Content="Hello">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Level1">
<MenuItem Header="Level2">
<MenuItem Header="Level3"></MenuItem>
</MenuItem>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>

WPF "static" binding in a List

I have a problem with a binding in a List.
I have a List of objects. This List is bound to a ListBox. Of every object in my List I can open a ContextMenu:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="First" IsEnabled="{Binding FirstEnabled}"/>
<MenuItem Header="Second" IsEnabled="{Binding SecondEnable}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
In the code like this my objects in the list having the two booleans to bind. Now I want to bind this two booleans not to the objects. I want to bind it "static" to my DataContext. This is not working like this and I have no idea how to realize it.
I googled a lot but found nothing helpful ...
Thanks for helping!
Since, ContextMenu applies to ListBoxItem it will have its DataContext value and ListBoxItem will be its PlacementTarget. So if you want to bind to property of ListBox.DataContext you need to pass current ListBox.DataContext as, for example, Tag to ListBoxItem and then you need to refer to it from ContextMenu via its PlacementTarget. It's all because ContextMenu uses Popup which creates its own visual tree so simple RelativeSource/ElementName binding won't work
<Style TargetType="ListBoxItem">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}, Path=DataContext}"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="First" IsEnabled="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.Tag.FirstEnabled}"/>
<MenuItem Header="Second" IsEnabled="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.Tag.SecondEnable}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
You can reference every datacontext with the ElementName Syntax:
<ListBox x:Name="myListBox">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="First" IsEnabled="{Binding Path=DataContext.FirstEnabled, ElementName=myListBox}"/>
<MenuItem Header="Second" IsEnabled="{Binding Path=DataContext.SecondEnable, ElementName=myListBox}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
With this syntax you use the DataContext of your ListBox and not from the ListItem.

Connect handler to ContextMenu in "Style" section

I need to create structure of ContextMenu in runtime, because existence of MenuItem's of the menu is based on many factors. And I try add handler to ContextMenuOpening event this way:
XAML
<TreeView x:Name="ArticlesTreeView" Grid.Column="0" AllowDrop="True">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenuService.ShowOnDisabled" Value="True" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Opened="ContextMenu_OnOpened">
<MenuItem Header="First item"></MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
This code throws XamlParseException on <ContextMenu Opened="ContextMenu_OnOpened"> line. Can I solve issue in another way?
UPD I find that to me it is necessary not ContextMenuOpening and Opened event. I corrected upper code.
Try creating your ContextMenu on your ItemTemplate of the treeview. Try the below code (it is not complete but i hope you get the idea)
<TreeView .....>
<TreeView.ItemTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.ContextMenu>
<ContextMenu ContextMenuOpening="ContextMenu_OnContextMenuOpening">
<MenuItem Header="First item"></MenuItem>
</ContextMenu>
</ContentControl.ContextMenu>
---------Your Item Template here
</ContentControl>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Your question is already answered here.
Placing event handlers in event setter did the work.
<TreeView x:Name="ArticlesTreeView" AllowDrop="True">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenuService.ShowOnDisabled" Value="True" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="First item">
<MenuItem.Style>
<Style TargetType="MenuItem">
<EventSetter Event="Click" Handler="ContextMenu_ContextMenuOpening_1"></EventSetter>
</Style>
</MenuItem.Style>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeViewItem>
</TreeViewItem>
</TreeView>
Also change your code behind event handler as
private void ContextMenu_ContextMenuOpening_1(object sender, RoutedEventArgs e)
{
}

Categories