Archive

Archive for 2010年10月

MVVMでのServiceLocatorの導入

MVVM Lighttoolkit では、ViewModelはServiceLocatorが提供する仕組みになっています。いや、別にこれまでのエントリーのように、ServiceLocatorを使わなくても構築できるんですけれど、そうなっているからには何かしらの理由が有るはずです。。今回は、ServiceLocatorって何者よっていうのを解説してみようと思います。

ServiceLocatorとは

ServiceLocatorとは、プログラマーの代わりにクラスのインスタンスを管理してくれる子です。

通常クラスの実体が欲しければ、newキーワードでインスタンス化するわけですが、ServiceLocatorにクラスのインスタンスを管理してもらうことで、クラスの生成時に処理を割りこませたり、インスタンスを差し替えたりできるので、MVVMなどのレイヤー型のアーキテクチャーを採用しているプロジェクトとはそれなりに相性が良いです。これがもう少し進むとIoCが出てくるんですが、まぁそこまで行かなくてもいいでしょう。

インスタンスの差し替えと生成の割り込み

MVVMに限らないのですが、内部に持っているサービスクラスを差し替えたいことがあります。

例えば、作ったモジュールの単体テストをしたいんだけれど、依存している部分のせいでテストが行えなかったり、外部のWebサービスを参照していたり、参照するクラスライブラリがまだ実装していたりしない場合などです。このような場合、サービスの振る舞いをインターフェイスに抽象化して、サービスをテスト用のものに差し替えると、このような問題を回避できます。

具体的にはこんな感じです。

    public class SampleViewModel: ViewModelBase
    {
        public IHelloService service { get; set; }

        public SampleViewModel()
        {
        }

        public string GetHelloMessage()
        {
            return service.GetMessage();
        }
    }

    public class SampleServiceLocator
    {
        public SampleViewModel Sample
        {
            get {
                return new SampleViewModel
                {
                    service = new HelloService(),
                };
            }
        }
    }

この場合、HelloServiceはIHelloServiceとして抽象化されているので、例えばテスト時にはこんなクラスに差し替えてSampleViewModelのテストをすることができます。また、XAMLでクラスのインスタンスを作成する場合、引数なしコンストラクターが呼び出されるのですが、ServiceLocator側でプロパティーとして公開し、内部で引数付きのコンストラクターを呼び出すことも可能です。

単体テストが嬉しい

SampleViewModelの単体テストをしたいけれど、HelloServiceがまだ出来ていないとか、Webサービスを参照しているとか行った場合、HelloServiceの都合で、SampleViewModelがテストできないなんてことになります。ただ、サービスをインターフェイスに切りだしてあれば、単体テスト時にモック用のサービスに差し替えることでこんなふうにテストできますよね。

    [TestClass]
    public class Tests
    {
        [TestMethod]
        public void TestMethod1()
        {
            var target = new SampleViewModel();
            target.service = new HelloServiceMock();
            var actual = target.GetHelloMessage();
            Assert.AreEqual<string>("てすと", actual);
        }
    }
デザイン時に嬉しい

Silverlightでは、Blendなどで画面を編集する場合はコンストラクターでWebServiceからデータの取得などを行っているとデザイン画面が開けない。なんてことが起きるので、通常はViewModel内部で実行時の動作なのか、デザイン時の動作などかを判断し、コードを振り分けるなんてことをします。

この場合も、デザイン用のサービスをViewModelに与えることで、ViewModel内に実行時かどうかの判断を各必要がなくなります。たとえば、さっきのGetHelloMessageメソッド内部で判断しようとするとこうなるけれど、

        public string GetHelloMessage()
        {
            if (DesignerProperties.IsInDesignTool)
            {
                return "でざいんようめっせーじ";
            }
            else
            {
                service.GetMessage();
            }
        }

これよりは、デザイン用のサービスを用意して、生成時に差し替えて、GetHelloMessageメソッド自体では単に与えられたサービスを呼び出すほうが、ifの分岐がなくなる分嬉しいですよね。

    public class SampleServiceLocator
    {
        public SampleViewModel Sample
        {
            get {
                return new SampleViewModel
                {
                    service = (DesignerProperties.IsInDesignTool) 
                                    ? new DesingHelloService()
                                    : new HelloService(),
                };
            }
        }
    }
まとめ

MVVM Lighttoolkitでは、プロジェクトテンプレートで、MVVMのプロジェクトを作ると、ServiceLocatorをApp.xamlでインスタンス化し、各XAMLページでは、このLocatorが持っている各ViewModelをバインドすることでViewModelをViewに関連付けています。

ということで、ServiceLocatorを導入することで、クラスの生成に割り込めるので、テストがしやすくなったり、ブレンドでのデザインがしやすくなるというのを確認してもらえたのではないかと思います。

カテゴリー:Silverlight タグ: ,

MVVM勉強会で基礎編やります。

@ufcpp さんの所で、MVVMについての勉強会を開くということで、楽しみにしてたんですが、このブログでMVVMについて取り上げていたら、しゃべってみない?ということで基本編をお話します。他に話をする @Ugaya40 さんや @TatsuyaIto さんほど高度なことはできないので、前のエントリーをベースに基本的なところをつっついていきます。

第60回codeseek勉強会・第2回日本C#ユーザー会勉強会

まともに話をするのはいつぶりだろう。とりあえず資料はともかく、伝えたい内容はしっかり伝えるように心がけよっと。

MVVM Light Toolkitを使ってみよう。その3 MVVM Light Toolkitの場合

その2 MVVMの復習では、MVVMパターンでどのようにアプリケーションを作成するかの復習をしました。今回は前回のアプリケーションと同じものをMVVM Light Toolkitを使って作成してみます。MVVM Light Toolkitには その1 MVVM Light Toolkitをインストールしてみる。で環境設定をしたとおり、プロジェクトテンプレートやアイテムテンプレートが含まれますが、とりあえずこのテンプレートは使わずに、通常のSilverlightアプリケーションとして作成されたアプリケーションからMVVM Light Toolkitを使ってみようと思います。

アセンブリの参照

MVVM Light Toolkitで利用するアセンブリを参照します。このアセンブリの中には、前回自前で作成したRelayCommandやViewModelのベースクラスなどが含まれます。

その1で説明した通りにインストールしている場合、必要なアセンブリは次のフォルダーにあります。

C:\Program Files\Laurent Bugnion (GalaSoft)\Mvvm Light Toolkit\Binaries\Silverlight4

image

コードの変更

今回行ったコードの変更は次の2つです。

  • 継承元をINotifyPropertyChanged⇒GalaSoft.MvvmLight.ViewModelBaseへ変更
  • RelayCommandをMVVM Light Toolkit標準のものへ変更
  • using System.Windows;
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Command;
    using PlaneMVVMApplication.Web.Model;
    using PlaneMVVMApplication.Services;
    
    namespace PlaneMVVMApplication.Views.Hello
    {
        public class UserViewModel: ViewModelBase
        {
            public IHelloService service { get; set; }
    
            public UserViewModel()
            {
                service = new HelloService();
                OnSave = new RelayCommand<ユーザー>(Save, parameter => true);
                OnLoad = new RelayCommand(Load, () => true);
            }
    
            private ユーザー _ユーザー;
            public ユーザー ユーザー
            {
                get { return _ユーザー; }
                set
                {
                    if (ユーザー == value)
                        return;
                    _ユーザー = value;
                    RaisePropertyChanged("ユーザー");
                }
            }
    
            public RelayCommand<ユーザー> OnSave { get; private set; }
            public void Save(ユーザー parameter)
            {
                // Save
                MessageBox.Show(string.Format(
                        "Id={0}\n名前={1}\n郵便番号={2}",
                        ユーザー.Id, ユーザー.名前, ユーザー.郵便番号));
            }
    
            public RelayCommand OnLoad { get; private set; }
            public void Load()
            {
                // 実際にはサービス経由でユーザーを検索する。
                ユーザー = new ユーザー
                {
                    Id = 1,
                    名前 = "かるあ",
                    郵便番号 = "",
                };
            }
        }
    }
    

実行すると、前回と同じ結果になるのを確認できると思います。

    画面読込時に、データを検索したい

    これまでのサンプルでは、画面ロード時にユーザーのインスタンスが空の場合は、新ししく作成していましたが、実際にはサーバーから対象のユーザーを検索してデータを表示したいといった要望があるでしょう。

    このような場合、この詳細画面を呼び出す側のページでユーザーIdなどを詳細画面に伝え、Loadedイベント等でサーバー側にユーザーの情報を問い合せます。単純に考えれば、UserViewModelのLoadメソッドをLayoutRootのLoadedイベントで呼び出せばいいのですが、単純にViewModelのメソッドをViewのイベントから呼び出すことはできません。

    ここで登場するのが、MVVM Light ToolkitのEventToCommandビヘイビアです。このビヘイビアは名前の通りEventのイベントハンドラーに対してDataContextにバインドされたViewModelのComandをバインドすることができます。

    EventToCommandビヘイビアの追加

    ビヘイビアの適用はVisual Studioを使って、直にXAMLを記述することも可能ですが、ここではExpressionBlendを使ってビヘイビアを適用してみましょう。UserView.xamlをExpressionBlendで開き、アセットライブラリーからEventToCommandビヘイビアをLoyoutRootにドロップします。

    image

    コマンドを起動するイベントの設定

    EventNameにLoadedイベントを設定します。

    無題

    コマンドをバインド

    Commandのバインドを行ないます。■部分をクリックして、データバインドを選択、UserViewModelのOnLoadメソッドを設定します。

    image

ここまで設定すると次のようにビヘイビアが設定され、起動時にOnLoadedイベントから、ViewModelのOnLoadメソッドが実行されるようになります。

<navigation:Page 
    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"
    xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
    xmlns:local="clr-namespace:PlaneMVVMApplication.Views.Hello"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    xmlns:GalaSoft_MvvmLight_Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.SL4" 
    x:Class="PlaneMVVMApplication.Views.Hello.HelloView"
    mc:Ignorable="d"
    Title="HelloView Page">

    <navigation:Page.Resources>
        <local:UserViewModel x:Key="viewModel" />
</navigation:Page.Resources>
    
    <Grid x:Name="LayoutRoot" DataContext="{StaticResource viewModel}" Height="120" Width="250">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="70*"/>
        </Grid.ColumnDefinitions>
        
    	<i:Interaction.Triggers>
    		<i:EventTrigger>
    			<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding OnLoad, Mode=OneWay}"/>
    		</i:EventTrigger>
    	</i:Interaction.Triggers>
        
        <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 OnLoad}" />
            <Button Content="保存" Command="{Binding OnSave}" CommandParameter="{Binding ユーザー}" />
        </StackPanel>
    </Grid>
</navigation:Page>
    実際にサーバーのサービスを呼び出す

    ここまでで、ViewModelのデータを読み込んだり、書き込んだり、メソッドを呼び出したりといったViewModelの大まかな流れをMVVM Light Toolkit を使って実装してみました。それでは実際に、Loadメソッドが呼ばれたタイミングでWCFを通じてWebサービス経由でデータの読込を行って見ましょう。

    サービス側はまぁ単純にこんなサービスを提供してみることとします。

        [ServiceContract(Namespace = "")]
        [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
        public class Service1
        {
            [OperationContract]
            public ユーザー FindユーザーById(int Id)
            {
                return new ユーザー
                {
                    Id = 1,
                    名前 = "かるあ",
                    郵便番号 = "",
                };
            }
        }

     

    Silverlight側では、上記のWCFサービスをラップするHelloServiceクラス経由でサービスを呼び出します。HelloServiceクラス内部では、WCFのプロキシクラスをインスタンスとして持ちます。FindユーザーByIdメソッドでは、WCFで公開されたメソッドを呼び出し、渡されたコールバック変数を呼び出し終了時のイベントに設定しています。

    using System;
    using PlaneMVVMApplication.ServiceReference1;
    
    namespace PlaneMVVMApplication.Services
    {
        public class HelloService
        {
            private Service1Client service = new ServiceReference1.Service1Client();
    
            public void FindユーザーById(int Id, Action<object, FindユーザーByIdCompletedEventArgs> callback)
            {
                service.FindユーザーByIdCompleted += new EventHandler<FindユーザーByIdCompletedEventArgs>(callback);
                service.FindユーザーByIdAsync(Id);
            }
        }
    }

    ViewModel側では、ViewModelのコンストラクターでHelloServiceをインスタンス化し

    public IHelloService service { get; set; }
    
    public UserViewModel()
    {
        service = new HelloService();
        OnSave = new RelayCommand<ユーザー>(Save, parameter => true);
        OnLoad = new RelayCommand(Load, () => true);
    }

    Loadメソッド実行時にサービスを呼び出します。

    public RelayCommand OnLoad { get; private set; }
    public void Load()
    {
        service.FindユーザーById(1, (s, e) => {
            ユーザー = new ユーザー
            {
                Id = e.Result.Id,
                名前 = e.Result.名前,
                郵便番号 = e.Result.郵便番号,
            };
        });
    }
    次回予告

    なんとなくそれっぽいものはできましたね。次回は、Service Locatorを使ったViewModelの生成とBlendとさらに仲良くなる方法にあたりを見てみましょうか。

    カテゴリー:Silverlight タグ: ,

    Team Foundation Server 2010 で新しいチームプロジェクトを作る。

    ちょうど自分のあまり時間と、省電力で余っているPCがあったのでおうちにTFS2010を入れてみることにしました。

    今回サーバーにしたのは、ASUSのEeeBoxで、AtomCPUのデスクトップタイプのPCです。メモリは2Gにしました。さすがにCPUの面で不安が残っていますがまぁいいでしょう。

    インストール

    とりあえずインストールはTFSのインストールガイドと@ITのリバティーさんの記事で、TFS2008のときも2005に比べ簡単になったーっていうイメージは合ったんですが、2010はさらに簡単になりましたね。
    http://www.atmarkit.co.jp/fdotnet/introtfs/introtfs01/introtfs01_01.html

    チームプロジェクトの作成

    まぁ普通は、TFSをインストールしたユーザーでチームプロジェクトを作成するんでしょうが、今回はクライアントPCでVisual Studioのチームエクスプローラーでチームプロジェクトを作成してみようと思います。

    TFSへの接続

    とりあえず、Visual StudioでTFSに接続します。
    Visual StudioからTFSに接続するためには、チームエクスプローラを利用します。もしインストールされていない場合は、TFSのインストールCDからインストールできます。また、インストール済みなのに表示されていない場合は、Visual Studioのメニューで、表示⇒チームエクスプローラから表示できます。

    チームエクスプローラで、+ボタンをクリックして、TFSサーバーに接続します。すでに一度TFSサーバーに接続したことがあれば、サーバーの一覧がドロップダウンリストに表示されるので、接続先のサーバーを選択して接続ボタンをクリクします。
    imageimage

    初めてTFSに接続する場合は、サーバーボタンをクリックして、新規にTFSサーバーを登録します。
    image
    image

    チームプロジェクトの作成
    チームエクスプローラーのコンテキストメニューから、新しいチームプロジェクトを選択してプロジェクトの追加を始めます。
    image

     

    プロジェクト名はお好きにどうぞ

    プロジェクトテンプレートもとりあえずMSF for Agile Software Development 5.0でいいと思います。ここで選択したプロセステンプレートによって、作業項目の内容なんかが変わってきます。もし作業項目を利用しないのなら何でもいいです。
    imageimage

    SharePoint(チームポータル)を作成する場合は新しいSharePointサイトを作成するにチェック
    image

    ソース管理データベースの設定、新規で作成する場合はそのまま次へ

    image

    設定内容を確認して確認ボタンをクリック

    image

    権限が問題なく設定されていれば、これで問題なくチームプロジェクトやチームサイトが作成されます。

    つづく…

    カテゴリー:TFS タグ:

    MVP再受賞いただきました。

    2006年からいただいている、Microsoft MVPですが今年も受賞させていただきました。

    コンピテンシーは引き続きVisual Basicです。
    ここ数年Visual Basicにはほとんど触っていないので、周りの人からも「なぜVB?」とか聞かれるのですが、まぁマイペースで続けていけたらと思います。

    元もブログのほうにも書きましたが、新しいLive EssentialsでLive Writerがアップデートされて、ウェブリブログのほうだと微妙に記事の更新がしにくくなったんだけれど、Wordpress.comとは相性がいいようなので、ここ数年の遅れを取り戻すつもりでブログのほうにも力を入れていこうかと思っとります。

    それでは、今年度もよろしくお願いします。

    #あっ追記
    10/1になってTwitterでおめでとうのつぶやきが出てたにも関わらず、僕の所にはなかなか受賞のメールが来なかったので、今年はダメかな?とか思ってたんですが、Gmailの迷惑メールに振り分けられていましたw。TwitterのTL見てたらまっちゃさんも同じく迷惑メールに振り分けられていたとか。別に英語だけでもないし、どんなフィルターに引っかかったんだろう。

    カテゴリー:雑記 タグ:

    CodeZine Silverlight 連載

    CodeZineでSilverlightの連載をしています。Silverlight 2, 3, 4と連載そのものは分かれているんだけれど、それぞれバージョン違いの焼き直しというわけではなく、新しいトピックの追加という形でやっているので古い記事でもまだ使えると思い、今まで書いた記事をリストにしておきます。

    Silverlight2連載
    Silverlight3連載
    Silverlight4連載
      さすがにSilverlight2のころの記事だと、連携先のサービスの仕様が変わったり、DLRのロードほうほうやバージョンが違ったりでそのまま動かなかったりするんですよね。サーバーサービスとの連携に関しても、双方向の通信についてもちょっと触れてみたいなーなんて思っているんだけれど、それはここでそのうちかなー。
    カテゴリー:Silverlight タグ: ,

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

    2010年10月3日 1件のコメント

    さて、ぼーっとしているうちに日があいてしまいましたが、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の情報を操作します。

    128601374947516122036_20101002-01_20101002190229

    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}" CommandParameter="{Binding ユーザー}"  />
           </StackPanel>
       </Grid>

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

    サンプルの実行結果

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

    128601385345416209878_20101002-02

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

    RelayCommand

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

    次回予告

    Silverlight 4になり、ボタンなどの一部のコントロールにコマンドバインディングがサポートされましたが、それ以外のイベントに対するコマンドのバインドはサポートされていません。また、ViewModelのベースクラスやRelayCommandなど自前で作る必要がありますし、ViewModelの定型コードを書くためのスニペットなども複数人で開発する際は書かせないでしょう。

    ここらへんを楽にしてくれるのが今回紹介するMVVM Light Toolkitというワケですね。

    ということで、次回から実際にMVVM Light Toolkitを使ってどんなふうに実装していくかを見ていきます。

    参考

    日本語

    カテゴリー:Silverlight タグ: ,