【问题标题】:WPF default TextBox ContextMenu stylingWPF 默认 TextBox ContextMenu 样式
【发布时间】:2015-09-05 14:26:37
【问题描述】:

总结:我的全局“ContextMenu”样式未应用于文本框和其他控件的默认上下文菜单。

细节:我的应用程序中有一些没有显式上下文菜单的文本框。因此,当您右键单击它们时,您会看到剪切、复制和粘贴的标准上下文菜单选项。但是,该上下文菜单最终并未使用我在 Generic.xaml 中设置的全局“ContextMenu”样式。 TextBox 上的默认上下文菜单实际上不是 ContextMenu 吗?

如果我明确设置了文本框的 ContextMenu,那么菜单将使用我的全局 ContextMenu 样式。例如,这很好用:

<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>

但我真的不想为了让 WPF 应用正确的样式而创建这个完全多余的 ContextMenu。此外,除了 TextBox 之外,还有其他控件在单击时显示 ContextMenus,并且这些控件也不会采用全局 ContextMenu 样式。

那么,当我右键单击没有明确定义 ContectMenu 的 TextBox 时,实际显示的是什么?那东西不是上下文菜单吗?为什么不使用全局 ContextMenu 样式?

编辑: 使用 Snoop 进一步研究这一点,我发现当我显式添加 ContextMenu 时,它在可视化树中显示为 ContextMenu。但是显示的默认 ContextMenu 在可视化树中显示为 EditorContextMenu。那么,下一个问题是如何全局设置 EditorContextMenu 的样式。

【问题讨论】:

  • 自 2010 年以来情况可能发生了变化,但根据 MSDN 版主的回复,无法做到:social.msdn.microsoft.com/Forums/en-US/…
  • 你是如何发现 Snoop 的?我看不到树的任何不可见内容,所以没有上下文菜单。

标签: wpf xaml


【解决方案1】:

如您所知,我们无法直接在 ResourceDictionary xaml 中针对内部或私有类重新定义任何样式,但我们可以从后面的代码中完成。

所以,我们只需要通过反射找到类型,并使用我们默认的 ContextMenu 和 MenuItem 样式创建新样式。

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);
  }
}

此外,我们可以生成自定义 ResourceDictionary 以重新定义默认隐藏样式,并生成一个虚拟 DefaultHiddenStyle.xaml 以允许将其像其他人一样包含在 MergedDictionaries 中。

<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);
      }
    }
  }
}

【讨论】:

    【解决方案2】:

    您无法创建自定义上下文菜单,因为它的样式确实不受支持,据我所知,这是不可能的。但是,我想建议您只定义每个项目的命令并且没有明确的标题文本:

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

    仅定义命令将自动设置用户系统语言的标题文本和快捷键。

    此外,不要忘记在 PasswordBox、ComboBox 和其他文本编辑控件上应用此上下文菜单。祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-05
      • 2022-01-04
      • 2015-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多