2

I am deriving a class from Gtk.Button that is supposed to run some code when the button is clicked, but only after the user-defined event handlers.

Based on .NET conventions, this wouldn't be a problem. The following exemplary Windows Forms code shows a subclass of System.Windows.Forms.Button that executes some code before the event handlers, then invokes the event handlers (by calling the inherited OnClick method; the same would work in WPF with System.Windows.Controls.Button.OnClick), and then executes some code after the event handlers:

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);
            }
        }
    }
}

As expected, the output is:

before
event
after

However, I have so far failed to replicate this behaviour in Gtk#. Contrary to .NET conventions, the inherited OnClicked method of Gtk.Button does not seem to fire the Clicked event. In fact, the docs call the OnClicked method a "default handler".

To verify that Gtk# behaves differently, here's some sample code:

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();
            }
        }
    }
}

Unfortunately, the output is

before
after
event

i.e. even code at the end of OnClicked runs already before the Clicked event handlers.


My question is: What is the proper way in Gtk# to execute some code upon an event only after the event handlers?


I have found two dissatisfying workarounds so far:

The first is to define a replacement for the Clicked event, say, a Clicking event (with an according OnClicking method that fires the Clicking event handlers). I could invoke OnClicking in my overridden version of OnClicked and thus run code before and after the event handlers as I like. Additionally, any subclasses of my button class could use OnClicking in the expected way, i.e. calling base.OnClicking when the Clicking event handlers should run.

This is not very clean, though, because nothing would prevent users of my button class from registering their event handlers with the original Clicked event rather with the new Clicking event that is properly embedded into the button's logic. In particular, I could not even modify the API documentation for the inherited Clicked event to express there that Clicked should not be used and users should turn to Clicking instead.

The other workaround works only in this specific case, as there happens to be an OnReleased method that is always executed after the Clicked event handlers. I could use that method to insert some code that is guaranteed to run only after any Clicked event handlers, but obviously, this solution couldn't be transferred to events that are not conveniently followed by some other events.

4

1 回答 1

1

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

使用 C 中的 Gtk+,您可以在类处理程序和其他回调被调用之后g_signal_connect_after()注册要调用的回调,但这似乎不适用于 Gtk#。

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

protected override void OnClicked()
{
    Console.WriteLine("before");
    base.OnClicked();
    GLib.Idle.Add(delegate {
                Console.WriteLine("after");
                return false;
            });
}
于 2013-01-30T10:03:49.360 回答