Events 什么';在事件处理程序之后运行代码的正确方法是什么?

Events 什么';在事件处理程序之后运行代码的正确方法是什么?,events,event-handling,gtk#,Events,Event Handling,Gtk#,我从Gtk.Button派生了一个类,该类应该在单击按钮时运行一些代码,但只能在用户定义的事件处理程序之后运行 基于.NET约定,这不会是一个问题。下面的示例性Windows窗体代码显示了System.Windows.Forms.Button的子类,该子类在事件处理程序之前执行一些代码,然后调用事件处理程序(通过调用继承的;在WPF中也适用),然后在事件处理程序之后执行一些代码: using System; using System.Windows.Forms; using System.Dra

我从
Gtk.Button
派生了一个类,该类应该在单击按钮时运行一些代码,但只能在用户定义的事件处理程序之后运行

基于.NET约定,这不会是一个问题。下面的示例性Windows窗体代码显示了
System.Windows.Forms.Button
的子类,该子类在事件处理程序之前执行一些代码,然后调用事件处理程序(通过调用继承的;在WPF中也适用),然后在事件处理程序之后执行一些代码:

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约定相反,继承的似乎不会触发。事实上,文档将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
i、 e.甚至在单击
事件处理程序之前,
OnClicked
末尾的代码也已经运行


我的问题是:在Gtk中,只有在事件处理程序之后才对事件执行某些代码的正确方法是什么?


到目前为止,我发现了两个令人不满意的解决办法:

第一种方法是定义
Clicked
事件的替换,例如
Clicking
事件(使用相应的
OnClicking
方法触发
Clicking
事件处理程序)。我可以在被重写的
OnClicked
版本中调用
onclick
,从而在事件处理程序之前和之后运行代码。此外,button类的任何子类都可以按预期的方式使用
OnClicking
,即在
单击事件处理程序应该运行时调用
base.OnClicking

但这并不是很干净,因为没有任何东西可以阻止button类的用户将其事件处理程序注册到原始的
Clicked
事件中,而不是注册到按钮逻辑中正确嵌入的新
Clicking
事件中。特别是,我甚至无法修改继承的
Clicked
事件的API文档,以表示不应该使用
Clicked
,用户应该转而使用
Clicking

另一种解决方法仅在这种特定情况下有效,因为在单击
事件处理程序之后,总是会执行一个。我可以使用该方法插入一些代码,这些代码保证只在单击任何
事件处理程序后运行,但显然,此解决方案无法转移到不方便跟随其他事件的事件中。

在Gtk+中,因此在Gtk中是这样的
Gtk.Button.OnClicked
作为信号的类处理程序安装。由于该信号属于“Run First”类型,因此处理程序在连接到它的任何其他回调之前运行

使用C中的Gtk+,您就有了
g_signal\u connect\u after()
,它注册了一个回调,以便在调用类处理程序和其他回调之后调用,但这似乎对Gtk不可用

传递后运行代码的最简单方法是使用一次空闲回调。是的,这有点黑客,但它适用于任何情况,即使与Gtk信号无关:

protected override void OnClicked()
{
    Console.WriteLine("before");
    base.OnClicked();
    GLib.Idle.Add(delegate {
                Console.WriteLine("after");
                return false;
            });
}

因为似乎没有人知道一种可以称之为正确的方法,所以这似乎是最好的解决方案。现在,我没有看到任何缺点或缺陷,所以我会接受这一点。