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

怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション土岐 孝平
 
.NET 7期待の新機能
.NET 7期待の新機能.NET 7期待の新機能
.NET 7期待の新機能TomomitsuKusaba
 
Unity開発で使える設計の話+Zenjectの紹介
Unity開発で使える設計の話+Zenjectの紹介Unity開発で使える設計の話+Zenjectの紹介
Unity開発で使える設計の話+Zenjectの紹介torisoup
 
イマドキのExcelスクショの撮り方
イマドキのExcelスクショの撮り方イマドキのExcelスクショの撮り方
イマドキのExcelスクショの撮り方Yoshitaka Kawashima
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Kohei Tokunaga
 
UnityでUI開発を高速化した件
UnityでUI開発を高速化した件UnityでUI開発を高速化した件
UnityでUI開発を高速化した件Grenge, Inc.
 
【Unite Tokyo 2019】Understanding C# Struct All Things
【Unite Tokyo 2019】Understanding C# Struct All Things【Unite Tokyo 2019】Understanding C# Struct All Things
【Unite Tokyo 2019】Understanding C# Struct All ThingsUnityTechnologiesJapan002
 
こわくない Git
こわくない Gitこわくない Git
こわくない GitKota Saito
 
コンテナとimmutableとわたし。あとセキュリティ。(Kubernetes Novice Tokyo #15 発表資料)
コンテナとimmutableとわたし。あとセキュリティ。(Kubernetes Novice Tokyo #15 発表資料)コンテナとimmutableとわたし。あとセキュリティ。(Kubernetes Novice Tokyo #15 発表資料)
コンテナとimmutableとわたし。あとセキュリティ。(Kubernetes Novice Tokyo #15 発表資料)NTT DATA Technology & Innovation
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」Takuto Wada
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)Yoshitaka Kawashima
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーyoku0825
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するYoshifumi Kawai
 
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)NTT DATA Technology & Innovation
 
【BS11】毎年訪れる .NET のメジャーバージョンアップに備えるために取り組めること
【BS11】毎年訪れる .NET のメジャーバージョンアップに備えるために取り組めること 【BS11】毎年訪れる .NET のメジャーバージョンアップに備えるために取り組めること
【BS11】毎年訪れる .NET のメジャーバージョンアップに備えるために取り組めること 日本マイクロソフト株式会社
 
ドメイン駆動設計 モデリング_実装入門勉強会_2020.3.8
ドメイン駆動設計 モデリング_実装入門勉強会_2020.3.8ドメイン駆動設計 モデリング_実装入門勉強会_2020.3.8
ドメイン駆動設計 モデリング_実装入門勉強会_2020.3.8Koichiro Matsuoka
 
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
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Taku Miyakawa
 

What's hot (20)

怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション
 
.NET 7期待の新機能
.NET 7期待の新機能.NET 7期待の新機能
.NET 7期待の新機能
 
Unity開発で使える設計の話+Zenjectの紹介
Unity開発で使える設計の話+Zenjectの紹介Unity開発で使える設計の話+Zenjectの紹介
Unity開発で使える設計の話+Zenjectの紹介
 
イマドキのExcelスクショの撮り方
イマドキのExcelスクショの撮り方イマドキのExcelスクショの撮り方
イマドキのExcelスクショの撮り方
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
UnityでUI開発を高速化した件
UnityでUI開発を高速化した件UnityでUI開発を高速化した件
UnityでUI開発を高速化した件
 
【Unite Tokyo 2019】Understanding C# Struct All Things
【Unite Tokyo 2019】Understanding C# Struct All Things【Unite Tokyo 2019】Understanding C# Struct All Things
【Unite Tokyo 2019】Understanding C# Struct All Things
 
こわくない Git
こわくない Gitこわくない Git
こわくない Git
 
コンテナとimmutableとわたし。あとセキュリティ。(Kubernetes Novice Tokyo #15 発表資料)
コンテナとimmutableとわたし。あとセキュリティ。(Kubernetes Novice Tokyo #15 発表資料)コンテナとimmutableとわたし。あとセキュリティ。(Kubernetes Novice Tokyo #15 発表資料)
コンテナとimmutableとわたし。あとセキュリティ。(Kubernetes Novice Tokyo #15 発表資料)
 
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
 
Keycloakの最近のトピック
Keycloakの最近のトピックKeycloakの最近のトピック
Keycloakの最近のトピック
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
 
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
オススメのJavaログ管理手法 ~コンテナ編~(Open Source Conference 2022 Online/Spring 発表資料)
 
【BS11】毎年訪れる .NET のメジャーバージョンアップに備えるために取り組めること
【BS11】毎年訪れる .NET のメジャーバージョンアップに備えるために取り組めること 【BS11】毎年訪れる .NET のメジャーバージョンアップに備えるために取り組めること
【BS11】毎年訪れる .NET のメジャーバージョンアップに備えるために取り組めること
 
ドメイン駆動設計 モデリング_実装入門勉強会_2020.3.8
ドメイン駆動設計 モデリング_実装入門勉強会_2020.3.8ドメイン駆動設計 モデリング_実装入門勉強会_2020.3.8
ドメイン駆動設計 モデリング_実装入門勉強会_2020.3.8
 
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 との闘い〜
 
MVVM入門
MVVM入門MVVM入門
MVVM入門
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方
 

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
 
C#や.NET Frameworkがやっていること
C#や.NET FrameworkがやっていることC#や.NET Frameworkがやっていること
C#や.NET Frameworkがやっていること信之 岩永
 
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 アプリケーション開発
 
C#や.NET Frameworkがやっていること
C#や.NET FrameworkがやっていることC#や.NET Frameworkがやっていること
C#や.NET Frameworkがやっていること
 
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編〜
 
Mvpvm pattern
Mvpvm patternMvpvm pattern
Mvpvm pattern
 
作られては消えていく泡のように儚いクラスタの運用話
作られては消えていく泡のように儚いクラスタの運用話作られては消えていく泡のように儚いクラスタの運用話
作られては消えていく泡のように儚いクラスタの運用話
 
今流行りのウェブアプリ開発環境Yeoman
今流行りのウェブアプリ開発環境Yeoman今流行りのウェブアプリ開発環境Yeoman
今流行りのウェブアプリ開発環境Yeoman
 
Mojoliciousでつくる! Webアプリ入門
Mojoliciousでつくる! Webアプリ入門Mojoliciousでつくる! Webアプリ入門
Mojoliciousでつくる! Webアプリ入門
 

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の 機能を最大限生かす