I have a vertical menu set to the left side of the window. Its items open just on (above) it and this prevents the user from having a full view of the menu when an item is open.
I want each element to open just on the right of the menu, so as to have an entire view of both the rest of menu and the opened elements. How can this be done? May be with the aid of transforms or triggers?
Here is some code:
<MenuItem Header="Maths">
<MenuItem Background="LightGray" Header="Add"/>
<MenuItem Background="LightGray" Header="Subtract"/>
<MenuItem Background="LightGray" Header="Multiply"/>
<MenuItem Background="LightGray" Header="Divide"/>
</MenuItem>
So just to be clear, the MenuItem 'Maths' above is in a WPF Menu and you have changed that Menu's ItemsPanel to be a Vertical StackPanel or something so 'Maths' is above/below other sibling menu items. If so what is happening is that the default template for MenuItem's whose role is TopLevelHeader (MenuItem that has child Items and is directly within a Menu) is such that the popup is below (or above) the menu item. You will probably want to retemplate those menu items. On hacky (and ugly alternative) is to use the template that would be used for SubmenuHeader role menu items (i.e. a MenuItem that has child Items and is within another MenuItem). e.g.
<Menu HorizontalAlignment="Left">
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<Menu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Style.Triggers>
<Trigger Property="Role" Value="TopLevelHeader">
<Setter Property="Template" Value="{DynamicResource {x:Static MenuItem.SubmenuHeaderTemplateKey}}" />
</Trigger>
<Trigger Property="Role" Value="TopLevelItem">
<Setter Property="Template" Value="{DynamicResource {x:Static MenuItem.SubmenuItemTemplateKey}}" />
</Trigger>
</Style.Triggers>
</Style>
</Menu.ItemContainerStyle>
<MenuItem Header="Just Item" />
<MenuItem Header="Maths">
<MenuItem Header="Add" />
<MenuItem Header="Subtract" />
</MenuItem>
<MenuItem Header="Misc">
<MenuItem Header="Other" />
</MenuItem>
</Menu>
Related
I have my PDFDocument bound to the FlowDocumentScrollViewer.
<FlowDocumentScrollViewer
Document="{Binding Path=PDFDocument}"
/>
How can I add a new context menu item to a text box inside of the viewing area
Eventually I found how to do it
You can attach the context menu to each of TextBox Elements using a style property setter like this:
<Window.Resources>
<ContextMenu x:Key="contextMenu" >
<MenuItem Name="mnuOpen" Header="_Open Link" Command="{Binding TextBoxContextMenuCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
<MenuItem Name="mnuView" Header="_View Properties" Command="{Binding TextBoxContextMenuCommand}"/>
</ContextMenu>
<Style TargetType="TextBox">
<Setter Property="ContextMenu" Value="{DynamicResource contextMenu}" />
</Style>
</Window.Resources>
I am trying to add an item to a WPF-ContextMenu that is only used to "describe" the items below but I am not sure how to add a simple line of text above all the items, where the text is centrally aligned and the text is not selectable like the normal MenuItems.
I tried something like this:
<ContextMenu Grid.Row="0" StaysOpen="False">
<TextBlock Text="Add New:" IsEnabled="False" HorizontalAlignment="Center"/>
<MenuItem Header="one"/>
<MenuItem Header="two (horizontal)"/>
<MenuItem Header="two (vertical)"/>
<MenuItem Header="three"/>
<MenuItem Header="four"/>
<MenuItem Header="six"/>
</ContextMenu>
but unfortunately the TextBlock is neither aligned centrally, nor is it unselectable.
The problem is that using a MenuItem and setting the IsEnabled-property to false, the text is not normal black anymore and also I can't really align it centrally.
Hopefully, someone can think of a easy solution here, I simply couldn't find anything.
I will suggest you to create the SubMenu items for menuitems. In this way menuitems will group all the submenuitems under it.
I had given the answer to create the context menu with menuitem and submenuitems purely using mvvm. Here you can refer to it.
WPF Context Menu with dropdown list showing hyperlinks
OR For the case you mentioned. I tried this:
<ContextMenu StaysOpen="False">
<MenuItem Header="Add New:" IsEnabled="False" HorizontalAlignment="Center">
<MenuItem.Style>
<Style TargetType="{x:Type MenuItem}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
<MenuItem Header="one"/>
<MenuItem Header="two (horizontal)"/>
<MenuItem Header="two (vertical)"/>
<MenuItem Header="three"/>
<MenuItem Header="four"/>
<MenuItem Header="six"/>
</ContextMenu>
and I got menu like
Add New: is non-selectable and also mouse over does not highlight it.
Hope it helps. Thanks
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"/>
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 am working on some XAML for a wpf application and I am having some trouble getting it to do what I want. Here is a sample of my XAML:
<!-- Tool Bar Tray -->
<ToolBarTray Name="toolBarTray1" DockPanel.Dock="Top">
<!-- File And Edit Tools -->
<ToolBar Name="toolBar1" Band="1" BandIndex="1">
<!-- Regular Items -->
<Button>A</Button>
<Button>B</Button>
<!-- Overflow Menu For Special Items -->
<MenuItem ToolBar.OverflowMode="Always" Header="Special Items">
<MenuItem Header="C"/>
<MenuItem Header="D"/>
</MenuItem>
</ToolBar>
</ToolBarTray>
When I click on the overflow button of my toolbar, the "Special Items" MenuItem appears with a little arrow next to it, indicating nested elements. However, when I hover the mouse over "Special Items" or try to click on it, the MenuItems "C" and "D" are not being displayed.
I was hoping that MenuItem would just work outside of a Menu, but I tried to do the straight-forward thing, just in case. Including these MenuItems inside a Menu and, instead, giving this Menu the ToolBar.OverflowMode="Always" property produces some unwanted styling. The arrow is no longer present, the "Special Items" entry needs to be clicked on to activate the sub-menu and the sub-menu positioning looks a little off.
Does anyone know what is going on?
Edit: Adding a menu to the overflow is producing exactly what I requested (big surprise). What I am after is a way to convert top-level headers and items to the sub-menu level. I have turned towards this control template example on MSDN for a solution (below).
Edit,Edit:
#gcores (comment discussion): Really? Am I missing something?
<ToolBar Name="toolBar1" Band="1" BandIndex="4">
<!-- Displayed Buttons -->
<Button>A</Button>
<Button>B</Button>
<!-- Special Items Menu -->
<Menu ToolBar.OverflowMode="Always" >
<MenuItem Style="{StaticResource MenuItemStyle}" Header="Special">
<MenuItem Header="C"/>
<MenuItem Header="D"/>
</MenuItem>
</Menu>
</ToolBar>
This snippet doesn't work for me. I have to click on 'Special' for the sub-menu to display.
Another solution is to use the existing templates and override the Template for the TopLevelHeader with the Template of the SubmenuHeader.
<Style x:Key="MenuItemStyle" TargetType="{x:Type MenuItem}">
<Style.Triggers>
<Trigger Property="Role" Value="TopLevelHeader">
<Setter Property="Template"
Value="{StaticResource {x:Static MenuItem.SubmenuHeaderTemplateKey}}"/>
</Trigger>
</Style.Triggers>
</Style>
And use this style in your top level MenuItem. That should simplify your code.
EDIT:
You're right, it only works when you click on it (don't know how I convinced myself it worked, sorry :)).
It's functionality is like a TopLevelMenu even though the Template says otherwise, its quite confusing.
Only thing I can think of is adding a Trigger to show the Submenu on IsMenuOver and handling the Click event so it does nothing but I don't know how well that would work.
After more reading, a solution I am using is below.
<!-- Resource Dictionary Stuff -->
<!-- Some Brushes -->
<SolidColorBrush x:Key="Brush_1"
Color="White" />
<LinearGradientBrush x:Key="Brush_2"
StartPoint="0 0"
EndPoint="0 1">
<GradientStop
Color="White"
Offset="0"/>
<GradientStop
Color="DarkSeaGreen"
Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Brush_3"
Color="DarkOliveGreen"/>
<!-- Custom MenuItem - Top Level Header - Style 1 -->
<Style x:Key="MenuItem_TLH_Style1"
TargetType="MenuItem">
<!--<EventSetter Event="PreviewMouseDown" Handler="DoNothing"/>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Name="ControlTemplate"
TargetType="MenuItem">
<!-- A headered text that may display a submenu
on a trigger. This submenu is the host for a
menu item's items. -->
<Border x:Name="BoundaryBorder"
Background="{StaticResource Brush_1}"
BorderThickness="1">
<Grid x:Name="ContainerGrid">
<ContentPresenter x:Name="HeaderContent"
Margin="6 3 6 3"
ContentSource="Header"
RecognizesAccessKey="True"/>
<Popup x:Name="SubmenuPopup"
Placement="Bottom"
IsOpen="{TemplateBinding IsSubmenuOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Fade">
<Border x:Name="SubmenuBoundaryBorder"
SnapsToDevicePixels="True"
Background="{StaticResource Brush_1}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1">
<StackPanel x:Name="ItemsStackPanel"
IsItemsHost="True"
KeyboardNavigation.DirectionalNavigation="Cycle"/>
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- -->
<Trigger
Property="IsSuspendingPopupAnimation"
Value="true">
<Setter
TargetName="SubmenuPopup"
Property="PopupAnimation"
Value="Fade"/>
</Trigger>
<!-- On mouse-over, show the submenu and highlight the header. -->
<Trigger
Property="IsMouseOver"
Value="true">
<Setter
TargetName="BoundaryBorder"
Property="Background"
Value="{StaticResource Brush_2}"/>
<Setter
TargetName="BoundaryBorder"
Property="BorderBrush"
Value="{StaticResource Brush_3}"/>
<Setter
Property="IsSubmenuOpen"
Value="true"/>
<!-- sloppy? -->
<Setter
TargetName="SubmenuPopup"
Property="IsOpen"
Value="true"/>
</Trigger>
<Trigger
SourceName="SubmenuPopup"
Property="AllowsTransparency"
Value="true">
<Setter
TargetName="SubmenuBoundaryBorder"
Property="CornerRadius"
Value="0 0 4 4"/>
<Setter
TargetName="SubmenuBoundaryBorder"
Property="Padding"
Value="0 0 0 3"/>
</Trigger>
<!-- Visually indicate an unaccessible menu item. -->
<Trigger
Property="IsEnabled"
Value="false">
<Setter
Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ... -->
<!-- Inside a window XAML file -->
<!-- Tool Bar Tray -->
<ToolBarTray x:Name="toolBarTray1"
DockPanel.Dock="Top">
<!-- File And Edit Tools -->
<ToolBar x:Name="toolBar1"
Band="1" BandIndex="1">
<!-- Displayed Buttons -->
<Button x:Name="ButtonA"
Content="A"/>
<Button x:Name="ButtonB"
Content="B"/>
<!-- Overflow Menu For Special Items -->
<Menu x:Name="OverflowMenu"
ToolBar.OverflowMode="Always">
<MenuItem x:Name="SpecialsMenuItem"
Style="{StaticResource MyStyle}"
Header="Special Items">
<MenuItem x:Name="CMenuItem"
Header="C">
<MenuItem x:Name="DMenuItem"
Header="D"/>
</MenuItem>
</MenuItem>
</Menu>
</ToolBar>
</ToolBarTray>
I attack the behavior of 'SubmenuPopup' on a mouse-over, rather than handling the click event. I'd like to understand this more fully, so I tried commenting out this part of the trigger and adding an event handler that calls a 'DoNothing()' method on the 'PreviewMouseDown' event. It turns out that I am missing something and I think it is related to focusing and/or how a menu handles its items collection. Not allowing an event to propagate after 'DoNothing()' (routedEventArgs.Handled = true) seems to eliminate the problems when clicking on the "Special Items" menu item. However, if one navigated away from the menu or added another menu item and then clicked on that, the hover behavior can be turned off or switched on and off.
The only way I could find to even come close to generating this behaviour was to create a menu in the overflow which contained a single menu item whose header was itself another menu item called "Special Items" and containing the proper children. It worked as intended but looked bizarre (this could be remedied by custom templates) and also seems like a huge hack. The only "proper" way to do this that I can think of would be making your own MenuItem-like control which pops up a ContextMenu or Popup when hovered upon, since I don't think that a custom ControlTemplate can change the default behaviour of a menu so as not to require a click on the top level item.