10

LINQtoSQLを使用してSQLServerデータベースに接続するWPFアプリを構築しています。

アプリのメインウィンドウにはListView、一連の詳細ビューが含まれています。のItemSourceListView、ルートビューモデルのプロパティとして公開されている詳細ビューモデルオブジェクトのコレクションにバインドされています。各詳細ビューモデルオブジェクトは、いくつかICommandのプロパティと、詳細モデルオブジェクトを公開するプロパティを構成します。これにより、UIに表示されるさまざまなデータフィールドが公開されます。

ANTSメモリプロファイラーを使用した分析では、リークされているオブジェクトは、詳細モデルオブジェクトに含まれているオブジェクトと、それらがバインドされている一部のUIクラスであることが示されています。以前の更新からのこれらのオブジェクトのインスタンスは、ガベージコレクションされていません。

ANTSには、ユーザーが参照チェーンをトレースして、不要なメモリが保持されている理由を特定できるツールがあります。私がそれを使うとき、私は現れるすべての鎖がICommandそれらの中にあるのを見つけます。したがって、問題のあるものを削除したICommandところ、メモリリークが解消されていることがわかりました。

残念ながら、ICommandいくつかの重要な機能を実装する必要があります。私を本当に混乱させているのは、そもそも詳細モデルオブジェクトへの参照があることです。これらは、詳細ビューモデルオブジェクト内の2つの完全に別個のインスタンス変数です。

詳細ビューモデルオブジェクトのコンストラクターは次のとおりです(RootViewModelへの参照は、ICommandsに接続されている一部のメソッドのコールバックに使用されます。これにより、参照の循環チェーンが発生している可能性があります。問題がありますが、それを削除しても効果はないようです。)

public CarDataViewModel(CarData carDataItem, RootViewModel parentViewModel)
    {

        _parentViewModel = parentViewModel;
        CarDataModel = carDataItem;
        CompetingCheckboxStatus = CarDataModel.CurrentCar.Competing;
        AcknowledgeAlarm = new ParameterlessCommand(AcknowledgeAlarmClicked);
        Acknowledge = new ParameterlessCommand(AcknowledgeClicked);
        ShowReport = new ParameterlessCommand(ShowReportClicked);
        Cancel = new ParameterlessCommand(CancelClicked);
    }

バインディングが設定されているxamlは次のとおりです。AcknowledgeAlarmはICommand、CarDataModelは詳細モデルオブジェクトです。

<ListView x:Name="itemGridView"Grid.Row="1"ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding CarDataViewModels}" IsSynchronizedWithCurrentItem="True" Margin="0,0,0,0">
        <ListView.ItemTemplate>
            <DataTemplate>
                </DataTemplate.Resources>
                <Button Command="{Binding AcknowledgeAlarm}">
                    <Border DataContext="{Binding CarDataModel}" BorderBrush="{StaticResource GrayFadeBrush}" Background="White" BorderThickness="5">
                        <Grid> . . .
4

1 に答える 1

10

CanExecuteChangedイベントハンドラーは、リークに関係している可能性があります。

WPFは、ICommand実装がイベントハンドラーへの弱参照を使用することを想定しています。強力な参照を使用する通常の.NETイベントを使用しているため、このリークが発生する可能性があります。

ParameterlessCommandインスタンスを作成する方法は、それCanExecuteが常に当てはまるように思われ、イベントはまったく必要ありません。実際にどこかでイベントを発生させていますか、それともOnCanExecuteChanged未使用のコードですか?

そうでない場合は、イベント定義を次のように置き換えます。

public event EventHandler CanExecuteChanged { add {} remove {} }

このように、イベントはハンドラーを格納せず、ビューモデルはUI要素への強い参照を持つことを回避します。

イベントを発生させる必要がある場合、最も簡単な解決策は、を使用することCommandManager.RequerySuggestedです。これは、ICommandに期待される弱いイベントセマンティクスと一致します。

public event EventHandler CanExecuteChanged {
    add {
        CommandManager.RequerySuggested += value;
    }
    remove {
        CommandManager.RequerySuggested -= value;
    }
}

もう1つすべきことはINotifyPropertyChanged、ビューモデルに実装し(まだ実装していない場合)、NameChangedプロパティごとに個別のイベントなどを設定する代わりに、それを使用することです。これは、個々のプロパティを処理するWPFのロジックが、ビューモデルからUI要素への参照がある場合にメモリリークを引き起こすためです:http ://support.microsoft.com/kb/938416

INotifyPropertyChanged実際に変更イベントがない場合でも、実装する必要があります。


私の推測では、これら2つの問題のいずれかを修正すると、リークが消えます。誤って実装するCanExecuteChangedと、ビューモデルからビューへの強力な参照が発生します。これは、不足がINotifyPropertyChangedリークを引き起こす状況です。

ただし、両方の問題を修正することをお勧めします。それらの1つだけではありません。

于 2012-10-18T00:10:40.887 に答える