返回首页

{A}
{S0}简介
本文介绍了一种可以在WinForms应用程序使用的组件,并允许用户自定义菜单快捷方式。
很多残疾人士在使用他们的手麻烦,从而有麻烦,同时按下几个键。一个解决方案,在这里可以是一个应用程序可以自定义菜单快捷方式。例如,按Ctrl O打开一个文件,而不是(禁用)用户可以分配F2键。背景
基本的想法是创建一个组件,它是易于使用。在WinForms应用程序,这部分需要用户代码只有两行,得到它的工作。定制快捷方式保存在user.config(即使当应用程序本身并不具备之一)。
这个项目的目的是帮助生产,是无障碍的软件!使用该组件
要使用的组件,它拖到你想允许用户自定义菜单快捷方式的形式。组件的默认名称,然后customizeMenuShortCuts1。
组件提供几种语言选择:DE,EN,ES,FR,PT,RU(大多数语言翻译使用谷歌翻译,如果有任何错误,请让我知道)。
要加载user.config文件的快捷方式,使用此代码在Form_Load事件:

private void Form1_Load(object sender, System.EventArgs e)

{

    customizeMenuShortCuts1.LoadCustomShortCuts(menuStrip1);

}

做的定制也很简单:{C}实施
这篇文章是基于我先前关于此主题的文章(见{A2})。因此,不是每一个细节,在这里解释,因为他们已经对我以前的文章(代码是很好的注释太)。用户界面
组件的主要组成部分,是一种定制的用户界面。在加载的形式,一个TreeView充满菜单结构。快捷方式只能被分配到ToolStripMenuItems,因此每个项目的检查。
private void FillTreeView(TreeNodeCollection nodes, ToolStripItemCollection items)

{

    foreach (ToolStripItem item in items)

    {

        ToolStripMenuItem menuItem = item as ToolStripMenuItem;



        // Check for ToolStripMenuItem

        if (menuItem != null)

        {

            TreeNode tNode = new TreeNode();

            tNode.Text = menuItem.Text.Replace("&", string.Empty);

            tNode.Tag = menuItem;

            tNode.ImageKey = "Sub";

            nodes.Add(tNode);



            // Add the shortcut to the list for checking if a 

            // shortcut already exists:

            if (menuItem.ShortcutKeys != Keys.None)

                _assignedShortCuts.Add(menuItem.ShortcutKeys);



            // Recursion needed?

            if (menuItem.HasDropDownItems)

            {

                tNode.ImageKey = "Main";

                FillTreeView(tNode.Nodes, menuItem.DropDownItems);

            }

        }

    }

}

在上面的片段,每一个快捷方式添加到一个通用的哈希表(。NET 3.5中的一个新功能),这就是后来用于检查如果已经被分配一个快捷。
当选择一个TreeView的节点(即一个菜单项),实际分配的快捷方式设置的形式:
private void tvMenu_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)

{

    // Get selected item:

    _menuItem = e.Node.Tag as ToolStripMenuItem;



    // Check if the selected item is a parent for other toolstrip-items.

    // If so no shortcut can be assigned:

    if (_menuItem.HasDropDownItems)

    {

        MessageBox.Show(

            MyRessource.ShortCutNotPossible,

            Application.ProductName,

            MessageBoxButtons.OK,

            MessageBoxIcon.Exclamation);



        return;

    }



    // Enable editing:

    groupBox1.Enabled = true;



    // Set the actually assigned shortcut. The shortcut is a

    // combination of different Keys so they're bit-masked:

    Keys shortCut = _menuItem.ShortcutKeys;

    chkCtrl.Checked = (shortCut & Keys.Control) == Keys.Control;

    chkAlt.Checked = (shortCut & Keys.Alt) == Keys.Alt;

    chkShift.Checked = (shortCut & Keys.Shift) == Keys.Shift;



    // To get the letter or the F-key we have to eliminate the 

    // modifyers. This is done by combining all set modifiers 

    // and then by removing them from the shortcut with XOR.

    // Example: shortCut    = 111000101

    //            Ctrl        = 111000000 XOR

    //            -----------------------

    //            Letter        = 000000101

    Keys modifiers = Keys.None;

    if (chkCtrl.Checked) modifiers |= Keys.Control;

    if (chkAlt.Checked) modifiers |= Keys.Alt;

    if (chkShift.Checked) modifiers |= Keys.Shift;

    Keys buchstabe = shortCut ^ modifiers;



    // Select the value in the combobox:

    cmbKeys.SelectedValue = buchstabe;

}

用户修改/定制快捷方式后,我们已经申请:
private void btnApply_Click(object sender, EventArgs e)

{

    // Applying happens in a try-catch-block. When there's a faulty

    // input the shortcut is removed.

    // (Maybe there's a better approach - but in the moment I'm to

    // lazy for that;-)

    try

    {

        // When no letter selected in the combobox remove the shortcut:

        if (cmbKeys.SelectedIndex == -1)

            throw new Exception();



        // Create the shortcut:

        Keys shortCut = (Keys)cmbKeys.SelectedValue;

        if (chkCtrl.Checked) shortCut |= Keys.Control;

        if (chkAlt.Checked) shortCut |= Keys.Alt;

        if (chkShift.Checked) shortCut |= Keys.Shift;



        // Check if the shortcut exists:

        if (_assignedShortCuts.Contains(shortCut))

        {

            MessageBox.Show(

                MyRessource.ShortCutAlreadyAssigned,

                Application.ProductName,

                MessageBoxButtons.OK,

                MessageBoxIcon.Exclamation);



            return;

        }



        // Manage the list of assigned shortcuts:

        Keys oldShortCut = _menuItem.ShortcutKeys;

        if (shortCut != oldShortCut)

            _assignedShortCuts.Remove(oldShortCut);



        // Assign the new shortcut:

        _menuItem.ShortcutKeys = shortCut;

    }

    catch

    {

        _menuItem.ShortcutKeys = Keys.None;

    }

    finally

    {

        // Disable editing:

        groupBox1.Enabled = false;

    }

}
按键名单扩展设计器生成的设置
要坚持快捷方式,我使用Visual Studio生成的默认的设计师设置和扩展它们来存储一个通用的列表。
该列表中的项目定义如下。请注意,它必须有Seri​​alizable属性,否则,它不能被保存的user.config。
[Serializable()]

internal sealed class UserConfigEntry

{

    public string Text { get; set; }

    public Keys ShortCut { get; set; }

}

因为设计师不支持泛型列表,我只使用生成的代码,是用于访问配置的设计师。存储列表,即序列列表,使用下面的代码:
/// <summary>

/// Extends the designer generated Settings so that a 

/// custom list can be persisted.

/// </summary>

partial class Settings

{

    /// <summary>

    /// ShortCuts for the menus

    /// </summary>

    /// <remarks>

    /// Is serialized as base64-string

    /// </remarks>

    [UserScopedSetting()]

    [SettingsSerializeAs(SettingsSerializeAs.Binary)]

    [DefaultSettingValue("")]

    public List<UserConfigEntry> UserConfigEntries

    {

        get { return (List<UserConfigEntry>)this["UserConfigEntries"]; }

        set { this["UserConfigEntries"] = value; }

    }

}

正如你可以看到,序列化使用二进制格式化完成,虽然可以通过其他方法进行序列化。组件
实现一个组件是指一个类从基类组件的派生:
[ToolboxBitmap(typeof(CustomizeMenuShortCuts))]

[Description("Allows the user to customize menu shortcuts")]

public sealed class CustomizeMenuShortCuts : Component

{

...

}
加载的快捷方式,并将其分配给
public void LoadCustomShortCuts(MenuStrip menuStrip)

{

    // Get the list from the user.config:

    List<UserConfigEntry> userList =

        Properties.Settings.Default.UserConfigEntries;



    if (userList.Count == 0) return;



    // Create a list of the menu-items:

    List<ToolStripItem> menuList = MenuToList(menuStrip.Items);



    // Assign the shortcuts from the user.config to the menulist.

    // It can't be assumed that the list from the user.config has

    // the same length as the list from the menu-items, because

    // there might be changes in the menuStrip (due to updates, etc.)

    // Therefore the list from the menu-items gets iterated and

    // for each item the proper item in the list from the 



    // user.config is searched (the variant with the loops is the

    // fastest in comparison with LINQ and binary search).



    foreach (ToolStripItem menuEntry in menuList)

    {

        ToolStripMenuItem menuItem = menuEntry as ToolStripMenuItem;

        if (menuItem == null) break;

                

        // Search:

        foreach (UserConfigEntry userEntry in userList)

            if (userEntry.Text == menuItem.Text)

            {

                // Found -> assign shortcur:

                menuItem.ShortcutKeys = userEntry.ShortCut;



                break;

            }

    }

}

因此,一个私有方法是使用:
private List<ToolStripItem> MenuToList(ToolStripItemCollection items)

{

    List<ToolStripItem> list = new List<ToolStripItem>();



    // Run throug all items:

    foreach (ToolStripItem item in items)

    {

        ToolStripMenuItem menuItem = item as ToolStripMenuItem;



        // Check if it's a ToolStripMenuItem (i.e. no seperator):

        if (menuItem != null)

        {

            // Add to list:

            list.Add(menuItem);



            // Recursion needed?

            if (menuItem.HasDropDownItems)

                list.AddRange(MenuToList(menuItem.DropDownItems));

        }

    }



    return list;

}
为定制的方法
public void CustomizeShortCuts(MenuStrip menuStrip)

{

    // Show form that allows for customization:

    frmMain frmMain = new frmMain(menuStrip);

    frmMain.ShowDialog();



    // No we will persist the shortcuts of the menuStrip:

    List<ToolStripItem> menuList = MenuToList(menuStrip.Items);

    Properties.Settings.Default.UserConfigEntries =

        new List<UserConfigEntry>(menuList.Count);



    // Iterate over all menu-items

    foreach (ToolStripItem item in menuList)

    {

        ToolStripMenuItem menuItem = item as ToolStripMenuItem;



        // Separators, for instance, won't be a ToolStripMenuItem

        // so check if it isn't null:

        if (menuItem == null) break;



        Properties.Settings.Default.UserConfigEntries.Add(

            new UserConfigEntry

            {

                Text = menuItem.Text,

                ShortCut = menuItem.ShortcutKeys

            });

    }



    // The settings from a classlibrary have to be saved here,

    // otherwise they will be lost:

    Properties.Settings.Default.Save();

}

注意:重要的是这里保存设置(在这个类库),因为否则,设置不会被保存到user.config。兴趣点扩展设计器生成的设置。一个通用的列表存储在user.config。谢谢
我感谢{A3}启发我写这个代码和文章。特别是他的想法制作软件无障碍。 历史2008年10月21日:首次发布。

回答

评论会员:AndrusM 时间:2011/12/27
!很好的工作

加强思想:

1。如何改变这种使还允许添加ToolStrip的按钮,每一个MenuStrip中形成的ToolStrip。
这是非常好的,也允许定义ToolStrip的按钮。

2。如果菜单项分配的图片,树视图应显示它也

问题:

1。有可能的WinForms菜单设计器中定义的快捷键。此代码不会删除,预定义的快捷键的快捷键文本。
旧的快捷键"文本仍然在下拉菜单中的快捷键本身时被删除。

2。在TreeView点击折叠/展开菜单树显示annoyning的MessageBox。

安德鲁斯
评论会员:AndrusM 时间:2011/12/27
!谢谢

我将修复/改善时,我不那么忙

AndrusM写道:1。如何改变这种使还允许添加ToolStrip的按钮,每一个MenuStrip中形成的ToolStrip。
这是非常好的,也允许定义ToolStrip的按钮。
我不明白你的意思 - 你能解释多一点

干杯
半滑舌鳎
评论会员:AndrusM 时间:2011/12/27
谢谢你答复典型applicaton包含允许用户点击按钮的工具栏。例如,Internet Explorer已经回来了,取消,刷新等按钮。
通过点击这些按钮发出的命令,可从菜单中也发出

这将是很好,如果这个组件允许创建的ToolStrip按钮菜单中的"捷径"也。
对于这一点,setter方法​​应采取的ToolStrip作为第二个参数
用户可以选择菜单项目是要被放置到工具栏。
这componet这些项目添加到工具栏和工具栏按钮的OnClick事件执行命令

menuItem.PerformClick()

执行相应的菜单命令。

我搜索了很长时间,但havent发现任何自定义工具栏。

安德鲁斯