Met*_*450 1 .net c# events delegates winforms
我正在尝试找出一种方法,让我的 WinForms 应用程序将每次按钮单击记录到文件中。到目前为止,我找到的最佳解决方案(来自Log all button clicks in Win Forms app)如下所示:
public class ButtonLogger
{
public static void AttachButtonLogging(ControlCollection controls)
{
foreach (var control in controls.Cast<Control>())
{
if (control is Button)
{
Button button = (Button)control;
button.Click += LogButtonClick;
}
else
{
AttachButtonLogging(control.Controls);
}
}
}
private static void LogButtonClick(object sender, EventArgs eventArgs)
{
Button button = sender as Button;
WriteLog("Click: " + button.Parent.Name.ToString() + "." + button.Name.ToString() + " (\"" + button.Text + "\")");
}
private static void WriteLog(string message)
{
//...
}
}
Run Code Online (Sandbox Code Playgroud)
然后在每个表单的构造函数的末尾:
ButtonLogger.AttachButtonLogging(this.Controls);
Run Code Online (Sandbox Code Playgroud)
虽然这是一个很好的方法,但它有 3 个主要缺点,所有这些缺点都是由于日志事件总是在“真实”事件(由设计者创建)之后发生:
1) 如果单击按钮打开一个模式对话框,则该对话框中的单击将在父级单击之前被记录(也称为日志显示无序)。这是因为只有在模式对话框关闭后(即在该对话框内单击之后)才会记录父级的单击。
2) 同样,如果该模式对话框中的某些操作导致应用程序崩溃,则打开它的单击将永远不会被记录,从而使其看起来好像崩溃发生在父级中。
3)如果单击按钮关闭当前对话框,则button.Parent将为空(因为记录它的事件将在对话框已被“第一个”事件关闭后被触发)。因此,我们无法记录“关闭”点击来自的表单的名称。
我一直在绞尽脑汁寻找一种颠倒顺序的方法(因此日志事件在“真实”事件之前被触发),但无法想出任何不妨碍使用 Designer 的方法。问题是设计器在 InitializeComponents() 中创建并分配事件,而我们无法编辑这些事件。
对可能(不成功)解决方案的思考:
从设计器在按钮创建和添加事件之间放置的内容调用 AttachButtonLogging()。然而,唯一的事情是调用 SuspendLayout(),它不是虚拟的,因此不能被覆盖(此时按钮尚未添加到 ControlCollection 中)。
让 AttachButtonLogging() 删除每个按钮的现有事件,添加日志事件,然后重新添加原始事件。但是,您无法从类外部枚举该类的事件(即 AttachbuttonLogging 无法枚举按钮的事件)。
似乎这样的事情应该可以通过一些创造性的思维来实现,但我还没有什么想法。注意:目标是在构建新对话框时以一种不麻烦的方式记录点击- 类似于上面的解决方案,但没有提到的三个陷阱。
只需将IMessageFilter()与Application.AddMessageFilter()结合使用即可在消息发送到您的应用程序之前获取消息。这适用于应用程序所有形式的所有按钮(无论它们嵌套多深)。它甚至适用于在运行时动态添加的按钮。 最好的部分是,您根本不需要对现有代码和控件进行任何更改。 您所要做的就是从启动表单的 Load() 事件中添加一次消息过滤器:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Application.AddMessageFilter(new ButtonLogger());
}
}
public class ButtonLogger : IMessageFilter
{
private const int WM_KEYUP = 0x101;
private const int WM_LBUTTONUP = 0x202;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_LBUTTONUP || (m.Msg == WM_KEYUP && ((int)m.WParam == 32 || (int)m.WParam == 13)))
{
Control ctl = Control.FromHandle(m.HWnd);
if (ctl is Button)
{
LogButtonClick((Button)ctl);
}
}
return false; // allow normal processing of all messages
}
private void LogButtonClick(Button btn)
{
WriteLog("Click: " + btn.Parent.Name.ToString() + "." + btn.Name.ToString() + " (\"" + btn.Text + "\")");
}
private void WriteLog(string message)
{
Console.WriteLine(message);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2209 次 |
| 最近记录: |