2

ボタンがクリックされたときにコードを実行することになっているクラスを派生していますが、ユーザー定義のイベントハンドラーのGtk.Buttonでのみです。

.NETの規則に基づくと、これは問題にはなりません。次のWindowsフォームコードの例は、イベントハンドラーの前System.Windows.Forms.Buttonにいくつかのコードを実行し、イベントハンドラーを呼び出し(継承されたメソッドを呼び出すことによって;同じことがWPFでも機能します)、イベントハンドラーのにいくつかのコードを実行するサブクラスを示しています。OnClickSystem.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の規則に反して、の継承されたOnClickedメソッドはGtk.ButtonClickedイベントを発生させないようです。実際、ドキュメントでは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#で、イベントハンドラーの後でのみイベントに対してコードを実行する適切な方法は何ですか?


これまでに、2つの不満足な回避策を見つけました。

Clicked1つ目は、イベント、たとえばイベントの置換を定義することです(イベントハンドラーを起動Clickingするそれに応じたOnClickingメソッドを使用)。オーバーライドされたバージョンのをClicking呼び出して、イベントハンドラーの前後で好きなようにコードを実行できます。さらに、ボタンクラスのサブクラスは、期待どおりに使用できます。つまり、イベントハンドラーを実行するタイミングを呼び出すことができます。OnClickingOnClickedOnClickingbase.OnClickingClicking

ただし、これはあまりクリーンではありません。ボタンクラスのユーザーが、ボタンのロジックに適切に埋め込まれClickedた新しいイベントではなく、元のイベントにイベントハンドラーを登録することを妨げるものは何もないからです。Clicking特に、継承されたイベントのAPIドキュメントを変更して、使用すべきではなく、ユーザーが代わりに使用する必要Clickedがあることを表現することさえできませんでした。ClickedClicking

他の回避策は、この特定の場合にのみ機能します。これは、イベントハンドラーの後に常に実行されるOnReleasedメソッドが存在するためです。Clickedそのメソッドを使用して、イベントハンドラーの後でのみ実行されることが保証されているコードを挿入できますClickedが、明らかに、このソリューションは、他のイベントが適切に続かないイベントに転送できませんでした。

4

1 に答える 1

1

Gtk +では、したがってGtk#ではそれがその通りです。Gtk.Button.OnClickedシグナルのクラスハンドラーとしてインストールされます。また、このシグナルは「最初に実行」の種類であるため、ハンドラーは、それに接続されている他のコールバックの前に実行されます。

CのGtk+を使用すると、クラスハンドラーと他のコールバックが呼び出された後g_signal_connect_after()に呼び出されるコールバックを登録することができますが、これはGtk#では利用できないようです。

何かが通過した後にコードを実行する最も簡単な方法は、1回限りのアイドルコールバックを使用することです。はい、それは少しハックですが、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 に答える