SlideShare a Scribd company logo
1 of 48
わんくま同盟 東京勉強会 #119
5年間の試行錯誤で進化した
MVPVMパターン
暁 紫電
@akatukisiden
わんくま同盟 東京勉強会 #119
自己紹介
• 本名:伊藤 伸男
• フリーランス プログラマー
• スキル
– C#
– C++
– C++/CLI
– JavaScript
– Salesforce
わんくま同盟 東京勉強会 #119
はじめに
• 2014/06/07 東京勉強会 #90
「きっと怖くないMVVM&MVPVM」から5年
• 仕事でWPFの経験を積みながら
• 何かか作りたいアプリが出来た時の為に
MVPVMフレームワークだけでも作っておこうと思って
作り始めるも、なかなかうまくいかなかったり、他の勉強を
始めたりで中断
という事を繰り返してきました
• その試行錯誤の成果がまとまりつつあるので
紹介したいと思います。
わんくま同盟 東京勉強会 #119
MVPVMとは
• Model-View-Presenter-ViewModel
• MVVMパターンにMVPパターンを合体させた
もの
• MVVMでは,ViewとViewModelに分散してい
た画面遷移に関する処理をPresenter一つに
まとめることができるため処理が追いやすい
わんくま同盟 東京勉強会 #119
Model
• ビジネスロジック
• View,Presenter,ViewModel以外の部分
View
• ユーザーインターフェース
• UIへの出力とUIからの入力を担当する。
• FrameworkElementの派生クラス
• XAMLの記述+対応する partial class
わんくま同盟 東京勉強会 #119
ViewModel
• Viewの情報を保持(データバインド)
• Viewから受け取った入力やコマンドをPresenterに渡す
Presenter
• View,ViewModelを保持、その接続を管理する
• ViewModelから受け取った入力やコマンドを処理し
Modelを呼び出す。
• ViewModelのプロパティを通してViewを変更する。
• ナビゲーション・画面遷移の管理
わんくま同盟 東京勉強会 #119
参照関係
Model
ViewModel
View
Presenter
(DataContext)
わんくま同盟 東京勉強会 #119
MVPVMパターン
Model
ViewModel
View
Presenter
ナビゲーション
コマンド等
データバインド
呼び出し
コマンドの
内容はPに実装
イベント
わんくま同盟 東京勉強会 #119
画面遷移時にどちらのコントロールを使うか
• ContentControl
ひとつのコントロールを表示するだけ
シンプルで扱いやすい
• Frame,NavigationWindow (UWPではFrameのみ)
ナビゲーション前・後イベント
履歴(戻る、進む)
Pageインスタンスのキャッシュ等
画面遷移に関する機能を持つ
正直、履歴機能とかあまり使わないので
ContentControlで良い気がする
わんくま同盟 東京勉強会 #119
UWPアプリでは
Frameの使用がほぼ必須
画面遷移時のアニメーションサポート機能が付いているため
わんくま同盟 東京勉強会 #119
{x:Bind}マークアップ
• UWPで追加されたバインド方法
• {Binding}より高速
• PathのルートはDataContext ではなく、Page
• コンパイル時にバインド式が検証されコードが生成される
(obj/*.g.cs に生成)
• バインド式が間違っていたらコンパイルエラーが発生
わんくま同盟 東京勉強会 #119
{x:Bind}マークアップ
• Pageを表すクラスからバインディング ソース(ViewModel)
クラスを公開する必要がある
public sealed partial class Page1 : Page
{
public Page1()
{
this.InitializeComponent();
this.ViewModel = new Page1ViewModel();
}
public Page1ViewModel ViewModel { get; set; }
}
<Page x:Class=“Page1" ... >
<TextBlock Text="{x:Bind Path=ViewModel.Text, Mode=OneWay}" ... />
</Page>
わんくま同盟 東京勉強会 #119
obj/Page1.g.cs ({x:bind})
partial class Page1 :…
{
private static class XamlBindingSetters
{
public static void Set_Windows_UI_Xaml_Controls_TextBlock_Text
(TextBlock obj, String value, string targetNullValue)
{
if (value == null && targetNullValue != null)
{value = targetNullValue;}
obj.Text = value ?? String.Empty;
}
};
private class Page1_obj1_Bindings : …
{
public void Update()
{
this.Update_(this.dataRoot, NOT_PHASED);
this.initialized = true;
}
private void Update_(Views.Page1 obj, int phase)
{
if (obj != null)
{
if ((phase & (NOT_PHASED | (1 << 0))) != 0)
{this.Update_ViewModel(obj.ViewModel, phase);}
}
}
private void Update_ViewModel(Page1ViewModel obj, int phase)
{
if (obj != null)
{
if ((phase & (NOT_PHASED | (1 << 0))) != 0)
{this.Update_ViewModel_Text(obj.Text, phase);}
}
}
private void Update_ViewModel_Text(System.String obj, int phase)
{
if ((phase & ((1 << 0) | NOT_PHASED )) != 0)
{
if (!isobj2TextDisabled)
{
XamlBindingSetters.
Set_Windows_UI_Xaml_Controls_TextBlock_Text(
this.obj2, obj, null);
}
}
}
}
}
わんくま同盟 東京勉強会 #119
obj/Page1.g.cs ({Binding})
partial class Page1 :…
{
private class Page1_obj1_Bindings : …
{
public void Update()
{
this.Update_(this.dataRoot, NOT_PHASED);
this.initialized = true;
}
private void Update_(Views.Page1 obj, int phase)
{
if (obj != null)
{
}
}
}
}
わんくま同盟 東京勉強会 #119
{x:Bind}とイベント、関数
• コマンドだけでなく、イベント(と関数)のバインドが可能
• <Button Click="{x:Bind ViewModel.OnClick}" />
• RoutedEventも使用可
<Button Click="{x:Bind ViewModel.Clicked}" />
• Bind構文内で関数を呼び出して
Converterのように使うことができる。
<TextBlock Text=“{x:Bind ViewModel.ConvertFunc(ViewModel.Text) }” />
わんくま同盟 東京勉強会 #119
便利そうだけどこれって
厳密に言うと密結合では?
わんくま同盟 東京勉強会 #119
気にしないことにしよう!
わんくま同盟 東京勉強会 #119
最終的にこんな感じに
public sealed partial class Page1 : Page
{
public Page1ViewModel ViewModel
{
set { this.DataContext = value; }
get { return this.DataContext as Page1ViewModel; }
}
}
わんくま同盟 東京勉強会 #119
クラス図(UWP)
Frame
PresenterBase
Page
PresenterBase
Parent
PresenterBaseCore
ChildPage
PresenterBaseCore
PresenterBase
PresenterBaseCore
〇〇FramePresenter
〇〇Page
Presenter
Window
PresenterBase
〇〇Window
Presenter
通常のWindowとは
別物
View,ViewMode
非ジェネリック
View,ViewMode
ジェネリック
わんくま同盟 東京勉強会 #119
クラス図(WPF)
Frame
PresenterBase
NavigationWindow
PresenterBase
Page
PresenterBase
Parent
PresenterBaseCore
ChildPage
PresenterBaseCore
PresenterBase
PresenterBaseCore
Window
PresenterBase
〇〇FramePresenter
〇〇NavigationWindow
Presenter
〇〇Page
Presenter
〇〇Window
Presenter
Frame,NavigationWindowで
できる限り実装を共有
わんくま同盟 東京勉強会 #119
サンプル
• UWP
• Frame(NavigationWindow)で
ボタンを押すとPage1からPage2,Page2から
Page1へと遷移する
• https://github.com/akatukisiden/MVPVM
わんくま同盟 東京勉強会 #119
事前起動
• OS がバックグラウンドでプロセスを起動する
ことで、使用開始時の読み込み時間を短縮
パフォーマンスを向上させる
わんくま同盟 東京勉強会 #119
アプリケーションクラス
public sealed partial class App : Application
{
public WindowPresenter WindowPresenter { get; set; }
public App()
{
// 事前起動有効化
CoreApplication.EnablePrelaunch(true);
this.InitializeComponent();
this.EnteredBackground += App_EnteredBackground;
this.LeavingBackground += App_LeavingBackground;
this.Resuming += App_Resuming;
this.Suspending += App_Suspending;
this.UnhandledException += App_UnhandledException;
WindowPresenter = new WindowPresenter();
}
わんくま同盟 東京勉強会 #119
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{ WindowPresenter.Initialize(); }
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{ /* 以前中断したアプリケーションから状態を読み込みます*/ }
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
WindowPresenter.Navigate(typeof(Page1));
}
WindowPresenter.Activate();
}
else
{ /* 事前起動 */ }
}
初期化処理
事前起動済みの時は本起動時
は実行しない
本起動時のみ実行
画面を表示する
わんくま同盟 東京勉強会 #119
事前起動のテスト方法
• デバッグ>その他のデバッグターゲット>ユニバーサルWindowsアプリ事前起動のデバッグ
• 事前起動後の本起動のデバッグはスタートメニューなどからの起動で可能
わんくま同盟 東京勉強会 #119
WindowPresenterBase.cs
public abstract class WindowPresenterBase
{
public WindowPresenterBase()
{ }
public Window Window { set; get; }
public virtual void Initialize()
{
if (Window == null)
{
Window = Window.Current;
Window.Activated += Window_Activated;
Window.Closed += Window_Closed;
Window.SizeChanged += Window_SizeChanged;
Window.VisibilityChanged += Window_VisibilityChanged;
}
}
public virtual void Cleanup()
{
Window.Activated -= Window_Activated;
Window.Closed -= Window_Closed;
Window.SizeChanged -= Window_SizeChanged;
Window.VisibilityChanged -= Window_VisibilityChanged;
Window = null;
}
わんくま同盟 東京勉強会 #119
WindowPresenter.cs
public class WindowPresenter :WindowPresenterBase
{
public RootFramePresenter RootFramePresenter { get; set; }
public WindowPresenter()
{
RootFramePresenter = new RootFramePresenter();
}
public override void Initialize()
{
base.Initialize();
// ナビゲーション コンテキストとして動作するフレームを作成
var root = new Frame();
this.Window.Content = root;
this.RootFramePresenter.View = root;
this.RootFramePresenter.Initialize();
}
public bool Navigate(Type key) { return RootFramePresenter.Navigate(key);}
public override void Cleanup() { base.Cleanup(); }
わんくま同盟 東京勉強会 #119
Frame.Navigate(Type, object,NavigationTransitionInfo)
• 遷移先ページを指すType,パラメータ、ナビゲーション時のア
ニメの種類を引数に取る
• WPFではアニメの種類は無しでTypeの代わりにUriを用いる
– Pageのインスタンスを使うオーバーロードもあるが、ナビ
ゲーションのキャンセル時等は、
作成したインスタンスが使われないままになるので個人的
には非推奨
• Navigating(キャンセル可),Navigatedイベントが呼
び出される
わんくま同盟 東京勉強会 #119
Frame.Navigate(Type, object,NavigationTransitionInfo)
• Navigatingイベントでキャンセルされた場合
NavigationStoppedイベントが発火
– WPFではキャンセル時には発生しないがStopLoading()
を呼び出すことで手動で発生させることができる
– ナビゲーション中に別のナビゲーションを開始しても
発生するらしいが詳しい条件は不明
• Navigatedイベント中に例外がが発生すると
NavigationFailedイベントが発火
– WPFだとIOExceptionじゃないといけないので注意
わんくま同盟 東京勉強会 #119
RootFramePresenter.cs
public class RootFramePresenter: FramePresenterBase<Frame,EmptyViewModel>
{
public RootFramePresenter()
{
AddChild(typeof(Page1), new Page1Presenter());
AddChild(typeof(Page2), new Page2Presenter());
}
public override void Initialize()
{
base.Initialize();
this.IsNavigationStackEnabled = true;
}
public override void Cleanup()
{
base.Cleanup();
}
}
最初に、Presenterのインスタンスを作成
Viewの型とセットで登録
わんくま同盟 東京勉強会 #119
Presenterの作成タイミング
• Presenter,とViewModelは最初にまとめて作成して
おく。
• Viewのインスタンスが作られるタイミングで
Presenterを作ろうとしたこともあるのだが、
気が付いたらFrameのもつ履歴やインスタンス
キャッシュの機能をPresenterに再実装しようとして
しまったり、
Viewの型に応じたPresenterを作成するしくみが必
要になったりしたため、
これなら最初に作成しておいた方が楽だと判断した
わんくま同盟 東京勉強会 #119
FramePresenterBase
public abstract class FramePresenterBase<TView, TViewModel>
: ParentPresenterBaseCore
where TView : Windows.UI.Xaml.Controls.Frame, new()
where TViewModel : ViewModelBase, new()
{
public FramePresenterBase() { ViewModel = new TViewModel(); }
public bool IsNavigationStackEnabled
{
get { return View.IsNavigationStackEnabled; }
set { View.IsNavigationStackEnabled = value; }
}
public TView View { get { return this.ViewBase as TView; } set { this.ViewBase = value; } }
public TViewModel ViewModel
{ get { return this.ViewModelBase as TViewModel; } set { this.ViewModelBase = value; } }
ナビゲーション履歴を使うかどうか
Viewの同名プロパティにアクセス
非ジェネリック基底クラス(Core)の
プロパティをキャスト
わんくま同盟 東京勉強会 #119
FramePresenterBase
public override void Initialize()
{
base.Initialize();
IsNavigationStackEnabled = false;
View.Navigating += View_Navigating;
View.Navigated += View_Navigated;
View.NavigationFailed += View_NavigationFailed;
View.NavigationStopped += View_NavigationStopped;
}
public override void Cleanup()
{
View.Navigating -= View_Navigating;
View.Navigated -= View_Navigated;
View.NavigationFailed -= View_NavigationFailed;
View.NavigationStopped -= View_NavigationStopped;
base.Cleanup();
}
わんくま同盟 東京勉強会 #119
FramePresenterBase
public override bool Navigate
(Type key, object extraData = null, NavigationTransitionInfo infoOrverride =null)
{ return View.Navigate(key, extraData, infoOrverride); }
public override void GoForward()
{
View.GoForward();
}
public override void GoBack(NavigationTransitionInfo infoOrverride = null)
{ View.GoBack(infoOrverride); }
private void View_Navigated(object sender, NavigationEventArgs e)
{ View_NavigatedCore(sender, e); }
}
画面遷移関係の関数
Frameの同名関数を呼び出し
通常の遷移,進む,戻る
遷移時のアニメーションの種類
ParentPresenterBaseCore
の関数を呼び出し
わんくま同盟 東京勉強会 #119
ParentPresenterBaseCore.cs
public abstract class ParentPresenterBaseCore : PresenterBaseCore
{
protected
Dictionary<Type, ChildPagePresenterBaseCore> Children { get; private set; };
public void AddChild(Type key, ChildPagePresenterBaseCore child)
{
Children.Add(key, child);
}
public void RemoveChild(Type key)
{
Children.Remove(key);
}
本体はDictionary
子Presenterの登録
子Presenterの解除
わんくま同盟 東京勉強会 #119
ParentPresenterBaseCore.cs
public ParentPresenterBaseCore()
{ Children = new Dictionary<Type, ChildPagePresenterBaseCore>(); }
public ChildPagePresenterBaseCore CurrentChild { get; internal set; };
public override void Initialize()
{ base.Initialize(); }
public override void Cleanup()
{
if (CurrentChild != null)
{
CurrentChild.Cleanup();
CurrentChild = null;
}
base.Cleanup();
}
表示中の子(Page)Presenter
わんくま同盟 東京勉強会 #119
ParentPresenterBaseCore.cs
protected void View_NavigationStopped(object sender, NavigationEventArgs e)
{
CurrentChild.InvokeOnNavigationStopped(e);
}
protected void View_Navigating(object sender, NavigatingCancelEventArgs e)
{
if (CurrentChild != null)
{ CurrentChild.InvokeOnNavigatingFrom(e); }
}
protected void View_NavigationFailed(object sender, NavigationFailedEventArgs e)
{ CurrentChild.InvokeOnNavigationFailed(e); }
Internal 関数を通してイベントに対応するPagePresenterの関数を呼び出し
わんくま同盟 東京勉強会 #119
ParentPresenterBaseCore.cs
protected internal void View_NavigatedCore(object sender, NavigationEventArgs e)
{
if (e != null)
{
if (CurrentChild != null)
{
CurrentChild.InvokeOnNavigatedFrom(e);
CurrentChild.Cleanup();
CurrentChild = null;
}
Type key = e.Content.GetType();
var childPresenter = Children[key];
var element = e.Content as FrameworkElement;
childPresenter.Parent = this;
childPresenter.ViewBase = element;
CurrentChild = childPresenter;
childPresenter.Initialize();
childPresenter.InvokeOnNavigatedTo(e);
}
}
表示中のページの終了処理
遷移先Pageに対応したPagePresenterを取得
遷移先Presenterの初期化処理
internal関数を介して
PagePresenter.OnNavigatedFrom(e)
の呼び出し
PagePresenter.OnNavigatedTo(e)
の呼び出し
わんくま同盟 東京勉強会 #119
• UWPのPageには3つのオーバーライド用関数が存在
– OnNavigatedTo (Pageへ遷移してきたときに実行)
– OnNavigatingFrom (Pageから遷移するときに実行,キャンセル可)
– OnNavigatedFrom (Pageから遷移したときに実行)
• MVPVMではできるだけViewにコードを書きたくないため同
名の関数をPagePresenterに用意して同じような感覚で使え
るようにしている
わんくま同盟 東京勉強会 #119
Page1Presenter
public class Page1Presenter : PagePresenterBase<Views.Page1, Page1ViewModel>
{
public override void Initialize()
{
base.Initialize();
ViewModel.ClickEvent += Click;
this.ViewModel.Text = "Page1";
}
public void Click(object sender, RoutedEventArgs args)
{ this.Parent.Navigate(typeof(Page2), null); }
public override void Cleanup()
{
ViewModel.ClickEvent -= Click;
base.Cleanup();
}
x:bindで呼び出すイベント
わんくま同盟 東京勉強会 #119
PagePresenterBase
public abstract class PagePresenterBase<TView, TViewModel> : ChildPagePresent
erBaseCore
where TView : Page, new()
where TViewModel : ViewModelBase, new()
{
public PagePresenterBase()
{ ViewModel = new TViewModel(); }
public TView View
{ get { return this.ViewBase as TView; } set { this.ViewBase = value; } }
public TViewModel ViewModel
{
get { return this.ViewModelBase as TViewModel; }
set { this.ViewModelBase = value; }
}
わんくま同盟 東京勉強会 #119
PagePresenterBase
public override void Initialize()
{
base.Initialize();
View.Loaded += OnPageLoaded;
}
protected virtual void OnPageLoaded(object sender, RoutedEventArgs e)
{}
public override void Cleanup()
{
View.Loaded -= OnPageLoaded;
base.Cleanup();
}
}
わんくま同盟 東京勉強会 #119
ChildPresenterBaseCore
public abstract class ChildPagePresenterBaseCore : PresenterBaseCore
{
public ParentPresenterBaseCore Parent { get; set; }
public override void Initialize()
{
base.Initialize();
}
public override void Cleanup()
{
Parent = null;
base.Cleanup();
}
親FramePresenter
参照外し
わんくま同盟 東京勉強会 #119
ChildPresenterBaseCore
internal void InvokeOnNavigatedTo(NavigationEventArgs e)
{ OnNavigatedTo(e);}
internal void InvokeOnNavigatingFrom(NavigatingCancelEventArgs e)
{ OnNavigatingFrom(e); }
internal void InvokeOnNavigatedFrom(NavigationEventArgs e)
{ OnNavigatedFrom(e); }
internal void InvokeOnNavigationStopped(NavigationEventArgs e)
{OnNavigationStopped (e); }
internal void InvokeOnNavigationFailed(NavigationFailedEventArgs e)
{ OnNavigationFailed(e); }
わんくま同盟 東京勉強会 #119
ChildPresenterBaseCore
protected virtual void OnNavigatedTo(NavigationEventArgs e)
{ }
protected virtual void OnNavigatingFrom(NavigatingCancelEventArgs e)
{}
protected virtual void OnNavigatedFrom(NavigationEventArgs e)
{}
protected virtual void OnNavigationFailed(NavigationFailedEventArgs e)
{}
protected virtual void OnNavigationStopped(NavigationEventArgs e)
{}
}
わんくま同盟 東京勉強会 #119
PresenterBaseCore
public abstract class PresenterBaseCore
{
protected TaskScheduler UIThreadTaskScheduler { get; private set; }
public bool IsEnable { get; private set; }
internal FrameworkElement ViewBase { get; set; }
internal ViewModelBase ViewModelBase { get; set; }
public PresenterBaseCore()
{
this.IsEnable = false;
}
Presenterが有効かどうか
Initialize()でtrue
CleanUp()でfalse
View,ViewModel
非ジェネリック基底クラス用
わんくま同盟 東京勉強会 #119
PresenterBaseCore
public virtual void Initialize()
{
this.UIThreadTaskScheduler
= TaskScheduler.FromCurrentSynchronizationContext();
ViewBase.DataContext = ViewModelBase;
IsEnable = true;
}
public virtual void Cleanup()
{
if (ViewBase != null)
{
ViewBase.DataContext = null;
ViewBase = null;
}
IsEnable = false;
}
ViewとViewModelを接続
接続解除
Viewの参照外し
破壊ではなく解放
管理をViewフレームワークに
返すイメージ
わんくま同盟 東京勉強会 #119
まとめ
• UWPでは画面遷移アニメーションがあるので
ContentControlではなくてFrameを使う
• Presenter,ViewModelは最初にまとめて作る
• View(Page)の作成はできる限りフレームワー
クに任せ、
使用後は参照を解除し
管理権をフレームワークに返すことでViewの
機能を最大限生かす

More Related Content

What's hot

C#の強み、或いは何故PHPから乗り換えるのか
C#の強み、或いは何故PHPから乗り換えるのかC#の強み、或いは何故PHPから乗り換えるのか
C#の強み、或いは何故PHPから乗り換えるのかYoshifumi Kawai
 
DDD 20121106 SEA Forum November
DDD 20121106 SEA Forum NovemberDDD 20121106 SEA Forum November
DDD 20121106 SEA Forum November増田 亨
 
Springを何となく使ってる人が抑えるべきポイント
Springを何となく使ってる人が抑えるべきポイントSpringを何となく使ってる人が抑えるべきポイント
Springを何となく使ってる人が抑えるべきポイント土岐 孝平
 
ドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解するドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解する増田 亨
 
DI(依存性注入)について
DI(依存性注入)についてDI(依存性注入)について
DI(依存性注入)についてYui Ito
 
SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021Hiroshi Tokumaru
 
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみようドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみよう増田 亨
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているKoichi Tanaka
 
WPF開発での陥りやすい罠
WPF開発での陥りやすい罠WPF開発での陥りやすい罠
WPF開発での陥りやすい罠Sho Okada
 
より速く より運用しやすく 進化し続けるJVM(Java Developers Summit Online 2023 発表資料)
より速く より運用しやすく 進化し続けるJVM(Java Developers Summit Online 2023 発表資料)より速く より運用しやすく 進化し続けるJVM(Java Developers Summit Online 2023 発表資料)
より速く より運用しやすく 進化し続けるJVM(Java Developers Summit Online 2023 発表資料)NTT DATA Technology & Innovation
 
.NET 7期待の新機能
.NET 7期待の新機能.NET 7期待の新機能
.NET 7期待の新機能TomomitsuKusaba
 
ドメイン駆動設計をゲーム開発に活かす
ドメイン駆動設計をゲーム開発に活かすドメイン駆動設計をゲーム開発に活かす
ドメイン駆動設計をゲーム開発に活かす増田 亨
 
GoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンGoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンAkihiko Horiuchi
 
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)Yoshiro Tokumasu
 
【BS3】Visual Studio 2022 と .NET 6 での Windows アプリ開発技術の紹介
【BS3】Visual Studio 2022 と .NET 6 での Windows アプリ開発技術の紹介 【BS3】Visual Studio 2022 と .NET 6 での Windows アプリ開発技術の紹介
【BS3】Visual Studio 2022 と .NET 6 での Windows アプリ開発技術の紹介 日本マイクロソフト株式会社
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 TipsTakaaki Suzuki
 
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門増田 亨
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?Yoshitaka Kawashima
 
C#や.NET Frameworkがやっていること
C#や.NET FrameworkがやっていることC#や.NET Frameworkがやっていること
C#や.NET Frameworkがやっていること信之 岩永
 
Node.js Native ESM への道 〜最終章: Babel / TypeScript Modules との闘い〜
Node.js Native ESM への道  〜最終章: Babel / TypeScript Modules との闘い〜Node.js Native ESM への道  〜最終章: Babel / TypeScript Modules との闘い〜
Node.js Native ESM への道 〜最終章: Babel / TypeScript Modules との闘い〜Teppei Sato
 

What's hot (20)

C#の強み、或いは何故PHPから乗り換えるのか
C#の強み、或いは何故PHPから乗り換えるのかC#の強み、或いは何故PHPから乗り換えるのか
C#の強み、或いは何故PHPから乗り換えるのか
 
DDD 20121106 SEA Forum November
DDD 20121106 SEA Forum NovemberDDD 20121106 SEA Forum November
DDD 20121106 SEA Forum November
 
Springを何となく使ってる人が抑えるべきポイント
Springを何となく使ってる人が抑えるべきポイントSpringを何となく使ってる人が抑えるべきポイント
Springを何となく使ってる人が抑えるべきポイント
 
ドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解するドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解する
 
DI(依存性注入)について
DI(依存性注入)についてDI(依存性注入)について
DI(依存性注入)について
 
SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021
 
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみようドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみよう
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っている
 
WPF開発での陥りやすい罠
WPF開発での陥りやすい罠WPF開発での陥りやすい罠
WPF開発での陥りやすい罠
 
より速く より運用しやすく 進化し続けるJVM(Java Developers Summit Online 2023 発表資料)
より速く より運用しやすく 進化し続けるJVM(Java Developers Summit Online 2023 発表資料)より速く より運用しやすく 進化し続けるJVM(Java Developers Summit Online 2023 発表資料)
より速く より運用しやすく 進化し続けるJVM(Java Developers Summit Online 2023 発表資料)
 
.NET 7期待の新機能
.NET 7期待の新機能.NET 7期待の新機能
.NET 7期待の新機能
 
ドメイン駆動設計をゲーム開発に活かす
ドメイン駆動設計をゲーム開発に活かすドメイン駆動設計をゲーム開発に活かす
ドメイン駆動設計をゲーム開発に活かす
 
GoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホンGoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホン
 
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)
JDK 16 で導入された JEP 396 にご注意!! (JJUG CCC 2021 Spring)
 
【BS3】Visual Studio 2022 と .NET 6 での Windows アプリ開発技術の紹介
【BS3】Visual Studio 2022 と .NET 6 での Windows アプリ開発技術の紹介 【BS3】Visual Studio 2022 と .NET 6 での Windows アプリ開発技術の紹介
【BS3】Visual Studio 2022 と .NET 6 での Windows アプリ開発技術の紹介
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips
 
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?
 
C#や.NET Frameworkがやっていること
C#や.NET FrameworkがやっていることC#や.NET Frameworkがやっていること
C#や.NET Frameworkがやっていること
 
Node.js Native ESM への道 〜最終章: Babel / TypeScript Modules との闘い〜
Node.js Native ESM への道  〜最終章: Babel / TypeScript Modules との闘い〜Node.js Native ESM への道  〜最終章: Babel / TypeScript Modules との闘い〜
Node.js Native ESM への道 〜最終章: Babel / TypeScript Modules との闘い〜
 

Similar to T119_5年間の試行錯誤で進化したMVPVMパターン

脱・独自改造! GebでWebDriverをもっとシンプルに
脱・独自改造! GebでWebDriverをもっとシンプルに脱・独自改造! GebでWebDriverをもっとシンプルに
脱・独自改造! GebでWebDriverをもっとシンプルにHiroko Tamagawa
 
T90 きっと怖くないmvvm & mvpvm
T90 きっと怖くないmvvm & mvpvmT90 きっと怖くないmvvm & mvpvm
T90 きっと怖くないmvvm & mvpvm伸男 伊藤
 
Guide to GraalVM (Oracle Groundbreakers APAC 2019 Tour in Tokyo)
Guide to GraalVM (Oracle Groundbreakers APAC 2019 Tour in Tokyo)Guide to GraalVM (Oracle Groundbreakers APAC 2019 Tour in Tokyo)
Guide to GraalVM (Oracle Groundbreakers APAC 2019 Tour in Tokyo)Koichi Sakata
 
Knockout.js を利用したインタラクティブ web アプリケーション開発
Knockout.js を利用したインタラクティブ web アプリケーション開発Knockout.js を利用したインタラクティブ web アプリケーション開発
Knockout.js を利用したインタラクティブ web アプリケーション開発Daizen Ikehara
 
T51 jQueryで学ぶJavaScriptでのフロント開発~ASP.NET MVC3もあるよっ!~
T51 jQueryで学ぶJavaScriptでのフロント開発~ASP.NET MVC3もあるよっ!~T51 jQueryで学ぶJavaScriptでのフロント開発~ASP.NET MVC3もあるよっ!~
T51 jQueryで学ぶJavaScriptでのフロント開発~ASP.NET MVC3もあるよっ!~normalian
 
iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East
iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 EastiOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East
iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 Eastirgaly
 
WP-CLI (WordBench Sendai 20140628)
WP-CLI (WordBench Sendai 20140628)WP-CLI (WordBench Sendai 20140628)
WP-CLI (WordBench Sendai 20140628)Kazue Igarashi
 
2012 05-19第44回cocoa勉強会発表資料
2012 05-19第44回cocoa勉強会発表資料2012 05-19第44回cocoa勉強会発表資料
2012 05-19第44回cocoa勉強会発表資料OCHI Shuji
 
大規模なJavaScript開発の話
大規模なJavaScript開発の話大規模なJavaScript開発の話
大規模なJavaScript開発の話terurou
 
レスポンシブWebデザイン【発展編】
レスポンシブWebデザイン【発展編】レスポンシブWebデザイン【発展編】
レスポンシブWebデザイン【発展編】Yasuhito Yabe
 
JavaOne2013報告会 JavaFX Update
JavaOne2013報告会 JavaFX UpdateJavaOne2013報告会 JavaFX Update
JavaOne2013報告会 JavaFX UpdateTakashi Aoe
 
企業におけるSpring@日本springユーザー会20090624
企業におけるSpring@日本springユーザー会20090624企業におけるSpring@日本springユーザー会20090624
企業におけるSpring@日本springユーザー会20090624Yusuke Suzuki
 
iPhoneアプリ開発の歩き方〜Swift編〜
iPhoneアプリ開発の歩き方〜Swift編〜iPhoneアプリ開発の歩き方〜Swift編〜
iPhoneアプリ開発の歩き方〜Swift編〜Yusuke SAITO
 
作られては消えていく泡のように儚いクラスタの運用話
作られては消えていく泡のように儚いクラスタの運用話作られては消えていく泡のように儚いクラスタの運用話
作られては消えていく泡のように儚いクラスタの運用話Tsuyoshi Torii
 
今流行りのウェブアプリ開発環境Yeoman
今流行りのウェブアプリ開発環境Yeoman今流行りのウェブアプリ開発環境Yeoman
今流行りのウェブアプリ開発環境Yeomantomo_masakura
 
Mojoliciousでつくる! Webアプリ入門
Mojoliciousでつくる! Webアプリ入門Mojoliciousでつくる! Webアプリ入門
Mojoliciousでつくる! Webアプリ入門Yusuke Wada
 

Similar to T119_5年間の試行錯誤で進化したMVPVMパターン (20)

脱・独自改造! GebでWebDriverをもっとシンプルに
脱・独自改造! GebでWebDriverをもっとシンプルに脱・独自改造! GebでWebDriverをもっとシンプルに
脱・独自改造! GebでWebDriverをもっとシンプルに
 
T90 きっと怖くないmvvm & mvpvm
T90 きっと怖くないmvvm & mvpvmT90 きっと怖くないmvvm & mvpvm
T90 きっと怖くないmvvm & mvpvm
 
Guide to GraalVM (Oracle Groundbreakers APAC 2019 Tour in Tokyo)
Guide to GraalVM (Oracle Groundbreakers APAC 2019 Tour in Tokyo)Guide to GraalVM (Oracle Groundbreakers APAC 2019 Tour in Tokyo)
Guide to GraalVM (Oracle Groundbreakers APAC 2019 Tour in Tokyo)
 
Knockout.js を利用したインタラクティブ web アプリケーション開発
Knockout.js を利用したインタラクティブ web アプリケーション開発Knockout.js を利用したインタラクティブ web アプリケーション開発
Knockout.js を利用したインタラクティブ web アプリケーション開発
 
T51 jQueryで学ぶJavaScriptでのフロント開発~ASP.NET MVC3もあるよっ!~
T51 jQueryで学ぶJavaScriptでのフロント開発~ASP.NET MVC3もあるよっ!~T51 jQueryで学ぶJavaScriptでのフロント開発~ASP.NET MVC3もあるよっ!~
T51 jQueryで学ぶJavaScriptでのフロント開発~ASP.NET MVC3もあるよっ!~
 
iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East
iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 EastiOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East
iOS の動画アプリ開発に Xamarin を使ってみた @JXUG #2 East
 
WP-CLI (WordBench Sendai 20140628)
WP-CLI (WordBench Sendai 20140628)WP-CLI (WordBench Sendai 20140628)
WP-CLI (WordBench Sendai 20140628)
 
2012 05-19第44回cocoa勉強会発表資料
2012 05-19第44回cocoa勉強会発表資料2012 05-19第44回cocoa勉強会発表資料
2012 05-19第44回cocoa勉強会発表資料
 
大規模なJavaScript開発の話
大規模なJavaScript開発の話大規模なJavaScript開発の話
大規模なJavaScript開発の話
 
レスポンシブWebデザイン【発展編】
レスポンシブWebデザイン【発展編】レスポンシブWebデザイン【発展編】
レスポンシブWebデザイン【発展編】
 
Spring3.1概要x di
Spring3.1概要x diSpring3.1概要x di
Spring3.1概要x di
 
Pronama 0707 wf4
Pronama 0707 wf4Pronama 0707 wf4
Pronama 0707 wf4
 
JavaOne2013報告会 JavaFX Update
JavaOne2013報告会 JavaFX UpdateJavaOne2013報告会 JavaFX Update
JavaOne2013報告会 JavaFX Update
 
企業におけるSpring@日本springユーザー会20090624
企業におけるSpring@日本springユーザー会20090624企業におけるSpring@日本springユーザー会20090624
企業におけるSpring@日本springユーザー会20090624
 
iPhoneアプリ開発の歩き方〜Swift編〜
iPhoneアプリ開発の歩き方〜Swift編〜iPhoneアプリ開発の歩き方〜Swift編〜
iPhoneアプリ開発の歩き方〜Swift編〜
 
作られては消えていく泡のように儚いクラスタの運用話
作られては消えていく泡のように儚いクラスタの運用話作られては消えていく泡のように儚いクラスタの運用話
作られては消えていく泡のように儚いクラスタの運用話
 
今流行りのウェブアプリ開発環境Yeoman
今流行りのウェブアプリ開発環境Yeoman今流行りのウェブアプリ開発環境Yeoman
今流行りのウェブアプリ開発環境Yeoman
 
Mojoliciousでつくる! Webアプリ入門
Mojoliciousでつくる! Webアプリ入門Mojoliciousでつくる! Webアプリ入門
Mojoliciousでつくる! Webアプリ入門
 
vSphere環境での自動化とテスト
vSphere環境での自動化とテストvSphere環境での自動化とテスト
vSphere環境での自動化とテスト
 
Word bench7月号
Word bench7月号Word bench7月号
Word bench7月号
 

More from 伸男 伊藤

没セッション 知識ゼロから学んだソフトウェアテスト
没セッション 知識ゼロから学んだソフトウェアテスト没セッション 知識ゼロから学んだソフトウェアテスト
没セッション 知識ゼロから学んだソフトウェアテスト伸男 伊藤
 
わんくまT84 kinect深度情報処理入門
わんくまT84 kinect深度情報処理入門わんくまT84 kinect深度情報処理入門
わんくまT84 kinect深度情報処理入門伸男 伊藤
 
わんくまT78 mfcを始めようとしてみた
わんくまT78 mfcを始めようとしてみたわんくまT78 mfcを始めようとしてみた
わんくまT78 mfcを始めようとしてみた伸男 伊藤
 
Kinect深度情報処理入門
Kinect深度情報処理入門Kinect深度情報処理入門
Kinect深度情報処理入門伸男 伊藤
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門伸男 伊藤
 

More from 伸男 伊藤 (6)

T93 com入門
T93 com入門T93 com入門
T93 com入門
 
没セッション 知識ゼロから学んだソフトウェアテスト
没セッション 知識ゼロから学んだソフトウェアテスト没セッション 知識ゼロから学んだソフトウェアテスト
没セッション 知識ゼロから学んだソフトウェアテスト
 
わんくまT84 kinect深度情報処理入門
わんくまT84 kinect深度情報処理入門わんくまT84 kinect深度情報処理入門
わんくまT84 kinect深度情報処理入門
 
わんくまT78 mfcを始めようとしてみた
わんくまT78 mfcを始めようとしてみたわんくまT78 mfcを始めようとしてみた
わんくまT78 mfcを始めようとしてみた
 
Kinect深度情報処理入門
Kinect深度情報処理入門Kinect深度情報処理入門
Kinect深度情報処理入門
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門
 

T119_5年間の試行錯誤で進化したMVPVMパターン

  • 2. わんくま同盟 東京勉強会 #119 自己紹介 • 本名:伊藤 伸男 • フリーランス プログラマー • スキル – C# – C++ – C++/CLI – JavaScript – Salesforce
  • 3. わんくま同盟 東京勉強会 #119 はじめに • 2014/06/07 東京勉強会 #90 「きっと怖くないMVVM&MVPVM」から5年 • 仕事でWPFの経験を積みながら • 何かか作りたいアプリが出来た時の為に MVPVMフレームワークだけでも作っておこうと思って 作り始めるも、なかなかうまくいかなかったり、他の勉強を 始めたりで中断 という事を繰り返してきました • その試行錯誤の成果がまとまりつつあるので 紹介したいと思います。
  • 4. わんくま同盟 東京勉強会 #119 MVPVMとは • Model-View-Presenter-ViewModel • MVVMパターンにMVPパターンを合体させた もの • MVVMでは,ViewとViewModelに分散してい た画面遷移に関する処理をPresenter一つに まとめることができるため処理が追いやすい
  • 5. わんくま同盟 東京勉強会 #119 Model • ビジネスロジック • View,Presenter,ViewModel以外の部分 View • ユーザーインターフェース • UIへの出力とUIからの入力を担当する。 • FrameworkElementの派生クラス • XAMLの記述+対応する partial class
  • 6. わんくま同盟 東京勉強会 #119 ViewModel • Viewの情報を保持(データバインド) • Viewから受け取った入力やコマンドをPresenterに渡す Presenter • View,ViewModelを保持、その接続を管理する • ViewModelから受け取った入力やコマンドを処理し Modelを呼び出す。 • ViewModelのプロパティを通してViewを変更する。 • ナビゲーション・画面遷移の管理
  • 9. わんくま同盟 東京勉強会 #119 画面遷移時にどちらのコントロールを使うか • ContentControl ひとつのコントロールを表示するだけ シンプルで扱いやすい • Frame,NavigationWindow (UWPではFrameのみ) ナビゲーション前・後イベント 履歴(戻る、進む) Pageインスタンスのキャッシュ等 画面遷移に関する機能を持つ 正直、履歴機能とかあまり使わないので ContentControlで良い気がする
  • 11. わんくま同盟 東京勉強会 #119 {x:Bind}マークアップ • UWPで追加されたバインド方法 • {Binding}より高速 • PathのルートはDataContext ではなく、Page • コンパイル時にバインド式が検証されコードが生成される (obj/*.g.cs に生成) • バインド式が間違っていたらコンパイルエラーが発生
  • 12. わんくま同盟 東京勉強会 #119 {x:Bind}マークアップ • Pageを表すクラスからバインディング ソース(ViewModel) クラスを公開する必要がある public sealed partial class Page1 : Page { public Page1() { this.InitializeComponent(); this.ViewModel = new Page1ViewModel(); } public Page1ViewModel ViewModel { get; set; } } <Page x:Class=“Page1" ... > <TextBlock Text="{x:Bind Path=ViewModel.Text, Mode=OneWay}" ... /> </Page>
  • 13. わんくま同盟 東京勉強会 #119 obj/Page1.g.cs ({x:bind}) partial class Page1 :… { private static class XamlBindingSetters { public static void Set_Windows_UI_Xaml_Controls_TextBlock_Text (TextBlock obj, String value, string targetNullValue) { if (value == null && targetNullValue != null) {value = targetNullValue;} obj.Text = value ?? String.Empty; } }; private class Page1_obj1_Bindings : … { public void Update() { this.Update_(this.dataRoot, NOT_PHASED); this.initialized = true; } private void Update_(Views.Page1 obj, int phase) { if (obj != null) { if ((phase & (NOT_PHASED | (1 << 0))) != 0) {this.Update_ViewModel(obj.ViewModel, phase);} } } private void Update_ViewModel(Page1ViewModel obj, int phase) { if (obj != null) { if ((phase & (NOT_PHASED | (1 << 0))) != 0) {this.Update_ViewModel_Text(obj.Text, phase);} } } private void Update_ViewModel_Text(System.String obj, int phase) { if ((phase & ((1 << 0) | NOT_PHASED )) != 0) { if (!isobj2TextDisabled) { XamlBindingSetters. Set_Windows_UI_Xaml_Controls_TextBlock_Text( this.obj2, obj, null); } } } } }
  • 14. わんくま同盟 東京勉強会 #119 obj/Page1.g.cs ({Binding}) partial class Page1 :… { private class Page1_obj1_Bindings : … { public void Update() { this.Update_(this.dataRoot, NOT_PHASED); this.initialized = true; } private void Update_(Views.Page1 obj, int phase) { if (obj != null) { } } } }
  • 15. わんくま同盟 東京勉強会 #119 {x:Bind}とイベント、関数 • コマンドだけでなく、イベント(と関数)のバインドが可能 • <Button Click="{x:Bind ViewModel.OnClick}" /> • RoutedEventも使用可 <Button Click="{x:Bind ViewModel.Clicked}" /> • Bind構文内で関数を呼び出して Converterのように使うことができる。 <TextBlock Text=“{x:Bind ViewModel.ConvertFunc(ViewModel.Text) }” />
  • 18. わんくま同盟 東京勉強会 #119 最終的にこんな感じに public sealed partial class Page1 : Page { public Page1ViewModel ViewModel { set { this.DataContext = value; } get { return this.DataContext as Page1ViewModel; } } }
  • 21. わんくま同盟 東京勉強会 #119 サンプル • UWP • Frame(NavigationWindow)で ボタンを押すとPage1からPage2,Page2から Page1へと遷移する • https://github.com/akatukisiden/MVPVM
  • 22. わんくま同盟 東京勉強会 #119 事前起動 • OS がバックグラウンドでプロセスを起動する ことで、使用開始時の読み込み時間を短縮 パフォーマンスを向上させる
  • 23. わんくま同盟 東京勉強会 #119 アプリケーションクラス public sealed partial class App : Application { public WindowPresenter WindowPresenter { get; set; } public App() { // 事前起動有効化 CoreApplication.EnablePrelaunch(true); this.InitializeComponent(); this.EnteredBackground += App_EnteredBackground; this.LeavingBackground += App_LeavingBackground; this.Resuming += App_Resuming; this.Suspending += App_Suspending; this.UnhandledException += App_UnhandledException; WindowPresenter = new WindowPresenter(); }
  • 24. わんくま同盟 東京勉強会 #119 protected override void OnLaunched(LaunchActivatedEventArgs e) { Frame rootFrame = Window.Current.Content as Frame; if (rootFrame == null) { WindowPresenter.Initialize(); } if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { /* 以前中断したアプリケーションから状態を読み込みます*/ } if (e.PrelaunchActivated == false) { if (rootFrame.Content == null) { WindowPresenter.Navigate(typeof(Page1)); } WindowPresenter.Activate(); } else { /* 事前起動 */ } } 初期化処理 事前起動済みの時は本起動時 は実行しない 本起動時のみ実行 画面を表示する
  • 25. わんくま同盟 東京勉強会 #119 事前起動のテスト方法 • デバッグ>その他のデバッグターゲット>ユニバーサルWindowsアプリ事前起動のデバッグ • 事前起動後の本起動のデバッグはスタートメニューなどからの起動で可能
  • 26. わんくま同盟 東京勉強会 #119 WindowPresenterBase.cs public abstract class WindowPresenterBase { public WindowPresenterBase() { } public Window Window { set; get; } public virtual void Initialize() { if (Window == null) { Window = Window.Current; Window.Activated += Window_Activated; Window.Closed += Window_Closed; Window.SizeChanged += Window_SizeChanged; Window.VisibilityChanged += Window_VisibilityChanged; } } public virtual void Cleanup() { Window.Activated -= Window_Activated; Window.Closed -= Window_Closed; Window.SizeChanged -= Window_SizeChanged; Window.VisibilityChanged -= Window_VisibilityChanged; Window = null; }
  • 27. わんくま同盟 東京勉強会 #119 WindowPresenter.cs public class WindowPresenter :WindowPresenterBase { public RootFramePresenter RootFramePresenter { get; set; } public WindowPresenter() { RootFramePresenter = new RootFramePresenter(); } public override void Initialize() { base.Initialize(); // ナビゲーション コンテキストとして動作するフレームを作成 var root = new Frame(); this.Window.Content = root; this.RootFramePresenter.View = root; this.RootFramePresenter.Initialize(); } public bool Navigate(Type key) { return RootFramePresenter.Navigate(key);} public override void Cleanup() { base.Cleanup(); }
  • 28. わんくま同盟 東京勉強会 #119 Frame.Navigate(Type, object,NavigationTransitionInfo) • 遷移先ページを指すType,パラメータ、ナビゲーション時のア ニメの種類を引数に取る • WPFではアニメの種類は無しでTypeの代わりにUriを用いる – Pageのインスタンスを使うオーバーロードもあるが、ナビ ゲーションのキャンセル時等は、 作成したインスタンスが使われないままになるので個人的 には非推奨 • Navigating(キャンセル可),Navigatedイベントが呼 び出される
  • 29. わんくま同盟 東京勉強会 #119 Frame.Navigate(Type, object,NavigationTransitionInfo) • Navigatingイベントでキャンセルされた場合 NavigationStoppedイベントが発火 – WPFではキャンセル時には発生しないがStopLoading() を呼び出すことで手動で発生させることができる – ナビゲーション中に別のナビゲーションを開始しても 発生するらしいが詳しい条件は不明 • Navigatedイベント中に例外がが発生すると NavigationFailedイベントが発火 – WPFだとIOExceptionじゃないといけないので注意
  • 30. わんくま同盟 東京勉強会 #119 RootFramePresenter.cs public class RootFramePresenter: FramePresenterBase<Frame,EmptyViewModel> { public RootFramePresenter() { AddChild(typeof(Page1), new Page1Presenter()); AddChild(typeof(Page2), new Page2Presenter()); } public override void Initialize() { base.Initialize(); this.IsNavigationStackEnabled = true; } public override void Cleanup() { base.Cleanup(); } } 最初に、Presenterのインスタンスを作成 Viewの型とセットで登録
  • 31. わんくま同盟 東京勉強会 #119 Presenterの作成タイミング • Presenter,とViewModelは最初にまとめて作成して おく。 • Viewのインスタンスが作られるタイミングで Presenterを作ろうとしたこともあるのだが、 気が付いたらFrameのもつ履歴やインスタンス キャッシュの機能をPresenterに再実装しようとして しまったり、 Viewの型に応じたPresenterを作成するしくみが必 要になったりしたため、 これなら最初に作成しておいた方が楽だと判断した
  • 32. わんくま同盟 東京勉強会 #119 FramePresenterBase public abstract class FramePresenterBase<TView, TViewModel> : ParentPresenterBaseCore where TView : Windows.UI.Xaml.Controls.Frame, new() where TViewModel : ViewModelBase, new() { public FramePresenterBase() { ViewModel = new TViewModel(); } public bool IsNavigationStackEnabled { get { return View.IsNavigationStackEnabled; } set { View.IsNavigationStackEnabled = value; } } public TView View { get { return this.ViewBase as TView; } set { this.ViewBase = value; } } public TViewModel ViewModel { get { return this.ViewModelBase as TViewModel; } set { this.ViewModelBase = value; } } ナビゲーション履歴を使うかどうか Viewの同名プロパティにアクセス 非ジェネリック基底クラス(Core)の プロパティをキャスト
  • 33. わんくま同盟 東京勉強会 #119 FramePresenterBase public override void Initialize() { base.Initialize(); IsNavigationStackEnabled = false; View.Navigating += View_Navigating; View.Navigated += View_Navigated; View.NavigationFailed += View_NavigationFailed; View.NavigationStopped += View_NavigationStopped; } public override void Cleanup() { View.Navigating -= View_Navigating; View.Navigated -= View_Navigated; View.NavigationFailed -= View_NavigationFailed; View.NavigationStopped -= View_NavigationStopped; base.Cleanup(); }
  • 34. わんくま同盟 東京勉強会 #119 FramePresenterBase public override bool Navigate (Type key, object extraData = null, NavigationTransitionInfo infoOrverride =null) { return View.Navigate(key, extraData, infoOrverride); } public override void GoForward() { View.GoForward(); } public override void GoBack(NavigationTransitionInfo infoOrverride = null) { View.GoBack(infoOrverride); } private void View_Navigated(object sender, NavigationEventArgs e) { View_NavigatedCore(sender, e); } } 画面遷移関係の関数 Frameの同名関数を呼び出し 通常の遷移,進む,戻る 遷移時のアニメーションの種類 ParentPresenterBaseCore の関数を呼び出し
  • 35. わんくま同盟 東京勉強会 #119 ParentPresenterBaseCore.cs public abstract class ParentPresenterBaseCore : PresenterBaseCore { protected Dictionary<Type, ChildPagePresenterBaseCore> Children { get; private set; }; public void AddChild(Type key, ChildPagePresenterBaseCore child) { Children.Add(key, child); } public void RemoveChild(Type key) { Children.Remove(key); } 本体はDictionary 子Presenterの登録 子Presenterの解除
  • 36. わんくま同盟 東京勉強会 #119 ParentPresenterBaseCore.cs public ParentPresenterBaseCore() { Children = new Dictionary<Type, ChildPagePresenterBaseCore>(); } public ChildPagePresenterBaseCore CurrentChild { get; internal set; }; public override void Initialize() { base.Initialize(); } public override void Cleanup() { if (CurrentChild != null) { CurrentChild.Cleanup(); CurrentChild = null; } base.Cleanup(); } 表示中の子(Page)Presenter
  • 37. わんくま同盟 東京勉強会 #119 ParentPresenterBaseCore.cs protected void View_NavigationStopped(object sender, NavigationEventArgs e) { CurrentChild.InvokeOnNavigationStopped(e); } protected void View_Navigating(object sender, NavigatingCancelEventArgs e) { if (CurrentChild != null) { CurrentChild.InvokeOnNavigatingFrom(e); } } protected void View_NavigationFailed(object sender, NavigationFailedEventArgs e) { CurrentChild.InvokeOnNavigationFailed(e); } Internal 関数を通してイベントに対応するPagePresenterの関数を呼び出し
  • 38. わんくま同盟 東京勉強会 #119 ParentPresenterBaseCore.cs protected internal void View_NavigatedCore(object sender, NavigationEventArgs e) { if (e != null) { if (CurrentChild != null) { CurrentChild.InvokeOnNavigatedFrom(e); CurrentChild.Cleanup(); CurrentChild = null; } Type key = e.Content.GetType(); var childPresenter = Children[key]; var element = e.Content as FrameworkElement; childPresenter.Parent = this; childPresenter.ViewBase = element; CurrentChild = childPresenter; childPresenter.Initialize(); childPresenter.InvokeOnNavigatedTo(e); } } 表示中のページの終了処理 遷移先Pageに対応したPagePresenterを取得 遷移先Presenterの初期化処理 internal関数を介して PagePresenter.OnNavigatedFrom(e) の呼び出し PagePresenter.OnNavigatedTo(e) の呼び出し
  • 39. わんくま同盟 東京勉強会 #119 • UWPのPageには3つのオーバーライド用関数が存在 – OnNavigatedTo (Pageへ遷移してきたときに実行) – OnNavigatingFrom (Pageから遷移するときに実行,キャンセル可) – OnNavigatedFrom (Pageから遷移したときに実行) • MVPVMではできるだけViewにコードを書きたくないため同 名の関数をPagePresenterに用意して同じような感覚で使え るようにしている
  • 40. わんくま同盟 東京勉強会 #119 Page1Presenter public class Page1Presenter : PagePresenterBase<Views.Page1, Page1ViewModel> { public override void Initialize() { base.Initialize(); ViewModel.ClickEvent += Click; this.ViewModel.Text = "Page1"; } public void Click(object sender, RoutedEventArgs args) { this.Parent.Navigate(typeof(Page2), null); } public override void Cleanup() { ViewModel.ClickEvent -= Click; base.Cleanup(); } x:bindで呼び出すイベント
  • 41. わんくま同盟 東京勉強会 #119 PagePresenterBase public abstract class PagePresenterBase<TView, TViewModel> : ChildPagePresent erBaseCore where TView : Page, new() where TViewModel : ViewModelBase, new() { public PagePresenterBase() { ViewModel = new TViewModel(); } public TView View { get { return this.ViewBase as TView; } set { this.ViewBase = value; } } public TViewModel ViewModel { get { return this.ViewModelBase as TViewModel; } set { this.ViewModelBase = value; } }
  • 42. わんくま同盟 東京勉強会 #119 PagePresenterBase public override void Initialize() { base.Initialize(); View.Loaded += OnPageLoaded; } protected virtual void OnPageLoaded(object sender, RoutedEventArgs e) {} public override void Cleanup() { View.Loaded -= OnPageLoaded; base.Cleanup(); } }
  • 43. わんくま同盟 東京勉強会 #119 ChildPresenterBaseCore public abstract class ChildPagePresenterBaseCore : PresenterBaseCore { public ParentPresenterBaseCore Parent { get; set; } public override void Initialize() { base.Initialize(); } public override void Cleanup() { Parent = null; base.Cleanup(); } 親FramePresenter 参照外し
  • 44. わんくま同盟 東京勉強会 #119 ChildPresenterBaseCore internal void InvokeOnNavigatedTo(NavigationEventArgs e) { OnNavigatedTo(e);} internal void InvokeOnNavigatingFrom(NavigatingCancelEventArgs e) { OnNavigatingFrom(e); } internal void InvokeOnNavigatedFrom(NavigationEventArgs e) { OnNavigatedFrom(e); } internal void InvokeOnNavigationStopped(NavigationEventArgs e) {OnNavigationStopped (e); } internal void InvokeOnNavigationFailed(NavigationFailedEventArgs e) { OnNavigationFailed(e); }
  • 45. わんくま同盟 東京勉強会 #119 ChildPresenterBaseCore protected virtual void OnNavigatedTo(NavigationEventArgs e) { } protected virtual void OnNavigatingFrom(NavigatingCancelEventArgs e) {} protected virtual void OnNavigatedFrom(NavigationEventArgs e) {} protected virtual void OnNavigationFailed(NavigationFailedEventArgs e) {} protected virtual void OnNavigationStopped(NavigationEventArgs e) {} }
  • 46. わんくま同盟 東京勉強会 #119 PresenterBaseCore public abstract class PresenterBaseCore { protected TaskScheduler UIThreadTaskScheduler { get; private set; } public bool IsEnable { get; private set; } internal FrameworkElement ViewBase { get; set; } internal ViewModelBase ViewModelBase { get; set; } public PresenterBaseCore() { this.IsEnable = false; } Presenterが有効かどうか Initialize()でtrue CleanUp()でfalse View,ViewModel 非ジェネリック基底クラス用
  • 47. わんくま同盟 東京勉強会 #119 PresenterBaseCore public virtual void Initialize() { this.UIThreadTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); ViewBase.DataContext = ViewModelBase; IsEnable = true; } public virtual void Cleanup() { if (ViewBase != null) { ViewBase.DataContext = null; ViewBase = null; } IsEnable = false; } ViewとViewModelを接続 接続解除 Viewの参照外し 破壊ではなく解放 管理をViewフレームワークに 返すイメージ
  • 48. わんくま同盟 東京勉強会 #119 まとめ • UWPでは画面遷移アニメーションがあるので ContentControlではなくてFrameを使う • Presenter,ViewModelは最初にまとめて作る • View(Page)の作成はできる限りフレームワー クに任せ、 使用後は参照を解除し 管理権をフレームワークに返すことでViewの 機能を最大限生かす