Xceed splitbutton with menu items and sub menuitems - wpf

I`m trying to have a dropdown menu under a split button. Some of these menuItems should have sub-menu items. If you want an example, click on the bookmark button in Firefox (top right).
I can't use Menu, because that is always oriented horizontally. So I went with a stackpanel:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="MainWindow" Height="350" Width="525">
<Grid>
<xctk:SplitButton Content="SplitButton" BorderThickness="1" BorderBrush="Black" Margin="0,0,408,290">
<xctk:SplitButton.DropDownContent>
<StackPanel Width="161" HorizontalAlignment="Left">
<MenuItem Header="MenuItem1" HorizontalAlignment="Left" Width="517">
<MenuItem.Items>
<MenuItem Header="submenuItem1"/>
<MenuItem Header="submenuItem2"/>
</MenuItem.Items>
</MenuItem>
<MenuItem Header="MenuItem2"/>
<MenuItem Header="MenuItem3"/>
</StackPanel>
</xctk:SplitButton.DropDownContent>
</xctk:SplitButton>
</Grid>
Problem here is that the sub menu items don't show up. They don't even have the little arrows next to them. You can do this without the SplitButton, just leaving the stackpanel and everything in it, you'll have the same problem. I've tried putting the parent Menu item in its own tag, but I want the sub-menu items to appear to the right of their parent (just like the firefox example: Assuming your firefox window is not maximized, and you've allowed enough screen-space for the item to appear).

the way you're using the MenuItem control is wrong, it is meant to be hosted inside the Menu control or another MenuItem.
As for the solution to your problem, there are two. the first one is to write a custom control that reuses the ContextMenu to host the menu items, you will write something like this:
<m:SplitButton Content="Split Button" Placement="Bottom">
<MenuItem Header="MenuItem 1"/>
<MenuItem Header="MenuItem 2">
<MenuItem Header="MenuItem 1"/>
<MenuItem Header="MenuItem 2"/>
</MenuItem>
</m:SplitButton
http://www.codeproject.com/Articles/20612/A-WPF-SplitButton
the second approach is to host the Menu control inside the DropDownContent and re-style everything, there will be a lot of xaml markup.
the Menu control will be easy to re-style, the only thing you need to do is make sure that menu items are displayed vertically instead of horizontally, using the following markup:
<Style TargetType="Menu">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" IsItemsHost="True" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
the hardest part is styling the MenuItems, they are styled based on their Role property. It can have four possible values:
TopLevelHeader : direct child of Menu with sub-menu items.
TopLevelItem : direct child of Menu without sub-menu items.
SubmenuHeader : direct child of MenuItem with sub-menu items.
SubmenuItem : direct child of MenuItem without sub-menu items.
Regards

Related

Using a user control as a context menu in WPF

I've created a WPF user control that contains some grids, buttons, and sliders. I'd like to use this control as (or in place of) a context menu in my main application window. When a user right-clicks the mouse button, I'd like my user control to be displayed, rather than a normal looking context menu with standard menu items.
What's the best approach to take in displaying a user defined WPF control in place of a context menu?
You could define the ControlTemplate of a ContextMenu however you want. Try this:
<Window ... xmlns:local="clr-namespace:WpfApplication1">
<Grid Background="Transparent">
<StackPanel.ContextMenu>
<ContextMenu>
<ContextMenu.Template>
<ControlTemplate TargetType="ContextMenu">
<local:UserControl1 />
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</StackPanel.ContextMenu>
Just add the control to the ContextMenu. For example:
<Window>
<Window.ContextMenu>
<ContextMenu>
<local:YourUserControl />
</ContextMenu>
</Window.ContextMenu>
</Window>

Extend/Merge/Share Context Menus from custom control

I use a custom control in multiple views. In that custom control I defined a context menu with common MenuItems, that need to be available in different views. Each view than can extend that context menu by their own MenuItems.
How do I accomplish this? I can only replace the exsting context menu, but not extending it.
I read about merging context menus here: http://wpftutorial.net/ContextMenu.html.
But it doesn't show how to merge with the existing ones.
Use resources. You define all the menu items in the window/usercontrol's resource, and then reference them with the key of the menu items.
<Window.Resources>
<MenuItem Header="Add sub-folder"
Command="{Binding FolderNewCmd}"
x:Key="AddFolderMenu">
<MenuItem.Icon>
<Image Source="/YourAssemblyNameSpace;component/Images/16/abc.png"
Width="16"
RenderOptions.BitmapScalingMode="NearestNeighbor">
</Image>
</MenuItem.Icon>
</MenuItem>
... <--other menu items-->
<Window.Resources>
When you want to reference this menu item in a menu (e.g. a context menu):
<TreeView.ContextMenu>
<StaticResource ResourceKey="AddFolderMenu" />
<StaticResource ResourceKey="OtherMenuItemInResource" />
...
</TreeView.ContextMenu>

MenuItems with fixed length

I've got a Menu which receives a dynamic ItemsSource. The number of items and their length varies from time to time.
<Menu ItemsSource="{loc:CustomBinding CurrentMenuItems}" />
The menu's orientation is Horizontal, at times this causes the menu to overflow.
The Menu Items Template :
<ControlTemplate TargetType="{x:Type MenuItem}">
<TextBlock Text="{loc:CustomBinding Title}"
MaxWidth="200"
TextTrimming="CharacterEllipsis"/>
</ControlTemplate>
When there are more than 4 items, the menu overflows outside the left side of the screen
(sorry can't upload pictures from the work place).
Are there any built in overflow options to WPF menu?
Is there an option to control overflow of menu items from the menu itself? Something along the lines of ItemsMaxLength which I could of bound to a calculated field?
I'm not clear what you want to happen when the rightmost menu items cannot fit on the screen. Is it?:
wrap them to the next line
crop them fully
crop them partially
put them in an overflow dropdown?
show an ellipse or other "more" indicator?
The default ItemsPanel template for a Menu uses a WrapPanel...that should wrap them to the next line (unless you have set a height for the menu or your menu is in a container that is isn't allowing it to have its desired height).
We can change the ItemsPanel used to change how items are layed out.
In the example XAML below I show 4 ways to display a menu. You can use this within Kaxaml to see the different behaviours...resize the Window to a small width to see what happens when there isn't enough horizontal space.
the original menu style...wraps menu items that don't fit
a menu that fully crops the menu item that doesn't fit
a menu that partially crops the menu item that doesn't fit
a menu that uses a Toolbar...any items that don't fit go into an overflow panel (note this example isn't perfect...it doesn't exactly adhere to the standard menu
selection behaviour with the mouse/keyboard because its using separate menus for each item...and the style is a little off...and it shows a toolbar gripper....more work would be needed).
If 4 is closest to what you want, then you somehow need to combine the behaviour or a ToolBar with it's overflow panel...with the behaviour of a menu and it's tracking/navigation between menu items.
You might be able to achieve that by creatively re-templating the menu, or the toolbar, or creating your own custom control.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<StackPanel Orientation="Vertical">
<Menu>
<MenuItem Header="_File">
<MenuItem Header="_Exit"/>
</MenuItem>
<MenuItem Header="_Edit"/>
<MenuItem Header="_Window"/>
</Menu>
<Menu>
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<ToolBarPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<MenuItem Header="_File">
<MenuItem Header="_Exit"/>
</MenuItem>
<MenuItem Header="_Edit"/>
<MenuItem Header="_Window"/>
</Menu>
<Menu>
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<MenuItem Header="_File">
<MenuItem Header="_Exit"/>
</MenuItem>
<MenuItem Header="_Edit"/>
<MenuItem Header="_Window"/>
</Menu>
<ToolBar>
<ToolBar.Resources>
<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>
</ToolBar.Resources>
<Menu>
<MenuItem Header="_File">
<MenuItem Header="_Exit"/>
</MenuItem>
</Menu>
<Menu>
<MenuItem Header="_Edit"/>
</Menu>
<Menu>
<MenuItem Header="_Window"/>
</Menu>
</ToolBar>
</StackPanel>
</Window>

Dynamically binding to ViewModel commands on Window's MenuItem

Working on a WPF application using the MVVM structure.
My Window displays a menu and the current ViewModel. On one of the Menu's MenuItems, I want to list some Commands found in the current ViewModel. The commands listed in the Menu will change depending on the ViewModel.
I got this to work just fine, but the style is messed up - the Command MenuItems are inside another menu box or something. I'll attach a screenshot.
I wrapped the ViewModel's ICommand objects (RelayCommands, in this instance) in CommandViewModel, which expose the Command and the Display string I want on the menu. These CommandViewModels are in a list: CurrentWorkspace.AdditionalOptionsCommands.
Here is the XAML for the Menu. Like I said, it works, it shows the right items and the commands are executed. The display is just incorrect - can anybody tell me why and how to fix it? See the screenshot.
<Menu>
<MenuItem Header="_Additional Options..." ItemsSource="{Binding Path=CurrentWorkspace.AdditionalOptionsCommands}">
<MenuItem.ItemTemplate>
<DataTemplate DataType="{x:Type vm:CommandViewModel}">
<MenuItem Header="{Binding Path=DisplayText}" Command="{Binding Path=Command}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
<MenuItem Header="_Testing">
<MenuItem Header="This looks right" />
<MenuItem Header="This looks right" />
</MenuItem>
</Menu>
Current Appearance:
Desired Appearance:
This is because when you specify menu items via ItemsSource each item gets automatically wrapped into a MenuItem object. This way the content defined in the DataTemplate (MenuItem element) gets wrapped into one more MenuItem.
What you need to do instead of defining a DataTemplate is to define a style for the MenuItem where you setup bindings to the view model's properties and use this style as ItemContainerStyle on the parent MenuItem:
<Window.Resources>
<Style x:Key="CommandMenuItemStyle"
TargetType="{x:Type MenuItem}">
<Setter Property="Header"
Value="{Binding Path=DisplayText}"/>
<Setter Property="Command"
Value="{Binding Path=Command}"/>
</Style>
</Window.Resources>
...
<Menu>
<MenuItem Header="_Additional Options..."
ItemsSource="{Binding Path=CurrentWorkspace.AdditionalOptionsCommands}"
ItemContainerStyle="{StaticResource CommandMenuItemStyle}"/>
<MenuItem Header="_Testing">
<MenuItem Header="This looks right" />
<MenuItem Header="This looks right" />
</MenuItem>
</Menu>
See http://drwpf.com/blog/2008/03/25/itemscontrol-i-is-for-item-container/ for an in-depth explanation on how item containers work with ItemsControl controls.

WPF Menu: Wrap items

I'm working on a WPF control that gets placed inside a TabControl in another window, and I have a menu that stretches across the top of my custom control with the letters of the alphabet as such (for indexing purposes):
<UserControl x:Class="thispageclass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<Grid>
<StackPanel Orientation="Vertical">
<Menu Name="mnu">
<MenuItem Header="A" />
<MenuItem Header="B" />
<MenuItem Header="C" />
...
<MenuItem Header="Y" />
<MenuItem Header="Z" />
</Menu>
[other elements]
</StackPanel>
</Grid>
</UserControl>
If all the letters are present, the menu gets wider than the window so you can't see the last few items without making the window wider. It seems like it should be trivial to make the menu wrap around if the window is too small but I can't seem to figure out how.
Just override the ItemsPanel of your Menu to a WrapPanel
Sample
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</Menu.ItemsPanel>
I created my own minimal sample which worked, leading me to figuring out it was my custom styles that were messing up my menu.

Resources