15

WPF でデータ バインディングを使用すると、ターゲット コントロールがバインディング ソースのイベントをリッスンします。たとえば、でイベントをListViewリッスンしている場合があります。CollectionChangedObservableCollection

イベント ソースの有効期間がイベント リスナーの有効期間を超えることが予想される場合は、メモリ リークが発生する可能性があるため、弱いイベント パターンを使用する必要があります。

WPF データバインディングは弱いイベントパターンに従いますか? 私の寿命が私のObservableCollection寿命よりも長い場合、ガベージコレクションの対象になりますか?ListViewListView


これが、WPF コントロールが弱いイベント パターンを実装していないと私が疑う理由です。DerivedListView Collected!もしそうなら、私はとの両方DerivedTextBlock Collected!がコンソールに出力されることを期待します。代わりに、あるだけDerivedTextBlock Collected!です。

コードのバグを修正した後、両方のオブジェクトが収集されます。どう考えたらいいのかわからない。

Window1.xaml.cs

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace LeakDetector
{
    public class DerivedListView : ListView
    {
        ~DerivedListView()
        {
            Console.WriteLine("DerivedListView Collected!");
        }
    }

    public class DerivedTextBlock : TextBlock
    {
        ~DerivedTextBlock()
        {
            Console.WriteLine("DerivedTextBlock Collected!");
        }
    }

    public partial class Window1 : Window
    {
        // The ListView will bind to this collection and listen for its
        // events. ObColl will hold a reference to the ListView.
        public ObservableCollection<int> ObColl { get; private set; }

        public Window1()
        {
            this.ObColl = new ObservableCollection<int>();
            InitializeComponent();

            // Trigger an event that DerivedListView should be listening for
            this.ObColl.Add(1);

            // Get rid of the DerivedListView
            this.ParentBorder.Child = new DerivedTextBlock();

            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

            this.ParentBorder.Child = null;

            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

            Console.WriteLine("Done");
        }
    }
}

Window1.xaml

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:LeakDetector"
    x:Class="LeakDetector.Window1"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Height="300" Width="300"
    Title="Leak Detector">
    <Border x:Name="ParentBorder">
        <local:DerivedListView ItemsSource="{Binding Path=ObColl}" />
    </Border>
</Window>
4

2 に答える 2

11

本質的に、WPFコントロール自体は弱いイベントとは何の関係もありません。代わりに、弱いイベントパターンを実装するWPFのバインディングエンジンに関連する特定のクラスがあります。クラスPropertyChangedEventManagerは、WeakEventManagerを実装します。また、Reflectorを使用すると、いくつかのクラスがMS.Internal.Data名前空間にIWeakEventListenerを実装していることがわかります(特に、PropertyChangedEventManagerを直接使用するMS.Internal.Data.PropertyPathWorkerクラスです)。これらのオブジェクトは、データバインディングを実行するためにWPFによって内部的に使用されます。

ItemsControlsイベントとCollectionChangedイベントは別の話であり、Bindingsとは何の関係もありません。ほら、コードビハインドで「listView.ItemsSource = myObservableCollection」のようなことをすることができ、コレクションが変更された通知は引き続き機能します。ここでは、バインディングオブジェクトはまったく含まれていません。ここでは、別の「ウィークイベント関連クラス」のセットが使用されています。 ItemCollectionItemContainerGeneratorはIWeakEventListenerを実装し、 CollectionChangedEventManager(WeakEventManagerを実装)と連携して機能します。

于 2010-09-14T22:12:59.273 に答える
2

リンク先の MSDN 記事の 2 番目の文は、WPF が Weak Event Pattern を使用していることを明確に示しています。実際、WPFがこのパターンを導入したとさえ言えます。

編集:

「WPF コントロールは弱いイベント パターンを実装している」と明示的に述べているドキュメントを見つけたいと思っていました。–エムダドリー

いくつかの調査を行った後、その質問に対する答えは「いいえ」だと思います。答えが「いいえ」である理由は、WPF が UI コントロールが一時的であることを想定していないためだと思います。CollectionChangedEventManagerイベントに対する弱いイベント用に特別に構築されたクラスがありますが、コレクションに対して弱いイベントを使用するために必要なCollectionChangedデータバインディングをサポートするコントロールは実装されていないようです。IWeakEventListener

パターンと使用法は、ビューよりも一時的である可能性が高いビューではなく、ViewModel 用に構築されていると思います。

編集2:

コードのバグを修正した後、両方のオブジェクトが収集されます。したがって、WPF コントロールは弱いイベント パターンを使用していると思います。

興味深い結果です。弱いイベントを実装する場合は、内部で行う必要があります。

于 2010-09-14T19:47:16.463 に答える