2011-01-06 20 views
2

コマンドビヘイビアを使用してカスタムコントロールを作成していて、何か奇妙なことが起こりました。私が見つけたいくつかの記事は、CanExecuteChangedHandler EventHandlerを静的と宣言し、他は静的ではないと宣言しました。 MicrosoftのSDKドキュメントは静的を示していますが、静的として宣言すると、複数のコントロールを使用すると奇妙な動作が発生します。WPFコントロールで静的または非静的としてEventHandlerを宣言する

private static EventHandler canExecuteChangedHandler; 

private void AddSecureCommand(ISecureCommand secureCommand) 
{ 
    canExecuteChangedHandler = new EventHandler(CanExecuteChanged); 
    securityTypeChangedHandler = new EventHandler(SecurityTypeChanged); 

    if (secureCommand != null) 
    { 
     secureCommand.CanExecuteChanged += canExecuteChangedHandler; 
     secureCommand.SecurityTypeChanged += securityTypeChangedHandler; 
    } 
} 

誰でも適切な方法を知っていますか?静的なEventHandlerが動作しない原因となっている何か間違ったことをしていますか?

答えて

1

EventHandlerのローカルコピーを保存する理由は、WPFコマンドサブシステムが内部的に弱い参照を使用するため、CanExecuteChangedイベントに追加される特定の委任オブジェクトへの参照を保持する必要があるということです。事実、いつでもサブシステムイベントを追加するときは、SecurityTypeChangedのようにこの習慣を守るべきです。

あなたの質問への短い答えはcanExecuteChangedHandler静的することができますが、一度それを初期化するだけに注意しなければならないということです。静的にすることができる理由は、CanExecuteChangedが静的​​であれば、すべてnew EventHandler(CanExecuteChanged)が同じことをするということです。一度初期化するのは、異なるインスタンスが異なるからです。

読み取り専用セマンティクス権利を持っている私有財産は、次のとおりです。

static EventHandler canExecuteChangedHandler 
{ 
    get 
    { 
     if (internalCanExecuteChangedHandler == null) 
      internalCanExecuteChangedHandler = new EventHandler(CanExecuteChanged); 
     return internalCanExecuteChangedHandler; 
    } 

} 
static EventHandler internalCanExecuteChangedHandler; 

しかしCanExecuteChangedが静的​​である場合にのみ機能します。そうでない場合は、static修飾子を削除します。どちらの場合でも、実際にプロパティを使用するよう注意する必要があります。

この特定の例では、AddSecureCommandが最初にcanExecuteChangedHandlerと呼び出された2回目は、ガベージコレクションのリスクがあります。

最後に、このすべてが黒マジックのように聞こえる場合は、何が起こっているかを示すコード例があります。

public class Container 
{ 
    private WeakReference reference; 
    public object Object 
    { 
     get { return reference.IsAlive ? reference.Target : null; } 
     set { reference = new WeakReference(value); } 
    } 
} 

public class DelegateTest 
{ 
    private EventHandler eventHandler; 
    private Container container1; 
    private Container container2; 

    void MyEventHandler(object sender, EventArgs args) 
    { 
    } 

    public DelegateTest() 
    { 
     this.eventHandler = new EventHandler(MyEventHandler); 
     this.container1 = new Container { Object = this.eventHandler }; 
     this.container2 = new Container { Object = new EventHandler(MyEventHandler) }; 
     GC.Collect(); 
     Console.WriteLine("container1: {0}", this.container1.Object == null); 
     Console.WriteLine("container2: {0}", this.container2.Object == null); 
    } 
} 

これは、この出力を生成する:

container1: False 
container2: True 

ガベージコレクションの間に第2の容器がそのEventHandler「アウトその下から」ガベージコレクトしたことことを示しています。これは、弱い参照が機能する方法と、あなたの説明が自分自身への参照を保持する必要があるように設計されています。

+0

私はガベージコレクションの理由を理解しており、私はクラスレベルで宣言されたEventHandlerを持たなければならないことを知っています。私が抱えている問題は、クラスが複数回インスタンス化されたときに、静的なときに奇妙な動作が発生することです。 – Brady

+0

改行を追加してコメントを投稿しました。上記のコメントを編集しようとしましたが、明らかに私はそれを更新するのに5分しかかかりません。上で詳述する...私が抱えている問題は、クラス(またはコントロール)を複数回インスタンス化するときに、静的なときに奇妙な動作が発生することです。私はEventHandlerデリゲートのターゲットがインスタンス固有のものであることをもう少し詳しく調べました。私の場合は静的なEventHandlerを使用して問題を引き起こしています。私は静的なEventHandlerを使うことができる時があるかもしれないが、私の場合はうまくいかないと思う。 – Brady

+0

あなたの要点を要約すると、イベントハンドラの参照は、メソッドが静的な場合にのみ静的になることがあります。コマンドイベントに追加した後も変更しないように注意する必要があります。私は答えを更新します。 –

関連する問題