3

次のように、Windowsフォームのボタンクリックに接続された2つのイベントハンドラーがあります。

this.BtnCreate.Click += new System.EventHandler(new RdlcCreator().FirstHandler);
this.BtnCreate.Click += new System.EventHandler(this.BtnCreate_Click);

どちらも正しく呼び出されています。

しかし、処刑FirstHandler()を防ぐことは可能ですか?BtnCreate_Click()何かのようなもの:

void FirstHandler(object sender, EventArgs e)
{
   if (ConditionSatisfied)
     //Prevent next handler in sequence being executed

}

イベントのサブスクライブを解除できることはわかっていますが、これをプログラムで (メソッド内から) 実行できますか?

4

6 に答える 6

6

私の知る限り、これに対する解決策はありません。これは、イベントが発生したときにイベント ハンドラーが呼び出される順序が保証されていないためです。

そのため、いかなる方法でもそれらの順序に依存することは想定されていません。

于 2012-06-14T10:57:42.553 に答える
2

それらを 1 つのイベント ハンドラに置き換えてみませんか? このようなもの:

var rdlc = new RdlcCreator();
this.BtnCreate.Click += (sender, e) => {
    rdlc.FirstHandler(sender, e);
    if (!rdlc.HasHandledStuff) { // <-- You would need some kind of flag 
        this.BtnCreate_Click(sender, e);
    }
};

そうすれば、ハンドラーの順序を保証することもできます。または、上記の実装を使用しますが、条件を示す bool を返すように FirstHandler の署名を変更します (この場合、イベントの署名はもう必要ありません)。

    if (!rdlc.FirstHandler(sender, e)) { 
        this.BtnCreate_Click(sender, e);
    }

編集: または、2 番目のハンドラーを FirstHandler に渡すだけです。
FirstHandler の署名を次のように変更します。

void FirstHandler(object sender, EventArgs e, EventHandler nextHandler) {
    if (ConditionSatisfied) {
        // do stuff
    }
    else if (nextHandler != null) {
        nextHandler(sender, e);
    }
}

その後:

this.BtnCreate.Click += 
    (s, e) => new RdlcCreator().Firsthandler(s, e, this.BtnCreate_Click);
于 2012-06-14T11:02:09.700 に答える
2

System.ComponentModel名前空間には、CancelEventHandlerこの目的で使用されるデリゲートが含まれています。それが提供する引数の 1 つは、呼び出しリストの実行を停止する必要があることを通知するためにハンドラーのいずれかに設定できるCancelEventArgsboolean プロパティを含むインスタンスです。Cancel

ただし、プレーンなEventHandlerデリゲートにアタッチするには、次のような独自のラッパーを作成する必要があります。

public static class CancellableEventChain
{
    public static EventHandler CreateFrom(params CancelEventHandler[] chain)
    {
        return (sender, dummy) =>
        {
            var args = new CancelEventArgs(false);
            foreach (var handler in chain)
            {
                handler(sender, args);
                if (args.Cancel)
                    break;
            }
        };
    }
}

あなたの例では、次のように使用します。

this.BtnCreate.Click += CancellableEventChain.CreateFrom(
    new RdlcCreator().FirstHandler,
    this.BtnCreate_Click
    /* ... */
);

もちろん、後でサブスクライブを解除 (デタッチ) する必要がある場合は、作成されたチェーン ハンドラーをフィールドにキャプチャする必要があります。

于 2012-06-14T11:34:36.607 に答える
0

2番目のイベントであるthis.BtnCreate_Clickに次の条件を追加します

BtnCreate_Click(object sender, EventArgs e)  
{     
   if (!ConditionSatisfied)       //Prevent next handler in sequence being executed 
   {
     // your implementation goes here
   }   
}  
于 2012-06-14T10:59:59.527 に答える
0

ある種のクラスラッパーを作成することをお勧めします。したがって、ある種のイベント フラグ グループ(たとえば、16 ビット整数) と、個々のビットを設定または設定解除するいくつかのメソッド (それぞれが呼び出すかどうかを意味します)を格納できますEventHandlerEventhandlersまたはの任意の数をクラスに簡単に保存Actionsし、任意の順序で呼び出すことができます。

于 2012-06-14T11:03:54.690 に答える
0

同じ質問に対する解決策を見つけていましたが、うまくいきませんでした。だから自分で解決しなければなりませんでした。Cancelable イベント引数の基本クラス

public class CancelableEventArgs
{
    public bool Cancelled { get; set; }
    public void CancelFutherProcessing()
    {
        Cancelled = true;
    }
}

次に、EventHandler の拡張メソッドを定義します。呼び出しリストのサブスクライバーは逆の順序で呼び出されることに注意してください (私の場合、UI 要素は、コンポーネントに追加されたときにイベントをサブスクライブするため、後でレンダリングされる要素が最も可視性が高く、優先度が高くなります)。

public static class CommonExtensions
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void SafeInvokeWithCancel<T>(this EventHandler<T> handler, object sender, T args) where T : CancelableEventArgs
    {
        if (handler != null)
        {
            foreach (var d in handler.GetInvocationList().Reverse())
            {
                d.DynamicInvoke(sender, args);
                if (args.Cancelled)
                {
                    break;
                }
            }
        }
    }

で、使用感はこちら

public class ChessboardEventArgs : CancelableEventArgs
{
    public Vector2 Position { get; set; }
}

そのため、UI 要素がイベントに対して何らかの動作をする場合、それ以降の処理はキャンセルされます。

        game.OnMouseLeftButtonDown += (sender, a) =>
        {
            var xy = GetChessboardPositionByScreenPosition(a.XY);
            if (IsInside(xy))
            {
                var args = new ChessboardEventArgs { Position = xy };
                OnMouseDown.SafeInvokeWithCancel(this, args);
                a.CancelFutherProcessing();
            }
        };
于 2015-06-13T00:28:23.260 に答える