【问题标题】:Disable underscore interpretation for MenuItems禁用 MenuItems 的下划线解释
【发布时间】:2016-02-21 05:15:34
【问题描述】:

我的应用程序中有一个菜单可以直接从外部绑定的字符串列表 (ItemsSource = Settings.RecentlyLoadedFiles) 中获取其内容。这些字符串是文件名,当用户单击菜单中的某个项目时,将加载该文件。

<ui:SplitButton
    Grid.Column="0"
    Command="{Binding LoadLogCommand}" ToolTip="Load log" ToolTipService.ShowOnDisabled="True"
    Focusable="False"
    ItemsSource="{Binding Settings.RecentlyLoadedFiles}"
    MenuItem.Click="SplitButton_Click">
    <ui:SplitButton.Icon>
        <ui:MenuIconImage Source="/Images/folder_open.png"/>
    </ui:SplitButton.Icon>
</ui:SplitButton>

现在文件名中可以​​很容易地包含下划线,并且不会显示,因为 WPF 认为它可能是加速键。它不是。但是我怎么能告诉菜单呢?

没有简单的方法将这些下划线加倍以显示其中一个。该列表来自与此类事情无关的来源。

我是否需要编写一个自定义转换器将该列表转换为带有双下划线的字符串列表才能禁用该功能?

或者我是否需要从某处复制菜单项的整个模板,以便在其中某处以不同方式设置一个特定属性?

难道没有一些附加属性或任何东西可以传播到进行解释的地方吗?

【问题讨论】:

  • 一点解释:MenuItem 在其ControlTemplate 中包含一个ContentPresenter,而RecognizesAccessKey 设置为true。我的观察是它所做的是检测内容是什么,如果它是一个包含_的字符串,那么它将内容包装在AccessText 元素中,AccessKey 设置为下划线后面的字符。我不知道有任何附加属性会影响这一点。在ContentPresenter 之后将RecognizesAccessKey 更改为false 没有效果。下面给出的答案是一个可能的解决方案。
  • @LonelyPixel - 您必须覆盖 MenuItem 的默认模板并将RecongnizesAccessKey 设置为False,它应该可以工作。也许您可以将 MenuItem 的默认样式放在菜单资源中,这样您就不必显式覆盖所有菜单项。

标签: .net wpf


【解决方案1】:

使用转换器。如果您不使用绑定,则可以通过 CommandParameter 传递值,以下是您在 XAML 中的操作方式:

    <Menu DockPanel.Dock="Top">
        <Menu.Resources>
            <converters:MenuTextConverter x:Key="MenuTextConverter" />
        </Menu.Resources>
        <MenuItem Header="{Binding Converter={StaticResource MenuTextConverter}, ConverterParameter='_File'}" />
        <MenuItem Header="{Binding Converter={StaticResource MenuTextConverter}, ConverterParameter='_Edit'}" />
    </Menu>

以下是您在代码隐藏中的操作方式:

        InitializeComponent(); // do this first

        var menuItem1 = new MenuItem();
        var binding1 = new Binding();
        binding1.Converter = converter;
        binding1.ConverterParameter = "_File";
        BindingOperations.SetBinding(menuItem1, MenuItem.HeaderProperty, binding1);
        this.theMenu.Items.Add(menuItem1);

        var menuItem2 = new MenuItem();
        var binding2 = new Binding();
        binding2.Converter = converter;
        binding2.ConverterParameter = "_Edit";
        BindingOperations.SetBinding(menuItem2, MenuItem.HeaderProperty, binding2);
        this.theMenu.Items.Add(menuItem2);

这是转换器本身的代码:

public class MenuTextConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return parameter.ToString().Replace("_", "__");
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

如果您通过绑定本身传递 menuitem 字符串,那么显然您只需将转换器更改为使用 value 而不是 parameter

【讨论】:

  • 转换器过于激进并替换所有下划线,而它应该只用两个下划线替换前导下划线。除此之外,这可能是一个可行的解决方案,具体取决于 OP 如何将文件名绑定到他的菜单,以及他们是否会设置菜单项的样式而不使用字符串。很好的替代答案。
  • @KoryGill 好点,谢谢!我已经相应地更新了代码。
  • 我需要更新转换器以使用列表而不是单个字符串。我会将这部分代码添加到我的问题中。
  • @MarkFeldman,我认为您的原始答案是最好的。我想了更多,如果您不将所有 _ 替换为 __,那么像 _one_underscore 这样的名称会将 u 视为加速器,这可能不是我们想要的。
  • 我已经回滚了编辑,因为我还认为我需要替换所有下划线。否则第二个下划线将被视为下划线并被删除。
【解决方案2】:

如果您不能(或不想)加倍下划线并使用新列表,您也可以不直接将字符串添加到菜单中,而是将每个字符串放入 TextBlock 并添加文本块到菜单。

这个 sn-p 演示了这两种技术在没有第二和第三个加速器的情况下按预期工作。

var s = "_UnderScore";
var mi = new MenuItem();
mi.Header = s;
myMenu.Items.Add(mi);

var d = "__UnderScore";
mi = new MenuItem();
mi.Header = d;
myMenu.Items.Add(mi);

var t = new TextBlock();
t.Text = s;
mi = new MenuItem();
mi.Header = t;
myMenu.Items.Add(mi);

就个人而言,我通常避免将任何字符串直接添加到菜单之类的内容中,因为您接下来要做的就是扩展解决方案以添加文件类型的图标、在悬停时显示工具提示等,这会让您失望使用控件或其他东西“托管”每个菜单项的路径。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-21
    • 2011-02-12
    • 2016-01-08
    • 2011-09-19
    • 1970-01-01
    • 1970-01-01
    • 2018-08-13
    • 1970-01-01
    相关资源
    最近更新 更多