WinForms - 当按钮不可见时 PerfromClick() 不起作用

WinForms - PerfromClick() doesn't work when the button is not visible

我创建了一个动态创建的表单菜单,shows/hides 单击父项目后子项目。我有一个代码可以检查 BaseMenuForm 上的键盘按键。当检测到快捷方式时,我需要为相关按钮执行点击事件。我的问题是当子项不可见时 PerformClick() 事件不起作用。

我通过将事件分配给 ShortcutAction 属性 找到了解决方法。 我认为没有必要将同一事件分配两次,但目前我不知道如何解决这个问题。我不想显示快捷方式的所有菜单项。

如果配置了快捷方式,您可以看到“TODO”注释和执行解决方法的第二个构造函数。当用户单击该按钮时,Click 事件按预期工作,因此它不会以编程方式工作。

这是我的部分代码(只有相关部分):

下面属性在设计器上设置。 BaseMenuForm_KeyDown 事件按预期调用。

BaseMenuForm.KeyPreview = true;

我的物品class:

    /// <summary>
    /// The menu item settings.
    /// </summary>
    public class MyItem : Button
    {
        #region Properties

        /// <summary>
        /// The keyboard keys shortcut.
        /// </summary>
        public Shortcut Shortcut { get; }

        /// <summary>
        /// The action that will be called by the shortcut.
        /// TODO: why not working when I call PerformClick()
        /// </summary>
        public Action ShortcutAction { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes the item instance.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="text">The text.</param>
        /// <param name="onClickEvent">The event that will be triggered on cick.</param>
        public MyItem(string name, string text, EventHandler onClickEvent) : this(name, text)
        {
            if (onClickEvent == null)
            {
                throw new ArgumentNullException(nameof(onClickEvent));
            }
            else
            {
                Click += onClickEvent;
            }
        }

        /// <summary>
        /// Initializes the item instance.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="text">The text.</param>
        /// <param name="onClickEvent">The event that will be triggered on cick.</param>
        /// <param name="shortcut">The keyboard shortcut.</param>
        public MyItem(string name, string text, EventHandler onClickEvent, Shortcut shortcut) : this(name, text, onClickEvent)
        {
            ShortcutAction = () => onClickEvent(this, EventArgs.Empty); //TODO: Why do I need this?
            Shortcut = shortcut;
        }

        #endregion

        #region Methods

       
    }

BaseMenuForm:这个 class 包含表单,每个继承此表单的表单都会有一个菜单。

        #region Shortcut

        private void BaseMenuForm_KeyDown(object sender, KeyEventArgs e)
        {
            CheckShortcut(e.KeyData);
        }

        /// <summary>
        /// Checks if the pressed keys is a shortcut combination.
        /// </summary>
        /// <param name="keyData">The keys.</param>
        private void CheckShortcut(Keys keyData)
        {
            var shortut = (Shortcut)keyData;
            Logger.LogDebug($"Got shortcut {shortut}");
            if(Shortcuts.TryGetValue(shortut, out var button) && button.ShortcutAction != null)
            {
                button.ShortcutAction();
                //button.PerformClick(); //TODO: why not working !
            }
            else
            {
                Logger.LogDebug("No click");
            }
        }

        #endregion

当您按下该键时,您的其中一个按钮处于焦点位置。它阻止父控件 (BaseMenuForm) 处理此事件。

修复:

BaseMenuForm.KeyPreview = true;

我试过这段代码,它按预期工作:

public class MyItem : Button
{
    public Shortcut Shortcut { get; }

    public MyItem(EventHandler onClickEvent, Shortcut shortcut)
    {
        Click += onClickEvent ?? throw new ArgumentNullException(nameof(onClickEvent));
        Shortcut = shortcut;
    }
}

public partial class Form1 : Form
{
    private readonly Dictionary<Shortcut, MyItem> _shortcuts;

    public Form1()
    {
        var myItems = new[]
        {
            new MyItem(OnClickEvent, Shortcut.CtrlG) {Left = 0, Text = "Ctrl+G"},
            new MyItem(OnClickEvent, Shortcut.CtrlH) {Left = 50, Text = "Ctrl+H"}
        };

        _shortcuts = myItems.ToDictionary(x => x.Shortcut);

        Controls.AddRange(myItems);

        KeyPreview = true;
        KeyDown += Form1_KeyDown;

        InitializeComponent();
    }

    private static void OnClickEvent(object sender, EventArgs e)
    {
        MessageBox.Show($"{((Button)sender).Text} Clicked!");
    }

    private void Form1_KeyDown(object sender, KeyEventArgs e)
    {
        if (_shortcuts.TryGetValue((Shortcut) e.KeyData, out var myItem))
        {
            myItem.PerformClick();
        }
    }
}

我将回答以下问题:

  1. 为什么 PerfromClick 对不可见按钮不起作用?
  2. 此问题的解决方法是什么?

对 1 的回答 - 如果您查看 source code of the PerfromClick method, you see the first statement is a check: if (CanSelect) and if follow the code further,您会看到 CanSelect 方法 returns false 当控件不存在时启用或不可见。原来如此。

对 2 的回答 - 作为解决方法,您可以使用按钮的可访问对象和 invoke the default action,它基本上执行按钮的点击:

button1.AccessibilityObject.DoDefaultAction();

或者作为另一种解决方法,使用反射来调用 OnClick 方法:

button1.GetType().GetMethod("OnClick", 
    System.Reflection.BindingFlags.NonPublic |
    System.Reflection.BindingFlags.Instance)
    .Invoke(button, new object[] { EventArgs.Empty });