【问题标题】:WinForms - PerfromClick() doesn't work when the button is not visibleWinForms - 当按钮不可见时,PerfromClick() 不起作用
【发布时间】:2021-12-25 13:59:54
【问题描述】:

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

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

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

这是我的代码的一部分(仅相关部分):

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

BaseMenuForm.KeyPreview = true;

我的物品类:

    /// <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:这个类包含表单,每一个继承这个表单的表单都会有一个菜单。

        #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

【问题讨论】:

    标签: c# winforms events click eventhandler


    【解决方案1】:

    我将回答以下问题:

    1. 为什么PerfromClick 不适用于隐形按钮?
    2. 有什么办法可以解决这个问题?

    回答 1 - 如果查看 source code of the PerfromClick 方法,您会看到第一条语句是检查:if (CanSelect),如果 follow the code further,您会看到 CanSelect 方法返回当控件未启用或不可见时为 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 });
    

    【讨论】:

    • 我更喜欢将以下注释与答案分开:通过覆盖 ProcessCmdKey 方法来处理快捷键几乎总是一个更好的主意。菜单组件也非常好用且易于使用的解决方案来分配和检测快捷方式。当菜单不可见时,它也可以工作。另外,如果您的按钮不可见,则基本上意味着您不需要它,您只需要创建检测到快捷键时调用的方法即可。
    【解决方案2】:

    当您按下该键时,您的一个按钮将成为焦点。它阻止父控件(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();
            }
        }
    }
    

    【讨论】:

    • 在发布问题之前,我在设计器上配置了 KeyPreview = true。调用 KeyDown 事件并且该方法执行操作(我的解决方法)。它不适用于 PerformClick()。它与 KeyDown 属性无关,因为如上所述调用了代码。
    • 我认为需要额外的信息,因为我的示例代码可以正常工作。
    • 我有一些代码,我的表单继承了其他表单,但它不应该影响行为。如前所述,只有 PerformClick 不起作用。我可以从键盘获取快捷方式
    • 你能把我的代码放在你的表单上吗?有用吗?
    • 当我创建一个新的 MyItem 并将其添加到表单本身的快捷方式字典中时,我可以看到我的代码也可以工作。菜单形式派生出三种形式。这不是问题,因为它如前所述。我添加了从管理器类启动所有按钮的代码。我验证了在菜单表单的 c'tors 之后调用了 init 方法。请看一下 deviceSettingsItem ,我添加了一个注释,因为这是与 PerformClick() 一起使用的唯一快捷方式。我不明白有什么区别。
    猜你喜欢
    • 1970-01-01
    • 2021-08-14
    • 2017-12-18
    • 2016-11-25
    • 2016-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-28
    相关资源
    最近更新 更多