Prismは好きなんですがDelegateCommandのCanExecuteイベントハンドラが弱参照でイベントハンドラを持っているのと、CommandManagerのRequerySuggestedイベントも弱参照でイベントハンドラを持っているので、以下のようなコードを書くと、GCが走るとイベントに登録されてるDelegateがGCに回収されちゃって悲しいことになります。
CommandManager.RequerySuggested += (sender, args) => { HogeCommand.RaiseCanExecuteChanged(); };
以下のフォーラムでも討論されています。
http://compositewpf.codeplex.com/discussions/74364
とりあえず、ここで紹介しようとしている内容を最後につたない英語で書いておいたのですが・・・まぁいいです。英語できないので。日本語でここに書きます。要は弱参照でしか持たれてないなら、どっかで強参照で持ってしまえばいいじゃない?という発想です。1つ1つ手でやるのもめんどくさかったので、ViewModelの基本クラスでサクッとやっちゃいました。
以下のコードがそれです。
namespace Okazuki.MVVM.PrismSupport.ViewModels { using System; using System.Collections.Generic; using System.Linq; using System.Windows.Input; using Microsoft.Practices.Prism.Commands; using Microsoft.Practices.Prism.ViewModel; using Okazuki.MVVM.PrismSupport.Utils; /// <summary> /// ViewModelの基本クラス。 /// InteractionRequestクラスのインスタンスの自動初期化機能と、DelegateCommandのプロパティと /// CommandManager.RequerySuggestedの自動接続機能を提供します。 /// </summary> public abstract class ViewModelBase : NotificationObject { protected ViewModelBase() { this.InitializeCommandProperties(); this.InitializeInteractionRequestProperties(); } /// <summary> /// AutoInitAttributeのついているIInteractionRequestの派生型のプロパティの値を初期化します。 /// </summary> private void InitializeInteractionRequestProperties() { ObjectUtils.Init(this); } /// <summary> /// CommandManager.RequerySuggestedへのイベントハンドラの参照を保持します。 /// </summary> private ICollection<EventHandler> eventHandlers = new List<EventHandler>(); /// <summary> /// クラスに定義されたDelegateCommand型のプロパティのCanExecuteChangedイベントの発生と /// CommandManager.RequerySuggestedイベントの同期を行います。 /// </summary> private void InitializeCommandProperties() { var commands = this.GetType().GetProperties() .Where(p => typeof(DelegateCommandBase).IsAssignableFrom(p.PropertyType)) .Select(p => p.GetValue(this, null) as DelegateCommandBase); foreach (var command in commands) { EventHandler eventHandler = this.MakeEventHandler(command); CommandManager.RequerySuggested += eventHandler; this.eventHandlers.Add(eventHandler); } } private EventHandler MakeEventHandler(DelegateCommandBase command) { return (sender, e) => command.RaiseCanExecuteChanged(); } } }
InitializeCommandPropertiesがそれですね、DelegateCommandBaseのプロパティをかきあつめて、RaiseCanExecuteChangedを呼び出すイベントハンドラをCommandManagerに登録します。そのあと、内部でフィールドのList
とりあえず、これで問題なく動いています。
Prismに、個人的に欲しいなと思ってるようなクラスを実装しているものを、CodePlexで公開しています。まだ、公開しているのはアイテムテンプレートだけですが、ソースコードを覗いて貰うと、各種コードが入っています。何かの参考にして頂ければ幸いです。