かるあ のメモ

アクセスカウンタ

zoom RSS MVVM Light Toolkitを使ってみよう。その2 MVVMの復習

<<   作成日時 : 2010/10/02 21:24   >>

ナイス ブログ気持玉 7 / トラックバック 1 / コメント 0

さて、ぼーっとしているうちに日があいてしまいましたが、MVVM Light Toolkitの使い方を説明する前に、MVVMについて復習しておきましょう。

■ MVVMとは?
Model-View-ViewModelの頭文字をとった、アプリケーションのUI分離パターンの一つです。
UI分離パターンを使うと、各層の依存関係が薄くなり、アプリケーションの修正、複数人数での分散開発、単体テストのしやすさなどの面で利点があります。
また、ExpressionBlend 4からは、デザインツール側でもMVVMがサポートされていることから、デザインツール側でアプリケーションをデザインする際にも各層が分離されているということが重要になってきます。

MVVMでは、有名なMVC(Model-View-Controller)をベースに、バインド機能をより使いやすい形に変換したパターンになっています。

MVC、MVPの詳しい説明に関しては、猪股さんの解説が分かりやすいのでこちらへw。
http://matarillo.com/general/uipatterns.php

実はMVVMの考え方は、この中で解説されているアプリケーションモデルにとても似ています。MVVMでは、MVCが苦手としているModelのデータとViewのデータの同期と、Modeleがもつメソッドの呼び出しなどを、ViewからViewModelへのバインド経由で実行するといった形を取ります。
ViewModel経由でバインドされることで、ModelとViewModelはViewから切り離され、テストや分散開発がしやすい状況になるわけです。

■ 何はともあれ、最初のMVVMアプリケーション
まずはMVVMがどんなものかを理解するためにも、MVVM Light Toolkitを使わないで簡単なMVVMの更新処理を書いてみましょう。



○どんなサンプル?
今回サンプルにするのは、ユーザーの編集画面です。このサンプルでは入力されたデータを保存したり、読出したりするサンプルです。
サンプルではViewModelにロードされたユーザー情報と、データの保存、読み込みと行ったコマンドを、Viewにバインドしユーザーのアクションに応じてViewModelからModelの情報を操作します。



○Model-(データとデータに対する振る舞い)
モデルは特定のドメイン(領域)の問題を解決するために分析されたデータモデルです。
今回は単にユーザーを識別するIdや名前、郵便番号のデータを持つエンティティークラスです。特にこのモデルに対する振る舞いは定義していません。

    public class ユーザー
  {
       public int Id { get; set; }
       public string 名前 { get; set; }
       public string 郵便番号 { get; set; }
   }


○ViewModel -(ViewにバインドしてModelの操作を行う)
Viewに対してバインドを行うモデルとモデルに対するコマンド、他のViewModelに対するメッセージなどを公開します。
プロパティーの実装
View側では、ViewModelで公開されたプロパティーに対してバインドを行うため、ViewModelではユーザークラスをプロパティーとして公開します。
またView側での変更通知を有効にするため、INotifyPropertyChangedインターフェイスを実装します。
public class UserViewModel: INotifyPropertyChanged
   {
       public UserViewModel()
       {
           if (ユーザー == null)
               ユーザー = new ユーザー();
       }

       #region プロパティー
       // Viewにバインドするため、ユーザーモデルをプロパティーとして公開する。
       private ユーザー _ユーザー;
       public ユーザー ユーザー
       {
           get { return _ユーザー; }
           set
           {
               if (ユーザー == value)
                   return;
               _ユーザー = value;
               RaisePropertyChange("ユーザー");
           }
       }
       #endregion

       #region INotifyPropertyChanged
       public event PropertyChangedEventHandler PropertyChanged;
       public void RaisePropertyChange(string propertyName)
       {
           if (PropertyChanged == null)
               return;
           PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
       }
       #endregion

       #region コマンド
       // コマンド
       #endregion
   }


コマンドの実装
プロパティー同様に、Viewからバインドするコマンド(OnSave、OnLoad)を定義し、RelayCommand経由でそれぞれの処理(Saveメソッド、Loadメソッド、)に関連付けます。
コマンド自体はICommandインターフェイスを実装しているクラスである必要がありますが、コマンドごとにクラスを定義するのも大変なので、更新処理をデリゲートとして定義できるRelayCommandクラスを作成しています。(RelayCommandについては後述)
また、SaveメソッドとLoadメソッドはサンプルとして固定データを読み込んだり、Viewで入力されたデータを表示するようにしています。実際にはWCFやWCF RIA Servicesなどを使って、サーバー側のサービス経由でデータの操作を行ないます。
public class UserViewModel: INotifyPropertyChanged
   {
       public UserViewModel()
       {
           if (ユーザー == null)
               ユーザー = new ユーザー();

           OnSave = new RelayCommand(Save, parameter =&gt; true);
           OnLoad = new RelayCommand(Load, parameter =&gt; true);
       }

       #region プロパティー
       #endregion
       #region INotifyPropertyChanged
       #endregion

       #region コマンド
       public RelayCommand OnSave { get; private set; }
       public void Save(object parameter)
       {
           // 入力されたデータを表示する。
           // 実際はサービスの呼び出しを行ってデータをサーバー側に保存する。
           MessageBox.Show(string.Format(
                   "Id={0}\n名前={1}\n郵便番号={2}",
                   ユーザー.Id, ユーザー.名前, ユーザー.郵便番号));
       }

       public RelayCommand OnLoad { get; private set; }
       public void Load(object parameter)
       {
           // 固定データをユーザークラスにロードする。
           // 実際にはサービス経由でユーザーを検索する。
           ユーザー = new ユーザー
           {
               Id = 1,
               名前 = "かるあ",
               郵便番号 = "181-0013",
           };
       }
       #endregion
   }


○View-(Silverlightの画面)
ViewはViewModelを表示するためのユーザーインターフェイスです。

ViewModelのインスタンス化
View側でViewModelを利用するためにxmlnsでxmlの名前空間(9行目)を定義します。
同時にStaticResourceとしてlocal:UserViewModelを定義(12行目〜14行目)することで、Viewが実態化したさいにViewModelも実体化されます。
<navigation:Page
   x:Class="PlaneMVVMApplication.Views.Hello.HelloView"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   mc:Ignorable="d"
   xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
   xmlns:local="clr-namespace:PlaneMVVMApplication.Views.Hello"
   Title="HelloView Page">

   <UserControl.Resources>
       <local:UserViewModel x:Key="viewModel" />
   </UserControl.Resources>
   <Grid x:Name="LayoutRoot" DataContext="{StaticResource viewModel}" Height="120" Width="250">
   </Grid>
</navigation:Page>


プロパティーとコマンドのバインド
GridのDataContextに、StaticResourceとして定義したUserViewModelをバインドし、TextBoxやTextBlockにユーザーの各プロパティー情報をバインドします。

また、読み込みや保存と行ったボタンに対し、ViewModelで公開したOnLoadやOnSaveといったRelayCommandをバインドします。
   <Grid x:Name="LayoutRoot" DataContext="{StaticResource viewModel}" Height="120" Width="250">
       <!-- RowDefinitions, ColumnDefinitionsの定義 -->
       
       <TextBlock Text="Id" Grid.Column="0" Grid.Row="0" />
       <TextBlock Text="{Binding ユーザー.Id}" Grid.Column="1" Grid.Row="0" />
       
       <TextBlock Text="名前" Grid.Column="0" Grid.Row="1" />
       <TextBox Text="{Binding ユーザー.名前, Mode=TwoWay}" Grid.Column="1" Grid.Row="1"/>

       <TextBlock Text="郵便番号" Grid.Column="0" Grid.Row="2" />
       <TextBox Text="{Binding ユーザー.郵便番号, Mode=TwoWay}" Grid.Column="2" Grid.Row="2"/>

       <StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="3">
           <Button Content="読み込み" Command="{Binding Path=OnLoad}" />
           <Button Content="保存" Command="{Binding Path=OnSave}"  />
       </StackPanel>
   </Grid>

Silverlight 3まではButtonコントロールはICommandインターフェイスを実装していなかったので、コマンドバインディングを使いたいときはMVVM Light ToolkitやPrismが提供するコマンドバインディングを利用する必要がありました。



○サンプルの実行結果
読み込みボタンをクリックすると、ViewModelに定義したLoadメソッドが呼び出され、ユーザーがViewに表示されます。

また、保存ボタンをクリックすると、ViewModelに定義したSaveメソッドが呼び出され、Viewで修正したデータが表示されるのを確認できます。


バインドの機能を利用してUIとViewModelのデータとコマンドをバインドすることで、Viewで入力されたデータの詰め替えとメソッドの呼び出しロジックがSilverlightのコードビハインドからなくなったことを確認できると思います。

○RelayCommand
コマンドバインドでバインドできるコマンドは、ICommandインターフェイスを実装したクラスである必要があります。
ただ、保存や読込といったコマンドごとにクラスを作っていくのは大変なので、多くの場合RelayCommandというユーティリティークラスを各自定義してコマンドをバインドします。

RelayCommandのサンプル
   public class RelayCommand : ICommand
   {
       public event EventHandler CanExecuteChanged;
       private Func<object, bool> _canExecuteAction;
       private Action<object> _executeAction;

       public RelayCommand(Action<object> executeAction, Func<object, bool> canExecuteAction)
       {
           this._executeAction = executeAction;
           this._canExecuteAction = canExecuteAction;
       }

       public bool CanExecute(object parameter)
       {
           return _canExecuteAction(parameter);
       }

       public void Execute(object parameter)
       {
           _executeAction(parameter);
       }

       public void RaiseCanExecuteChanged()
       {
           if (CanExecuteChanged != null)
           {
               CanExecuteChanged(this, EventArgs.Empty);
           }
       }
   }


○次回予告
Silverlight 4になり、ボタンなどの一部のコントロールにコマンドバインディングがサポートされましたが、それ以外のイベントに対するコマンドのバインドはサポートされていません。

また、ViewModelのベースクラスやRelayCommandなど自前で作る必要がありますし、ViewModelの定型コードを書くためのスニペットなども複数人で開発する際は書かせないでしょう。ここらへんを楽にしてくれるのが今回紹介するMVVM Light Toolkitというワケですね。
ということで、次回から実際にMVVM Light Toolkitを使ってどんなふうに実装していくかを見ていきます。

○参考
日本語
Model-View-ViewModel デザイン パターンによる WPF アプリケーション
設計パターン - モデル - ビュー - ビューモデル (MVVM: Model-View-ViewModel) の問題点とその解決策
英語
Getting Started with the MVVM Pattern in Silverlight Applications - Dan Wahlin's WebLog
UNDERSTANDING THE MODEL-VIEW-VIEWMODEL PATTERN

テーマ

注目テーマ 一覧


月別リンク

ブログ気持玉

クリックして気持ちを伝えよう!
ログインしてクリックすれば、自分のブログへのリンクが付きます。
→ログインへ
気持玉数 : 7
ナイス ナイス ナイス ナイス ナイス
なるほど(納得、参考になった、ヘー)
面白い

トラックバック(1件)

タイトル (本文) ブログ名/日時
MVVM Light Toolkitを使ってみよう。その2 MVVMの復習
素敵なエントリーの登録ありがとうございます - .NET Clipsからのトラックバック ...続きを見る
.NET Clips
2010/10/02 23:05

トラックバック用URL help


自分のブログにトラックバック記事作成(会員用) help

タイトル
本 文

コメント(0件)

内 容 ニックネーム/日時

コメントする help

ニックネーム
URL(任意)
本 文
MVVM Light Toolkitを使ってみよう。その2 MVVMの復習 かるあ のメモ/BIGLOBEウェブリブログ
文字サイズ:       閉じる