WPF keep ContextMenu Open - c#

I want the user has the possibility to edit the header value.
The Header Value can be edited in a textbox when the property IsEditable is set to true.
Then I would like prevent the context menu closing when the header value is empty.
How can I achieve that ?
I tried to handle ContextMenuClosing event without success.
<MenuItem Header="{Binding Menu.Business}" Visibility="{Binding allowUI, Converter={StaticResource BoolToVisConverter} }" ContextMenuClosing="check" >
<MenuItem.Style>
<Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Menu.IsEditable}" Value="True">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Width="200">
<MenuItem>
<MenuItem.Header>
<TextBox Text="{Binding Menu.Business, UpdateSourceTrigger=PropertyChanged}" Width="150" Margin="0,0,-58,0"/>
</MenuItem.Header>
<MenuItem.Icon>
<Image Source="Images/closed_folder_grey_24.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
<MenuItem.Icon>
<Image Source="Images/closed_folder_grey_24.png" Margin="0" Height="26" Loaded="MenuItemImage_Loaded" />
</MenuItem.Icon>
</MenuItem>
private void check(object sender, ContextMenuEventArgs e)
{
if (String.IsNullOrEmpty(myViewModel.Menu.Business))
e.Handled = true;
}

Related

WPF Context Menu Click event works in one place, not in another

I have the following XAML that produces a ListBox where each item contains another ListBox inside an Expander, and I have defined PageContextMenu as the context menu for the top level list and FrameContextMenu for the lower level list.
The problem: Both are displayed correctly, but click events are only working on the top level context menu and not the lower level context menu. For example, clicking on Delete Selected in PageContextMenu correctly invokes the associated handler, but clicking on Delete Selected Frame(s) in FrameContextMenu does NOT fire the associated handler. I'm not seeing any indication of an error, and even if I put a breakpoint in ContextDeleteFrames_Click it doesn't get hit. It's as if there's no handler associated with that menu entry at all.
I've looked at a number of other questions relating to context menus not working, but none seemed applicable. Is there some problem with the two list boxes being nested?
XAML:
<ListBox Name="PageListBox" ItemsSource="{Binding CurrentPack.Pages}" HorizontalAlignment="Stretch" SelectionMode="Extended">
<ListBox.Resources>
<ContextMenu x:Key="PageContextMenu">
<MenuItem Header="_Add" Name="ContextAddAddPage"/>
<MenuItem Header="_Edit" Name="ContextEditPage"/>
<MenuItem Header="_Delete Selected" Name="ContextDeletePage" Click="ContextDeletePage_Click"/>
</ContextMenu>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContextMenu" Value="{StaticResource PageContextMenu}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate >
<Border BorderThickness="2" BorderBrush="White" HorizontalAlignment="Stretch">
<StackPanel HorizontalAlignment="Stretch">
<Label Content="{Binding PresentationName}"/>
<Expander VerticalAlignment="Top" HorizontalAlignment="Stretch">
<Expander.Header>
<Label Content="{Binding FrameStatusText}"/>
</Expander.Header>
<ListBox Name="FrameListBox" ItemsSource="{Binding Frames}" HorizontalAlignment="Stretch" SelectionMode="Extended">
<ListBox.Resources>
<ContextMenu x:Key="FrameContextMenu">
<MenuItem Header="_Add Frame" Name="ContextAddFrame"/>
<MenuItem Header="_Edit Frame" Name="ContextEditFrame"/>
<MenuItem Header="_Delete Selected Frame(s)" Name="ContextDeleteFrames" Click="ContextDeleteFrames_Click"/>
<MenuItem Header="Show _Preview" Name="ContextShowPreview" Click="ContextShowPreview_Click"/>
</ContextMenu>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContextMenu" Value="{StaticResource FrameContextMenu}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding PresentationName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
Code Behind:
private void ContextDeletePage_Click(object sender, RoutedEventArgs e)
{ //this works
Workspace.Content.DeleteSelectedPages();
}
private void ContextDeleteFrames_Click(object sender, RoutedEventArgs e)
{ //this doesn't!
Workspace.Content.DeleteSelectedFrames();
}
Don't use events in a DataTemplate. They won't work.
Either move your second context menu from the DataTemplate's resources into the PageListBox resources like this:
<ListBox Name="PageListBox">
<ListBox.Resources>
<!-- ... other resources... -->
<ContextMenu x:Key="FrameContextMenu">
<MenuItem Header="_Add Frame" Name="ContextAddFrame"/>
<MenuItem Header="_Edit Frame" Name="ContextEditFrame"/>
<MenuItem Header="_Delete Selected Frame(s)" Click="ContextDeleteFrames_Click"/>
<MenuItem Header="Show _Preview" Name="ContextShowPreview" Click="ContextShowPreview_Click"/>
</ContextMenu>
</ListBox.Resources>
</ListBox>
...or use commands instead of events:
<MenuItem Header="_Delete Selected Frame(s)" Command="{Binding DeleteFrameCommand}"/>
where DeleteFrameCommand is a property of type ICommand or RoutedCommand.
If you want to use commands, you should be aware that a context menu is not in the visual tree of its PlacementTarget, so you'll have to use some helpers to make the bindings work (a binding proxy or PlacementTarget.Tag property etc.)

How to set 2 color background to a TreeView (WPF)

I want to set two color background to a custom tree view (wpf). You can set this propriety in a DataGrid. I could not figure out a way to do it for a tree view.
I also want to set the selection of a element to be as big as the cell.
<TreeView Grid.Row="2" Name="TreeView" DataContext="{Binding Path=TreeModel}" ItemsSource="{Binding TreeItems}" SelectedItemChanged="TreeView_OnSelectedItemChanged">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:NodeViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding NameNode}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Add" Command="{Binding AddMachinePart_Command}"/>
<MenuItem Header="Remove" Command="{Binding RemoveMachinePart_Command}" IsEnabled="{Binding IsModule}"/>
<MenuItem Header="Edit" Command="{Binding EditMachinePart_Command}" IsEnabled="{Binding IsModule}"/>
<Separator></Separator>
<MenuItem Header="Copy path" Command="{Binding CopyPath_Command}" IsEnabled="{Binding IsModule}"></MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
Edit:
When using the solution from
https://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.alternationindex(v=vs.110).aspx suggested #jschroedl
It would be ideal to be as large as the tree view grid like shown in picture below:
You will probably want to use ItemsControl.AlternationIndex in a Style to control how many colors to cycle (2 in your case).
MSDN Docs for ItemsControl.AlternationIndex has an example with ListBox which you can probably adapt to TreeView as well.

Binding a DataGridCell ContextMenu MenuItem to a Command on a ViewModel

I know that there are similar questions on SO.
I have reviewed them, tried them, tried combinations of them, and retried them for the past day an half with very little success. I'm apparently dense or missing something.
The basic approaches covered were:
Placement Target
Using the Tag Attribute on a Parent
Using Ancestor Relative Source
Making use of ElementName root for the DataContext
Nothing seems to work and in only one instance am I even able to hit a break point in a Test Converter to see whats being bound. It will only work in the Tag Attribute of the DataCell. I am exasperated. This is as close as I have gotten:
<xcdg:DataGridControl.Resources>
<Style TargetType="{x:Type xcdg:DataCell}">
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource AncestorType={ x:Type xcdg:DataCell}}, Path=Tag, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</xcdg:DataGridControl.Resources>
As a variation on this approach I thought that sense the Binding on the Tag Attribute worked and was correct that I could use in the DataContext for the ContextMenu or the even just the MenuItem itself.
<ContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" StaysOpen="True">
or
<MenuItem Header="Acknowledge" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" Command="{Binding Path=AcknowledgeViolationCommand}" />
With this next one I also tried PlacementTarget.DataContext and PlacementTarget.Tag with no results.
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
Other attempts:
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem DataContext="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=DataContext}" Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
Here I tried making use of the ElementName root approach in different ways but it would only work in the Tag for the Cell and not the DataContext or Tag for the ContextMenu
<Setter Property="Tag" Value="{Binding DataContext, ElementName=root}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=PacementTarget.Tag.AcknowledgeViolationCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource TestConverter}}" />
</ContextMenu>
</Setter.Value>
</Setter>
<!-- variation of above -->
<Setter Property="Tag" Value="{Binding DataContext, ElementName=root}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=PlacementTarget.DataContext, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
I can list countless failed attempts. PlacementTargets, Tags, Ancestors, RelativeSources, why can't I make this work?. And why can I only seem to step into my TestConverter when its used on the DataCell Tag Binding and no where else?
Heres the Convert Method:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
When it fires for Tag binding I can actually see the ViewModel being passed just like I want and I can even execute my Command from the immediate window. Frankly I'm at my wits end on what should have been a 15 minute thing.
Can anyone tell me what I'm doing or wrong?
Additional Example(s)
From Lee O.s Comment I tried the BindingProxy approach without any luck.
<xcdg:DataGridControl.Resources>
<vl:BindingProxy x:Key="proxy" Data="{Binding }" />
<Style TargetType="{x:Type xcdg:DataCell}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{StaticResource proxy}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
Additional tries. These next attempts did do something new. Now at least when the app is started the breakpoint for the AcknowledgeViolationCommand fires once. However selecting the Item from the Context Menu still does nothing.
<ContextMenu DataContext="{Binding Data, Source={StaticResource proxy}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
and
<ContextMenu StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=Data.AcknowledgeViolationCommand, Source={StaticResource proxy}}" />
</ContextMenu>
also tried x:Reference with the following:
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=DataContext.AcknowledgeViolationCommand, Source={x:Reference dummyControl}, Converter={StaticResource TestConverter}}" />
</ContextMenu>
</Setter.Value>
</Setter>
These approaches all have one thing in common in that they all seem to cause a breakpoint I have set in the command to hit ONCE as the grid loads. Further more adding the TestConverter to the reference and setting a breakpoint causes it to hit once for each row in the grid as it loads, but never when the command is clicked.
Here is the code for the Command in the ViewModel
public ICommand AcknowledgeViolationCommand
{
get
{
if (acknowledgeViolationCommand == null) // BreakPoint is here
acknowledgeViolationCommand = new RelayCommand(param => Test());
return acknowledgeViolationCommand;
}
}
private void Test()
{
string a = "a"; //Breakpoint here
}
Made a change to the command in case for some reason it was wanting the canExecute predicate. I confirmed the breakpoint for the canExecute method does fire.
public ICommand AcknowledgeViolationCommand
{
get
{
if (acknowledgeViolationCommand == null) // BP here
acknowledgeViolationCommand = new RelayCommand(param => Test(), param => YesDoIt());
return acknowledgeViolationCommand;
}
}
private bool YesDoIt()
{
return true; //BP here
}

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

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}"/>

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