WPF default TextBox ContextMenu styling
Synopsis: My global style "ContextMenu" does not apply to default context menus in text windows and other controls.
Features: I have multiple TextBoxes in my application that don't have an explicit ContextMenu. Therefore, when you right-click on them, you see the standard cut, copy, and paste context menu options. However, this context menu does not use the global ContextMenu style that I set in Generic.xaml. Is the default context menu in a TextBox actually a ContextMenu?
If I explicitly set the ContextMenu of the textbox, then the menu uses my global ContextMenu style. For example, this works great:
<Style TargetType="{x:Type TextBox}">
<Setter Property="ContextMenu" Value="{StaticResource StandardContextMenu}"/>
</Style>
<ContextMenu x:Key="StandardContextMenu">
<MenuItem Header="Cut" Command="ApplicationCommands.Cut"/>
<MenuItem Header="Copy" Command="ApplicationCommands.Copy"/>
<MenuItem Header="Paste" Command="ApplicationCommands.Paste"/>
</ContextMenu>
But I really don't want to create this completely redundant ContextMenu to force WPF to style correctly. In addition, besides the TextBox, there are other controls that display the ContextMenus when clicked and also do not create a global ContextMenu style.
So what is actually displayed when I right click on a TextBox that does not have an explicit ContectMenu? Isn't that ContextMenu? Why isn't it using the global ContextMenu style?
Edit : After researching this using Snoop, I found that when I explicitly add a ContextMenu, it shows up as a ContextMenu in the visual tree. However, the default Displayed ContextMenu appears as the EditorContextMenu in the visual tree. The next question, therefore, is how to create a global EditorContextMenu.
source to share
You are stuck with creating a custom context menu as styling is really not supported and as far as I know it is not possible. However, I would like to recommend that you ONLY define each element command and NO explicit header text:
<ContextMenu x:Key="TextBoxContextMenu">
<MenuItem Command="Cut"/>
<MenuItem Command="Copy"/>
<MenuItem Command="Paste"/>
</ContextMenu>
Defining a command only automatically sets the title text and keyboard shortcuts to the user's system language.
Also, don't forget to apply this context menu to password controls, ComboBox, and other text editing tools. Good luck!
source to share
As you know, we cannot override any style for an inner or private class directly in the ResourceDictionary xaml, but we can do it from the code behind.
So, we just need to find the type by reflection and create a new style using BasedOn in our default ContextMenu and MenuItem styles.
var presentationFrameworkAssembly = typeof(Application).Assembly;
var contextMenuStyle = FindResource(typeof(ContextMenu)) as Style;
var editorContextMenuType = Type.GetType("System.Windows.Documents.TextEditorContextMenu+EditorContextMenu, " + presentationFrameworkAssembly);
if (editorContextMenuType != null)
{
var editorContextMenuStyle = new Style(editorContextMenuType, contextMenuStyle);
Application.Current.Resources.Add(editorContextMenuType, editorContextMenuStyle);
}
var menuItemStyle = Application.Current.FindResource(typeof(MenuItem)) as Style;
var editorMenuItemType = Type.GetType("System.Windows.Documents.TextEditorContextMenu+EditorMenuItem, " + presentationFrameworkAssembly);
if (editorMenuItemType != null)
{
var editorContextMenuStyle = new Style(editorMenuItemType, menuItemStyle);
Application.Current.Resources.Add(editorMenuItemType, editorContextMenuStyle);
}
In addition, we can generate our own ResourceDictionary to override the default hidden styles and a dummy DefaultHiddenStyle.xaml to allow it to be included in MergedDictionaries like others.
<local:DefaultHiddenStyleResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Styles">
<!-- No entries are required -->
</local:DefaultHiddenStyleResourceDictionary>
namespace Styles
{
public class DefaultHiddenStyleResourceDictionary : ResourceDictionary
{
public DefaultHiddenStyleResourceDictionary()
{
// Run OnResourceDictionaryLoaded asynchronously to ensure other ResourceDictionary are already loaded before adding new entries
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(OnResourceDictionaryLoaded));
}
private void OnResourceDictionaryLoaded()
{
var presentationFrameworkAssembly = typeof(Application).Assembly;
AddEditorContextMenuDefaultStyle(presentationFrameworkAssembly);
AddEditorMenuItemDefaultStyle(presentationFrameworkAssembly);
}
private void AddEditorContextMenuDefaultStyle()
{
var presentationFrameworkAssembly = typeof(Application).Assembly;
var contextMenuStyle = Application.Current.FindResource(typeof(ContextMenu)) as Style;
var editorContextMenuType = Type.GetType("System.Windows.Documents.TextEditorContextMenu+EditorContextMenu, " + presentationFrameworkAssembly);
if (editorContextMenuType != null)
{
var editorContextMenuStyle = new Style(editorContextMenuType, contextMenuStyle);
Add(editorContextMenuType, editorContextMenuStyle);
}
}
private void AddEditorMenuItemDefaultStyle(Assembly presentationFrameworkAssembly)
{
var menuItemStyle = Application.Current.FindResource(typeof(MenuItem)) as Style;
var editorMenuItemType = Type.GetType("System.Windows.Documents.TextEditorContextMenu+EditorMenuItem, " + presentationFrameworkAssembly);
if (editorMenuItemType != null)
{
var editorContextMenuStyle = new Style(editorMenuItemType, menuItemStyle);
Add(editorMenuItemType, editorContextMenuStyle);
}
}
}
}
source to share