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

时间:2013-01-20 12:50:15

标签: events event-handling gtk#

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

基于.NET约定,这不是问题。以下示例Windows窗体代码显示了System.Windows.Forms.Button的子类,它在事件处理程序之前执行一些代码,然后调用事件处理程序(通过调用继承的OnClick method;同样的使用System.Windows.Controls.Button.OnClick)在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 method of Gtk.Button似乎不会触发Clicked event。实际上,文档将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事件处理程序)。我可以在我重写的OnClicking版本中调用OnClicked,从而在我喜欢的事件处理程序之前和之后运行代码。此外,我的按钮类的任何子类都可以按预期方式使用OnClicking,即在base.OnClicking事件处理程序运行时调用Clicking

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

其他解决方法仅适用于此特定情况,因为恰好在Clicked事件处理程序之后始终执行OnReleased method。我可以使用该方法插入一些保证仅在任何Clicked事件处理程序之后运行的代码,但很明显,此解决方案无法转移到其他事件不方便的事件。

1 个答案:

答案 0 :(得分:1)

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

使用来自C的Gtk +你有g_signal_connect_after()类处理程序和其他回调被调用之后注册一个被称为的回调,但这似乎不适用于Gtk #。

在经过一些事情之后运行代码的最简单方法是使用和一次空闲回调。是的,它有点hackish,但它适用于任何情况,甚至与Gtk信号无关:

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