【问题标题】:Positioning of shortcut key of ToolStripMenu item properlyToolStripMenu 项的快捷键定位正确
【发布时间】:2015-11-10 16:06:08
【问题描述】:

WinForms中,我列出了toolstripmenu items如下:

我们可以看到快捷键列表没有正确缩进。

我已经搜索了解决方案,发现可以使用 空格,但我已经尝试过了,但它不能正常工作。

那么,是否可以为所有菜单项定位快捷键,如下图某个位置

【问题讨论】:

  • 应该走的路应该是:设置ShortcutKeys属性,这样ShortcutKeyDisplayString会自动设置。将ShowShortcutKeys 设置为 true 应该会正确显示项目旁边的快捷方式。
  • 我刚给你试过了。然后这些项目右对齐。看起来你必须扩展类并覆盖绘画。 ://
  • @Koryu,对不起,我不知道覆盖这幅画。你能提供一些进一步的帮助吗?
  • 抱歉没那么容易。如果我是你,我会选择ShowShortcutKeys 属性并与正确的对齐方式一起生活,对于这么一点好处来说,工作量太大了。或者,如果您想要一个非常精美的菜单,请查看一些功能区工具栏。如果您想使用toolstripmenuitem,也许其他人可以帮助您使用overriding the OnPaint Method
  • 这样做是有充分理由的,一定要小心。你无法与 VS 菜单相比,微软在本地化他们的应用程序上花了很多钱。很难匹配,你不会喜欢发现字符串被剪掉了,比如臭名昭著的冗长的德语。

标签: vb.net winforms menustrip


【解决方案1】:

我搜索并阅读了有关该主题的一些内容,但找不到一个可行的示例。所以我想我试着创造一个。它并不完美,但它是入门的基础。例如,我没有尝试具有附加子项的项目。

要呈现菜单项,您不必重写项目的OnPaint 方法。我们最好使用ToolStripProfessionalRenderer。渲染器将​​管理显示菜单项所需的一切。因此我们必须创建自己的类 MyToolStripProfessionalRenderer 并设置Toolstrip.Renderer 属性。

Public Class Form1

  Public Sub New()

    InitializeComponent()
    MenuStrip1.Renderer = New MyToolStripProfessionalRenderer()

  End Sub
End Class

在我们的类中,我们必须重写OnRenderItemText 方法。此方法绘制项目名称和快捷方式的字符串。基本方法很简单,用左对齐绘制名称,用右对齐绘制快捷方式。我们的自定义方法应该用左对齐绘制名称,用左对齐绘制快捷方式。因此,我们需要找到绘制快捷方式的合适位置。我创建了一个循环检查所有项目的快捷方式文本以找到具有最高宽度的项目。从中创建一个矩形并在此矩形中绘制字符串。

注意:使用此示例时,您必须在设计器中手动设置ShortcutKeyDisplayString 属性,否则它总是为空。

/主要修改:

我们还必须更改 autosize 算法以设置每个下拉菜单的 with。

新的自动宽度:图像宽度 + 一些空间 + 最大的 Itemtext + 一些空格 + 最大的 ShortCutText + 一些空格

因此我重写了Initialize(toolStrip As System.Windows.Forms.ToolStrip) 方法。 首先,我添加了一些常量来设置空格。为了计算宽度,我从所有项目中获取并找到子项目的最大文本,然后为项目设置新宽度。

注意:如果您的下拉菜单有另一个下拉菜单,那么您必须 添加递归。

    Imports System.Windows.Forms


Public Class MyToolStripProfessionalRenderer
  Inherits ToolStripProfessionalRenderer



  Protected iconwidth As Integer = 22 ' the width of image icons
  Protected paddingIconToText As Integer = 3
  Protected paddingTextToShortCut As Integer = 20
  Protected paddingShortCutToBoarder As Integer = 20



  Private Enum TextType
    Text = 0
    Shortcut = 1
  End Enum


  Protected Overrides Sub OnRenderItemText(e As System.Windows.Forms.ToolStripItemTextRenderEventArgs)

    ' render only ToolStripMenuItems
    If e.Item.IsOnDropDown And TypeOf e.Item Is ToolStripMenuItem Then

      Dim MenuItem As ToolStripMenuItem = e.Item
      Dim Name As String = MenuItem.Text
      Dim Shortcut As String = MenuItem.ShortcutKeyDisplayString


      'avoid double draw. The method is called twice for each item, check what should be drawn, Text or Shortcut? 
      Dim Mode As TextType
      If e.Text = Name Then
        Mode = TextType.Text
      Else
        Mode = TextType.Shortcut
      End If


      If Mode = TextType.Text Then

        ' this is our column for the menuitem text
        Dim FirstColumn As Rectangle = New Rectangle(MenuItem.ContentRectangle.Left + iconwidth + paddingIconToText,
                                MenuItem.ContentRectangle.Top + 1,
                                MenuItem.Width - iconwidth - paddingIconToText,
                                MenuItem.Height)
        ' drawing the menu item
        e.Graphics.DrawString(Name, MenuItem.Font, New SolidBrush(MenuItem.ForeColor), FirstColumn)
        ' this is the Shortcut to display, be sure to have set it manually

      Else

        ' to align the text on the wanted position, we need to know the width for the shortcuts, this depends also on the other menu items
        Dim CurStrip As ToolStrip = MenuItem.GetCurrentParent()
        Dim fShortCutWidth As Single = 0
        ' lets find the other menuitems for this group
        For Each item As Object In CurStrip.Items
          ' lets look for the ToolStripMenuItem only
          If TypeOf item Is ToolStripMenuItem Then
            Dim ChildItem As ToolStripMenuItem = item
            Dim sCurShortcut As String = ChildItem.ShortcutKeyDisplayString
            ' how many pixels are needed to draw the current shortcut?
            Dim size As SizeF = e.Graphics.MeasureString(sCurShortcut, ChildItem.Font)
            If size.Width > fShortCutWidth Then
              fShortCutWidth = size.Width ' save it for later
            End If
          End If
        Next

        ' avoid to lose 1 pixel by casting to integer
        Dim ShortCutWidth As Integer = Convert.ToInt32(fShortCutWidth) + 1

        If fShortCutWidth > 0 Then
          ' this is our second column for the shortcut text
          Dim SecondColumn As Rectangle = New Rectangle(MenuItem.Width - ShortCutWidth - paddingShortCutToBoarder,
                               MenuItem.ContentRectangle.Top + 1,
                               ShortCutWidth,
                               MenuItem.Height)
          ' drawing the shortcut
          e.Graphics.DrawString(Shortcut, MenuItem.Font, New SolidBrush(MenuItem.ForeColor), SecondColumn)
        End If

      End If
    Else ' there might be other items, use the default method


      MyBase.OnRenderItemText(e)
    End If


  End Sub



  Protected Overrides Sub Initialize(toolStrip As System.Windows.Forms.ToolStrip)
    MyBase.Initialize(toolStrip)


    ' custom autosize algorithm
    ' 1: Find all dropdownbuttons  
    ' 2: Get all Menuitems within dropdown
    ' 3: find the largest string of the dropdownitems text
    ' 4: find the latgest string of the dropdownitems shortcuttext
    ' 5: set the width for all items = picture width + padding + longest_itemtext + padding + longest_shortcuttext + padding

    For Each item As ToolStripItem In toolStrip.Items  ' get all dropdownbuttons
      If TypeOf item Is ToolStripDropDownButton Then
        Dim btn As ToolStripDropDownButton = item
        If btn.HasDropDownItems Then ' dropdownitems
          Dim MaxSizeOfItemName As Single = 0
          Dim MaxSizeOfShortCutName As Single = 0
          Dim CurSizeOfItemName As Single = 0
          Dim CurSizeOfShortCutName As Single = 0

          For Each child As ToolStripItem In btn.DropDownItems ' menu items within dropdown menu
            ' find the largest strings of dropdownitems
            If TypeOf child Is ToolStripMenuItem Then
              Dim CurrentMenuItem As ToolStripMenuItem = child
              CurSizeOfItemName = TextRenderer.MeasureText(CurrentMenuItem.Text, child.Font).Width
              CurSizeOfShortCutName = TextRenderer.MeasureText(CurrentMenuItem.ShortcutKeyDisplayString, child.Font).Width
              MaxSizeOfItemName = Math.Max(MaxSizeOfItemName, CurSizeOfItemName)
              MaxSizeOfShortCutName = Math.Max(MaxSizeOfShortCutName, CurSizeOfShortCutName)
            End If
          Next
          If MaxSizeOfItemName > 0 Then
            Dim autowidth As Integer = iconwidth + paddingIconToText + Convert.ToInt32(MaxSizeOfItemName) + 1 + paddingTextToShortCut + Convert.ToInt32(MaxSizeOfShortCutName) + 1 + paddingShortCutToBoarder
            ' it's not enough to set only the dropdownitems' width, also have to change the ToolStripDropDownMenu width
            Dim menu As ToolStripDropDownMenu = btn.DropDownItems.Item(0).GetCurrentParent() ' maybe there is a better way to get the menuobject?!
            menu.AutoSize = False
            menu.Width = autowidth
            For Each child As ToolStripItem In btn.DropDownItems
              child.AutoSize = False
              child.Width = autowidth
            Next
          End If ' MaxSizeOfItemName

          ' CAUTION: this works only for the first level of menuitems, if your dropdownmenu has another dropdownmenu, move the code above into a method and add recursion for each dropdownbutton with subitems
        End If ' btn.HasDropDownItems
      End If ' TypeOf item Is ToolStripDropDownButton
    Next 'For Each item As ToolStripItem


  End Sub
End Class

【讨论】:

  • 感谢您的努力。我会试试看,然后回复你。
  • 您好@Koryu,我尝试过使用MenuStrip,但遇到了一个小问题。问题:设计时间(text+shortcutdisplaystring)和运行时间(text+shorcutdisplaystring)在RunTime中都是可见的。设计文本和运行时文本都是重叠的。如果解决了,它会很棒。你能编辑和帮助吗?谢谢。
  • @KnockKnock 你能发布设计师为你的菜单生成的代码吗?所以我可以将你的菜单复制到我的项目中。
  • 添加了自定义自动调整大小算法以避免重叠。删除了 C# 代码,请任何人修复语法高亮 :)
  • 你很有帮助,非常感谢。有用。我们不能以某种方式联系吗,因为你看起来很有帮助,我也需要一个导师:)。
【解决方案2】:

这是C#中的另一个解决方案

private class MenuRenderer : ToolStripProfessionalRenderer {

    Hashtable ht = new Hashtable();
    int shortcutTextMargin = 5;
    Font cachedFont = null;

    protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) {
        ToolStrip ts = e.Item.Owner;
        if (ts.Font != cachedFont) {
            cachedFont = ts.Font; // assumes all menu items use the same font
            ht.Clear();
        }

        var mi = e.Item as ToolStripMenuItem;

        if (mi != null && mi.ShortcutKeys != (Keys) 0) {
            if (e.Text != mi.Text) { // shortcut text
                ToolStripMenuItem owner = (ToolStripMenuItem) e.Item.OwnerItem;

                e.TextFormat = TextFormatFlags.VerticalCenter;
                Size sz = TextRenderer.MeasureText(e.Graphics, e.Text, e.TextFont);

                int w = owner.DropDown.Width;
                int x = w - (sz.Width + shortcutTextMargin);
                int? xShortcut = (int?) ht[owner];
                if (!xShortcut.HasValue || x < xShortcut.Value) {
                    xShortcut = x;
                    ht[owner] = xShortcut;
                    owner.DropDown.Invalidate();
                }

                Rectangle r = e.TextRectangle;
                r.X = xShortcut.Value;
                e.TextRectangle = r;
            }
        }

        base.OnRenderItemText(e);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多