我从Gtk.Button
派生一个类,它应该在单击按钮时运行一些代码,但只有在用户定义的事件处理程序之后。
基于。net惯例,这不会是一个问题。下面的示例Windows窗体代码显示了System.Windows.Forms.Button
的一个子类,它在事件处理程序之前执行一些代码,然后调用事件处理程序(通过调用继承的OnClick
方法;相同的将工作在WPF与System.Windows.Controls.Button.OnClick
),然后在事件处理程序后执行一些代码:
using System;
using System.Windows.Forms;
using System.Drawing;
namespace ButtonClickedTestSWF
{
class Program
{
private class MyButton : Button
{
protected override void OnClick(EventArgs e)
{
Console.WriteLine("before");
base.OnClick(e);
Console.WriteLine("after");
}
}
[STAThread]
public static void Main(string[] args)
{
using (Form win = new Form() {
Text = "Test"
}) {
win.StartPosition = FormStartPosition.CenterScreen;
win.Size = new Size(300, 200);
MyButton btn = new MyButton();
btn.Text = "Button";
btn.Click += delegate {
Console.WriteLine("event");
};
btn.Dock = DockStyle.Fill;
btn.Parent = win;
Application.Run(win);
}
}
}
}
如预期的那样,输出是:
before
event
after
然而,到目前为止,我还没有在gtk#中复制这个行为。与。net惯例相反,继承
Gtk.Button
的OnClicked
方法似乎不会触发Clicked
事件。事实上,文档称OnClicked
方法为"默认处理程序"。
为了验证Gtk#的行为是否不同,下面是一些示例代码:
using System;
using Gtk;
namespace ButtonClickedTest
{
class Program
{
private class MyButton : Button
{
public MyButton()
{
}
public MyButton(IntPtr raw) : base(raw)
{
}
protected override void OnClicked()
{
Console.WriteLine("before");
base.OnClicked();
Console.WriteLine("after");
}
}
[STAThread]
public static void Main(string[] args)
{
Application.Init();
using (Window win = new Window("Test")) {
win.WindowPosition = WindowPosition.Center;
win.SetSizeRequest(300, 200);
win.Hidden += delegate(object sender, EventArgs e) {
Application.Quit();
};
MyButton btn = new MyButton();
btn.Add(new Label("Button"));
btn.Clicked += delegate {
Console.WriteLine("event");
};
win.Add(btn);
win.ShowAll();
Application.Run();
}
}
}
}
不幸的是,输出是
before
after
event
。甚至在OnClicked
末尾的代码已经在Clicked
事件处理程序之前运行。
我的问题是:在gtk#中,在事件处理程序之后仅执行事件上的一些代码的正确方法是什么?
到目前为止,我发现了两个不令人满意的变通方法:
第一个是定义Clicked
事件的替代品,例如Clicking
事件(使用一个相应的OnClicking
方法来触发Clicking
事件处理程序)。我可以在重写的OnClicked
版本中调用OnClicking
,从而在事件处理程序之前和之后运行代码。此外,我的按钮类的任何子类都可以以预期的方式使用OnClicking
,即在Clicking
事件处理程序应该运行时调用base.OnClicking
。
这不是很干净,但是,因为没有什么可以阻止我的按钮类的用户将他们的事件处理程序注册为原始的Clicked
事件,而不是使用正确嵌入到按钮逻辑中的新Clicking
事件。特别是,我甚至不能修改继承的Clicked
事件的API文档来表示不应该使用Clicked
,用户应该转向Clicking
。
另一种解决方法仅在这种特定情况下有效,因为碰巧有一个OnReleased
方法总是在Clicked
事件处理程序之后执行。我可以使用该方法插入一些代码,保证仅在任何Clicked
事件处理程序之后运行,但显然,此解决方案不能转移到不方便跟随其他事件的事件。
在Gtk+中,因此在Gtk#中就是这样。Gtk.Button.OnClicked
被安装为信号的类处理程序。由于该信号属于"先运行"类型,因此该处理程序在连接到它的任何其他回调之前运行。
使用Gtk+从C你有g_signal_connect_after()
注册一个回调调用后,类处理程序和其他回调被调用,但这似乎不可用于Gtk#。
在代码传递后运行代码的最简单方法是使用一次性的空闲回调。是的,它有点粗俗,但它适用于任何情况,甚至与Gtk信号无关:
protected override void OnClicked()
{
Console.WriteLine("before");
base.OnClicked();
GLib.Idle.Add(delegate {
Console.WriteLine("after");
return false;
});
}