3
votes

Summary: My global "ContextMenu" style doesn't get applied to the default context menu on textboxes, and other controls.

Specifics: I've got a few TextBoxes in my application which don't have an explicit ContextMenu. So when you right click on them you see the standard context menu options of Cut, Copy, and Paste. However, that context menu doesn't end up using the global "ContextMenu" style I've set up in Generic.xaml. Is the default context menu on a TextBox not actually a ContextMenu?

If I explicitly set the ContextMenu of the Textbox, then the menu uses my global ContextMenu style. For example, this works fine:

<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 have to create this entirely redundant ContextMenu just to get WPF to apply the properly style. Plus, there are other controls besides TextBox that show ContextMenus when clicked, and those don't pick up the global ContextMenu style either.

So, what's actually getting displayed when I right-click a TextBox that doesn't have an explicit ContectMenu defined? Is that thing not a ContextMenu? Why doesn't it use the global ContextMenu style?

Edit: Looking into this further using Snoop, I found that when I explicitly add a ContextMenu, it's shown as a ContextMenu in the visual tree. But the default ContextMenu that gets displayed is shown as an EditorContextMenu in the visual tree. Next question, then, is how to style an EditorContextMenu globally.

Context Menus

2
Things may have changed since 2010, but according to a response by an MSDN moderator, it can't be done: social.msdn.microsoft.com/Forums/en-US/…Bobby
How did you find that with Snoop? I can't see any non-visible content of the tree, so there's no context menu.ygoe

2 Answers

1
votes

You're stuck with creating a custom contextmenu since styling it is indeed unsupported and, as far as I know, impossible. However, I'd like to recommend you to ONLY define each item's command and NO explicit header text:

<ContextMenu x:Key="TextBoxContextMenu">
    <MenuItem Command="Cut"/>
    <MenuItem Command="Copy"/>
    <MenuItem Command="Paste"/>
</ContextMenu>

Defining only the command will automatically set the header text and shortcut keys in the user´s system language.

Also, don´t forget to apply this context menu on the PasswordBox, ComboBox and other text editing controls. Good luck!

1
votes

As you know, we cannot redefine any style against to an internal or private class directly in a ResourceDictionary xaml, but we can do it from code behind.

So, we just need to find the type by reflection and create new style with BasedOn our default ContextMenu and MenuItem style.

private void Initialize()
{
  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);
  }
}

Also, we can generate our custom ResourceDictionary for redefining default hidden styles and a dummy DefaultHiddenStyle.xaml to allow include it in the MergedDictionaries as 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);
      }
    }
  }
}