I know there is a way to block or now show context menu by using ContextMenuOpening event.
But I still want to show the context menu, just disable everything in it, is there a way to do it?
How can I disable all the menu item at a same time?
<DataTemplate x:Key="ItemDataTemplate">
<Grid Background="Transparent">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="New" Click="New_Click" />
<Separator />
<MenuItem Header="Duplicate" Click="Duplicate_Click"/>
<MenuItem Header="Delete" Click="Delete_Click" />
<MenuItem Header="Rename" Click="Rename_Click" />
<Separator />
<MenuItem Header="Export..." Click="Export_Click" />
<MenuItem Header="Print..."
Command="ApplicationCommands.Print"
InputGestureText="" />
<Separator />
<MenuItem Header="Properties" Click="Properties_Click" />
</ContextMenu>
</Grid.ContextMenu>
<StackPanel Orientation="Horizontal"
Margin="0,0,10,0"
HorizontalAlignment="Stretch"
Background="Transparent"
IsHitTestVisible="False">
</StackPanel>
</Grid>
</DataTemplate>
<Style x:Key="z3r0_Style_TextBox_Default" BasedOn="{x:Null}"
TargetType="{x:Type TextBox}">
<Setter Property="FontSize" Value="11"/>
<Setter Property="Background" Value="{StaticResource z3r0_SolidColorBrush_DarkerGray}"/>
<Setter Property="BorderBrush" Value="{x:Null}"/>
<Setter Property="FontFamily" Value="Consolas"/>
<Setter Property="Foreground" Value="{StaticResource z3r0_SolidColorBrush_Green}"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="MinWidth" Value="10"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu IsEnabled="False" Visibility="Hidden">
</ContextMenu>
</Setter.Value>
</Setter>
For me, setting IsEnabled to False still caused a small empty context menu to be visible. After changing its visibility attribute, it was truly disabled.
IsEnabled = false on the ContextMenu?
Edit: As there appear to be closing problems when doing this i would suggest a container style:
<ContextMenu>
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="IsEnabled" Value="False"/>
</Style>
</ContextMenu.ItemContainerStyle>
<MenuItem Header="Test"/>
</ContextMenu>
Of course the actual value can be bound as well but in the container style the context is the individual item, so to bind to the parent context a RelativeSource binding is necessary (unless the data object also has a connection to the parent).
The entire context menu can be disabled at the root of the ContextMenu by setting the IsEnabled property to false; there is no need to loop through each menu item in code.
<ContextMenu IsEnabled="False">
Or
<ContextMenu IsEnabled={Binding Path=SomeModelBooleanProperty}">
This will enable the context menu to be viewed, but all items will be disabled.
I used ContextMenu="{x:Null}" to disable the Context Menu in a TextBox.
Here is how I solve my question.
Still use ContextMenuOpening event. Use a loop.
This why can help me disable certain context menu item in code, but not in XAML.
private void SetContextMenuItemsEnableStatus(object sender)
{
FrameworkElement element = sender as FrameworkElement;
if (element == null)
{
return;
}
ContextMenu contextMenu = element.ContextMenu;
if (contextMenu == null)
return;
//Use this way.
//You can disable every item or certain item in context menu.
foreach (Control item in contextMenu.Items)
{
item.IsEnabled = false;
}
}
Here is how I solve my question.
<ContextMenu x:Key="TreeViewEmptyContextMenu" Width="0" Height="0"/>
Related
I'm using a resource dictionary in my wpf proyect.
The dictionary has these 2 styles:
<Style x:Key="MyMenu" TargetType="Menu">
<Setter Property="Background" Value="Black"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style x:Key="MyToolbar" TargetType="ToolBar">
<Setter Property="Background" Value="Black"/>
</Style>
And in my XAML file, I use these 2 styles as it follows:
For the menu:
<Menu Name="menuMainBar" Style="{DynamicResource MyMenu}" IsMainMenu="True" VerticalAlignment="Top" Margin="0,10,0,0">
<MenuItem ...
</Menu>
For the toolbar:
<ToolBarTray Name="toolBarTrayRigth_wargames" Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" Width="50">
<ToolBar Name="toolBarRigth_wargames" Style="{StaticResource MyToolbar}" BorderThickness="0,0,1,0">
<Button ...
</ToolBar>
</ToolBarTray>
This is the result:
As you can notice, the menu takes the style correctly, but the toolbar isn't affected by the style at all.
I tried removing all the style elements from the toolbartray tag, as it follows:
<ToolBarTray Name="toolBarTrayRigth_wargames">
...
</ToolBarTray>
And now the toolbar works, but as you can see, now the toolbarTray is not how I want it to be (I want a vertical toolbar, not a horizontal toolbar):
I also tried setting the background of the ToolBar directly in the xaml file:
<ToolBarTray Name="toolBarTrayRigth_wargames" Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" Width="50">
<ToolBar Name="toolBarRigth_wargames" BorderThickness="0,0,1,0" Background="Black">
<Button ...
</ToolBar>
</ToolBarTray>
...and it works:
But I don't want to do that, I want to use a resource dictionary for my toolbar's style.
any ideas on how to achieve that?
I found, bizarrely, that if I set the Orientation of the parent ToolBarTray to Horizontal, that your ToolBar style was able to set the background on the ToolBar.
I also found that with the ToolBarTray orientation still being Vertical, this worked to set the ToolBar's background:
<Style x:Key="MyToolbar" TargetType="ToolBar">
<Style.Triggers>
<!--
This is intentional. A conventional setter was found not to set the background
when the parent ToolBarTray's Orientation was Vertical.
-->
<DataTrigger Binding="{Binding Source={x:Null}}" Value="{x:Null}">
<Setter Property="Background" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
I find this very strange.
as #EdPlunkett found, its a strange Setter in the default template in the Trigger for Vertical Orientation:
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="Margin" TargetName="Grid" Value="1,3,1,1"/>
<Setter Property="Style" TargetName="OverflowButton">
<Setter.Value>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#FFEEF5FD"/>
you can edit the template (in Document Outline panel, right click on the ToolBar element > Edit Template > Edit a Copy...), and remove this Setter.
I've got a view with two large context menus defined as resouces. They look something like this (only larger) :
<ContextMenu x:Key="ContextMenu1">
<MenuItem Header="Append" Command="{Binding AppendCommand}" />
<MenuItem Header="Edit" Command="{Binding AppendCommand}" />
</ContextMenu>
<ContextMenu x:Key="ContextMenu2">
<MenuItem Header="Delete" Command="{Binding DeleteCommand}" />
<MenuItem Header="Verify" Command="{Binding VerifyCommand}" />
</ContextMenu>
I know that I can dynamically show / hide items with the canExecute method of the command. But since this is two completely different modes I would like to just Bind to a bool property that decides which context menu to display. Something like this:
<ListView ContextMenu={binding ContextMenuSelector}>
Does anyone know how I can do something like that ?
Consider using a DataTemplateSelector.
The DataTemplateSelector enables you to display specific presentations based on the context of each datacontext item within your itemscontrol.
I have used it for context menu items that may differin behavior.
I got a few good ideas from the DataTemplateSelector, but I ended up with a style with data trigger:
<Style TargetType="{x:Type StackPanel}" x:Key="stackPanelStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AppendMode}" Value="True">
<Setter Property="ContextMenu" Value="{DynamicResource PanelContextMenuAttachFile}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=AppendMode}" Value="False">
<Setter Property="ContextMenu" Value="{DynamicResource PanelContextMenu}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
I've been stuck on this for a while now and I can't work out why. Basically, I have a ContextMenu with some MenuItem objects in it. I have declared Image objects for the MenuItem.Icon properties. I have Command objects bound to the MenuItems and that all works fine... in particular, when the Command.CanExecute method returns false, the MenuItem is correctly disabled and the MenuItem.Header text is greyed out.
I've been trying to set the Image.Opacity of the MenuItem Images to 0.5 when the MenuItem is disabled and this is where the problem is. For some reason, a binding in a DataTrigger in the Image.Style cannot find the MenuItem that I am trying to bind to. I have added a simplified example of my problem below.
<UserControl.Resources>
<Style x:Key="MenuItemIconStyle" TargetType="{x:Type Image}">
<Setter Property="Width" Value="16" />
<Setter Property="Height" Value="16" />
<Style.Triggers>
<!--This Binding is not working-->
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type MenuItem}}}" Value="False">
<Setter Property="Image.Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
<!--This is all working just fine-->
<ContextMenu x:Key="ContextMenu" DataContext="{Binding PlacementTarget.Tag,
RelativeSource={RelativeSource Self}}">
<MenuItem Header="Open" Command="{Binding Open}" CommandParameter="{Binding
PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu}}">
<MenuItem.Icon>
<Image Source="/Application;component/Images/Actions/FolderOpen_16.png"
Style="{StaticResource MenuItemIconStyle}" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
...
</UserControl.Resources>
Please note that this example is simplified... there are many MenuItems in my application. I am aware that I could individually name each MenuItem and use ElementName to find them, but there must be a better way.
Any help would be greatly appreciated.
UPDATE >>>
Thanks to punker76's answer, I realised that all I needed to do was to change the Image Trigger to the following:
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
Instead of trying to bind to the MenuItem.IsEnabled property with a DataTrigger, we can bind to the Image.IsEnabled property directly... this is because the when the MenuItem becomes disabled, it also disables its children. Much simpler!
try this one
<Style x:Key="MenuItemIconStyle" TargetType="{x:Type Image}">
<Setter Property="Width" Value="16" />
<Setter Property="Height" Value="16" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</Style.Triggers>
</Style>
<ContextMenu x:Key="ContextMenu" DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<!-- IsEnabled="False" is only for testing (tested with kaxaml) -->
<MenuItem IsEnabled="False" Header="Open" Command="{Binding Open}" CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu}}">
<MenuItem.Icon>
<Image Source="http://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Disambig-dark.svg/25px-Disambig-dark.svg.png"
Style="{StaticResource MenuItemIconStyle}" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
EDIT
here is another solution that works (the button gets the DataContext) with this tip that i found:
How to Solve Execution Problems of RoutedCommands in a WPF ContextMenu
The problem was, that the commands could not be
executed, even if the CommandBinding on the parent window allowed it.
The reason is, that ContextMenus are separate windows with their own
VisualTree and LogicalTree. The reason is that the CommandManager
searches for CommandBindings within the current focus scope. If the
current focus scope has no command binding, it transfers the focus
scope to the parent focus scope. The simplest solution is to initially
set the logical focus of the parent window that is not null. When the
CommandManager searches for the parent focus scope it finds the window
and handels the CommandBinding correctly. Another solution is to
manually bind the CommandTarget to the parent ContextMenu.
<Window.Resources>
<Style x:Key="MenuItemIconStyle"
TargetType="{x:Type Image}">
<Setter Property="Width"
Value="16" />
<Setter Property="Height"
Value="16" />
<Style.Triggers>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Opacity"
Value="0.5" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Button Content="With ContextMenu"
DataContext="{Binding ElementName=window, Path=DataContext}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Enabled"
CommandTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=PlacementTarget}"
Command="{Binding Open}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu}}">
<MenuItem.Icon>
<Image Source="http://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Disambig-dark.svg/25px-Disambig-dark.svg.png"
Style="{StaticResource MenuItemIconStyle}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Disabled"
CommandTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=PlacementTarget}"
Command="{Binding NotOpen}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu}}">
<MenuItem.Icon>
<Image Source="http://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Disambig-dark.svg/25px-Disambig-dark.svg.png"
Style="{StaticResource MenuItemIconStyle}" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Grid>
code behind
public partial class Window11 : Window
{
public static readonly DependencyProperty OpenProperty =
DependencyProperty.Register("Open", typeof(ICommand), typeof(Window11), new PropertyMetadata(default(ICommand)));
public static readonly DependencyProperty NotOpenProperty =
DependencyProperty.Register("NotOpen", typeof(ICommand), typeof(Window11), new PropertyMetadata(default(ICommand)));
public ICommand NotOpen {
get { return (ICommand)this.GetValue(NotOpenProperty); }
set { this.SetValue(NotOpenProperty, value); }
}
public ICommand Open {
get { return (ICommand)this.GetValue(OpenProperty); }
set { this.SetValue(OpenProperty, value); }
}
public Window11() {
this.DataContext = this;
this.InitializeComponent();
this.Open = new RoutedCommand("Open", typeof(Window11));
this.CommandBindings.Add(new CommandBinding(this.Open, null, (sender, args) =>
{
args.CanExecute = true;
}));
this.NotOpen = new RoutedCommand("NotOpen", typeof(Window11));
this.CommandBindings.Add(new CommandBinding(this.NotOpen, null, (sender, args) =>
{
args.CanExecute = false;
}));
}
}
hope this works
Can you have submenus with the top level set to checkable in WPF? I can't seem to get this to work.
<Window.ContextMenu>
<ContextMenu>
<MenuItem Header="Top Level 1" IsCheckable="True" IsChecked="True">
<MenuItem Header="Sub Level" />
<MenuItem Header="Sub Level" />
</MenuItem>
<MenuItem Header="Top Level 2">
<MenuItem Header="Sub Level" />
<MenuItem Header="Sub Level" />
</MenuItem>
</ContextMenu>
</Window.ContextMenu>
Top Level 1 is checkable, but the sub levels don't appear. Any thoughts?
If you dig into MenuItem's ControlTemplate, you will see that it uses different templates depending on it's Role property.
Reference:
Menu Styles and Templates
<Style x:Key="{x:Type MenuItem}"
TargetType="{x:Type MenuItem}">
<Setter Property="OverridesDefaultStyle"
Value="True" />
<Style.Triggers>
<Trigger Property="Role"
Value="TopLevelHeader">
<Setter Property="Template"
Value="{StaticResource {x:Static MenuItem.TopLevelHeaderTemplateKey}}" />
<Setter Property="Grid.IsSharedSizeScope"
Value="true" />
</Trigger>
<Trigger Property="Role"
Value="TopLevelItem">
<Setter Property="Template"
Value="{StaticResource {x:Static MenuItem.TopLevelItemTemplateKey}}" />
</Trigger>
<Trigger Property="Role"
Value="SubmenuHeader">
<Setter Property="Template"
Value="{StaticResource {x:Static MenuItem.SubmenuHeaderTemplateKey}}" />
</Trigger>
<Trigger Property="Role"
Value="SubmenuItem">
<Setter Property="Template"
Value="{StaticResource {x:Static MenuItem.SubmenuItemTemplateKey}}" />
</Trigger>
</Style.Triggers>
</Style>
Seems like it can either allow checking or subitems by default.
To workaround that, use following code:
XAML:
<ContextMenu>
<MenuItem Header="Top Level 1"
Mouse.PreviewMouseUp="MenuItem_MouseLeftButtonUp">
<MenuItem Header="Sub Level" />
<MenuItem Header="Sub Level" />
</MenuItem>
<MenuItem Header="Top Level 2">
<MenuItem Header="Sub Level" />
<MenuItem Header="Sub Level" />
</MenuItem>
</ContextMenu>
Code behind:
private void MenuItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
(sender as MenuItem).IsChecked = !(sender as MenuItem).IsChecked;
}
I strongly recommend converting/encapsulating this piece of functionality into an Attached Property or a Behavior.
To add to decyclone's answer:
Since the menu will sit there still open after doing this, and if you want it to close, you can then close the menu by setting IsOpen = false on the parent contextmenu:
private void MenuItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
(sender as MenuItem).IsChecked = !(sender as MenuItem).IsChecked;
((sender as MenuItem).Parent as ContextMenu).IsOpen = false;
}
Another approach is to simply use a CheckBox as the MenuItem's Icon:
<MenuItem>
<MenuItem.Icon>
<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding MyCheckProperty}"/>
</MenuItem.Icon>
<MenuItem Header="Item1"/>
<MenuItem Header="Item2"/>
</MenuItem>
In this case the user must click the CheckBox - not just anywhere on the MenuItem - in order to change the state, while clicking elsewhere will keep the default behavior of immediately opening the submenu (which may or may not be desired). Also, this allows for three-state values. In particular, this is excellent if the top level menu should function as a master switch for all its submenus, with the null state indicating that some substates are checked, and some are not. As for decyclone's answer, the menu will stay open unless further measures are taken.
I have have a MenuItem that creates its sub-menu-items dynamicly from the ItemsSource-property.
For grouping, I have Separators in the menu. The separator is created for each null-entry in the ItemsSource-collection by a ControlTemplate of the MenuItem.ItemContainerStyle.
This works fine, however has the separator not the same optical style as the other separators have which are placed in a the Items-collection of a menu.
Is there a way to change the look of the separator so that it looks equal to the "normal" menu-item-separators?
Here is the code I use:
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Title}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding }" Value="{x:Null}">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate>
<Separator /> <!-- THIS SEPARATOR IS NOT SHOWN AS COMMON MENUITEM-SEPARATORS ARE -->
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.ItemContainerStyle>
There is a Style that is declared in System.Resources with MenuItem.SeparatorStyleKey as the key. The parent MenuItem normally sets the style on children of type Separator, but since yours is a MenuItem, it won't, so you will have to do it manually:
<Separator Style="{StaticResource {x:Static MenuItem.SeparatorStyleKey}}" />
You may also want to read Bea Stollnitz's blog entry "How do I insert Separator objects in a data bound MenuItem?" for another approach.
Try wrapping the Seperator in a MenuItem
<ControlTemplate>
<MenuItem>
<MenuItem.Header>
<Separator />
</MenuItem.Header>
</MenuItem>
</ControlTemplate>