SlideShare une entreprise Scribd logo
1  sur  70
わんくま同盟 東京勉強会 #90
きっと怖くないMVVM&MVPVM
暁 紫電
@akatukisiden
わんくま同盟 東京勉強会 #90
自己紹介
• HN:暁 紫電
• Twitter: @akatukisiden
• 本名:伊藤 伸男
• フリーランス プログラマー
• 使用言語
–C++
–C#
–C++/CLI
わんくま同盟 東京勉強会 #90
アジェンダ
• 目的
• MVVMとは
• ViewとViewModelの分離
• 細かいことは程々にして
実装してみた
• MVVMでの画面遷移
• MVVMまとめ
• MVPVMとは
• とりあえず実装してみた
• MVPVMでの画面遷移
• MVPVMまとめ
• まとめ
わんくま同盟 東京勉強会 #90
このセッションの目的
• 細かいことは置いておいて、
とりあえずMVVM,MVPVMっぽい形で
プログラムを書けるようにする。
• MVVMでのナビゲーション手法について理解する
• MVPVMでのナビゲーションについて理解する
• 疎結合、密結合、コードビハインドなどの用語を
できる限り使わずに説明する
わんくま同盟 東京勉強会 #90
MVVM(Model-View-ViewModel)とは
• 最近流行りのUIアーキテクチャパターン
• いくつかの理由によりXAML系フレームワーク
(WPF,Silverlight,WinRT)では必須と言われている
• UI(View)とビジネスロジック(Model)のあいだにViewModelを
置くことで二つを分離する。
• View・ViewModel間のやり取りはデータバインドを用いる
わんくま同盟 東京勉強会 #90
View
• ユーザーインターフェース
• UIへの出力とUIからの入力を担当する。
• FrameworkElementの派生クラス
• XAMLの記述+対応する(partial )class
• Viewの必要な情報を保持公開
• Viewからの入力やコマンドを処理し
Modelを呼び出す
ViewModel Model
• ビジネスロジック
• プログラムの中核となる処理
• ViewとViewModel以外の部分
わんくま同盟 東京勉強会 #90
ViewとViewModelの分離(疎結合と密結合)
• MVVMの説明などでよく使われる用語、
疎結合と密結合
• ViewとViewModelを密結合にならないようにし、
疎結合に保つと良いらしい
• 「疎結合に保つ」「密結合になってしまっている」という記述
はよく見るが具体的にどのような状況を疎結合・密結合と
言うのか書かれていることはあまりない
• もしかしたら一般的な用語で説明する必要もないのかもし
れないが、
少なくとも自分にとってMVVM/MVPVMの文脈でしか聞か
ない言葉
わんくま同盟 東京勉強会 #90
さまざまな資料の記述を総合すると
• View/ViewModelで互いのインスタンスや
型名を直接扱うと密結合
• データバインドを使えば疎結合
わんくま同盟 東京勉強会 #90
具体的に何が許されて何が許されないのか
• 直接触れると密結合
– ViewはViewModelが特定の型であることに依存してはいけない
– ViewでViewModelのインスタンスを扱ってはいけない
– ViewModelはViewが特定の型であることに依存してはいけない
– ViewModelでViewのインスタンスを扱ってはいけない
• データバインドは疎結合
– ViewはViewModelがINotifyPropertyChangedを
実装していることに依存してよい。
– ViewはViewModelが特定の名前のプロパティを
持つことに依存してよい
わんくま同盟 東京勉強会 #90
厳密にいうとこれもだめかもしれない
<Window x:Class="MVVM1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300"
>
<Window.DataContext >
<MainViewModel />
</Window.DataContext>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
View内でViewModelの型を
扱っている
わんくま同盟 東京勉強会 #90
細かいことは置いておいて
• MainWindowのイベントハンドラに全部の処理を書いた状態
から
少しずつ修正してMVVMの形にしてみようと思います。
• テキストボックス、ラベル、ボタンを配置し、ボタンを押すとテ
キストボックスに入力した文字列を加工してラベルに出力する
アプリを作る
※小さすぎてMVVMにする意味がないとか言わないでください
意味がなくてもとりあえず始めることが大事です。
わんくま同盟 東京勉強会 #90
STEP1:全部イベントハンドラ等に記述
わんくま同盟 東京勉強会 #90
MainWindow.xaml
<Window x:Class=“Step1.MainWindow”
// 略
Title="MainWindow" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions><RowDefinition /><RowDefinition /><RowDefinition />
</Grid.RowDefinitions>
<Button Content="Button" Grid.Row="0“ HorizontalAlignment="Center"
VerticalAlignment="Center" Width="120" Click="Button_Click"/>
<TextBox Name="textBox1" Grid.Row="1“ HorizontalAlignment="Center"
VerticalAlignment="Center“ Height="23" Width="120" />
<Border Grid.Row="2" BorderThickness="1" BorderBrush="Black"
HorizontalAlignment="Center" VerticalAlignment="Center" >
<Label Name="label1" HorizontalAlignment="Center" VerticalAlignment="Center"
Width="120"/>
</Border>
</Grid>
</Window>
わんくま同盟 東京勉強会 #90
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object
sender,RoutedEventArgs e)
{
// なんか複数の関数が絡んだ複雑な処理
string f1 = Func1(textBox1.Text);
string f2 = Func2(f1);
string f3 = Func3(f2);
label1.Content = f3;
}
//頭にBをつける
private string Func1(string input)
{ return “B” + input; }
//末尾にEをつける
private string Func2(string input)
{ return input+"E"; }
//順番をひっくり返す。
private string Func3(string input)
{
var rev = input.Reverse().ToArray();
string ret = new string(rev);
return ret;
}
}
わんくま同盟 東京勉強会 #90
STEP2:
MainWindow.xaml.csは
イベントハンドラだけにしたいので
処理内容を別クラスに移す
C++で云うところの
pImpl
わんくま同盟 東京勉強会 #90
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private MainWindowImpl impl = new MainWindowImpl();
private void Button_Click(object sender, RoutedEventArgs e)
{
label1.Content = impl.Logic(textBox1.Text);
}
}
処理を移すための別クラス
別クラスに移した処理の
呼び出し
わんくま同盟 東京勉強会 #90
MainWindowImpl.cs
public class MainWindowImpl
{
public string Logic(string input)
{
string f1 = Func1(input);
string f2 = Func2(f1);
string f3 = Func3(f2);
return f3;
}
private string Func1(string input)
{ return "B" + input;}
private string Func2(string input)
{ return input + "E";}
private string Func3(string input)
{
var rev = input.Reverse().ToArray();
string ret = new string(rev);
return ret;
}
}
わんくま同盟 東京勉強会 #90
STEP3:
入出力(引数/戻り値)が多く必要になると
記述が大変なので
データバインディングを使ってみる
わんくま同盟 東京勉強会 #90
MainWindow.xaml
<Window x:Class=“Step3.MainWindow"
//略
Title="MainWindow" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions><RowDefinition /><RowDefinition /><RowDefinition
/></Grid.RowDefinitions>
<Button Content="Button" Grid.Row="0" HorizontalAlignment="Center"
VerticalAlignment="Center" Width="120" Click="Button_Click"/>
<TextBox Name="textBox1" Grid.Row="1" HorizontalAlignment="Center"
VerticalAlignment="Center" Height="23" Width="120" Text="{Binding Input}“
<Border Grid.Row="2" BorderThickness="1" BorderBrush="Black"
HorizontalAlignment="Center" VerticalAlignment="Center" >
<Label Name=“label1” HorizontalAlignment=“Center”
VerticalAlignment=“Center” Width=“120“ Content="{Binding Output}" />
</Border>
</Grid>
</Window>
わんくま同盟 東京勉強会 #90
MainWindow.xaml.cs
public partial class MainWindow : Window
{
// コントロールからの入力、出力をデータバインドに変換
private MainWindowImpl impl = new MainWindowImpl();
public MainWindow()
{
InitializeComponent();
this.DataContext = impl;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
impl.Logic();
}
}
バインド対象の設定
入出力はバインドしたので
引数・戻り値なしになっている
わんくま同盟 東京勉強会 #90
MainWindowImpl.cs
public class MainWindowImpl:
BindingSourceBase
{
public string input_;
public string Input
{
get { return input_; }
set
{
if (input_ != value)
{
input_ = value;
OnPropertyChanged("Input");
}
}
}
public void Logic()
{
string f1 = Func1(Input);
string f2 = Func2(f1);
string f3 = Func3(f2);
Output = f3;
}
private string Func1(string input){略}
private string Func2(string input){略}
private string Func3(string input){略}
}
public string output_;
public string Output
{
get { return output_; }
set
{
if (output_ != value)
{
output_ = value;
OnPropertyChanged("Output");
}
}
}
わんくま同盟 東京勉強会 #90
BindingSourceBase
public class
BindingSourceBase:System.ComponentModel.INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyname)
{
if(PropertyChanged != null)
{
PropertyChanged(this,new
PropertyChangedEventArgs(propertyname));
}
}
}
わんくま同盟 東京勉強会 #90
STEP4:
引数・戻り値なしなら直接呼出したいので、
イベントハンドラをImplに移動する。
わんくま同盟 東京勉強会 #90
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private MainWindowImpl impl = new MainWindowImpl();
public MainWindow()
{
InitializeComponent();
// バインド対象の設定
this.DataContext = impl;
Button1.Click += impl.Button_Click;
}
}
Clickイベントにimplクラスの
関数を登録
わんくま同盟 東京勉強会 #90
MainWindow.xaml
<Window x:Class=“Step3.MainWindow"
//略
Title="MainWindow" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions><RowDefinition /><RowDefinition /><RowDefinition
/></Grid.RowDefinitions>
<Button Content=“Button” Grid.Row=“0” HorizontalAlignment=“Center”
VerticalAlignment=“Center” Width=“120” />
<TextBox Name="textBox1" Grid.Row="1" HorizontalAlignment="Center"
VerticalAlignment="Center" Height="23" Width="120" Text="{Binding Input}" />
<Border Grid.Row="2" BorderThickness="1" BorderBrush="Black"
HorizontalAlignment="Center" VerticalAlignment="Center" >
<Label Name="label1" HorizontalAlignment="Center"
VerticalAlignment="Center" Width="120“
Content="{Binding Output}" />
</Border>
</Grid>
</Window>
イベントの登録部分を削除
わんくま同盟 東京勉強会 #90
MainWindowImpl.cs
public class MainWindowImpl:BindingSourceBase
{
public void Button_Click(object sender,RoutedEventArgs e)
{
this.Logic();
}
public string input_;
public string Input
{ get { return input_; } set { 略 } }
public string output_;
public string Output
{ get { return output_; } set { 略 } }
public void Logic()
{
string f1 = Func1(Input);
string f2 = Func2(f1);
string f3 = Func3(f2);
Output = f3;
}
private string Func1(string input){略}
private string Func2(string input){略}
private string Func3(string input){略}
}
Button.Clickイベントから呼
び出される
ビジネスロジック
わんくま同盟 東京勉強会 #90
STEP5:
Implクラスにデータバインドと
ビジネスロジック両方があるとじゃまなので
ビジネスロジックをさらに別クラスへ移動する。
わんくま同盟 東京勉強会 #90
MainWindowImpl.cs
public class MainWindowImpl:BindingSourceBase
{
private BusinessLogic BL = new BusinessLogic();
public void Button_Click(object sender, RoutedEventArgs e)
{
Output = BL.Logic(Input);
}
public string input_;
public string Input { get { return input_; } set {略} }
public string output_;
public string Output { get { return output_; } set {略}}
}
わんくま同盟 東京勉強会 #90
BusinessLogic.cs
public class BusinessLogic
{
public string Logic(string Input)
{
string f1 = Func1(Input);
string f2 = Func2(f1);
string f3 = Func3(f2);
return f3;
}
private string Func1(string input){略}
private string Func2(string input){略}
private string Func3(string input){略}
}
関数が増えてクラスが肥大化するので
あれば
外部とのインターフェースになる関数
だけ残して内部実装はさらに別のクラ
スに移した方がいいかも
わんくま同盟 東京勉強会 #90
STEP6:XAML環境ではロジックも
コマンドとしてバインドできるので
イベントハンドラからコマンドバインディングに変更する
※WinFormsでもFormクラス等にActionプロパティを作り
On○○メソッド等でアクションを発火するようにすれば可能
わんくま同盟 東京勉強会 #90
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private MainWindowImpl impl = new MainWindowImpl();
public MainWindow()
{
InitializeComponent();
// バインド対象の設定
this.DataContext = impl;
// Button1.Click += impl.Button_Click;
}
}
削除
わんくま同盟 東京勉強会 #90
MainWindow.xaml
<Window x:Class=“Step6.MainWindow"
//略
Title="MainWindow" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions><RowDefinition /><RowDefinition /><RowDefinition
/></Grid.RowDefinitions>
<Button Content="Button" Grid.Row="0" HorizontalAlignment="Center"
VerticalAlignment="Center" Width="120" Command="{Binding MainCommand}“ />
<TextBox Name="textBox1" Grid.Row="1" HorizontalAlignment="Center"
VerticalAlignment="Center" Height="23" Width="120" Text="{Binding Input}" />
<Border Grid.Row="2" BorderThickness="1" BorderBrush="Black"
HorizontalAlignment="Center" VerticalAlignment="Center" >
<Label Name="label1" HorizontalAlignment="Center"
VerticalAlignment="Center" Width="120" Content="{Binding Output}" />
</Border>
</Grid>
</Window>
わんくま同盟 東京勉強会 #90
MainWindowImpl.cs
public class MainWindowImpl:BindingSourceBase
{
private BusinessLogic BL = new BusinessLogic();
public ICommand MainCommand{get;set;}
public MainWindowImpl()
{
MainCommand = new Microsoft.TeamFoundation.
MVVM.RelayCommand(() => { Output = BL.Logic(Input); });
}
public string input_;
public string Input { get { return input_; } set { 略} }
public string output_;
public string Output { get { return output_; } set{ 略 } }
ロジックのバインド用のコマン
ド
コマンドの中身を作成。
わんくま同盟 東京勉強会 #90
(LastSTEP:)クラス名の変更
• MainWindowImpl → MainViewModel
• BindingSourceBase → ViewModelBase
• BussinesLogic → MainModel
わんくま同盟 東京勉強会 #90
とりあえずMVVM完成
※但し画面遷移無し
わんくま同盟 東京勉強会 #90
画面遷移とは
• 表示されているデータでは無く、
コントロール自体を変更する
• 主にユーザーコントロールやパネルの切り替え
わんくま同盟 東京勉強会 #90
MVVMでの画面遷移手法
• ViewModelの型に応じてコントロールを切り替える方法。
– データテンプレート
• 表示するデータの型に応じて表示方法を変える機能
• 指定した型のデータの表示方法を定義する。
• コントロールの切り替えロジックを呼び出し、
新しいコントロールに新しいViewModelを関連付ける手法。
– ViewModelからViewを呼び出す。
• 逆方向バインディングコマンド
• Behavior
• TriggerAndAction
わんくま同盟 東京勉強会 #90
とりあえずサンプル作る
• ボタンを押すことで背景色の異なる(赤・青)二つのユーザーコ
ントロールを切り替える。
• Modelは省略
• 切り替えロジック呼び出しのサンプルは
逆方向バインディングコマンドで実装
わんくま同盟 東京勉強会 #90
共通部分 (赤)
<UserControl x:Class="NavigationCommon.UCRed“
(略)
d:DesignHeight="150" d:DesignWidth="150"
Background="Red">
<Grid>
<TextBlock HorizontalAlignment="Center“
VerticalAlignment="Center" Text="{Binding Text}" />
</Grid>
</UserControl>
public partial class UCRed : UserControl
{
public UCRed()
{ InitializeComponent(); }
}
public class
RedViewModel:ViewModelBase
{
private string text = "赤";
public string Text
{
set{
if (text != value)
{
text = value;
OnPropertyChanged("Text");
}
}
get{ return text; }
}
}
View ViewModel
わんくま同盟 東京勉強会 #90
共通部分 (青)
<UserControl x:Class="NavigationCommon.UCBlue“
(略)
d:DesignHeight="150" d:DesignWidth="150"
Background=“Blue">
<Grid>
<TextBlock HorizontalAlignment="Center“
VerticalAlignment="Center" Text="{Binding Text}" />
</Grid>
</UserControl>
public partial class UCBlue : UserControl
{
public UCBlue()
{ InitializeComponent(); }
}
public class
BlueViewModel:ViewModelBase
{
private string text = “青";
public string Text
{
set{
if (text != value)
{
text = value;
OnPropertyChanged("Text");
}
}
get{ return text; }
}
}
View ViewModel
わんくま同盟 東京勉強会 #90
データテンプレート
わんくま同盟 東京勉強会 #90
View
<Window x:Class="MainWindow"
(略)
Title="MainWindow" >
<Window.Resources>
<DataTemplate DataType="{x:Type
navi:RedViewModel}">
<navi:UCRed x:Name="RedUC" />
</DataTemplate>
<DataTemplate DataType=“{x:Type
navi:BlueViewModel}”>
<navi:UCBlue x:Name="BlueUC" />
</DataTemplate>
</Window.Resources>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Name="ParentGrid">
<ContentControl Name="ContentControl“
Content="{Binding ChildViewModel}" />
</Grid>
<Grid Grid.Column="1" >
<Button Content="Button“ Command="{Binding
NavigationCommand}" HorizontalAlignment="Center"
VerticalAlignment="Center" Width="75" Height="25"
/>
</Grid>
</Grid>
</Window>
ViewModelの型を直接扱ってい
る。
public partial class MainWindow : Window
{ public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
} }
ViewModelを表示しようとしている
わんくま同盟 東京勉強会 #90
ViewModel
public class MainViewModel:ViewModelBase
{
ViewModelBase childVM;
public ViewModelBase ChildViewModel
{ get { return childVM; } set {略} }
ICommand nCommand;
public ICommand NavigationCommand
{ get { return nCommand; } set { 略 } }
public MainViewModel(){ NavigateRed(); }
private void NavigateRed()
{
ChildViewModel = new RedViewModel();
NavigationCommand = new
Microsoft.TeamFoundation.
MVVM.RelayCommand( (p) =>{NavigateBlue();});
}
private void NavigateBlue()
{
ChildViewModel = new BlueViewModel();
NavigationCommand = new
Microsoft.TeamFoundation.
MVVM.RelayCommand( (p) =>{ NavigateRed();} );
}
}
もう一度ボタンを押したら
元の色に戻るように
コマンドをリセット
データテンプレートで表示する子ViewModel
表示するViewModelを変更
わんくま同盟 東京勉強会 #90
逆方向バインディングコマンド
わんくま同盟 東京勉強会 #90
Command UserControl
public class DependencyCommandControl :
Control
{
public DependencyCommandControl():base(){ }
static DependencyCommandControl()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(DependencyCommandControl),
new FrameworkPropertyMetadata(
typeof(DependencyCommandControl))
);
}
public static readonly
DependencyProperty CommandProperty =
DependencyProperty.Register(
"Command“,
typeof(ICommand),
typeof(DependencyCommandControl)
);
public ICommand Command
{
set { SetValue(CommandProperty, value); }
get { return
(ICommand)GetValue(CommandProperty); }
}
}
XAML上で扱うには依存関係プロパティである必要があるので
依存関係プロパティ化したICommandを持つ
カスタムコントロールを作る
わんくま同盟 東京勉強会 #90
View
<Window x:Class="MainWindow"
(略)
Title="MainWindow">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/><ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Name="ParentGrid“ >
</Grid>
<Grid Grid.Column="1" >
<Button Content="Button" Command="{Binding NavigationCommand}"
HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" Height="25"/>
<DependencyCommandControl x:Name="CommandHolder“
Command="{Binding
Mode=OneWayToSource,Path='SouceToTargetCommand' }" />
</Grid>
</Grid>
</Window>
ボタンクリック時に呼ばれる順方向コマンド
ViewModelから呼ばれる逆方向コマンド逆方向バインディング
カスタムコントロール
わんくま同盟 東京勉強会 #90
ViewModel
public class MainViewModel:ViewModelBase
{
ViewModelBase childVM;
public ViewModelBase ChildViewModel{get;set}
ICommand navigationcommand;
public ICommand NavigationCommand
{ get { return navigationcommand; }set {略} }
ICommand sttCommand;
public ICommand SouceToTargetCommand
{ get { return sttCommand; } set{ 略 } }
public MainViewModel() { NavigateToRed(); }
ボタンクリックから呼び出される
(順方向)コマンド
ViewModelからViewを呼び出すための
逆方向バインディングをするコマンド
わんくま同盟 東京勉強会 #90
ViewModel
private void NavigateToRed()
{
ChildViewModel = new RedViewModel();
if (SouceToTargetCommand != null)
SouceToTargetCommand.
Execute(ChildViewModel);
NavigationCommand = new
Microsoft.TeamFoundation.
MVVM.RelayCommand(
(p) => { NavigateToBlue(); });
}
private void NavigateToBlue()
{
ChildViewModel = new BlueViewModel();
if (SouceToTargetCommand != null)
SouceToTargetCommand.
Execute(ChildViewModel);
NavigationCommand = new
Microsoft.TeamFoundation.
MVVM.RelayCommand(
(p) => { NavigateToRed(); } );
}
}
子コントロール用の新しいViewModelを作成、
コマンドでViewに送る
もう一度ボタンを押したら元に戻すために順方向コマンドをリセット
わんくま同盟 東京勉強会 #90
View
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var mainVM = new MainViewModel();
ChangeToRed(mainVM.ChildViewModel);
this.DataContext = mainVM;
}
private void ChangeToBlue(object vm)
{
ParentGrid.Children.Clear();
var newPanel = new UCBlue();
newPanel.DataContext = vm;
ParentGrid.Children.Add(newPanel);
CommandHolder.Command = new
Microsoft.TeamFoundation.
MVVM.RelayCommand(ChangeToRed);
}
private void ChangeToRed(object vm)
{
ParentGrid.Children.Clear();
var newPanel = new UCRed();
newPanel.DataContext = vm;
ParentGrid.Children.Add(newPanel);
CommandHolder.Command = new
Microsoft.TeamFoundation.
MVVM.RelayCommand(ChangeToBlue);
}
}
ViewModelから受け取った新ViewModelを
Viewで直接扱っている
新しいViewを作成
新しいViewModelを受け取る
わんくま同盟 東京勉強会 #90
MVVMまとめ
• View と ViewModel が互いを扱わなくていいはずのMVVMで
画面遷移を行おうとするとViewでViewModelを扱ってまう。
→ そもそもMVVMにはXAML環境に適応するための
最低限必要な機能しかなく、
画面遷移の存在を前提にしていないのでは?
• MVVMの利点(≒XAML環境への適応≒データバインディング)
を
生かしつつ画面遷移の存在を前提とするパターンが必要
→ MVPVM
わんくま同盟 東京勉強会 #90
MVPVM(Model-View-Presenter-ViewModel)とは
• MVPとMVVMを合体させたもの
• View,ViewModelの上にPresenterを置き、そこで、画面遷
移、
ナビゲーションを管理する。
• ViewModelに書かれていたロジックをPresenterに移動する
ことでViewModelにはバインド対象となるプロパティのみが
含まれるようにする。
• ViewModelにロジックが含まれなくなるので使いまわしが容
易になる。
わんくま同盟 東京勉強会 #90
View
• ユーザーインターフェース
• UIへの出力とUIからの入力を担当する。
• FrameworkElementの派生クラス
• XAMLの記述+対応する partial class
Model
• ビジネスロジック/プログラムの中核となる処理
• ViewとViewModel(とPresenter)以外の部分
わんくま同盟 東京勉強会 #90
• Viewの必要な情報を保持公開
• Viewから受け取った入力やコマンドををPresenterに渡す
(MVPVM)
• Viewからの入力やコマンドを処理しModelを呼び出す
(MVVM)
ViewModel
わんくま同盟 東京勉強会 #90
• View,ViewModelを保持、その接続を管理する
• ViewModelから受け取った入力やコマンドを処理しModel
を呼び出す。
• ViewModelのプロパティを通してViewを変更する。
• ナビゲーション・画面遷移の管理
(子View,子ViewModelの作成、接続)
Presenter
わんくま同盟 東京勉強会 #90
とりあえず組んでみた
• MVVMの時と同じようにテキストボックスから入力、加工して
ラベルに出力するアプリ
※画面遷移がないのに
MVPVMで組む必要性がないとか言わないように
わんくま同盟 東京勉強会 #90
View
<Window x:Class="MVPVM1.MainWindow"
(略)
Title="MainWindow" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition />
</Grid.RowDefinitions>
<Button Content="Button" Grid.Row="0" HorizontalAlignment="Center"
VerticalAlignment="Center" Width="120" Command="{Binding MainCommand}"/>
<TextBox Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"
Height="23" Width="120" Text="{Binding Input}" />
<Border Grid.Row="2" BorderThickness="1" BorderBrush="Black"
HorizontalAlignment="Center" VerticalAlignment="Center" >
<Label HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding
Output}" Width="120" />
</Border>
</Grid>
</Window>
public partial class MainWindow :
Window
{
public MainWindow()
{ InitializeComponent(); }
}
わんくま同盟 東京勉強会 #90
ViewModel
class MainViewModel:ViewModelBase
{
public MainViewModel()
{ }
public override void Cleanup()
{
MainCommand = null;
base.Cleanup();
}
private ICommand _mainCommand;
public ICommand MainCommand { set{略} get{ return _mainCommand;} }
private string _input;
public string Input { set{略} get{ return _input;} }
private string _output;
public string Output { set{略} get{ return _output;} }
}
Commandの中身の定義
Modelの関数の呼び出しコードがなくなっている
わんくま同盟 東京勉強会 #90
PresenterBase
class PresenterBase<TView,TViewModel>
where TView:System.Windows.FrameworkElement
where TViewModel:ViewModelBase
{
public PresenterBase(TView view, TViewModel viewmodel)
{
View = view;
ViewModel = viewmodel;
}
public TView View { set; get; }
public TViewModel ViewModel { set; get; }
public virtual void Initialize()
{ View.DataContext = ViewModel; }
DataContextの設定
View-ViewModelの接続
public virtual void CleanUp()
{
if (ViewModel != null)
{
ViewModel.Cleanup();
ViewModel = null;
}
if (View != null)
{
View.DataContext = null;
View = null;
}
}
}
View-ViewModel間の接続解除
View,ViewModelの参照解除
引数の
View,ViewModel
メンバに代入
わんくま同盟 東京勉強会 #90
Presenter
class MainPresenter:PresenterBase<MainWindow,MainViewModel>
{
MainModel model = new MainModel();
public MainPresenter(MainWindow view,MainViewModel viewmodel)
:base(view,viewmodel){ }
public override void Initialize()
{
ViewModel.MainCommand =
new Microsoft.TeamFoundation.MVVM.RelayCommand(
()=>{ViewModel.Output = model.Logic(ViewModel.Input); });
base.Initialize();
}
public override void CleanUp() { base.CleanUp(); }
}
ViewModelのコマンドの中身を作成
コンストラクタがViewとViewModelの
インスタンスを受け取る
Modelの関数の呼び出し。
Viewの操作はデータバインドした
ViewModelのプロパティを通して行う。
わんくま同盟 東京勉強会 #90
App.xaml
<Application x:Class="MVPVM1.App"
(略)
Startup="Application_Startup“
Exit="Application_Exit“
>
<Application.Resources>
</Application.Resources>
</Application>
public partial class App : Application
{
MainPresenter presenter ;
private void Application_Startup(object sender,
StartupEventArgs e)
{
var view =new MainWindow();
var viewmodel = new MainViewModel();
presenter = new MainPresenter(view, viewmodel);
presenter.Initialize();
view.Show();
}
private void Application_Exit(object sender, ExitEventArgs e)
{ presenter.CleanUp(); }
}
App.xaml.cs
StartupUri=“MainWindow.xaml” を削除
Startup,Exitを追加
アプリ起動時に、V,VM,Pを作成、
ウィンドウを表示
アプリ終了時にPresenterから全体をクリア
わんくま同盟 東京勉強会 #90
MVPVMでの画面遷移
• View,ViewModel両方を扱うことが出来る
Presenterに記述する
• MVVMではView,ViewModelの二か所に分割されていた
画面遷移ロジックをPresenter一つにまとめられるため
自然な形で実装することができる。
わんくま同盟 東京勉強会 #90
View
<Window x:Class="MVPVMNavigation.MainWindow"
(略)
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Name="ParentGrid" >
</Grid>
<Grid Grid.Column="1" >
<Button Content=“Button”
HorizontalAlignment=“Center” VerticalAlignment=“Center”
Command="{Binding NavigationCommand}" Width="75"
/>
</Grid>
</Grid>
</Window>
public partial class MainWindow
: Window
{
public MainWindow()
{ InitializeComponent(); }
}
Viewに画面遷移用のロ
ジックが存在しない
ボタンがクリックされた時に呼び出さ
れるコマンドのバインド
わんくま同盟 東京勉強会 #90
ViewModel
public class MainViewModel:ViewModelBase
{
ViewModelBase childVM;
public ViewModelBase ChildViewModel
{
get { return childVM; }
set {略}
}
ICommand navigationCommand;
public ICommand NavigationCommand
{
get { return navigationCommand; }
set {略}
}
public MainViewModel(){}
public override void Cleanup()
{
if (ChildViewModel != null)
{
NavigationCommand = null;
ChildViewModel.Cleanup();
ChildViewModel = null;
}
base.Cleanup();
}
}
子ViewModel
子Presenterからアクセスできるので
もしかしたら要らないかも
ボタンクリックで呼び出される
画面遷移用コマンド
各プロパティのクリア
わんくま同盟 東京勉強会 #90
子Presenter(赤,青)
public class RedPresenter
: PresenterBase<UCRed, RedViewModel>
{
public
RedPresenter(UCRed view, RedViewModel
viewmodel)
: base(view, viewmodel)
{ }
public override void Initialize()
{
base.Initialize();
}
public override void CleanUp()
{
base.CleanUp();
}
}
public class BluePresenter
:PresenterBase<UCBlue,BlueViewModel>
{
public
BluePresenter(UCBlue view, BlueViewModel
viewmodel)
: base(view, viewmodel)
{ }
public override void Initialize()
{
base.Initialize();
}
public override void CleanUp()
{
base.CleanUp();
}
}
わんくま同盟 東京勉強会 #90
MainPresenter(1)
public class MainPresenter: PresenterBase<MainWindow,MainViewModel>
{
public MainPresenter(MainWindow view,MainViewModel viewmodel)
:base(view,viewmodel){ }
public IPresenter ChildPresenter { get; set; }
public override void Initialize()
{
base.Initialize();
NavigateToRed();
}
public override void CleanUp()
{
ChildPresenter.CleanUp();
base.CleanUp();
}
子コントロール(赤・青)用の
Presenter
子Presenterの解放
わんくま同盟 東京勉強会 #90
MainPresenter(2)
private void NavigateToRed()
{
if (ChildPresenter != null)
{ ChildPresenter.CleanUp(); }
var v = new UCRed();
var vm = new RedViewModel();
var presenter = new RedPresenter(v, vm);
View.ParentGrid.Children.Clear();
View.ParentGrid.Children.Add(v);
ViewModel.ChildViewModel = vm;
ViewModel.NavigationCommand = new
Microsoft.TeamFoundation.MVVM.RelayCommand( (p) => {NavigateToBlue();} );
ChildPresenter = presenter;
ChildPresenter.Initialize();
);
新しいView,ViewModel,Presenter
の作成
次回ボタンクリック時に元の色に戻すた
めにコマンドをリセット
新しいViewをGridに追加
旧Presenterのクリア
わんくま同盟 東京勉強会 #90
MainPresenter(3)
private void NavigateToBlue()
{
if (ChildPresenter != null)
{ ChildPresenter.CleanUp(); }
var v = new UCBlue();
var vm = new BlueViewModel();
var presenter = new RedPresenter(v, vm);
View.ParentGrid.Children.Clear();
View.ParentGrid.Children.Add(v);
ViewModel.ChildViewModel = vm;
ViewModel.NavigationCommand = new
Microsoft.TeamFoundation.MVVM.RelayCommand( (p) => {NavigateToRed();} );
ChildPresenter = presenter;
ChildPresenter.Initialize();
);
新しいView,ViewModelPresenterの作
成
次回ボタンクリック時に元の色に戻すた
めにコマンドをリセット
新しいViewをGridに追加
旧Presenterのクリ
ア
わんくま同盟 東京勉強会 #90
MVPVMまとめ
• ViewModelはBinding対象になるプロパティのみを記述し
コマンドの中身はPresenterに記述する
• PresenterはView,ViewModelどちらも扱えるので
その二つに分けて記述していた画面遷移ロジックを
Presenter 1ヵ所に記述することができる。
→MVVMでは三ケ所すべてにロジックが含まれる可能性があったのが
MVPVMでは基本的にP,M の二か所に減る
• 何らかの事情(ActiveX,低スキル,etc…)によりViewがビジネスロジッ
クを持っている場合でもPresenterから直接呼べる
わんくま同盟 東京勉強会 #90
まとめ
• Viewとの入出力はバインディングで行い、
インターフェースになる関数だけ残して
実装は別クラスに移動することを繰り返せば
とりあえずMVVMの形にはなる
• PresenterでView,ViewModelを管理しViewModelのコマ
ンドの中身を
Presenterで作ればMVPVMになる。
• 一画面で完結するアプリならMVVMでもいいが、
ナビゲーション(画面遷移)を含む場合はMVPVMの方がオ
ススメ
わんくま同盟 東京勉強会 #90
今回扱わなかったこと
• XAML環境でデータバインディングが必須な理由
• コレクションコントロール
• エラー処理
• CommandのCanExecute()
• 非同期処理(Model)
• Viewに合わせた、ViewModel,Presenterそれぞれツリーの構
造

Contenu connexe

Tendances

磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!Ra Zon
 
Media Art II 2013 第4回:openFrameworks アニメーションを極める 動きを生みだす様々なアルゴリズム
Media Art II 2013  第4回:openFrameworks アニメーションを極める 動きを生みだす様々なアルゴリズムMedia Art II 2013  第4回:openFrameworks アニメーションを極める 動きを生みだす様々なアルゴリズム
Media Art II 2013 第4回:openFrameworks アニメーションを極める 動きを生みだす様々なアルゴリズムAtsushi Tadokoro
 
Node-v0.12の新機能について
Node-v0.12の新機能についてNode-v0.12の新機能について
Node-v0.12の新機能についてshigeki_ohtsu
 
Visual C++で使えるC++11
Visual C++で使えるC++11Visual C++で使えるC++11
Visual C++で使えるC++11nekko1119
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するYoshifumi Kawai
 
NDC14 - Rx와 Functional Reactive Programming으로 고성능 서버 만들기
NDC14 - Rx와 Functional Reactive Programming으로 고성능 서버 만들기NDC14 - Rx와 Functional Reactive Programming으로 고성능 서버 만들기
NDC14 - Rx와 Functional Reactive Programming으로 고성능 서버 만들기Jong Wook Kim
 
Deep Dive async/await in Unity with UniTask(UniRx.Async)
Deep Dive async/await in Unity with UniTask(UniRx.Async)Deep Dive async/await in Unity with UniTask(UniRx.Async)
Deep Dive async/await in Unity with UniTask(UniRx.Async)Yoshifumi Kawai
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪Takuto Wada
 
ゲームエンジンとMVC
ゲームエンジンとMVCゲームエンジンとMVC
ゲームエンジンとMVCAimingStudy
 
会社でClojure使ってみて分かったこと
会社でClojure使ってみて分かったこと会社でClojure使ってみて分かったこと
会社でClojure使ってみて分かったことRecruit Technologies
 
Windowsフォームで大丈夫か?一番良いのを頼む。
Windowsフォームで大丈夫か?一番良いのを頼む。Windowsフォームで大丈夫か?一番良いのを頼む。
Windowsフォームで大丈夫か?一番良いのを頼む。Yuya Yamaki
 
SPAのルーティングの話
SPAのルーティングの話SPAのルーティングの話
SPAのルーティングの話ushiboy
 
Unity開発で使える設計の話+Zenjectの紹介
Unity開発で使える設計の話+Zenjectの紹介Unity開発で使える設計の話+Zenjectの紹介
Unity開発で使える設計の話+Zenjectの紹介torisoup
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean ArchitectureAtsushi Nakamura
 
インタフェース完全に理解した
インタフェース完全に理解したインタフェース完全に理解した
インタフェース完全に理解したtorisoup
 
HoloLens2とMeta QuestではじめるWebXR
HoloLens2とMeta QuestではじめるWebXRHoloLens2とMeta QuestではじめるWebXR
HoloLens2とMeta QuestではじめるWebXRTakashi Yoshinaga
 
シェーダだけで世界を創る!three.jsによるレイマーチング
シェーダだけで世界を創る!three.jsによるレイマーチングシェーダだけで世界を創る!three.jsによるレイマーチング
シェーダだけで世界を創る!three.jsによるレイマーチングSho Hosoda
 
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
 

Tendances (20)

Map
MapMap
Map
 
磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!
 
Media Art II 2013 第4回:openFrameworks アニメーションを極める 動きを生みだす様々なアルゴリズム
Media Art II 2013  第4回:openFrameworks アニメーションを極める 動きを生みだす様々なアルゴリズムMedia Art II 2013  第4回:openFrameworks アニメーションを極める 動きを生みだす様々なアルゴリズム
Media Art II 2013 第4回:openFrameworks アニメーションを極める 動きを生みだす様々なアルゴリズム
 
Node-v0.12の新機能について
Node-v0.12の新機能についてNode-v0.12の新機能について
Node-v0.12の新機能について
 
Visual C++で使えるC++11
Visual C++で使えるC++11Visual C++で使えるC++11
Visual C++で使えるC++11
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
 
NDC14 - Rx와 Functional Reactive Programming으로 고성능 서버 만들기
NDC14 - Rx와 Functional Reactive Programming으로 고성능 서버 만들기NDC14 - Rx와 Functional Reactive Programming으로 고성능 서버 만들기
NDC14 - Rx와 Functional Reactive Programming으로 고성능 서버 만들기
 
Deep Dive async/await in Unity with UniTask(UniRx.Async)
Deep Dive async/await in Unity with UniTask(UniRx.Async)Deep Dive async/await in Unity with UniTask(UniRx.Async)
Deep Dive async/await in Unity with UniTask(UniRx.Async)
 
C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
ゲームエンジンとMVC
ゲームエンジンとMVCゲームエンジンとMVC
ゲームエンジンとMVC
 
会社でClojure使ってみて分かったこと
会社でClojure使ってみて分かったこと会社でClojure使ってみて分かったこと
会社でClojure使ってみて分かったこと
 
Windowsフォームで大丈夫か?一番良いのを頼む。
Windowsフォームで大丈夫か?一番良いのを頼む。Windowsフォームで大丈夫か?一番良いのを頼む。
Windowsフォームで大丈夫か?一番良いのを頼む。
 
SPAのルーティングの話
SPAのルーティングの話SPAのルーティングの話
SPAのルーティングの話
 
Unity開発で使える設計の話+Zenjectの紹介
Unity開発で使える設計の話+Zenjectの紹介Unity開発で使える設計の話+Zenjectの紹介
Unity開発で使える設計の話+Zenjectの紹介
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
 
インタフェース完全に理解した
インタフェース完全に理解したインタフェース完全に理解した
インタフェース完全に理解した
 
HoloLens2とMeta QuestではじめるWebXR
HoloLens2とMeta QuestではじめるWebXRHoloLens2とMeta QuestではじめるWebXR
HoloLens2とMeta QuestではじめるWebXR
 
シェーダだけで世界を創る!three.jsによるレイマーチング
シェーダだけで世界を創る!three.jsによるレイマーチングシェーダだけで世界を創る!three.jsによるレイマーチング
シェーダだけで世界を創る!three.jsによるレイマーチング
 
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 との闘い〜
 

En vedette

KnockoutJS勉強会 プロジェクトにmvvmを適用する狙い
KnockoutJS勉強会 プロジェクトにmvvmを適用する狙いKnockoutJS勉強会 プロジェクトにmvvmを適用する狙い
KnockoutJS勉強会 プロジェクトにmvvmを適用する狙いToshihiro Kawachi
 
今風なデスクトップアプリのモダンインストーラー開発
今風なデスクトップアプリのモダンインストーラー開発今風なデスクトップアプリのモダンインストーラー開発
今風なデスクトップアプリのモダンインストーラー開発Kaoru Nakajima
 
ModelとViewに分ける設計 - #JSオジサン
ModelとViewに分ける設計 - #JSオジサンModelとViewに分ける設計 - #JSオジサン
ModelとViewに分ける設計 - #JSオジサンGinpei Takanashi
 
Windows アプリケーション開発 はじめに ~ Windows アプリケーション開発初学者の方向け Visual Studio を使ったアプリケーショ...
Windows アプリケーション開発はじめに ~ Windows アプリケーション開発初学者の方向けVisual Studio を使ったアプリケーショ...Windows アプリケーション開発はじめに ~ Windows アプリケーション開発初学者の方向けVisual Studio を使ったアプリケーショ...
Windows アプリケーション開発 はじめに ~ Windows アプリケーション開発初学者の方向け Visual Studio を使ったアプリケーショ...Fujio Kojima
 
Xamarin と Visual Studio でまとめて作る iOS / Android / Windows アプリ ( Developers Summ...
Xamarin と Visual Studio でまとめて作る iOS / Android / Windows アプリ ( Developers Summ...Xamarin と Visual Studio でまとめて作る iOS / Android / Windows アプリ ( Developers Summ...
Xamarin と Visual Studio でまとめて作る iOS / Android / Windows アプリ ( Developers Summ...友太 渡辺
 
ADO.NETとORMとMicro-ORM -dapper dot netを使ってみた
ADO.NETとORMとMicro-ORM -dapper dot netを使ってみたADO.NETとORMとMicro-ORM -dapper dot netを使ってみた
ADO.NETとORMとMicro-ORM -dapper dot netを使ってみたNarami Kiyokura
 
「Entity Framework Coreを使ってみる」 公開用
「Entity Framework Coreを使ってみる」 公開用「Entity Framework Coreを使ってみる」 公開用
「Entity Framework Coreを使ってみる」 公開用ESM SEC
 
知って得するC# LINQ to Objects編
知って得するC# LINQ to Objects編知って得するC# LINQ to Objects編
知って得するC# LINQ to Objects編Shota Baba
 
20分でできる!Xamarin.Forms入門
20分でできる!Xamarin.Forms入門20分でできる!Xamarin.Forms入門
20分でできる!Xamarin.Forms入門Shinichi Hirauchi
 
IOS/Androidアプリの3つの大事な設計方針
IOS/Androidアプリの3つの大事な設計方針IOS/Androidアプリの3つの大事な設計方針
IOS/Androidアプリの3つの大事な設計方針Ken Morishita
 
リアクティブプログラミングとMVVMパターンについて
リアクティブプログラミングとMVVMパターンについてリアクティブプログラミングとMVVMパターンについて
リアクティブプログラミングとMVVMパターンについてHidenori Takeshita
 
ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発
ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発
ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発慎一 古賀
 
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門増田 亨
 
iOS/Androidアプリエンジニアが理解すべき「Model」の振る舞い
iOS/Androidアプリエンジニアが理解すべき「Model」の振る舞いiOS/Androidアプリエンジニアが理解すべき「Model」の振る舞い
iOS/Androidアプリエンジニアが理解すべき「Model」の振る舞いKen Morishita
 
ドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解するドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解する増田 亨
 

En vedette (18)

WPF MVVM Review
WPF MVVM ReviewWPF MVVM Review
WPF MVVM Review
 
KnockoutJS勉強会 プロジェクトにmvvmを適用する狙い
KnockoutJS勉強会 プロジェクトにmvvmを適用する狙いKnockoutJS勉強会 プロジェクトにmvvmを適用する狙い
KnockoutJS勉強会 プロジェクトにmvvmを適用する狙い
 
MVVM入門
MVVM入門MVVM入門
MVVM入門
 
今風なデスクトップアプリのモダンインストーラー開発
今風なデスクトップアプリのモダンインストーラー開発今風なデスクトップアプリのモダンインストーラー開発
今風なデスクトップアプリのモダンインストーラー開発
 
ModelとViewに分ける設計 - #JSオジサン
ModelとViewに分ける設計 - #JSオジサンModelとViewに分ける設計 - #JSオジサン
ModelとViewに分ける設計 - #JSオジサン
 
Windows アプリケーション開発 はじめに ~ Windows アプリケーション開発初学者の方向け Visual Studio を使ったアプリケーショ...
Windows アプリケーション開発はじめに ~ Windows アプリケーション開発初学者の方向けVisual Studio を使ったアプリケーショ...Windows アプリケーション開発はじめに ~ Windows アプリケーション開発初学者の方向けVisual Studio を使ったアプリケーショ...
Windows アプリケーション開発 はじめに ~ Windows アプリケーション開発初学者の方向け Visual Studio を使ったアプリケーショ...
 
Xamarin と Visual Studio でまとめて作る iOS / Android / Windows アプリ ( Developers Summ...
Xamarin と Visual Studio でまとめて作る iOS / Android / Windows アプリ ( Developers Summ...Xamarin と Visual Studio でまとめて作る iOS / Android / Windows アプリ ( Developers Summ...
Xamarin と Visual Studio でまとめて作る iOS / Android / Windows アプリ ( Developers Summ...
 
ADO.NETとORMとMicro-ORM -dapper dot netを使ってみた
ADO.NETとORMとMicro-ORM -dapper dot netを使ってみたADO.NETとORMとMicro-ORM -dapper dot netを使ってみた
ADO.NETとORMとMicro-ORM -dapper dot netを使ってみた
 
「Entity Framework Coreを使ってみる」 公開用
「Entity Framework Coreを使ってみる」 公開用「Entity Framework Coreを使ってみる」 公開用
「Entity Framework Coreを使ってみる」 公開用
 
知って得するC# LINQ to Objects編
知って得するC# LINQ to Objects編知って得するC# LINQ to Objects編
知って得するC# LINQ to Objects編
 
20分でできる!Xamarin.Forms入門
20分でできる!Xamarin.Forms入門20分でできる!Xamarin.Forms入門
20分でできる!Xamarin.Forms入門
 
IOS/Androidアプリの3つの大事な設計方針
IOS/Androidアプリの3つの大事な設計方針IOS/Androidアプリの3つの大事な設計方針
IOS/Androidアプリの3つの大事な設計方針
 
リアクティブプログラミングとMVVMパターンについて
リアクティブプログラミングとMVVMパターンについてリアクティブプログラミングとMVVMパターンについて
リアクティブプログラミングとMVVMパターンについて
 
ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発
ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発
ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発
 
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門
 
iOS/Androidアプリエンジニアが理解すべき「Model」の振る舞い
iOS/Androidアプリエンジニアが理解すべき「Model」の振る舞いiOS/Androidアプリエンジニアが理解すべき「Model」の振る舞い
iOS/Androidアプリエンジニアが理解すべき「Model」の振る舞い
 
Xamarin.forms入門
Xamarin.forms入門Xamarin.forms入門
Xamarin.forms入門
 
ドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解するドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解する
 

Similaire à T90 きっと怖くないmvvm & mvpvm

T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門伸男 伊藤
 
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会Jumpei Ogawa
 
TypeScript ファーストステップ (Rev.2) ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ (Rev.2) ~ Any browser. Any host. Any OS. Open Source. ~TypeScript ファーストステップ (Rev.2) ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ (Rev.2) ~ Any browser. Any host. Any OS. Open Source. ~Akira Inoue
 
ATLに見る魔術
ATLに見る魔術ATLに見る魔術
ATLに見る魔術egtra
 
わんくまT78 mfcを始めようとしてみた
わんくまT78 mfcを始めようとしてみたわんくまT78 mfcを始めようとしてみた
わんくまT78 mfcを始めようとしてみた伸男 伊藤
 
Live Coding で学ぶ C# 7
Live Coding で学ぶ C# 7Live Coding で学ぶ C# 7
Live Coding で学ぶ C# 7Takaaki Suzuki
 
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~Akira Inoue
 
GoF デザインパターン 2009
GoF デザインパターン 2009GoF デザインパターン 2009
GoF デザインパターン 2009miwarin
 
ぱっと見でわかるC++11
ぱっと見でわかるC++11ぱっと見でわかるC++11
ぱっと見でわかるC++11えぴ 福田
 
Jenkins x Kubernetesが簡単だと思ったら大変だった話
Jenkins x Kubernetesが簡単だと思ったら大変だった話Jenkins x Kubernetesが簡単だと思ったら大変だった話
Jenkins x Kubernetesが簡単だと思ったら大変だった話Masaki Yamamoto
 
ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)
ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)
ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)Takashi Yoshinaga
 
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~Akira Inoue
 
111008 silverlight square_datavalidation
111008 silverlight square_datavalidation111008 silverlight square_datavalidation
111008 silverlight square_datavalidationTakayoshi Tanaka
 
復習も兼ねて!C#6.0-7.0
復習も兼ねて!C#6.0-7.0復習も兼ねて!C#6.0-7.0
復習も兼ねて!C#6.0-7.0Yuta Matsumura
 
実装(3) 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第32回】
実装(3) 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第32回】実装(3) 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第32回】
実装(3) 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第32回】Tomoharu ASAMI
 

Similaire à T90 きっと怖くないmvvm & mvpvm (20)

VerilatorとSystemC
VerilatorとSystemCVerilatorとSystemC
VerilatorとSystemC
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門
 
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
 
TypeScript ファーストステップ (Rev.2) ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ (Rev.2) ~ Any browser. Any host. Any OS. Open Source. ~TypeScript ファーストステップ (Rev.2) ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ (Rev.2) ~ Any browser. Any host. Any OS. Open Source. ~
 
ATLに見る魔術
ATLに見る魔術ATLに見る魔術
ATLに見る魔術
 
わんくまT78 mfcを始めようとしてみた
わんくまT78 mfcを始めようとしてみたわんくまT78 mfcを始めようとしてみた
わんくまT78 mfcを始めようとしてみた
 
Live Coding で学ぶ C# 7
Live Coding で学ぶ C# 7Live Coding で学ぶ C# 7
Live Coding で学ぶ C# 7
 
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
 
GoF デザインパターン 2009
GoF デザインパターン 2009GoF デザインパターン 2009
GoF デザインパターン 2009
 
Pronama 0707 wf4
Pronama 0707 wf4Pronama 0707 wf4
Pronama 0707 wf4
 
ぱっと見でわかるC++11
ぱっと見でわかるC++11ぱっと見でわかるC++11
ぱっと見でわかるC++11
 
MoteMote Compiler Plugin
MoteMote Compiler PluginMoteMote Compiler Plugin
MoteMote Compiler Plugin
 
Jenkins x Kubernetesが簡単だと思ったら大変だった話
Jenkins x Kubernetesが簡単だと思ったら大変だった話Jenkins x Kubernetesが簡単だと思ったら大変だった話
Jenkins x Kubernetesが簡単だと思ったら大変だった話
 
ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)
ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)
ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)
 
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
 
111008 silverlight square_datavalidation
111008 silverlight square_datavalidation111008 silverlight square_datavalidation
111008 silverlight square_datavalidation
 
復習も兼ねて!C#6.0-7.0
復習も兼ねて!C#6.0-7.0復習も兼ねて!C#6.0-7.0
復習も兼ねて!C#6.0-7.0
 
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
 
emc++ chapter32
emc++ chapter32emc++ chapter32
emc++ chapter32
 
実装(3) 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第32回】
実装(3) 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第32回】実装(3) 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第32回】
実装(3) 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第32回】
 

T90 きっと怖くないmvvm & mvpvm

  • 2. わんくま同盟 東京勉強会 #90 自己紹介 • HN:暁 紫電 • Twitter: @akatukisiden • 本名:伊藤 伸男 • フリーランス プログラマー • 使用言語 –C++ –C# –C++/CLI
  • 3. わんくま同盟 東京勉強会 #90 アジェンダ • 目的 • MVVMとは • ViewとViewModelの分離 • 細かいことは程々にして 実装してみた • MVVMでの画面遷移 • MVVMまとめ • MVPVMとは • とりあえず実装してみた • MVPVMでの画面遷移 • MVPVMまとめ • まとめ
  • 4. わんくま同盟 東京勉強会 #90 このセッションの目的 • 細かいことは置いておいて、 とりあえずMVVM,MVPVMっぽい形で プログラムを書けるようにする。 • MVVMでのナビゲーション手法について理解する • MVPVMでのナビゲーションについて理解する • 疎結合、密結合、コードビハインドなどの用語を できる限り使わずに説明する
  • 5. わんくま同盟 東京勉強会 #90 MVVM(Model-View-ViewModel)とは • 最近流行りのUIアーキテクチャパターン • いくつかの理由によりXAML系フレームワーク (WPF,Silverlight,WinRT)では必須と言われている • UI(View)とビジネスロジック(Model)のあいだにViewModelを 置くことで二つを分離する。 • View・ViewModel間のやり取りはデータバインドを用いる
  • 6. わんくま同盟 東京勉強会 #90 View • ユーザーインターフェース • UIへの出力とUIからの入力を担当する。 • FrameworkElementの派生クラス • XAMLの記述+対応する(partial )class • Viewの必要な情報を保持公開 • Viewからの入力やコマンドを処理し Modelを呼び出す ViewModel Model • ビジネスロジック • プログラムの中核となる処理 • ViewとViewModel以外の部分
  • 7. わんくま同盟 東京勉強会 #90 ViewとViewModelの分離(疎結合と密結合) • MVVMの説明などでよく使われる用語、 疎結合と密結合 • ViewとViewModelを密結合にならないようにし、 疎結合に保つと良いらしい • 「疎結合に保つ」「密結合になってしまっている」という記述 はよく見るが具体的にどのような状況を疎結合・密結合と 言うのか書かれていることはあまりない • もしかしたら一般的な用語で説明する必要もないのかもし れないが、 少なくとも自分にとってMVVM/MVPVMの文脈でしか聞か ない言葉
  • 8. わんくま同盟 東京勉強会 #90 さまざまな資料の記述を総合すると • View/ViewModelで互いのインスタンスや 型名を直接扱うと密結合 • データバインドを使えば疎結合
  • 9. わんくま同盟 東京勉強会 #90 具体的に何が許されて何が許されないのか • 直接触れると密結合 – ViewはViewModelが特定の型であることに依存してはいけない – ViewでViewModelのインスタンスを扱ってはいけない – ViewModelはViewが特定の型であることに依存してはいけない – ViewModelでViewのインスタンスを扱ってはいけない • データバインドは疎結合 – ViewはViewModelがINotifyPropertyChangedを 実装していることに依存してよい。 – ViewはViewModelが特定の名前のプロパティを 持つことに依存してよい
  • 10. わんくま同盟 東京勉強会 #90 厳密にいうとこれもだめかもしれない <Window x:Class="MVVM1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="300" Width="300" > <Window.DataContext > <MainViewModel /> </Window.DataContext> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = new MainViewModel(); } View内でViewModelの型を 扱っている
  • 11. わんくま同盟 東京勉強会 #90 細かいことは置いておいて • MainWindowのイベントハンドラに全部の処理を書いた状態 から 少しずつ修正してMVVMの形にしてみようと思います。 • テキストボックス、ラベル、ボタンを配置し、ボタンを押すとテ キストボックスに入力した文字列を加工してラベルに出力する アプリを作る ※小さすぎてMVVMにする意味がないとか言わないでください 意味がなくてもとりあえず始めることが大事です。
  • 13. わんくま同盟 東京勉強会 #90 MainWindow.xaml <Window x:Class=“Step1.MainWindow” // 略 Title="MainWindow" Height="300" Width="300"> <Grid> <Grid.RowDefinitions><RowDefinition /><RowDefinition /><RowDefinition /> </Grid.RowDefinitions> <Button Content="Button" Grid.Row="0“ HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Click="Button_Click"/> <TextBox Name="textBox1" Grid.Row="1“ HorizontalAlignment="Center" VerticalAlignment="Center“ Height="23" Width="120" /> <Border Grid.Row="2" BorderThickness="1" BorderBrush="Black" HorizontalAlignment="Center" VerticalAlignment="Center" > <Label Name="label1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120"/> </Border> </Grid> </Window>
  • 14. わんくま同盟 東京勉強会 #90 MainWindow.xaml.cs public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender,RoutedEventArgs e) { // なんか複数の関数が絡んだ複雑な処理 string f1 = Func1(textBox1.Text); string f2 = Func2(f1); string f3 = Func3(f2); label1.Content = f3; } //頭にBをつける private string Func1(string input) { return “B” + input; } //末尾にEをつける private string Func2(string input) { return input+"E"; } //順番をひっくり返す。 private string Func3(string input) { var rev = input.Reverse().ToArray(); string ret = new string(rev); return ret; } }
  • 16. わんくま同盟 東京勉強会 #90 MainWindow.xaml.cs public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private MainWindowImpl impl = new MainWindowImpl(); private void Button_Click(object sender, RoutedEventArgs e) { label1.Content = impl.Logic(textBox1.Text); } } 処理を移すための別クラス 別クラスに移した処理の 呼び出し
  • 17. わんくま同盟 東京勉強会 #90 MainWindowImpl.cs public class MainWindowImpl { public string Logic(string input) { string f1 = Func1(input); string f2 = Func2(f1); string f3 = Func3(f2); return f3; } private string Func1(string input) { return "B" + input;} private string Func2(string input) { return input + "E";} private string Func3(string input) { var rev = input.Reverse().ToArray(); string ret = new string(rev); return ret; } }
  • 19. わんくま同盟 東京勉強会 #90 MainWindow.xaml <Window x:Class=“Step3.MainWindow" //略 Title="MainWindow" Height="300" Width="300"> <Grid> <Grid.RowDefinitions><RowDefinition /><RowDefinition /><RowDefinition /></Grid.RowDefinitions> <Button Content="Button" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Click="Button_Click"/> <TextBox Name="textBox1" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Height="23" Width="120" Text="{Binding Input}“ <Border Grid.Row="2" BorderThickness="1" BorderBrush="Black" HorizontalAlignment="Center" VerticalAlignment="Center" > <Label Name=“label1” HorizontalAlignment=“Center” VerticalAlignment=“Center” Width=“120“ Content="{Binding Output}" /> </Border> </Grid> </Window>
  • 20. わんくま同盟 東京勉強会 #90 MainWindow.xaml.cs public partial class MainWindow : Window { // コントロールからの入力、出力をデータバインドに変換 private MainWindowImpl impl = new MainWindowImpl(); public MainWindow() { InitializeComponent(); this.DataContext = impl; } private void Button_Click(object sender, RoutedEventArgs e) { impl.Logic(); } } バインド対象の設定 入出力はバインドしたので 引数・戻り値なしになっている
  • 21. わんくま同盟 東京勉強会 #90 MainWindowImpl.cs public class MainWindowImpl: BindingSourceBase { public string input_; public string Input { get { return input_; } set { if (input_ != value) { input_ = value; OnPropertyChanged("Input"); } } } public void Logic() { string f1 = Func1(Input); string f2 = Func2(f1); string f3 = Func3(f2); Output = f3; } private string Func1(string input){略} private string Func2(string input){略} private string Func3(string input){略} } public string output_; public string Output { get { return output_; } set { if (output_ != value) { output_ = value; OnPropertyChanged("Output"); } } }
  • 22. わんくま同盟 東京勉強会 #90 BindingSourceBase public class BindingSourceBase:System.ComponentModel.INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyname) { if(PropertyChanged != null) { PropertyChanged(this,new PropertyChangedEventArgs(propertyname)); } } }
  • 24. わんくま同盟 東京勉強会 #90 MainWindow.xaml.cs public partial class MainWindow : Window { private MainWindowImpl impl = new MainWindowImpl(); public MainWindow() { InitializeComponent(); // バインド対象の設定 this.DataContext = impl; Button1.Click += impl.Button_Click; } } Clickイベントにimplクラスの 関数を登録
  • 25. わんくま同盟 東京勉強会 #90 MainWindow.xaml <Window x:Class=“Step3.MainWindow" //略 Title="MainWindow" Height="300" Width="300"> <Grid> <Grid.RowDefinitions><RowDefinition /><RowDefinition /><RowDefinition /></Grid.RowDefinitions> <Button Content=“Button” Grid.Row=“0” HorizontalAlignment=“Center” VerticalAlignment=“Center” Width=“120” /> <TextBox Name="textBox1" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Height="23" Width="120" Text="{Binding Input}" /> <Border Grid.Row="2" BorderThickness="1" BorderBrush="Black" HorizontalAlignment="Center" VerticalAlignment="Center" > <Label Name="label1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120“ Content="{Binding Output}" /> </Border> </Grid> </Window> イベントの登録部分を削除
  • 26. わんくま同盟 東京勉強会 #90 MainWindowImpl.cs public class MainWindowImpl:BindingSourceBase { public void Button_Click(object sender,RoutedEventArgs e) { this.Logic(); } public string input_; public string Input { get { return input_; } set { 略 } } public string output_; public string Output { get { return output_; } set { 略 } } public void Logic() { string f1 = Func1(Input); string f2 = Func2(f1); string f3 = Func3(f2); Output = f3; } private string Func1(string input){略} private string Func2(string input){略} private string Func3(string input){略} } Button.Clickイベントから呼 び出される ビジネスロジック
  • 28. わんくま同盟 東京勉強会 #90 MainWindowImpl.cs public class MainWindowImpl:BindingSourceBase { private BusinessLogic BL = new BusinessLogic(); public void Button_Click(object sender, RoutedEventArgs e) { Output = BL.Logic(Input); } public string input_; public string Input { get { return input_; } set {略} } public string output_; public string Output { get { return output_; } set {略}} }
  • 29. わんくま同盟 東京勉強会 #90 BusinessLogic.cs public class BusinessLogic { public string Logic(string Input) { string f1 = Func1(Input); string f2 = Func2(f1); string f3 = Func3(f2); return f3; } private string Func1(string input){略} private string Func2(string input){略} private string Func3(string input){略} } 関数が増えてクラスが肥大化するので あれば 外部とのインターフェースになる関数 だけ残して内部実装はさらに別のクラ スに移した方がいいかも
  • 31. わんくま同盟 東京勉強会 #90 MainWindow.xaml.cs public partial class MainWindow : Window { private MainWindowImpl impl = new MainWindowImpl(); public MainWindow() { InitializeComponent(); // バインド対象の設定 this.DataContext = impl; // Button1.Click += impl.Button_Click; } } 削除
  • 32. わんくま同盟 東京勉強会 #90 MainWindow.xaml <Window x:Class=“Step6.MainWindow" //略 Title="MainWindow" Height="300" Width="300"> <Grid> <Grid.RowDefinitions><RowDefinition /><RowDefinition /><RowDefinition /></Grid.RowDefinitions> <Button Content="Button" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Command="{Binding MainCommand}“ /> <TextBox Name="textBox1" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Height="23" Width="120" Text="{Binding Input}" /> <Border Grid.Row="2" BorderThickness="1" BorderBrush="Black" HorizontalAlignment="Center" VerticalAlignment="Center" > <Label Name="label1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Content="{Binding Output}" /> </Border> </Grid> </Window>
  • 33. わんくま同盟 東京勉強会 #90 MainWindowImpl.cs public class MainWindowImpl:BindingSourceBase { private BusinessLogic BL = new BusinessLogic(); public ICommand MainCommand{get;set;} public MainWindowImpl() { MainCommand = new Microsoft.TeamFoundation. MVVM.RelayCommand(() => { Output = BL.Logic(Input); }); } public string input_; public string Input { get { return input_; } set { 略} } public string output_; public string Output { get { return output_; } set{ 略 } } ロジックのバインド用のコマン ド コマンドの中身を作成。
  • 34. わんくま同盟 東京勉強会 #90 (LastSTEP:)クラス名の変更 • MainWindowImpl → MainViewModel • BindingSourceBase → ViewModelBase • BussinesLogic → MainModel
  • 36. わんくま同盟 東京勉強会 #90 画面遷移とは • 表示されているデータでは無く、 コントロール自体を変更する • 主にユーザーコントロールやパネルの切り替え
  • 37. わんくま同盟 東京勉強会 #90 MVVMでの画面遷移手法 • ViewModelの型に応じてコントロールを切り替える方法。 – データテンプレート • 表示するデータの型に応じて表示方法を変える機能 • 指定した型のデータの表示方法を定義する。 • コントロールの切り替えロジックを呼び出し、 新しいコントロールに新しいViewModelを関連付ける手法。 – ViewModelからViewを呼び出す。 • 逆方向バインディングコマンド • Behavior • TriggerAndAction
  • 38. わんくま同盟 東京勉強会 #90 とりあえずサンプル作る • ボタンを押すことで背景色の異なる(赤・青)二つのユーザーコ ントロールを切り替える。 • Modelは省略 • 切り替えロジック呼び出しのサンプルは 逆方向バインディングコマンドで実装
  • 39. わんくま同盟 東京勉強会 #90 共通部分 (赤) <UserControl x:Class="NavigationCommon.UCRed“ (略) d:DesignHeight="150" d:DesignWidth="150" Background="Red"> <Grid> <TextBlock HorizontalAlignment="Center“ VerticalAlignment="Center" Text="{Binding Text}" /> </Grid> </UserControl> public partial class UCRed : UserControl { public UCRed() { InitializeComponent(); } } public class RedViewModel:ViewModelBase { private string text = "赤"; public string Text { set{ if (text != value) { text = value; OnPropertyChanged("Text"); } } get{ return text; } } } View ViewModel
  • 40. わんくま同盟 東京勉強会 #90 共通部分 (青) <UserControl x:Class="NavigationCommon.UCBlue“ (略) d:DesignHeight="150" d:DesignWidth="150" Background=“Blue"> <Grid> <TextBlock HorizontalAlignment="Center“ VerticalAlignment="Center" Text="{Binding Text}" /> </Grid> </UserControl> public partial class UCBlue : UserControl { public UCBlue() { InitializeComponent(); } } public class BlueViewModel:ViewModelBase { private string text = “青"; public string Text { set{ if (text != value) { text = value; OnPropertyChanged("Text"); } } get{ return text; } } } View ViewModel
  • 42. わんくま同盟 東京勉強会 #90 View <Window x:Class="MainWindow" (略) Title="MainWindow" > <Window.Resources> <DataTemplate DataType="{x:Type navi:RedViewModel}"> <navi:UCRed x:Name="RedUC" /> </DataTemplate> <DataTemplate DataType=“{x:Type navi:BlueViewModel}”> <navi:UCBlue x:Name="BlueUC" /> </DataTemplate> </Window.Resources> <Grid > <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid Grid.Column="0" Name="ParentGrid"> <ContentControl Name="ContentControl“ Content="{Binding ChildViewModel}" /> </Grid> <Grid Grid.Column="1" > <Button Content="Button“ Command="{Binding NavigationCommand}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" Height="25" /> </Grid> </Grid> </Window> ViewModelの型を直接扱ってい る。 public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = new MainViewModel(); } } ViewModelを表示しようとしている
  • 43. わんくま同盟 東京勉強会 #90 ViewModel public class MainViewModel:ViewModelBase { ViewModelBase childVM; public ViewModelBase ChildViewModel { get { return childVM; } set {略} } ICommand nCommand; public ICommand NavigationCommand { get { return nCommand; } set { 略 } } public MainViewModel(){ NavigateRed(); } private void NavigateRed() { ChildViewModel = new RedViewModel(); NavigationCommand = new Microsoft.TeamFoundation. MVVM.RelayCommand( (p) =>{NavigateBlue();}); } private void NavigateBlue() { ChildViewModel = new BlueViewModel(); NavigationCommand = new Microsoft.TeamFoundation. MVVM.RelayCommand( (p) =>{ NavigateRed();} ); } } もう一度ボタンを押したら 元の色に戻るように コマンドをリセット データテンプレートで表示する子ViewModel 表示するViewModelを変更
  • 45. わんくま同盟 東京勉強会 #90 Command UserControl public class DependencyCommandControl : Control { public DependencyCommandControl():base(){ } static DependencyCommandControl() { DefaultStyleKeyProperty.OverrideMetadata( typeof(DependencyCommandControl), new FrameworkPropertyMetadata( typeof(DependencyCommandControl)) ); } public static readonly DependencyProperty CommandProperty = DependencyProperty.Register( "Command“, typeof(ICommand), typeof(DependencyCommandControl) ); public ICommand Command { set { SetValue(CommandProperty, value); } get { return (ICommand)GetValue(CommandProperty); } } } XAML上で扱うには依存関係プロパティである必要があるので 依存関係プロパティ化したICommandを持つ カスタムコントロールを作る
  • 46. わんくま同盟 東京勉強会 #90 View <Window x:Class="MainWindow" (略) Title="MainWindow"> <Grid > <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/><ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid Grid.Column="0" Name="ParentGrid“ > </Grid> <Grid Grid.Column="1" > <Button Content="Button" Command="{Binding NavigationCommand}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" Height="25"/> <DependencyCommandControl x:Name="CommandHolder“ Command="{Binding Mode=OneWayToSource,Path='SouceToTargetCommand' }" /> </Grid> </Grid> </Window> ボタンクリック時に呼ばれる順方向コマンド ViewModelから呼ばれる逆方向コマンド逆方向バインディング カスタムコントロール
  • 47. わんくま同盟 東京勉強会 #90 ViewModel public class MainViewModel:ViewModelBase { ViewModelBase childVM; public ViewModelBase ChildViewModel{get;set} ICommand navigationcommand; public ICommand NavigationCommand { get { return navigationcommand; }set {略} } ICommand sttCommand; public ICommand SouceToTargetCommand { get { return sttCommand; } set{ 略 } } public MainViewModel() { NavigateToRed(); } ボタンクリックから呼び出される (順方向)コマンド ViewModelからViewを呼び出すための 逆方向バインディングをするコマンド
  • 48. わんくま同盟 東京勉強会 #90 ViewModel private void NavigateToRed() { ChildViewModel = new RedViewModel(); if (SouceToTargetCommand != null) SouceToTargetCommand. Execute(ChildViewModel); NavigationCommand = new Microsoft.TeamFoundation. MVVM.RelayCommand( (p) => { NavigateToBlue(); }); } private void NavigateToBlue() { ChildViewModel = new BlueViewModel(); if (SouceToTargetCommand != null) SouceToTargetCommand. Execute(ChildViewModel); NavigationCommand = new Microsoft.TeamFoundation. MVVM.RelayCommand( (p) => { NavigateToRed(); } ); } } 子コントロール用の新しいViewModelを作成、 コマンドでViewに送る もう一度ボタンを押したら元に戻すために順方向コマンドをリセット
  • 49. わんくま同盟 東京勉強会 #90 View public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var mainVM = new MainViewModel(); ChangeToRed(mainVM.ChildViewModel); this.DataContext = mainVM; } private void ChangeToBlue(object vm) { ParentGrid.Children.Clear(); var newPanel = new UCBlue(); newPanel.DataContext = vm; ParentGrid.Children.Add(newPanel); CommandHolder.Command = new Microsoft.TeamFoundation. MVVM.RelayCommand(ChangeToRed); } private void ChangeToRed(object vm) { ParentGrid.Children.Clear(); var newPanel = new UCRed(); newPanel.DataContext = vm; ParentGrid.Children.Add(newPanel); CommandHolder.Command = new Microsoft.TeamFoundation. MVVM.RelayCommand(ChangeToBlue); } } ViewModelから受け取った新ViewModelを Viewで直接扱っている 新しいViewを作成 新しいViewModelを受け取る
  • 50. わんくま同盟 東京勉強会 #90 MVVMまとめ • View と ViewModel が互いを扱わなくていいはずのMVVMで 画面遷移を行おうとするとViewでViewModelを扱ってまう。 → そもそもMVVMにはXAML環境に適応するための 最低限必要な機能しかなく、 画面遷移の存在を前提にしていないのでは? • MVVMの利点(≒XAML環境への適応≒データバインディング) を 生かしつつ画面遷移の存在を前提とするパターンが必要 → MVPVM
  • 51. わんくま同盟 東京勉強会 #90 MVPVM(Model-View-Presenter-ViewModel)とは • MVPとMVVMを合体させたもの • View,ViewModelの上にPresenterを置き、そこで、画面遷 移、 ナビゲーションを管理する。 • ViewModelに書かれていたロジックをPresenterに移動する ことでViewModelにはバインド対象となるプロパティのみが 含まれるようにする。 • ViewModelにロジックが含まれなくなるので使いまわしが容 易になる。
  • 52. わんくま同盟 東京勉強会 #90 View • ユーザーインターフェース • UIへの出力とUIからの入力を担当する。 • FrameworkElementの派生クラス • XAMLの記述+対応する partial class Model • ビジネスロジック/プログラムの中核となる処理 • ViewとViewModel(とPresenter)以外の部分
  • 53. わんくま同盟 東京勉強会 #90 • Viewの必要な情報を保持公開 • Viewから受け取った入力やコマンドををPresenterに渡す (MVPVM) • Viewからの入力やコマンドを処理しModelを呼び出す (MVVM) ViewModel
  • 54. わんくま同盟 東京勉強会 #90 • View,ViewModelを保持、その接続を管理する • ViewModelから受け取った入力やコマンドを処理しModel を呼び出す。 • ViewModelのプロパティを通してViewを変更する。 • ナビゲーション・画面遷移の管理 (子View,子ViewModelの作成、接続) Presenter
  • 55. わんくま同盟 東京勉強会 #90 とりあえず組んでみた • MVVMの時と同じようにテキストボックスから入力、加工して ラベルに出力するアプリ ※画面遷移がないのに MVPVMで組む必要性がないとか言わないように
  • 56. わんくま同盟 東京勉強会 #90 View <Window x:Class="MVPVM1.MainWindow" (略) Title="MainWindow" Height="300" Width="300"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Button Content="Button" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Command="{Binding MainCommand}"/> <TextBox Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Height="23" Width="120" Text="{Binding Input}" /> <Border Grid.Row="2" BorderThickness="1" BorderBrush="Black" HorizontalAlignment="Center" VerticalAlignment="Center" > <Label HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding Output}" Width="120" /> </Border> </Grid> </Window> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } }
  • 57. わんくま同盟 東京勉強会 #90 ViewModel class MainViewModel:ViewModelBase { public MainViewModel() { } public override void Cleanup() { MainCommand = null; base.Cleanup(); } private ICommand _mainCommand; public ICommand MainCommand { set{略} get{ return _mainCommand;} } private string _input; public string Input { set{略} get{ return _input;} } private string _output; public string Output { set{略} get{ return _output;} } } Commandの中身の定義 Modelの関数の呼び出しコードがなくなっている
  • 58. わんくま同盟 東京勉強会 #90 PresenterBase class PresenterBase<TView,TViewModel> where TView:System.Windows.FrameworkElement where TViewModel:ViewModelBase { public PresenterBase(TView view, TViewModel viewmodel) { View = view; ViewModel = viewmodel; } public TView View { set; get; } public TViewModel ViewModel { set; get; } public virtual void Initialize() { View.DataContext = ViewModel; } DataContextの設定 View-ViewModelの接続 public virtual void CleanUp() { if (ViewModel != null) { ViewModel.Cleanup(); ViewModel = null; } if (View != null) { View.DataContext = null; View = null; } } } View-ViewModel間の接続解除 View,ViewModelの参照解除 引数の View,ViewModel メンバに代入
  • 59. わんくま同盟 東京勉強会 #90 Presenter class MainPresenter:PresenterBase<MainWindow,MainViewModel> { MainModel model = new MainModel(); public MainPresenter(MainWindow view,MainViewModel viewmodel) :base(view,viewmodel){ } public override void Initialize() { ViewModel.MainCommand = new Microsoft.TeamFoundation.MVVM.RelayCommand( ()=>{ViewModel.Output = model.Logic(ViewModel.Input); }); base.Initialize(); } public override void CleanUp() { base.CleanUp(); } } ViewModelのコマンドの中身を作成 コンストラクタがViewとViewModelの インスタンスを受け取る Modelの関数の呼び出し。 Viewの操作はデータバインドした ViewModelのプロパティを通して行う。
  • 60. わんくま同盟 東京勉強会 #90 App.xaml <Application x:Class="MVPVM1.App" (略) Startup="Application_Startup“ Exit="Application_Exit“ > <Application.Resources> </Application.Resources> </Application> public partial class App : Application { MainPresenter presenter ; private void Application_Startup(object sender, StartupEventArgs e) { var view =new MainWindow(); var viewmodel = new MainViewModel(); presenter = new MainPresenter(view, viewmodel); presenter.Initialize(); view.Show(); } private void Application_Exit(object sender, ExitEventArgs e) { presenter.CleanUp(); } } App.xaml.cs StartupUri=“MainWindow.xaml” を削除 Startup,Exitを追加 アプリ起動時に、V,VM,Pを作成、 ウィンドウを表示 アプリ終了時にPresenterから全体をクリア
  • 61. わんくま同盟 東京勉強会 #90 MVPVMでの画面遷移 • View,ViewModel両方を扱うことが出来る Presenterに記述する • MVVMではView,ViewModelの二か所に分割されていた 画面遷移ロジックをPresenter一つにまとめられるため 自然な形で実装することができる。
  • 62. わんくま同盟 東京勉強会 #90 View <Window x:Class="MVPVMNavigation.MainWindow" (略) Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid Grid.Column="0" Name="ParentGrid" > </Grid> <Grid Grid.Column="1" > <Button Content=“Button” HorizontalAlignment=“Center” VerticalAlignment=“Center” Command="{Binding NavigationCommand}" Width="75" /> </Grid> </Grid> </Window> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } Viewに画面遷移用のロ ジックが存在しない ボタンがクリックされた時に呼び出さ れるコマンドのバインド
  • 63. わんくま同盟 東京勉強会 #90 ViewModel public class MainViewModel:ViewModelBase { ViewModelBase childVM; public ViewModelBase ChildViewModel { get { return childVM; } set {略} } ICommand navigationCommand; public ICommand NavigationCommand { get { return navigationCommand; } set {略} } public MainViewModel(){} public override void Cleanup() { if (ChildViewModel != null) { NavigationCommand = null; ChildViewModel.Cleanup(); ChildViewModel = null; } base.Cleanup(); } } 子ViewModel 子Presenterからアクセスできるので もしかしたら要らないかも ボタンクリックで呼び出される 画面遷移用コマンド 各プロパティのクリア
  • 64. わんくま同盟 東京勉強会 #90 子Presenter(赤,青) public class RedPresenter : PresenterBase<UCRed, RedViewModel> { public RedPresenter(UCRed view, RedViewModel viewmodel) : base(view, viewmodel) { } public override void Initialize() { base.Initialize(); } public override void CleanUp() { base.CleanUp(); } } public class BluePresenter :PresenterBase<UCBlue,BlueViewModel> { public BluePresenter(UCBlue view, BlueViewModel viewmodel) : base(view, viewmodel) { } public override void Initialize() { base.Initialize(); } public override void CleanUp() { base.CleanUp(); } }
  • 65. わんくま同盟 東京勉強会 #90 MainPresenter(1) public class MainPresenter: PresenterBase<MainWindow,MainViewModel> { public MainPresenter(MainWindow view,MainViewModel viewmodel) :base(view,viewmodel){ } public IPresenter ChildPresenter { get; set; } public override void Initialize() { base.Initialize(); NavigateToRed(); } public override void CleanUp() { ChildPresenter.CleanUp(); base.CleanUp(); } 子コントロール(赤・青)用の Presenter 子Presenterの解放
  • 66. わんくま同盟 東京勉強会 #90 MainPresenter(2) private void NavigateToRed() { if (ChildPresenter != null) { ChildPresenter.CleanUp(); } var v = new UCRed(); var vm = new RedViewModel(); var presenter = new RedPresenter(v, vm); View.ParentGrid.Children.Clear(); View.ParentGrid.Children.Add(v); ViewModel.ChildViewModel = vm; ViewModel.NavigationCommand = new Microsoft.TeamFoundation.MVVM.RelayCommand( (p) => {NavigateToBlue();} ); ChildPresenter = presenter; ChildPresenter.Initialize(); ); 新しいView,ViewModel,Presenter の作成 次回ボタンクリック時に元の色に戻すた めにコマンドをリセット 新しいViewをGridに追加 旧Presenterのクリア
  • 67. わんくま同盟 東京勉強会 #90 MainPresenter(3) private void NavigateToBlue() { if (ChildPresenter != null) { ChildPresenter.CleanUp(); } var v = new UCBlue(); var vm = new BlueViewModel(); var presenter = new RedPresenter(v, vm); View.ParentGrid.Children.Clear(); View.ParentGrid.Children.Add(v); ViewModel.ChildViewModel = vm; ViewModel.NavigationCommand = new Microsoft.TeamFoundation.MVVM.RelayCommand( (p) => {NavigateToRed();} ); ChildPresenter = presenter; ChildPresenter.Initialize(); ); 新しいView,ViewModelPresenterの作 成 次回ボタンクリック時に元の色に戻すた めにコマンドをリセット 新しいViewをGridに追加 旧Presenterのクリ ア
  • 68. わんくま同盟 東京勉強会 #90 MVPVMまとめ • ViewModelはBinding対象になるプロパティのみを記述し コマンドの中身はPresenterに記述する • PresenterはView,ViewModelどちらも扱えるので その二つに分けて記述していた画面遷移ロジックを Presenter 1ヵ所に記述することができる。 →MVVMでは三ケ所すべてにロジックが含まれる可能性があったのが MVPVMでは基本的にP,M の二か所に減る • 何らかの事情(ActiveX,低スキル,etc…)によりViewがビジネスロジッ クを持っている場合でもPresenterから直接呼べる
  • 69. わんくま同盟 東京勉強会 #90 まとめ • Viewとの入出力はバインディングで行い、 インターフェースになる関数だけ残して 実装は別クラスに移動することを繰り返せば とりあえずMVVMの形にはなる • PresenterでView,ViewModelを管理しViewModelのコマ ンドの中身を Presenterで作ればMVPVMになる。 • 一画面で完結するアプリならMVVMでもいいが、 ナビゲーション(画面遷移)を含む場合はMVPVMの方がオ ススメ
  • 70. わんくま同盟 東京勉強会 #90 今回扱わなかったこと • XAML環境でデータバインディングが必須な理由 • コレクションコントロール • エラー処理 • CommandのCanExecute() • 非同期処理(Model) • Viewに合わせた、ViewModel,Presenterそれぞれツリーの構 造