web-dev-qa-db-ja.com

WPFユーザーコントロールの適切なクリーンアップ

私はWPFに比較的慣れていないので、WPFに関するいくつかのことは私にとってまったく異質です。 1つは、Windowsフォームとは異なり、WPFコントロール階層はIDisposableをサポートしていません。 Windowsフォームでは、ユーザーコントロールが管理対象リソースを使用している場合、すべてのコントロールが実装したDisposeメソッドをオーバーライドすることで、リソースを非常に簡単にクリーンアップできました。

WPFでは、話はそれほど単純ではありません。私はこれを数時間検索し、2つの基本的なテーマに遭遇しました。

最初のテーマは、WPFコントロールに管理されていないリソースがないため、WPFはIDisposableを実装しないことを明確に述べているMicrosoftです。それは本当かもしれませんが、WPFクラス階層へのユーザー拡張が実際に管理対象リソースを(モデルを介して直接的または間接的に)使用する可能性があるという事実を完全に見逃しているようです。 IDisposableを実装しないことにより、Microsoftは、カスタムWPFコントロールまたはウィンドウで使用される管理されていないリソースをクリーンアップできる唯一の保証されたメカニズムを効果的に削除しました。

次に、Dispatcher.ShutdownStartedへの参照がいくつか見つかりました。 ShutdownStartedイベントを使用しようとしましたが、すべてのコントロールで発生するわけではないようです。 ShutdownStartedのハンドラーを実装したWPFUserControlがたくさんありますが、呼び出されることはありません。 Windowsでのみ機能するのか、WPFAppクラスでのみ機能するのかわかりません。ただし、正しく起動されておらず、アプリが閉じるたびに開いているPerformanceCounterオブジェクトがリークしています。

Dispatcher.ShutdownStartedイベントよりも、管理されていないリソースをクリーンアップするより良い代替手段はありますか? Disposeが呼び出されるようにIDisposableを実装するためのトリックはありますか?可能であれば、ファイナライザーを使用することをお勧めします回避

21
jrista

Dispatcher.ShutdownStartedは、WPFがUserControlsのリソースを破棄するために提供する唯一のメカニズムであるように思われます。 (非常に 同様の質問 少し前に尋ねました)を参照してください。

この問題に取り組むもう1つの方法は、すべての使い捨てリソースを(可能であれば)コードの背後から別のクラス(MVVMパターンを使用する場合のViewModelなど)に移動することです。次に、より高いレベルで、メインウィンドウの終了を処理し、Messengerクラスを介してすべてのViewModelに通知できます。

Dispatcher.ShutdownStartedイベントが発生しないことに驚いています。その時点で、UserControlsはトップレベルウィンドウに接続されていますか?

12
Mark Heath

メカニズムがWinformsとは異なるため、IDisposableインターフェイスはWPFでは(ほとんど)意味がありません。 WPFでは、視覚的および論理的なツリーを念頭に置く必要があります。これが基本です。
したがって、視覚オブジェクトは通常、他のオブジェクトの子として存在します。 WPF構築メカニズムの基本は、ビジュアルオブジェクトを階層的にアタッチし、役に立たない場合はデタッチして破棄することです。

OnVisualParentChanged以降に公開されたUIElementメソッドを確認できると思います。このメソッドは、ビジュアルオブジェクトがアタッチされたときとデタッチされたときに呼び出されます。これは、管理されていないオブジェクト(ソケット、ファイルなど)を破棄するのに適切な場所である可能性があります。

10
venezia

私もこれを探していて、さまざまなオプションをテストした後、ベネチアのソリューションを実装しました

_protected override void OnVisualParentChanged(DependencyObject oldParent)
    {
        if (oldParent != null)
        {
            MyOwnDisposeMethod(); //Release all resources here
        }

        base.OnVisualParentChanged(oldParent);
    }
_

親がChildren.Clear()メソッドを呼び出し、すでに子にアイテムが追加されている場合、DependencyObjectに値があることに気付きました。しかし、親がアイテム(Children.Add(CustomControl))を追加し、子が空の場合、DependencyObjectはnullでした。

6
Jaime Marín

他の人がこの問題について本当に役立つ情報を提供してくれましたが、IDisposableがない理由について多くのことを説明する、あなたが持っていないかもしれない情報が少しあります。基本的に、WPF(およびSilverlight)はWeakReferencesを多用します。これにより、GCが引き続き収集できるオブジェクトを参照できます。

0
Pete OHanlon