2011年1月18日火曜日

ASP.NET MVC 3とEntity Framework Code Firstを触ってみた MySQL編

前回というかさっきなのだけどASP.NET MVC 3とEntity Framework Code Firstを触ってみた SQL Server編でSQL ServerをデータストレージとしたEF Code Firstの実装方法を解説した。今回はMySQLを使用する方法を解説する。

前回のソースコードをそのまま流用するのでこちらからダウンロードして欲しい。

・セットアップ
下記からそれぞれ自分の環境のものをダウンロードして欲しい。

MySQL Database
Download MySQL Community Server

MySQLのGUIツール
Download MySQL Workbench

MySQLのデータプロバイダー
Download Connector/Net

・MySQLのセットアップ
MySQLをはじめてインストールしたのだが「How to install MySQL on Windows」を参考に下記のようにセットアップした。

  • Typical Setup
  • スキップSign-Up
  • "Configure the MySQL Server now"をチェックする
  • "Detailed Configuration"
  • "Developer Machine"
  • "Multifunctional Database"
  • "InnoDB Tablespace Settings" 初期状態のままにする
  • "Decision Support (DSS)/OLAP"
  • "Enable TCP/IP Networking"をチェックする。またポート番号3306のままにする。"Enable Strict Mode"をチェックする
  • "Standard Character Set"
  • "Install As Windows Service"をチェックする。また"Launch the MySQL Server automatically"をチェックする
  • ルートパスワードを入力する。忘れないように!理由が無い限り"Enable root access from remote machines"はチェックしないほうが良いよ
  • "execute"をクリックしてインストール終了まで待機する


・接続文字列
ついでWeb.configの接続文字列を下記のように変更する。

<add name="Sengoku" connectionString="Server=localhost;Database=Sengoku;Uid=username;Pwd=yourpassword;" providerName="MySql.Data.MySqlClient" />

providerNameがMySqlClientになっているのに注目して欲しい。前述のConnector/Netがインストールされているとmachine.config(C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config)のDbProviderFactoriesセクションにMySql.Data.MySqlClientが登録されているので特に他の記述は必要ない。

・実行 with Code First
WindowsサービスでMySQLが動作しているのを確認し実行してみよう。これがSQL ServerならばEF Code FirstがDatabaseを作成してくれて正常動作するはずだ。しかし案に相違して「データベースSengokuが見つからない云々」という例外が発生する。

正直、Connector/NetがサポートしていないのかEF Code FirstがCTP5のためサポートされていないのかどちらなのかよく分からない。

・Sengoku Database作成
仕方が無いのでMySQL Administratorにログインし、Sengoku Databaseを作成した。
※ログイン時にUsernameとPasswordが尋ねられる。セットアップ時にPasswordの設定はするけれどUsernameの設定は無かったので面食らうと思うがここではrootと入力すれば良い。


また下記クエリをMySQL Query Browserで実行してTableをSengoku Database上に作成した。

DROP TABLE IF EXISTS `sengoku`.`bushous`;
CREATE TABLE  `sengoku`.`bushous` (
  `bushouid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` nvarchar(10) NOT NULL,
  `address` nvarchar(10) NOT NULL,
  PRIMARY KEY (`bushouid`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

DROP TABLE IF EXISTS `sengoku`.`comments`;
CREATE TABLE  `sengoku`.`comments` (

  `commentid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `commentedbushouid` int(10) unsigned NOT NULL,
  `commentbushouid` int(10) unsigned NOT NULL,
  `text` nvarchar(30) NOT NULL,
  PRIMARY KEY (`commentid`),
  FOREIGN KEY (commentedbushouid) REFERENCES bushous(bushouid),
  FOREIGN KEY (commentbushouid) REFERENCES bushous(bushouid)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

これで再度MySQL AdministratorでSengoku Databaseを確認するとTableが2つできているのが確認できると思う。

・実行
この状態で実行すると下図のように正常動作する。武将登録やコメント追加も行える。


・まとめ
MySQLの場合はDatabaseを事前に用意しておかないといけない、という違いはあるもののおおむねSQL Serverと同じ挙動だった。EF Code Firstが正式リリースされたときにここらへんの修正が入るのか、それともConnector/Netのほうの修正が必要なのかは不明だが、現状さしたる不都合は無さそうだ。

ASP.NET MVC 3とEntity Framework Code Firstを触ってみた SQL Server編

ASP.NET MVC 3がリリースされたので、Entity Framework Code First:CTP5と一緒に評価してみた。今回のデータストレージはSQL Server 2008を使用するがそのうちMySQLを使って解説したい。

・セットアップ
それぞれ下記からインストールして欲しい。
ASP.NET MVC 3
EF Code First CTP5

EF Code FirstはNuGetを使用したほうがより簡単に導入できるのでそちらの方法をお勧めする。NuGetを使用しての詳細な説明はこちら。NuGetでインストールする方法は、View->Other Windows->Package Manager Consoleで“Install-Package EFCodeFirst”と入力するだけだ。

はじめてASP.NET MVCを触る人は下記を参考にしてもらいたい。
Intro to ASP.NET MVC 3
ASP.NET MVC3の基礎を知ることができるので有用だ。これと合わせてユニットテスト用にRepositoryパターンなども勉強すると良いだろう。

・今回のアプリ
今回は次のようなWebアプリを作成する。武将の一覧があり、武将の登録、編集、削除が行え、かつ武将から武将へのコメントも行える。もちろんコメントの削除、編集も行える。

Index.cshtml

Create.cshtml - 検証(StringLength)

Create.cshtml - 検証(Required)

AddComment.cshtml

・EF Code First
今回はEntity Framework Code Firstでデータストレージを作成するので前もってDatabaseやTableを用意しておく必要がない。Data Modelは下記になる。

public class Bushou
    {
        public int BushouID { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }

        public virtual ICollection<Comment> Comments { get; set; }
    }

    public class Comment
    {
        public int CommentID { get; set; }
        public int CommentedBushouID { get; set; }
        public int CommentBushouID { get; set; }
        public string Text { get; set; }
   
        public virtual Bushou CommentedBushou { get; set; }
        public virtual Bushou CommentBushou { get; set; }
    }

    public class Sengoku : DbContext
    {
        public DbSet<Bushou> Bushous { get; set; }
        public DbSet<Comment> Comments { get; set; }

        protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Comment>().HasRequired(c => c.CommentedBushou)
                                  .WithMany(m => m.Comments)
                                  .HasForeignKey(c => c.CommentedBushouID)
                                  .WillCascadeOnDelete();

            modelBuilder.Entity<Comment>().HasRequired(c => c.CommentBushou)
                                          .WithMany()
                                          .HasForeignKey(c => c.CommentBushouID)
                                          .WillCascadeOnDelete(false);
        }
    }

Bushouクラス、Commentクラスともにvirtual属性以外のものがいずれTableの各列となる。またEF Code FirstはConvention(規約、協定みたいなもの)があり、たとえばBushouクラスであればID、またはBushouIDとなっているものを主キーとみなし自動的に設定を行ってくれる。また外部キーも同様に、今回の例ではないがCommentクラスにBushouIDなどがあればそれを外部キーとして設定を行ってくれる。ここでは、コメントされる人、コメントする人、と2つの外部キーがあるためConventionの法則に則っていないのでその設定は自動で行われない。そういう場合はDbContextのOnModelCreatingを使い自前で設定を行う。OnModelCreatingで行っている処理はFluent APIと呼ばれるもので、大抵の設定はこれで行えてしまう。Fluent APIの詳細な資料はこちらを参照してほしい。

他にも[Key]属性や[ForeignKey]属性などプロパティに設定することでFluent APIを使用せずとも明示的にフレームワークに通知することができる。

・Error Validation
EF Code FirstはData Annotationもサポートされている。つまり[Required]属性や[MaxLength]属性などでデータの説明を行えるようになっている。さらにData AnnotationはASP.NET MVC3と完璧に協調するのでクライアントサイド検証や、サーバサイド検証をフレームワークが自動で行ってくれる。しかしここに一つ問題がある。というのもData Annotationのデフォルト言語は英語のためエラーメッセージはすべて英語になってしまう。ここで前述のData Modelを検証と日本語に対応したものに更新しよう。

public class Bushou
    {
        public int BushouID { get; set; }

        [Required(ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = "Required")]
        [StringLength(10, ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = "StringLength")]
        [Display(Name="名称")]
        public string Name { get; set; }

        [Required(ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = "Required")]
        [StringLength(10, ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = "StringLength")]
        [Display(Name = "拠点")]
        public string Address { get; set; }

        public virtual ICollection<Comment> Comments { get; set; }
    }

    public class Comment
    {
        public int CommentID { get; set; }

        [Display(Name = "コメントされる人")]
        public int CommentedBushouID { get; set; }

        [Display(Name = "コメントする人")]
        public int CommentBushouID { get; set; }

        [Required(ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = "Required")]
        [StringLength(30, ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = "StringLength")]
        [Display(Name = "コメント")]
        public string Text { get; set; }

        public virtual Bushou CommentedBushou { get; set; }
        public virtual Bushou CommentBushou { get; set; }
    }

BushouクラスのNameプロパティやAddressプロパティにそれぞれRequired属性とStringLength属性が付与されている。またErrorMessageResourceTypeでリソースクラスの指定、ErrorMessageResourceNameでリソースキーの指定を行っている。リソースクラスを使う方法以外にもErrorMessageを直接指定する方法があるが、ここでは前者を採用している。ちなみにこのリソースクラスを使用する方法は多言語対応と同じ方法だ。

リソースクラスの追加方法を説明する。まずは下図のようにASP.NETフォルダの追加からApp_GlobalResourcesフォルダを追加し、ErrorMessages.ja.resxとErrorMessages.resxを追加しよう。


ついで、ErrorMessages.ja.resxに下図の内容を追加する。その際にAccess ModifierをPublicにすることを忘れないように注意しよう。


またErrorMessages.resxにも同様のキーを追加しておこう。ここにキーを追加しておかないと実行時にリソースキーが見つからないというエラーになる。

※Data Annotationのデフォルト言語を英語以外にする方法は現状無い
日本語のみを対象とするアプリケーションでは今回のような多言語対応の方法だとかなり冗長になる。そのためそれぞれリソースクラスを指定する方法ではなく、Data Annotationのデフォルトエラーメッセージ自体を変更できないかと色々と調べたけれどその方法はなさそうだ。というのもReflectorでSystem.ComponentModel.DataAnnotations.dllの中身を調べたけれど、エラーメッセージ表示部分で内部リソースを直接参照しており、そのリソースをアセンブリ外部から操作できないようになっていた。

・ASP.NET MVC3
ここからASP.NET MVC3のアプリケーションの説明をする。新規プロジェクトからASP.NET MVC 3 Web Applicationを選択し、Emptyプロジェクトを選択しよう。名前は適当につけてほしい。

まず、ControllersフォルダにHomeControllerを追加しよう。なぜHomeかというとデフォルトRouteを変更するのが面倒だからだ。デフォルトRouteを変更したい場合はGlobal.asax.csでRouteのマッピングを行っているのでそこで変更すればよい。

まず最初にIndexを追加する。

Sengoku db = new Sengoku();
        public ActionResult Index()
        {
            return View(db.Bushous.ToList());
        }

ここでは面倒なのでSengokuはHomeControllerのフィールド変数としているけれど、実際のプロジェクトではユニットテストでMockオブジェクトを使用するためにデータアクセス層を分けるのでこのような実装をすることはない。詳しくはRepositoryパターンを調べ欲しい。

またSengokuクラスをインスタンス化しているが、EF Code FirstのConventionの一つでクラス名と同一のコンフィグキーを探し、その接続文字列でDatabaseに接続するというのがある。そのため下記の接続文字列をWeb.configに追加しておこう。

<add name="Sengoku" connectionString="Data Source=.;Initial Catalog=Sengoku;Persist Security Info=True;User ID=yoo;Password=matsuosoftwareisgreat" providerName="System.Data.SqlClient" />

また最初のほうで言及したように接続時にSengoku Databaseが無ければEF Code Firstは定義されている内容でDatabaseを作成してくれる。今回はSQL Server 2008を使っているがExpressでも同様の動作をする。

ついで、Index.cshtmlを追加する。Index()で右クリック→Add ViewからIndex.cshtmlを追加しよう。その際にCreate a strongly-typed viewにチェックをして型を指定するとScaffold templateを指定できる。ちなみにここではIListを指定しているのでtemplateの選択はできない。


追加されたIndex.cshtmlを編集したもの。View追加時に指定したようにView EngineにRazorを使用している。Razorの文法についてはこちらを参照してほしい。

@model IList<EFCodeFirst.Models.Bushou>
@{
    ViewBag.Title = "武将一覧";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>
    一覧</h2>
@Html.ActionLink("武将登録", "Create")
<div style="margin-top:20px;">
    @foreach (var bushou in Model)
    { 
        <div style="padding:10px;border:1px dashed gray">
            <div style="width:320px;float:left;">
                @Html.ActionLink(bushou.Name, "Edit", new { id = bushou.BushouID }) @string.Format("({0}在住)", bushou.Address)
            </div>
            <div style="float:left;">
                @Html.ActionLink("削除", "Delete", new { id = bushou.BushouID }) |
                @Html.ActionLink("コメント追加", "AddComment", new { id = bushou.BushouID })
            </div>
            <div class="clear"></div>
        @foreach (var comment in bushou.Comments)
        {
            <div style="width:300px;float:left;padding-left:20px;">
                @Html.ActionLink(comment.Text, "EditComment", new { id = comment.CommentID }) by @comment.CommentBushou.Name
            </div>
            <div style="float:left;">
                @Html.ActionLink("削除", "DeleteComment", new { id = comment.CommentID })
            </div>
            <div class="clear"></div>
        }
        </div>
    }
</div>

@modelでこのページのModelの型を指定している。またLayoutで使用するASP.NETで言うところのマスターページを指定している。内部的には何も難しいことはしておらず、Modelをforeachでぐるぐると回してタグを作成している。ここで一点注目して欲しいのはコメントタグ作成部分だ。ちゃんとcomment.CommentBushou.Nameが取得されているのが分かる。

次にCreateを追加する。

public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Create(Bushou bushou)
        {
            if (ModelState.IsValid)
            {
                db.Bushous.Add(bushou);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            else
                return View(bushou);
        }

とくに難しいことも無いので説明は省略する。Viewの追加からBushouクラスをModelとしたCreate.cshtmlを追加する。

@model EFCodeFirst.Models.Bushou

@{
    ViewBag.Title = "武将登録";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>武将登録</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>武将</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Address)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Address)
            @Html.ValidationMessageFor(model => model.Address)
        </div>

        <div style="clear:both"></div>
        <p>
            <input type="submit" value="登録" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("一覧へ戻る", "Index")
</div>

クライアントサイドの検証を有効にするためにはWeb.configのClientValidationEnabledとUnobtrusiveJavaScriptEnabledをTrueに設定しておく必要がある。ASP.NET MVC3ではデフォルトでTrueになっている。また_Layout.cshtmlで下記スクリプトが指定されていることを確認しよう。

<script src="@Url.Content("~/Scripts/jquery-1.4.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

これでStringLengthで指定されている文字数以上を入力したり、Required指定されている項目を未入力にしたりするとエラーメッセージが表示される。

・まとめ
ここまで見てきて分かると思うがかなり開発しやすくなっている。RazorがデフォルトのView Engineとして提供されたおかげでプログラムコードとHtmlの分離が簡単になった。またData Annotation機能がさらに進化したおかげで検証の組み込みも簡易に行えるし、一つの設定でViewからDatabaseまで有機的に協調できているのが素晴らしい。何よりEF Code FirstはDatabaseの更新を容易に行えるという以上にユニットテストにおいて同一のData Modelが使えるというのが良い。実際に触ってもらうと分かると思うがより開発速度が高まったのを実感できると思う。

他にも今回のアプリではEditやAddCommentといった機能があるが大体ここまでで説明してきたことと一緒なので省略する。興味がある人はソースコードを添付しておくので下記から取得してほしい。


ソースコード

次回はデータストレージをMySQLにした場合の解説をする予定だ。

2011年1月6日木曜日

Canvasにリストの中身をBindingする方法

ObservableCollectionでもなんでもいいのだが、リストで保持している内容をCanvasに表示したいことはままある。しかしCanvas.Childrenに対して単純にBindingしても予期した動作にはならない。そんな場合はItemsControlのItemsPanelTemplateを使用する。

こんな風にデータをCanvas上に表示したい。

public ObservableCollection<TextData> TextList { get; set; }
TextList = new ObservableCollection<TextData>(
                new List<TextData>
                {
                    new TextData{ Left=110, Top=110, Text="嘘だと" },
                    new TextData{ Left=210, Top=210, Text="言ってよ" },
                    new TextData{ Left=310, Top=310, Text="バーニィ" },
                });
各TextDataクラスはLeft, TopでCanvas上の位置を保持している。

では、上記のリストをCanvas上に配置するためのXamlを見てみよう。
<ItemsControl ItemsSource="{Binding Path=TextList}"
                      VerticalAlignment="Stretch"
                      HorizontalAlignment="Stretch">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Text}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Canvas.Top" Value="{Binding Path=Top}" />
                    <Setter Property="Canvas.Left" Value="{Binding Path=Left}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
ポイントはItemsControl.ItemsPanelのItemsPanelTemplateを使用している箇所。そこでアイテム配置用のパネルにCanvasを指定しているので、下のほうのItemsControl.ItemContainerStyleで各アイテムのLeft, Topを指定して任意の位置にデータを表示させることが可能となる。

サンプルコードはこちら。
(Chromeだと下記iframeが表示されない模様。FireFoxでは表示確認済み)

2011年1月5日水曜日

SQL Serverのクエリプランをクエリと一緒に取得する便利スクリプト

元ネタはこちら:SQL SERVER – Get Query Plan Along with Query Text and Execution Count

稼働中のSQL Serverの状態をチェックしたくなることはままあることで、パフォーマンスを計る上でも各オブジェクトのクエリプランやら実行回数を知っておくのは悪くない。というわけで下記クエリを実行すると下図の結果が得られる。
(Where句をいじることで必要なオブジェクトのみ参照もできる)

SELECT cp.objtype AS ObjectType,
OBJECT_NAME(st.objectid,st.dbid) AS ObjectName,
cp.usecounts AS ExecutionCount,
st.TEXT AS QueryText,
qp.query_plan AS QueryPlan
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS st
--WHERE OBJECT_NAME(st.objectid,st.dbid) = 'YourObjectName'

上記のクエリからこういう結果がでる。

上図でQueryPlanのリンクをクリックすると下図のクエリプランが表示される。

型付けされたページでModelがいつもオブジェクトになってしまう場合 ASP.NET MVC 2

以前とあるASP.NET MVCのプロジェクトをMVC2に変換をし、とあるページを追加したところ、そのページはある型(便宜上TestModelとする)を使用していたのだが、どうにもこうにもその型を認識してくれない。何をやってもObjectとしてしか認識されず非常にイライラしたのでここに解決方法を紹介しておく。

解決方法と言ってもViewsフォルダー配下のWeb.configを開き、下記部分を修正するだけだ。

これを
<pages
    validateRequest="false"
    pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
  <controls>
    <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
  </controls>
</pages>

こちらに変更する。
<pages
    validateRequest="false"
    pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
  <controls>
    <add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
  </controls>
</pages>

これで正しく型が認識されるはずだ。

RelayCommandの実装いろいろ MVVM Light Toolkit

MVVM Light ToolkitのRelayCommandの実装方法を色々と紹介する。今回はWPFで実装している。


1、コマンドパラメータなし
何も特別なことはなし
Xaml
<Button Command="{Binding ClickCommand}" 
        Content="Click Me" />

ViewModel
public RelayCommand ClickCommand { get; private set; }
ClickCommand = new RelayCommand(() =>
{
  // Do something...
});


2、コマンドパラメータあり
ItemsControlのDataTemplate内要素のイベントをフックしている。ここでのDataTemplateのDataContextはDataItemになるので、単純にClickCommandをBindingしても期待した動作にはならない。そのためRelativeSrouceで一番上の親要素(Window)までたどってViewModelを取得している。CommandParameterにDataTemplateのDataContextをBindingしている。

Xaml
<ItemsControl ItemsSource="{Binding DataList}">
 <ItemsControol.ItemTemplate>
  <DataTemplate>
   <Button Command="{Binding DataContext.ClickCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
       CommandParameter="{Binding}"
           Content="Click Me" />
   </DataTemplate> 
 </ItemsControol.ItemTemplate>
</ItemsControl>

ViewModel
public ObservableCollection<DataItem> DataList { get; set; }
public RelayCommand<DataItem> ClickCommand { get; private set; }
ClickCommand = new RelayCommand(x =>
{
  // Do something...
});


3、ICommand以外をBinding
WindowのLoadedイベントなどをBindingしたい場合はEventToCommandを使用する。
・まずXAMLのネームスペースに下記を追加する:
 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
 xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
・後はフックしたいイベントを下記の要領で記述する

Xaml
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
        DataContext="{Binding ViewModel, Source={StaticResource Locator}}">
  <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <cmd:EventToCommand Command="{Binding WindowLoadedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

ViewModel
public RelayCommand WindowLoadedCommand{ get; private set; }
WindowLoadedCommand= new RelayCommand(() =>
{
  // Do something...
});


4、ICommand以外をBinding パラメータあり
WindowのClosingイベントなどでパラメータを渡したい場合はPassEventArgsToCommandをTrueに設定する。

Xaml
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
        DataContext="{Binding ViewModel, Source={StaticResource Locator}}">
  <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closing">
            <cmd:EventToCommand Command="{Binding WindowClosingCommand}" PassEventArgsToCommand="True" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

ViewModel
public RelayCommand WindowClosingCommand<System.ComponentModel.CancelEventArgs>{ get; private set; }
WindowClosingCommand= new RelayCommand(x =>
{
  // Do something...
});

今回解説したようにEventToCommandを使用するとICommandに対応していないイベントもBinding可能となる。Window要素のイベントに限らずどのような要素にも適用できるのでEventToCommandを使用すればコードビハインドを経由してイベントをリレーする必要がなくなる。