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();
}
}
}
我将回答以下问题:
- 为什么
PerfromClick
对不可见按钮不起作用?
- 此问题的解决方法是什么?
对 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 });
我创建了一个动态创建的表单菜单,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();
}
}
}
我将回答以下问题:
- 为什么
PerfromClick
对不可见按钮不起作用? - 此问题的解决方法是什么?
对 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 });