MVVM Lightのメッセンジャーとその柔軟性が大好きですが、受信者の登録を明示的に解除するのを忘れると(Silverlight 4で)メモリリークが発生します。
原因は説明されています ここ ですが、メッセンジャーの弱参照の使用に依存するのではなく、とにかく受信者の登録を明示的に解除することをお勧めしますので、問題ありません。問題は、口で言うほど簡単ではないということです。
ViewModelsは簡単です。通常、ライフサイクルを完全に制御でき、不要になったときにCleanup()
することができます。
一方、ビューは、DataTemplatesを介してインスタンス化および破棄されるため、注意が必要です。例: ItemsControl
とMyView
をDataTemplateとして、ObservableCollection<MyViewModel>
にバインドすると考えることができます。 MyView
コントロールはバインディングエンジンによって作成/収集され、手動でCleanup()を呼び出す良い方法はありません。
私は解決策を考えていますが、それがまともなパターンなのか、それとももっと良い選択肢があるのか知りたいのです。アイデアは、ViewModelから特定のメッセージを送信して、関連するビューに破棄するように指示することです。
public class MyViewModel : ViewModelBase
{
...
public override void Cleanup()
{
// unregisters its own messages, so that we risk no leak
Messenger.Default.Unregister<...>(this);
// sends a message telling that this ViewModel is being cleaned
Messenger.Default.Send(new ViewModelDisposingMessage(this));
base.Cleanup();
}
}
public class MyView : UserControl, ICleanup
{
public MyView()
{
// registers to messages it actually needs
Messenger.Default.Register<...>(this, DoSomething);
// registers to the ViewModelDisposing message
Messenger.Default.Register<ViewModelDisposingMessage>(this, m =>
{
if (m.SenderViewModel == this.DataContext)
this.Cleanup();
});
}
public void Cleanup()
{
Messenger.Default.Unregister<...>(this);
Messenger.Default.Unregister<ViewModelDisposingMessage>(this);
}
}
したがって、viewModelでCleanup()を呼び出すと、それをDataContextとして使用するすべてのビューがローカルのCleanup()も実行します。
どう思いますか?明らかな何かが欠けていますか?
ViewModelLocatorクラスは、ビューモデルの集中ストアを維持するのに役立ちます。このクラスを使用して、新しいバージョンの管理と古いバージョンのクリーンアップに役立てることができます。私は常にロケーターを介してビューからビューモデルを参照するので、これらを管理するために実行できるコードが常にあります。あなたはそれを試すことができます。
また、Cleanupメソッドを使用してMessenger.Unregister(this)
を呼び出します。これにより、そのオブジェクトのメッセンジャーからのすべての参照がクリーンアップされます。毎回.Cleanup()を呼び出す必要がありますが、これが人生です:)
私はMVVMLightを使用していません(素晴らしいことを聞いたことがありますが)が、WeakReferencesを使用するMessengerの実装が必要な場合は、ここに含まれているMessengerをチェックアウトしてください http://mvvmfoundation.codeplex.com/ 。
MVVM Light MessengerはWeakAction(WeakReference)を使用しています。したがって、明示的に登録を解除する必要はありません。