はじめに前回の記事ですが、ObservableCollection のために Microsoft.BCL.Build.Tasks.dll を利用しているというところ・・・不要でした; ビルドターゲットを .NETFramework 4.5 にすればそれだけで利用できていました
気を取り直して Prism を MVPVM 化する(1)の続きです!
MVVM から MVPVM に拡張するにあたり、View と Presenter の関連付けを解決できるようにする必要があります
それはなぜかというと、少なくともストアアプリでは画面遷移の起点が Presenter ではなく View になるためです
ViewModel は View の DataContect を参照することで解決できますが、Presenter 用の仕組みはないので View の拡張クラスを作って解決できるようにします
/// <summary> /// Presenter 属性 /// </summary> [AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false)] public class PresenterViewAttribute : Attribute { #region Privates /// <summary> /// Presenter の型 /// </summary> private Type type; #endregion //Privates /// <summary> /// Presenter の型 /// </summary> public Type Type { get { return this.type; } set { this.type = value; } } /// <summary> /// コンストラクタ /// </summary> /// <param name="type">Presenter の型</param> public PresenterViewAttribute(Type type) { this.type = type; } } /// <summary> /// Presenter つき FrameworkElement の拡張クラス /// </summary> public static class PresenterViewExtention { /// <summary> /// Presenter の型を取得する /// </summary> /// <param name="view">View</param> /// <returns>Presenter の型</returns> public static Type GetPresenterType(this FrameworkElement view) { return PresenterViewExtention.GetAttribute(view).Type; } /// <summary> /// Presenter のインスタンスを取得する /// </summary> /// <typeparam name="T">Presenter の型</typeparam> /// <param name="view">View</param> /// <returns>Presenter</returns> public static T GetPresenter<T>(this FrameworkElement view) where T : class { return PresenterLocator.Get(view.GetPresenterType()) as T; } /// <summary> /// Presenter のインスタンスを取得する /// </summary> /// <param name="view">View</param> /// <returns>Presenter</returns> public static object GetPresenter(this FrameworkElement view) { return PresenterLocator.Get(view.GetPresenterType()); } /// <summary> /// PresenterView の属性を取得する /// </summary> /// <param name="view">View</param> /// <returns>PresenterView の属性</returns> private static PresenterViewAttribute GetAttribute(FrameworkElement view) { var viewType = view.GetType(); var attribute = viewType.GetTypeInfo().GetCustomAttribute(typeof(PresenterViewAttribute), false) as PresenterViewAttribute; if (attribute == null) { throw new InvalidOperationException("この Page には PresenterView 属性が指定されていません"); } return attribute; } }
FrameworkElement の拡張クラスを作成し、拡張メソッド GetPresenterType と GetPresenter を実装します
FrameworkElement には PresenterViewAttribute という属性を付加することで対応する Presenter クラスを関連付けるようにし、属性定義から Presenter クラスの型を取得することで Presenter のインスタンスを取得できるようにするというわけです
下記のように View から Presenter を取得することができるようになります
/// <summary> /// トップ画面 /// </summary> [PresenterView(typeof(TopPagePresenter))] public sealed partial class TopPage : VisualStateAwarePage, IPresenterView<TopPagePresenter> { /// <summary> /// コンストラクタ /// </summary> public TopPage() { this.InitializeComponent(); } #region IPresenterView<TPresenter> /// <summary> /// この画面の Presenter /// </summary> public TopPagePresenter Presenter { get { return this.GetPresenter<TopPagePresenter>(); } } #endregion //IPresenterView<TPresenter> }
途中で PresenterLocator というコンテナを利用していますが、こちらは下記のように Unity の UnityContainer を利用しているだけです
/// <summary> /// Presenter 配置クラス /// </summary> public class PresenterLocator { #region Privates /// <summary> /// コンテナ /// </summary> private static readonly UnityContainer Container = new UnityContainer(); #endregion //Privates /// <summary> /// コンストラクタ /// </summary> static PresenterLocator() { // アプリケーション内の IPresenterBase を実装するクラスをコンテナに一括登録する var presenterTypes = AllClasses.FromApplication() .Where(t => t.GetTypeInfo().ImplementedInterfaces.Any(i => i == typeof(IPresenterBase))); // コンテナ内で Presenter インスタンスをシングルトン化する Container.RegisterTypes(presenterTypes, getLifetimeManager: t => new ContainerControlledLifetimeManager()); } /// <summary> /// Presenter を取得する /// </summary> /// <typeparam name="T">Presenter の型</typeparam> /// <returns>Presenter</returns> public static T Get<T>() where T : class { return Container.Resolve<T>(); } /// <summary> /// Presenter を取得する /// </summary> /// <param name="type">Presenter の型</param> /// <returns>Presenter</returns> public static object Get(Type type) { return Container.Resolve(type); } }
UnityContainer は WinRT 依存ですが、将来的には Portable Class Library 化されてほしいと思うほど便利なクラスです
とりあえず今回はここまでで MVPVM 化(3)に続きます!