Tip / idea how can I get a generic context menu? - c#

I want to develop a generic contextmenu in wpf with mvvm. I would like to display it in each viewModel that have a listview with the style: ItemContainerStyle="{DynamicResource ListViewItemContainerStyle}".
The context menu will have 2 options that will be enabled or not depending on some constraints in each view Model. The enabled or not will be managed by the canexecute of the command.
I have no problem if I put it in each view inside the listview, but I would like to have it only in one place other than in each listview. I have tried to put it in the ListViewItemContainerStyle but it shows an exception "it is not possible to add a System.Windows.Controls.ContextMenu to a System.Object". Here is my code snippet
<Style x:Key="ListViewItemContainerStyle"
TargetType="ListViewItem">
.
.
.
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="{Binding Path=Command}"
CommandParameter="AddNew">
<MenuItem.Header>
<TextBlock Text="{x:Static p:TextResources.New}" />
</MenuItem.Header>
</MenuItem>
<MenuItem Command="{Binding Path=Command}"
CommandParameter="Delete">
<MenuItem.Header>
<TextBlock Text="{x:Static p:TextResources.Delete}" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
.
.
.
Any idea of how to do this?

Try this:
<ContextMenu x:Shared="False" x:Key="ListViewContextMenu>
<MenuItem Command="{Binding Path=Command}"
CommandParameter="AddNew">
<MenuItem.Header>
<TextBlock Text="{x:Static p:TextResources.New}" />
</MenuItem.Header>
</MenuItem>
<MenuItem Command="{Binding Path=Command}"
CommandParameter="Delete">
<MenuItem.Header>
<TextBlock Text="{x:Static p:TextResources.Delete}" />
</MenuItem.Header>
</MenuItem>
</ContextMenu>
<Style x:Key="ListViewItemContainerStyle"
TargetType="ListViewItem">
<Setter Property="ContextMenu" Value="{StaticResource ListViewContextMenu}"/>

Related

How to define WPF ContextMenu with a Separator before the last element, using binding on ItemsSource instead of individual MenuItems

I am trying to create a ContextMenu that defines some filters via Checkable MenuItems, with a Separator separating the last MenuItem from the previous ones.
I know I could define and manage the MenuItems and the Separator in the following way:
<ContextMenu HasDropShadow="False" Placement="Bottom">
<MenuItem IsCheckable="True" IsChecked="{Binding Path=IsChecked1}" Header="Filter 1"/>
<MenuItem IsCheckable="True" IsChecked="{Binding Path=IsChecked2}" Header="Filter 2"/>
<MenuItem IsCheckable="True" IsChecked="{Binding Path=IsChecked3}" Header="Filter 3"/>
<MenuItem IsCheckable="True" IsChecked="{Binding Path=IsChecked4}" Header="Filter 4"/>
<MenuItem IsCheckable="True" IsChecked="{Binding Path=IsChecked5}" Header="Filter 5"/>
<MenuItem IsCheckable="True" IsChecked="{Binding Path=IsChecked6}" Header="Filter 6"/>
<MenuItem IsCheckable="True" IsChecked="{Binding Path=IsChecked7}" Header="Filter 7"/>
<MenuItem IsCheckable="True" IsChecked="{Binding Path=IsChecked8}" Header="Filter 8"/>
<Separator Styles.Separator="Default"/>
<MenuItem IsCheckable="True" IsChecked="{Binding Path=IsChecked9}" Header="Active"/>
</ContextMenu>
But I would rather try to avoid having 9 IsChecked properties in my ViewModel, on top of having the next dev need to add/remove a MenuItem and its associated property when we need to add/remove a filter.
What I'd rather do, is to use the ItemsSource property of the ContextMenu and define an ItemTemplate for each element of the ItemsSource. Unfortunately, if I do this, I am not sure how I can define the Separator before the last element of the ItemsSource like I could do in the previous code snippet.
Is there a way to achieve what I want to do without defining manually each individual MenuItem and its associated properties in the ViewModel?
Based on the following answer:
How do I dynamically bind and statically add MenuItems?
I found an hybrid approach to what I wanted to achieve, which makes sense in my context: the only MenuItem I need to manually add is the last one, which I need to handle differently anyway in my ViewModel, so I don't mind manually adding that one. All the others are however generated automatically based on my AllFiltersExceptTheLastOne ObservableCollection:
<UserControl.Resources>
<CollectionViewSource x:Key="AllFiltersExceptTheLastOne" Source="{Binding Path=Filters}"/>
</UserControl.Resources>
...
<Button.ContextMenu>
<ContextMenu HasDropShadow="False" Placement="Bottom">
<ContextMenu.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource AllFiltersExceptTheLastOne}}" />
<Separator Margin="0,5,0,5" Styles.Separator="Default"/>
<MenuItem Padding="2,2,2,2" IsCheckable="true" IsChecked="{Binding Path=Filter.IsChecked}" Header="{Binding Path=Filter.Status}" />
</CompositeCollection>
</ContextMenu.ItemsSource>
<ContextMenu.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Padding" Value="2,2,2,2"/>
<Setter Property="MenuItem.IsCheckable" Value="true"/>
<Setter Property="MenuItem.Header" Value="{Binding Path=Status}"/>
<Setter Property="MenuItem.IsChecked" Value="{Binding Path=IsChecked}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>

WPF MenuItem not filling available ContextMenu space

I'm having a weird problem with a simple ContextMenu using MahApps.Metro without any additional styling. When moving the cursor on top of the text or slightly around it, there is no problem. But when moving it further away, still inside the ContextMenu bounds, the Cursor is no longer on top of the MenuItem. Clicking now also doesn't result in any action at all besides closing the ContextMenu.
<ContextMenu ItemsSource="{Binding ContextItems}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Text}" Command="{Binding Command}"/>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
What am I doing wrong? Why doesn't the MenuItem use the available space?
If your ContextItems holds a collection with viewmodels then I think this could help you (not tested):
<ContextMenu ItemsSource="{Binding ContextItems}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Text}" />
<Setter Property="Command" Value="{Binding Command}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
Command and Text should be the properties on the viewmodel object.
I havent used MahApps.Metro . Though you can override the template like this
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Text}" Command="{Binding Command}"/>
<MenuItem.Template>
<ControlTemplate>
<ContentPresenter Content="{Binding Header,RelativeSource={RelativeSource TemplatedParent}}">
</ContentPresenter>
</ControlTemplate>
</MenuItem.Template>
</MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
I hope this will help.

MenuItem Vertical alignment issue

I am facing an issue with MenuItem that whenever I specify an access key, the header alignment is getting disrupted. Below is a sample image wherein I have specified "_New" for New menu item and bound it to New command. You can observe tha the Text "New" is align at bottom and shortcut key is aligned at top. Also for other menu items I have not specified any access key, so they have no issue.
Here is the XAML
<StackPanel DockPanel.Dock="Top">
<Menu Padding="0,5">
<MenuItem x:Name="MnuTask" Header="Task">
<MenuItem x:Name="MnuNew" Header="_New" Command="New"/>
<MenuItem x:Name="MnuSave" Header="Save" Command="Save"/>
<MenuItem x:Name="MnuDelete" Header="Delete" Command="Delete"/>
<Separator/>
<MenuItem x:Name="MnuRefresh" Header="Reload Data" Command="{x:Static Local:MainWindow.RefreshDataCommand}" />
<MenuItem x:Name="MnuHistory" Header="View Range History" Command="{x:Static Local:MainWindow.RangeHistoryCommand}" />
<Separator/>
<MenuItem x:Name="MnuExit" Header="Exit" Command="Close"/>
</MenuItem>
<MenuItem x:Name="MnuView" Header="View">
<MenuItem x:Name="MnuFind" Header="Find Formula"/>
</MenuItem>
</Menu>
</StackPanel>
Could any body let me know what's going on?
Found it. I had the below TextBlock Style present in Resources section of my window. Commenting those lines resolved the issue. (But now I need to apply TextBlock style explicitly using keys :( )
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="3,6,3,0"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
</Style>

Click event on a (Context)MenuItem of a ListBox (XamlParseException)

I have a ListBox and within it multiple ListBoxItem objects. When the user right-clicks on a ListBoxItem a ContextMenu should appear with some MenuItem objects. The problem that I have is that when I put a Click event on the MenuItem objects I get a XamlParseException stating the following:
A first chance exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll
Additional information: 'Set connectionId threw an exception.' Line number '31' and line >position '34'.
I have to admit that I don't fully understand styles and resources and the other aspects of WPF. When I was designing this I just copied my code from the Internet. The code is as follows:
<ListBox Grid.Column="1" Grid.Row="1" MouseDoubleClick="MainListBox_MouseDoubleClick" Name="mainListBox" SelectionChanged="MainListBox_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Click="OpenMenuItem_Click" Header="Open"/>
<Separator/>
<MenuItem Header="Cut"/>
<MenuItem Header="Copy"/>
<Separator/>
<MenuItem Header="Delete"/>
<MenuItem Header="Rename"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
It works when I remove the Click event but of course the ContextMenu becomes useless.
Assign ContextMenu for ListBoxItem as given below.
<ListBox Grid.Column="1" Grid.Row="1" MouseDoubleClick="MainListBox_MouseDoubleClick" Name="mainListBox" SelectionChanged="MainListBox_SelectionChanged">
<ListBox.Resources>
<ContextMenu x:Key="CMenu">
<MenuItem Click="OpenMenuItem_Click" Header="Open"/>
<Separator/>
<MenuItem Header="Cut"/>
<MenuItem Header="Copy"/>
<Separator/>
<MenuItem Header="Delete"/>
<MenuItem Header="Rename"/>
</ContextMenu>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu" Value="{StaticResource CMenu}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem Content="Test1"/>
</ListBox>

Showing multiple Context menu in WPF grid row

I wish to have two context menu in grid view in my WPf based desktop application .
Currently i am able to display one context menu, but I want to show context menu 1 on one condition and context menu 2 on another condition . How to do that?
I am usign following XAML code to show grid and context menu
<dg:UCGrid x:Name="grdLetVariables" Grid.Row="2" GridTypeSource="LetGrid"
DataContext="{Binding}" >
<dg:UCGrid.Resources>
<x:Array Type="{x:Type sys:Object}" x:Key="GridExtensions">
<MenuItem Header="Delete" Click="ContextMenuDelete">
<MenuItem.Icon>
<Image Height="10" Source="../images/Delete.png"/>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Move Up" Click="MoveUpLetGrdRow">
<MenuItem.Icon>
<Image Height="14" Source="../images/UpMove.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Move Down" Click="MoveDownLetGrdRow">
<MenuItem.Icon>
<Image Height="14" Source="../images/DownMove.png"/>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Cancel" Click="CancelLetGrdRowEdit"/>
</x:Array>
</dg:UCGrid.Resources>
<dg:UCGrid.ContextMenu>
<ContextMenu>
<ContextMenu.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{StaticResource GridExtensions}" />
</CompositeCollection>
</ContextMenu.ItemsSource>
</ContextMenu>
</dg:UCGrid.ContextMenu>
</dg:UCGrid>
</Grid>
Triggers on DataGrid can help you here. Code below is just for illustration ...
<UserContorl.Resources>
<ContextMenu x:Key="Condition1ContextMenu" ../>
<ContextMenu x:Key="Condition2ContextMenu" ../>
</UserControl.Resources>
...
<Style TargetType="{x:Type dg:UCGrid}">
<Style.Triggers>
<DataTrigger Binding="{Binding Condition1}" Value="Value1">
<Setter Property="ContextMenu" Value="{StaticResource Condition1ContextMenu}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Condition2}" Value="Value2">
<Setter Property="ContextMenu" Value="{StaticResource Condition2ContextMenu}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Ofcourse condition1 and condition2 must be exclusive of each other. If both of them are applicable on the data grid then due to order Condition2ContextMenu will take the precedence.
Let me know if this helps...

Categories

Resources