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



我从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.ButtonOnClicked方法似乎不会触发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;
            });
}

相关内容

  • 没有找到相关文章

最新更新