How do you underline AccessKey characters (mnemonics) in applications built using WinAppSDK/WinUI?

huangapple go评论60阅读模式
英文:

How do you underline AccessKey characters (mnemonics) in applications built using WinAppSDK/WinUI?

问题

Microsoft的辅助功能指南对于Win32应用程序开发要求为菜单项分配访问键:

为所有菜单项分配访问键。没有例外。

在Win32应用程序中,可以通过在菜单项的分配文本字符串中放置一个和符号来实现这一点,该符号代表要加下划线的字符。例如,"&File" 将呈现字符串 File,但字符 F 将被加下划线:

How do you underline AccessKey characters (mnemonics) in applications built using WinAppSDK/WinUI?

我正在尝试了解如何在使用WinUI的Windows App SDK应用程序中实现相同的功能。

我知道可以在 MenuBarItem 控件上设置 AccessKey 属性。这模仿了Microsoft Office在Ribbon控件中使用访问键的实现方式。它会导致指定的字符在按下 Alt 键后出现在工具提示中。

例如,这个标记:

<MenuBar>
    <MenuBarItem Title="File" AccessKey="F" />
</MenuBar>

在用户按下 Alt 键后会呈现如下内容:

How do you underline AccessKey characters (mnemonics) in applications built using WinAppSDK/WinUI?

但是这个标记不会导致 F 字符被加下划线,以便用户在按下 Alt 键之前就知道访问键的存在(假设在设置中启用了“始终显示加下划线的访问键”选项,这在Win32应用程序中是默认的)。

根据此文档,您必须使用 <Underline/> 元素手动执行这项操作:

访问键的下划线文本装饰不会自动提供。如果希望在UI中显示带下划线的文本,您必须在您的助记符中显式为特定键的文本添加内联下划线格式。

但是 AccessKey 属性的类型是 string,因此我不清楚如何将其设置为包含 <Underline/> 元素的文本块。

因此,使用上面的示例标记,我的问题是如何使“File”菜单项中的 F 字符带有下划线。

英文:

Microsoft's accessibility guidance for Win32 application development is to assign access keys to menu items:
> Assign access keys to all menu items. No exceptions.

In Win32 applications, this is done by placing an ampersand in a menu item's assigned text string, which represents the character that is to be underlined. For example, &quot;&amp;File&quot; would render the string File, but the F character would be underlined:

How do you underline AccessKey characters (mnemonics) in applications built using WinAppSDK/WinUI?

I'm trying to understand how to accomplish this same thing in a Windows App SDK application that's using WinUI.

I'm aware of the AccessKey property that can be set on a MenuBarItem control. This mimics Microsoft Office's implementation of access keys in the Ribbon control. It causes the specified character to appear in a tooltip after the Alt key is pressed.

For example, this markup:

&lt;MenuBar&gt;
    &lt;MenuBarItem Title=&quot;File&quot; AccessKey=&quot;F&quot; /&gt;
&lt;/MenuBar&gt;

Will render this after the user presses the Alt key:

How do you underline AccessKey characters (mnemonics) in applications built using WinAppSDK/WinUI?

But that markup doesn't result in the F character being underlined so that the user knows about the access key's existence before having to press the Alt key (as is the case for Win32 applications, assuming that "Always underline access keys" is enabled in Settings).

According to this document, you have to do this manually using the &lt;Underline/&gt; element:
> The underline text decoration for an access key is not provided automatically. You must explicitly underline the text for the specific key in your mnemonic as inline Underline formatting if you wish to show underlined text in the UI.

But the AccessKey property is of type string, so it's unclear to me how to set this to a text block that includes an &lt;Underline/&gt; element.

So using the example markup above, my question is how to make the F character underlined in the "File" menu item.

答案1

得分: 1

关于这个问题有一个讨论,但目前来说,据我所知,没有内置的方法来实现这个。

您可以创建一个自定义的 MenuBarItem,覆盖 Title 属性,将其从 string 类型更改为 object 类型:

MenuBarItemEx.cs

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace MenuBarExample
{
    public sealed class MenuBarItemEx : MenuBarItem
    {
        public static new readonly DependencyProperty TitleProperty =
            DependencyProperty.Register(
                nameof(Title),
                typeof(object),
                typeof(MenuBarItemEx),
                new PropertyMetadata(default, OnTitlePropertyChanged));

        public MenuBarItemEx()
        {
            this.DefaultStyleKey = typeof(MenuBarItem);
        }

        public new object Title
        {
            get => GetValue(TitleProperty);
            set => SetValue(TitleProperty, value);
        }

        private Button? ContentButton { get; set; }

        protected override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (GetTemplateChild("ContentButton") is Button contentButton)
            {
                ContentButton = contentButton;
                ContentButton.Content = Title;
            }
        }

        private static void OnTitlePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is MenuBarItemEx menuBarItemEx &&
                menuBarItemEx.ContentButton is Button contentButton)
            {
                contentButton.Content = e.NewValue;
            }
        }
    }
}

现在您可以这样做:

<MenuBar>
    <local:MenuBarItemEx AccessKey="F">
        <local:MenuBarItemEx.Title>
            <RichTextBlock>
                <Paragraph CharacterSpacing="0">
                    <!-- 这必须在一行中,否则在 'F' 和 'ile' 之间会有一个空格 -->
                    <Underline>F</Underline><Run>ile</Run>
                </Paragraph>
            </RichTextBlock>
        </local:MenuBarItemEx.Title>
    </local:MenuBarItemEx>
</MenuBar>

如何去掉 TextBlock 中 Runs 之间的空白?

英文:

There's a discussion about this but for the moment, AFAIK, there's no built-in way to do this.

What you can do is to create a custom MenuBarItem that overrides the Title property from string to object:

MenuBarItemEx.cs

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace MenuBarExample;

public sealed class MenuBarItemEx : MenuBarItem
{
    public static new readonly DependencyProperty TitleProperty =
        DependencyProperty.Register(
            nameof(Title),
            typeof(object),
            typeof(MenuBarItemEx),
            new PropertyMetadata(default, OnTitlePropertyChanged));

    public MenuBarItemEx()
    {
        this.DefaultStyleKey = typeof(MenuBarItem);
    }

    public new object Title
    {
        get =&gt; GetValue(TitleProperty);
        set =&gt; SetValue(TitleProperty, value);
    }

    private Button? ContentButton { get; set; }

    protected override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        if (GetTemplateChild(&quot;ContentButton&quot;) is Button contentButton)
        {
            ContentButton = contentButton;
            ContentButton.Content = Title;
        }
    }

    private static void OnTitlePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is MenuBarItemEx menuBarItemEx &amp;&amp;
            menuBarItemEx.ContentButton is Button contentButton)
        {
            contentButton.Content = e.NewValue;
        }
    }
}

Now you can do this:

&lt;MenuBar&gt;
    &lt;local:MenuBarItemEx AccessKey=&quot;F&quot;&gt;
        &lt;local:MenuBarItemEx.Title&gt;
            &lt;RichTextBlock&gt;
                &lt;Paragraph CharacterSpacing=&quot;0&quot;&gt;
                    &lt;!-- This have to be in a single line
                    unless you&#39;ll get a space between &#39;F&#39; and &#39;ile&#39; --&gt;
                    &lt;Underline&gt;F&lt;/Underline&gt;&lt;Run&gt;ile&lt;/Run&gt;
                &lt;/Paragraph&gt;
            &lt;/RichTextBlock&gt;
        &lt;/local:MenuBarItemEx.Title&gt;
    &lt;/local:MenuBarItemEx&gt;
&lt;/MenuBar&gt;

How to get rid of whitespace between Runs in TextBlock?

huangapple
  • 本文由 发表于 2023年6月16日 04:42:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76485386.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定