Using a user control as a context menu in WPF - 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>

Related

Why does my Ribbon Menu Button Popup disappear when I apply this Style?

Here is my XAML:
<Ribbon x:Name="ribbonMain" Height="200" ContextMenu="{x:Null}" VerticalAlignment="Top" ShowQuickAccessToolBarOnTop="False" >
<RibbonTab x:Name="ribbonTabMain" Header="Test Tab" ContextMenu="{x:Null}" >
<RibbonGroup x:Name="ribbonGroupMain" Header="Test Group" ContextMenu="{x:Null}">
<RibbonButton x:Name="ribbonButtonMain" Label="Test Button" ContextMenu="{x:Null}" />
</RibbonGroup>
<RibbonGroup x:Name="ribbonGroupMain2" Header="Test Group 2" ContextMenu="{x:Null}">
<RibbonMenuButton ContextMenu="{x:Null}" Name="ribbonMenuButtonMain" Label="Menu Button">
<RibbonMenuItem ContextMenu="{x:Null}" Name="ribbonMenuItemMain" Header="Menu Item"></RibbonMenuItem>
<RibbonMenuItem ContextMenu="{x:Null}" Name="ribbonMenuItemMain2" Header="Menu Item 2"></RibbonMenuItem>
</RibbonMenuButton>
</RibbonGroup>
</RibbonTab>
</Ribbon>
I then run this C# Code to get the Ribbon Menu Button Default Control Template:
string ribbonMenuButtonControlTemplate = XamlWriter.Save(ribbonMenuButtonMain.Template);
After that I set the x:Name and x:Key properties of the Control Template to something and then put that string of XAML in this:
<Style TargetType="RibbonMenuButton"
<Setter Property="Template">
<Setter.Value>
{DefaultControlTemplateHere}
</Setter.Value>
</Setter>
</Style>
Last I put that Style in my <Window.Resources>.
I wanted to alter the Style from there, but then I realized that the popup just wasn't working anymore.
I expected nothing to change. Seems I was mistaken.
Why does this happen?
Note:
I've tried running this code to see if the popup would open:
if (!ribbonMenuButtonMain.IsDropDownOpen)
{
ribbonMenuButtonMain.IsDropDownOpen = true;
}
With no Style applied that code runs fine and the popup opens.
But with the Style I get this exception:
System.InvalidOperationException: 'This Visual is not connected to a
PresentationSource.'
The XamlWriter.Save method has some serialization limitations that are mentioned here. One of them being that;
Common references to objects made by various markup extension formats, such as StaticResource or Binding, will be dereferenced by the serialization process. These were already dereferenced at the time that in-memory objects were created by the application runtime, and the Save logic does not revisit the original XAML to restore such references to the serialized output.
So your generated template is missing a TemplateBinding to the IsOpen property of the Popup:
<Popup ... IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropDownOpen}">
You may extract the default template including any bindings from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\WPF\System.Windows.Controls.Ribbon.dll using a decompiler such as for example dotPeek.

Xceed splitbutton with menu items and sub menuitems

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

How to customize default context menu

In my WPF application I want to make my all textboxes cut, copy and paste restricted.
One way to do this is set ContextMenu ="{x:Null}"
But by doing this I will loose the spell check suggestions which I don't want to loose. Also In my application I have 1000 textboxes so I want to do this is in a more optimize way.
Any advice will be appreciated.
If all you need is menu items related to spell checking, you can refer to this MSDN article:
How to: Use Spell Checking with a Context Menu.
If you want to apply custom ContextMenu to multiple (but not all) textboxes:
<Window.Resources>
<ContextMenu x:Key="MyCustomContextMenu">
<MenuItem Header="Ignore All" Command="EditingCommands.IgnoreSpellingError" />
</ContextMenu>
</Window.Resources>
<Grid>
<TextBox Height="23" Name="textBox1" Width="120" SpellCheck.IsEnabled="True"
ContextMenu="{StaticResource MyCustomContextMenu}" />
</Grid>
If you want to apply custom ContextMenu to ALL textboxes:
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem
Header="Ignore All"
Command="EditingCommands.IgnoreSpellingError" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TextBox Height="23" Name="textBox1" Width="120" SpellCheck.IsEnabled="True" />
</Grid>
NOTE:
You can move the context menu resource to application level instead of the window level.
The MSDN article mentions to get menu items via C# code and not via XAML. I could easily port the "Ignore All" command to XAML (code snippets above), but for spelling suggestions, you will have to do some R&D.

Why do my commandbindings only work if button is in toolbar?

I have a window with a toolbar which contains some buttons with commands.
The Buttons stopped working since i replaced the toolbar with a stackpanel containing the buttons.
In my understanding this shound not make any difference. The buttons still have the Command property set, i did not change anything in my custom commands class and the CommandBinding are also still the same. They are implemented some grids and usercontrols deeper than the button, but they do work, as long as the buttons are in a ToolBar control!
If i implement CommandBindings directly in the Window they work (but that is not what i want)
Here's the code (abridged):
<Window>
<Grid>
<StackPanel>
<Button Command="gui:GuiCommands.Hello">Hello</Button>
</StackPanel>
<Grid>
<TabControl>
<TabItem Header="MyTab">
<Grid>
<Grid.CommandBindings>
<CommandBinding Command="gui:GuiCommands.Hello" Executed="hello_Clicked"/> <!-- THIS WOULD NOT WORK -->
</Grid.CommandBindings>
<Grid>
</TabItem>
</TabControl>
</Grid>
</Grid>
<Window.CommandBindings>
<CommandBinding Command="gui:GuiCommands.Hello" Executed="hello_Clicked"/> <!-- THIS WOULD WORK -->
</Window.CommandBindings>
</Window>
I know it would not compile but i had to simplify it. This works as soon as i replace "StackPanel" with "ToolBar" with my app. How can that be?
Okay, i guess is figured it out by my self again (why does this always happen right after i posted the question?)
Short: I needed to set FocusManager.IsFocusScope="true" on the StackPanel
Long: see the answer to this question: Do I have to use CommandTarget? I thought any focused element would receive the Command
A StackPanel only arranges child elements into a single line that can be oriented horizontally or vertically.
While a Toolbar provides a container for a group of commands or controls.
So what happens if you put a StackPanel element inside of the ToolBar
<ToolBar>
<StackPanel>
<Button Command="gui:GuiCommands.Hello">Hello</Button>
</StackPanel>
</ToolBar>

Silverlight - specify stackpanel contents of a user control in the parent

I created a user control called 'RibbonTabX' which contains a stackpanel named 'spMain'. What I'd like to do, is when I declare an instance of my 'RibbonTabX' in xaml, within that same xaml I'd like to specify controls which will be inside the child stackPanel 'spMain'. Here is the code which will make what I'm trying to do much clearer:
<ribbon:RibbonTabX strHeaderText="Testing 123...">
<ribbon:RibbonTabX.spMain>
<sdk:Label Content="Hello" />
<sdk:Label Content="World" />
</ribbon:RibbonTabX.spMain>
</ribbon:RibbonTabX>
In the parent of RibbonTabX, I want to specify child contents of the stackpanel within my user control 'RibbonTabX'. Just like you can do with a 'TabItem' control. Any ideas how I can do this?
Thanks!
You need to create a custom content control, not a user control.
Start with this article
It is more complex than a user control as you have to hand-craft a generic template for it, but they are more versatile.
You want to use a ContentControl. Rather than specify that those controls go in the stack panel you probably should just place the Content in the stack panel. Have your RibbonTabX derive from ContentControl rather than UserControl, then where it is appropriate put the <ContentPresenter /> then the user of the ribbon can put whatever into it.
<ribbon:RibbonTabX strHeaderText="Testing 123...">
<StackPanel>
<sdk:Label Content="Hello" />
<sdk:Label Content="World" />
</StackPanel>
</ribbon:RibbonTabX>
Here is the most basic ContentControl possible:
<ContentControl x:Class="SilverlightControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="Orange">
<ContentPresenter />
</Grid>
</ContentControl>

Resources