WPF Archive

このエントリは、M-V-VMパターンについてのエントリを訳してみる試みの四本目です。今回は添付ビヘイビアというテクニックを解説した、CodeProject: Introduction to Attached Behaviors in WPFという記事を訳してみました。著者はMSDN MagazineのWPF のための MODEL-VIEW-VIEWMODEL (MVVM) デザイン パターンという記事も書かれたJosh Smithさん。Crack.NETの作者の方でもあります。

-訳ここから-

AttachedBehavior.jpg

導入 Introduction

この記事は添付ビヘイビアが何であるか、WPFアプリケーションでどう実装できるのかを解説します。この記事の読者はWPF、XAML、添付プロパティそしてModel-View-ViewModel(MVVM)パターンに多少親しんでいる必要があります。私の‘Simplifying the WPF TreeView by Using the ViewModel Pattern’という記事も読まれることを強くお勧めします。なぜなら、ここでの題材はその記事での題材の延長だからです。

This article explains what an attached behavior is, and how you can implement them in a WPF application. Readers of this article should be somewhat familiar with WPF, XAML, attached properties, and the Model-View-ViewModel (MVVM) pattern. I highly recommend that you also read my ‘Simplifying the WPF TreeView by Using the ViewModel Pattern’ article, because the material here is an extension of the material presented in it.

背景 Background

2008年の五月頃、私は‘Simplifying the WPF TreeView by Using the ViewModel Pattern’という記事を公開しました。その記事はMVVMパターンにフォーカスしていました。今朝、私は起きるとPascal Binggeliという方が記事のコメント欄に素晴らしい質問をされているのに気づきました。

Back in May of 2008, I published an article called ‘Simplifying the WPF TreeView by Using the ViewModel Pattern’. That article focused on the MVVM pattern. This morning I woke up to find that a fellow by the name of Pascal Binggeli had asked an excellent question on that article’s message board.

Pascalは、TreeViewと連携するViewModelオブジェクトがTreeViewItemを選択したときにどうやってそれをTreeViewコントロールの表示範囲にスクロールさせればよいかを知りたがっていました。この問いは十分にシンプルに見えますが、検討を重ねると、最初に想像していた程容易ではないのがわかるでしょう。目標、そして問題は、MVVMパターンの原則が破られないよう、選択されたTreeViewItemBringIntoView()メソッドを呼ぶコードを配置する適切な場所を見つけることです。

Pascal wanted to know how to scroll a TreeViewItem into the viewable area of the TreeView control when its associated ViewModel object selects it. That seems simple enough, but upon further examination, it is not quite as straightforward as one might initially expect. The objective, and problem, is to find the proper place to put code that calls BringIntoView() on the selected TreeViewItem, such that the principles of the MVVM pattern are not violated.

例えば、ユーザーが、TreeView内の、ユーザーが決定した検索文字列にマッチするテキストを表示する項目を探す場合を考えてみてください。検索ロジックがマッチする項目を見つけた場合、マッチするViewModelオブジェクトはそのIsSelectedプロパティをtrueに設定します。すると、データバインディングの魔術によってそのViewModelオブジェクトと連携するTreeViewItemは選択状態になります(即ち、そのIsSelectedプロパティもtrueにセットされます)。しかしながら、そのTreeViewItemは必ずしも表示範囲内に来るという訳ではありません。これは、ユーザーは検索文字列に一致する項目を見ることが出来ないということを意味します。PascalはViewModelがそれが選択状態であるかを決定するときにTreeViewItemが見える範囲に来ることを望んでいました。

For example, suppose that the user searches through a TreeView for an item whose display text matches a user-defined search string. When the search logic finds a matching item, the matching ViewModel object will have its IsSelected property set to true. Then, via the magic of data binding, the TreeViewItem associated with that ViewModel object enters into the selected state (i.e. its IsSelected property is set to true, too). However, that TreeViewItem will not necessarily be in view, which means the user will not see the item that matches their search string. Pascal wanted a TreeViewItem brought into view when the ViewModel determines that it is in the selected state.

ViewModelオブジェクトはTreeViewItemが存在するか、そしてそれにバインドされいるかについて感知しません。そのため、ViewModelオブジェクトにTreeViewItemを見える範囲に持ってこさせるのを望むのは筋違いです。この問題は今や、ViewModelがそれを選択状態においた時に、誰がTreeViewItemを見える範囲に持ってくる責任を負うのかという問題になりました。

The ViewModel objects have no idea that a TreeViewItem exists and is bound to them, so it does not make sense to expect the ViewModel objects to bring TreeViewItems into view. The question becomes, now, who is responsible for bringing a TreeViewItem into view when the ViewModel forces it to be selected?

ViewModelオブジェクトと視覚要素を連結するのは不自然さをもたらし、また、余計ですので、私達は確かにそのコードをViewModelに入れたくはありません。私達はそのコードをViewModelにバインドされたTreeViewのコードビハインドのあらゆる場所に入れたくありません。なぜなら、そうすることは、我々が初めにViewModelを使うことによって避けた問題のいくつかを再び引き起こすからです。私達は選択された時に自分自身を見える範囲に持っていくビルトインサポートを持ったTreeViewItemサブクラスを作成することは出来ますが、WPFの世界において、それは疑いようもなく、簡単な問題に対する重量級の解決策です。

We certainly do not want to put that code into the ViewModel because it introduces an artificial, and unnecessary, coupling between a ViewModel object and a visual element. We do not want to put that code in the code-behind of every place a TreeView is bound to a ViewModel, because it reintroduces some of the problems that we avoid by using a ViewModel in the first place. We could create a TreeViewItem subclass that has built-in support for bringing itself into view when selected, but, in the WPF world, that is definitely a heavy-handed solution to a lightweight problem.

私達は、この問題を簡単で再利用可能なやり方でどのようにエレガントに解決することが出来るでしょうか?

How can we elegantly solve this problem in a lightweight and reusable way?

添付ビヘイビア Attached Behaviors

今まで説明した問題の解決策は添付ビヘイビアを使うことです。オブジェクトに対する添付ビヘイビアはオブジェクトに何かそれ自身ではしようとしないことをさせることを単に意味しています。ここに私が‘Working with CheckBoxes in the WPF TreeView’という記事で書いた添付ビヘイビアの説明があります。

添付プロパティを公開するクラスから要素へのアクセスを得ることが出来るように、添付プロパティを要素にセットするというアイデアです。クラスが要素へのアクセスを得さえすれば、クラスは要素のイベントをフックすることができ、それらのイベント発生への反応において要素に普段はしないことをさせることが出来ます。それはサブクラスを作成して使うのに代わる非常に便利な選択肢であり、また非常にXAMLフレンドリでもあります。

あの記事では、デモアプリケーションは添付ビヘイビアを複雑な方法で使っていますが、この記事では、私達は添付ビヘイビアの使い方をシンプルに保ちました。背景と理論の話はこのぐらいにして、我らが友、Pascalが提示した問題を解決する添付ビヘイビアの作り方をみていきましょう。

The solution to the problem explained above is to use an attached behavior. Attaching a behavior to an object simply means making the object do something that it would not do on its own. Here is the explanation of attached behaviors that I wrote in my ‘Working with CheckBoxes in the WPF TreeView’ article:

The idea is that you set an attached property on an element so that you can gain access to the element from the class that exposes the attached property. Once that class has access to the element, it can hook events on it and, in response to those events firing, make the element do things that it normally would not do. It is a very convenient alternative to creating and using subclasses, and is very XAML-friendly.

In that article, the demo application uses attached behaviors in complicated ways, but in this article, we will keep it simple. Enough with the background and theory, let’s see how to create an attached behavior that solves the problem posed by our friend Pascal.

デモンストレーション Demonstration

この記事のデモアプリは、このページの頭でダウンロード可能ですが、‘Simplifying the WPF TreeView by Using the ViewModel Pattern’の記事で使用した検索デモを使っています。私はTreeViewにより多くの項目を追加したり、フォントサイズを大きくしたり、添付ビヘイビアを加えるなどの幾つかの変更をしました。添付ビヘイビアはTreeViewItemBehaviorという新しい静的クラス内に存在します。そのクラスは、TreeViewItemにセット出来るIsBroughtIntoViewWhenSelectedというBoolean型の添付ビヘイビアを公開します。これがTreeViewItemBehaviorクラスです:

This article’s demo app, which is available for download at the top of this page, uses the Text Search demo provided by the ‘Simplifying the WPF TreeView by Using the ViewModel Pattern’ article. I made a few changes, such as adding more items to the TreeView, increasing the font size, and adding an attached behavior. The attached behavior is in a new static class called TreeViewItemBehavior. That class exposes a Boolean attached property that can be set on a TreeViewItem, called IsBroughtIntoViewWhenSelected. Here is the TreeViewItemBehavior class:

 /// <summary>
/// Exposes attached behaviors that can be
/// applied to TreeViewItem objects.
/// </summary>
public static class TreeViewItemBehavior
{
    #region IsBroughtIntoViewWhenSelected

    public static bool GetIsBroughtIntoViewWhenSelected(TreeViewItem treeViewItem)
    {
        return (bool)treeViewItem.GetValue(IsBroughtIntoViewWhenSelectedProperty);
    }

    public static void SetIsBroughtIntoViewWhenSelected(
      TreeViewItem treeViewItem, bool value)
    {
        treeViewItem.SetValue(IsBroughtIntoViewWhenSelectedProperty, value);
    }

    public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty =
        DependencyProperty.RegisterAttached(
        "IsBroughtIntoViewWhenSelected",
        typeof(bool),
        typeof(TreeViewItemBehavior),
        new UIPropertyMetadata(false, OnIsBroughtIntoViewWhenSelectedChanged));

    static void OnIsBroughtIntoViewWhenSelectedChanged(
      DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        TreeViewItem item = depObj as TreeViewItem;
        if (item == null)
            return;

        if (e.NewValue is bool == false)
            return;

        if ((bool)e.NewValue)
            item.Selected += OnTreeViewItemSelected;
        else
            item.Selected -= OnTreeViewItemSelected;
    }

    static void OnTreeViewItemSelected(object sender, RoutedEventArgs e)
    {
        // Only react to the Selected event raised by the TreeViewItem
        // whose IsSelected property was modified.  Ignore all ancestors
        // who are merely reporting that a descendant's Selected fired.
        if (!Object.ReferenceEquals(sender, e.OriginalSource))
            return;

        TreeViewItem item = e.OriginalSource as TreeViewItem;
        if (item != null)
            item.BringIntoView();
    }

    #endregion // IsBroughtIntoViewWhenSelected
}

上の添付ビヘイビアは基本的にTreeViewItemSelectedプロパティをフックし、イベントが発生した時に項目のBringIntoView()を呼ぶ手の込んだ方法です。このパズルの最後のピースはTreeViewItemBehaviorクラスがTreeViewの各TreeViewItemへの参照を得る方法です。我々は以下のようにSetterTreeView内のTreeViewItemの各項目に適用されたStyleに加えることで実現しました:

The attached behavior seen above is basically just a fancy way of hooking the Selected property of a TreeViewItem and, when the event is raised, calling BringIntoView() on the item. The final piece of this puzzle is seeing how the TreeViewItemBehavior class gets a reference to every TreeViewItem in the TreeView. We accomplish that by adding a Setter to the Style applied to every item in the TreeView, as seen below:

 <TreeView.ItemContainerStyle>
  <Style TargetType="{x:Type TreeViewItem}">
    <!--
    This Setter applies an attached behavior to all TreeViewItems.
    -->
    <Setter 
      Property="local:TreeViewItemBehavior.IsBroughtIntoViewWhenSelected" 
      Value="True" 
      />

    <!-- 
    These Setters bind a TreeViewItem to a PersonViewModel.
    -->
    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
    <Setter Property="FontWeight" Value="Normal" />
    <Style.Triggers>
      <Trigger Property="IsSelected" Value="True">
        <Setter Property="FontWeight" Value="Bold" />
      </Trigger>
    </Style.Triggers>
  </Style>
</TreeView.ItemContainerStyle>

デモアプリケーションが読み込まれると、検索語は自動的に「Y」という文字にセットされます。Findボタンを何度か押すと、そのたびに項目が選択されるのがわかるでしょう。その項目は「Y」を含んでおり、見える範囲にスクロールされます。選択された時に項目が見える範囲にスクロールされるということは添付ビヘイビアがうまく働いているということです。

When the demo application loads up, the search text will be set to the letter Y automatically. Click the Find button a few times, and you will see that each time an item is selected, it will contain the letter Y and will scroll into view. The fact that it scrolls into view upon being selected means that the attached behavior is working properly.

結論 Conclusion

オブジェクトのイベントをフックし、発生したときに何かをするのは、どれだけ想像力を働かせても、確かに飛躍的なイノベーションではありません。その意味で、添付ビヘイビアは同じことをするありきたりの方法に過ぎません。しかしながら、このテクニックの重要性は名前を持ったことであり、これはデザインパターンのもっとも重要な側面かもしれません。加えて、システムの別の部分を変更することなしに、添付ビヘイビアを作成し、どんな要素にも適用することが出来ます。添付ビヘイビアはPascal Binggeliが提起した問題、そして非常に多くの別の問題への綺麗な解決策です。添付ビヘイビアは道具箱に備えておくべき非常に便利な道具です。

Hooking an event on an object and doing something when it fires is certainly not a breakthrough innovation, by any stretch of the imagination. In that sense, attached behaviors are just another way to do the same old thing. However, the importance of this technique is that it has a name, which is probably the most important aspect of any design pattern. In addition, you can create attached behaviors and apply them to any element, without having to modify any other part of the system. It is a clean solution to the problem raised by Pascal Binggeli, and many, many other problems. It’s a very useful tool to have in your toolbox.

参考文献 References

The Attached Behavior Pattern – John Gossman

Simplifying the WPF TreeView by Using the ViewModel Pattern – Josh Smith

Working with CheckBoxes in the WPF TreeView – Josh Smith

改定履歴 Revision History

  • August 30, 2008 – Created the article

ライセンス License

この記事と、関連するソースコードやファイルはThe Code Project Open License (CPOL)の下でライセンスされています。

(訳注:和訳に際してJosh Smithさんから許可を頂いています。)

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

著者について About the Author

Joshはソフトウェア開発者です。C#とXAMLは彼の愛好する表現方法です。

彼はInfragisticsにおいてエクスペリエンスデザインアプリケーションエンジニアとして働いており、.NETのデスクトップ環境をより良き場所にする手助けをしています。

彼はJ.S. バッハの曲をピアノで演奏もします。

とりわけ、彼は彼の素晴らしいガールフレンドと共に過ごすことを愛しています。

Crack.NETという彼のランタイムデバッキング、スクリプティングツールをここから手に入れよう![^].

彼のWPF.JoshSmithライブラリをここからダウンロードしよう[^]

ここから彼のWPFブログをチェックすることが出来ます[^].

ここから彼のWPFツアーに参加することが出来ます[^].

Mole for Visual Studioという彼が手掛けたパワフルなデバッガビジュアライザをここからチェックできます[^].

彼のMicrosoft MVPプロフィールはここで見ることが出来ます[^].

Occupation: 
ソフトウェア開発者 (上級)

Company:

Infragistics, Inc.

Location:

United States アメリカ合衆国

Josh creates software. C# and XAML are his preferred modes of expression.

He works at Infragistics as an Experience Design Application Engineer, helping to make the .NET desktop world a better place.

He also plays the music of J.S. Bach on the piano.

Most of all, he loves being with his wonderful girlfriend.

Get his runtime debugging and scripting tool, called Crack.NET, right here[^].

Download his WPF.JoshSmith library here[^]

You can check out his WPF blog here[^].

You can take his guided tour of WPF here[^].

You can check out a powerful debugger visualizer he worked on called Mole for Visual Studio here[^].

His Microsoft MVP profile can be viewed here[^].

Occupation:

Software Developer (Senior)

Company:

Infragistics, Inc.

Location:

United States United States

MAR
9

ItemsControl.ItemsSourceに指定するものは

Published:2009-03-09 23:29:10 UTC
WPF

M-V-VMパターンを試してみてのメモメモ。

M-V-VMパターンでは、ItemsControl系のControlのItemsSourceには、ItemsControlに表示したい項目を表すViewModelを列挙するIEnumerableインターフェイスの実装クラスがよさそう。

なぜかというと、ItemsControlの項目のDataContextはItemsSourceに指定されたIEnumenrableインターフェイスが列挙したものが自動的にセットされるので、項目内でボタンなどにCommandをバインドしたい場合に、Commandをプロパティとして公開してくれるViewModelが必要になるなど、DataContextにViewModelがセットされている必要が出てくるから。

このエントリは、M-V-VMパターンについてのエントリを訳してみる試みの三本目です。前回のDavid Hill’s WebLog : The ViewModel Patternという記事の続きの記事です。前回の記事で言及されていた、M-V-VMパターンに基づく、CollectionViewのSilverlightでの実装例を扱った記事です。元記事はこちら:David Hill’s WebLog : CollectionViewModel

-訳ここから-

この投稿では、選択ベースのコントロール(例えばListBoxやComboBox)のソート、フィルタリング、グルーピング、選択状態のトラッキングを可能にするSilverlight用のICollectionViewの実装について解説していきます。実装をデモを行うために、ユーザーがフィルタリング、ソート、グルーピング、選択ができる、アイテムのカタログを表示するサンプルアプリケーションを作成しました。サンプルはここで実行できます。また、ここからソースコードをダウンロードし、ユニットテストを完了させることができます。

In this post, I’m going to describe an implementation of ICollectionView for Silverlight that allows you to add sorting, filtering, grouping and selection tracking to any Selector based control (such as ListBox or ComboBox). To demonstrate the implementation, I’ve built a sample application that displays a catalog of items that the user can filter, sort, group and select. You can run the sample here, and you can download the source code, complete with unit tests, from here.

前回の記事では、ViewModelパターンの主要な特徴を概説し、WPFのCollectionViewは実際にCollection-basedモデルにViewModelパターンを適用した良い例であると述べました。この記事では、なぜCollectionViewがViewModelであると考えるかをより詳しく説明します。実際、この記事で解説するテクニックはどのような種類のViewModelの実装にも使用でき、CollectionViewのシナリオだけに限られることはありません。

In my last post I outlined the key features of the ViewModel pattern and said that WPF’s CollectionViews were actually good examples of the ViewModel pattern applied to collection-based models. In this post I explain in more detail why I think of CollectionViews as ViewModels. In fact, the techniques described in this post can be used to implement any kind of ViewModel and are not restricted to just CollectionView scenarios.

CollectionViews In WPF

Silverlightでの実装に入る前に、WPFのCollectionViewサポートについて少しみてみましょう。MSDNにはWPFのCollectionViewの実に良い概要がここに示されており、ユーザーがオークションサイトの商品のコレクションを閲覧したり編集したり出来る、関連したサンプルアプリケーションもここにあります。我々の観点から見て、興味深いことは、それがCollectionViewSourceクラスとICollectionViewインターフェイスを実装した関連づけられたクラスの基本的な能力を示し、また、どのようにそれらが一緒になって機能するかを示していることです。コードを見れば、アプリケーションのUIがCollectionViewSourceにバインドされたListBoxを持っているのが分かるでしょう。そしてCollectionViewSourceは、こちらはAuctionItemのコレクションに対して参照を持っているのがわかるでしょう。

Before we get stuck into the Silverlight implementation, let’s take a quick look at WPF’s CollectionView support. MSDN has a pretty good overview of CollectionViews in WPF here, and a related sample application here that allows the user to view and edit a collection of items for sale on an auction site. The interesting thing from our point of view is that it shows the basic capabilities of the CollectionViewSource class, the related classes that implement the ICollectionView interface, and shows how they all work together. If you look at the code, you’ll see that the application’s UI has a ListBox that’s bound to a CollectionViewSource, which in turn references a collection of AuctionItems.

<Window.Resources>
	<CollectionViewSource x:Key="listingDataView" 
		Source="{Binding Source={x:Static Application.Current},Path=AuctionItems}" />
</Window.Resources>

<ListBox Name="Master" 
	ItemsSource="{Binding Source={StaticResource listingDataView}}">
</ListBox>

CollectionViewSourceはICollectionViewインターフェイスを実装したオブジェクトを出す、ある種のXAMLフレンドリーなファクトリとして振舞います。同様に、コントロールをコレクションに直接バインドすることができ、デフォルトのICollectionViewオブジェクトは自動的に作成されます。

CollectionViewSource just acts as a kind of XAML friendly factory for dishing up objects that implement the ICollectionView interface. You can also bind a control directly to a collection and a default ICollectionView object will automatically be created.

WPFには二つの主要なICollectionViewインターフェイスの実装があります。IBindingListView/IBindingListコレクション、もしくはIListコレクションをラッピングしたBindingListCollectionViewと、ListCollectionViewです。上のサンプルでは、下層にあるAuctionItemのコレクションは(ObservableCollectionから派生した)IListに基づいており、ListCollectionViewオブジェクトは自動的に作成され、ItemSourceを通じてListBoxにバインドされます。

There are two main implementations of the ICollectionView interface in WPF – BindingListCollectionView, and ListCollectionView for wrapping IBindingListView/IBindingList collections, or IList collections respectively. In the sample above, the underlying collection of AuctionItems is based on IList (it derives from ObservableCollection) so a ListCollectionView object is created automatically and bound through ItemsSource to the ListBox.

UIと下層にあるCollectionViewの同期を常に取るために、ListBoxコントロールはCollectionViewとICollectionViewインターフェイスを通じて情報のやり取りを行います。この情報のやり取りは双方向です。ユーザーがUI上の項目を選択した場合、CollectionViewのCurrentItemプロパティとCurrentPositionプロパティは自動的に更新されます。CollectionViewの選択された項目が変化した場合は(例えば、アプリケーションロジックによって)、UIは、UI上の対応する項目を選択された状態で表示するために更新されます。ユーザーがUIを通じて項目をフィルタリング、ソート、グルーピングを行う場合、CollectionViewはそれに従って更新されます。何らかのアプリケーションロジックによってCollectionViewがフィルタリング、ソート、グルーピングをされた場合、UIは自動的に更新されます。この自動的なコントロールとCollectionViewの間の双方向の情報のやり取りがそれらをとても使いやすくしているのです。

The ListBox control interacts with the CollectionView through the ICollectionView interface so that the UI and the underlying CollectionView are always kept in sync. This interaction is two-way. If the user selects an item in the UI, the CollectionView’s CurrentItem and CurrentPosition properties are updated automatically. If the CollectionView’s selected item is changed (say, due to application logic), the UI is updated to show the corresponding item in the UI as selected. If the user decides to filter, sort or group the items through the UI, the CollectionView is updated accordingly. If the CollectionView is filtered, sorted or grouped due to some application logic, then the UI is automatically updated. It’s this automatic two-way interaction between the control and the CollectionView that makes them so useful.

CollectionView == ViewModel

どのICollectionViewの実装を使っても、これらのクラスは下層にあるコレクションをラップし、現在の選択をトラッキングするICollectionViewの実装の上にViewを配置し、何らかのUI内で表示する準備の整った下層にあるコレクションをソートし、グルーピングし、フィルタリングします。これらICollectionViewの実装はUI(View)と下層にあるオブジェクトのコレクション(Model)の間に位置し、プレゼンテーションのロジックと状態を管理する仲介者として振舞います。言い換えれば、これらはViewModelなのです!

Whatever implementation of ICollectionView you use, these classes wrap the underlying collection and provide a ‘view’ on top of it that tracks the current selection, and sorts, groups or filters the underlying collection ready for display in a UI of some sort. They sit between the UI (the View) and the underlying collection of objects (the Model) and act as a go-between to manage presentation logic and state. In other words, they are ViewModels!

ICollectionViewの実装について少し詳しく調べさせてください。―プレゼンテーションのロジックと状態によって、私はアプリケーションの動作を決めるロジックと状態を参照しています。しかし、その動作はアプリケーションのビジネスロジックとデータ、あるいはアプリケーションの見た目に固有ではありません。この例では、プレゼンテーションロジックはコレクション内の項目の選択、フィルタリング、ソート、グルーピングを管理します。これはアプリケーション内のデータに影響せず、その完全性を確保します。また、我々はこの情報がバックエンドのデータベースに残るのを望まないので、これは明らかにビジネスロジックでも状態でもありません。

Let me elaborate on that a little – by presentation logic and state I am referring to logic and state that defines the behavior of the application, but which is not specific to the application’s business logic and data or to the visual representation of the application. In this case, the presentation logic manages the selection, filtering, sorting, grouping of items in a collection. This doesn’t affect the data in the application or ensure its integrity, and we don’t want to persist this information to a back-end database, so it’s clearly not business logic or state.

ICollectionViewの実装はUIとも関係ありません。―はい、それはUIを駆動しますが、しかし我々はICollectionViewの実装をたくさんの様々な方法で表示します。そのため、それはUIに固有ではありません。「選択された項目」の状態をUIに固有だと誤って扱う開発者も居ますが、そうすることはユニットテストの非常に難しい致命的なアプリケーション動作に繋がります。例えば、支出報告書が承認されたか却下されたかのリストが表示されたアプリケーションがあったとして、そのアプリケーションが正しい支出報告書を承認するか却下するかテストしたいと思いませんか?

It also has nothing to do with the UI – yes, it drives the UI, but we can visually represent it in many different ways, so it’s not specific to the UI. Some developers mistakenly treat ‘Selected Item’ state as specific to the UI but that leads to critical application behavior that is very hard to unit test – for example, if you had an app that displayed a list of expense reports to approve or deny, wouldn’t you want to test that the application approves or denies the right expense report?!?

SilverlightにおけるCollectionView CollectionViews In Silverlight

残念なことに、Silverlight 2.0はCollectionViewが初期状態ではサポートされていません。ICollectionViewインターフェイスは定義されていますが、初期状態ではこのインターフェイスのパブリックな実装はありません。同様に、ICollectionViewを組み込みサポートするSilverlight 2.0コントロールはなく、使えるICollectionViewの実装があったとしても、コントロールは自動的にCollectionView内の選択された項目をトラッキングしたり更新したりはしません。

Unfortunately Silverlight 2.0 doesn’t support CollectionViews out of the box. The ICollectionView interface is defined, but there are no public implementations of this interface that you can use out of the box. Also, none of the Silverlight 2.0 controls have any built-in support for ICollectionView so even if there was an implementation that we could use, the controls won’t automatically track or update the selected item in the CollectionView.

メモ:DataGridコントロール(それも、Silverlight 2.0に搭載されたものではなくDecember release)は例外です。DataGridをコレクションにバインドすれば、DataGridは選択された項目をトラッキング、更新する内部的なCollectionViewを生成し、列ヘッダをクリックすることによるコレクションのソートのサポートを提供します。残念なことに、この実装は他のコントロールでは用意されていません。

Note: The DataGrid control (specifically, the December release, NOT the one that ships with Silverlight 2.0) is an exception – if you bind the DataGrid to a collection, it will create an internal CollectionView that tracks and updates the selected item and provides support for sorting the collection by clicking on the column headers. Unfortunately, this implementation is not available for use with other controls.

それで、SilverlightでCollectionViewのサポートを受けるためには、二つのものが必要になります。ICollectionViewインターフェイスの実装と、(コードビハインドファイル内のコード無しで)UI内のコントロールがICollectionViewの実装と情報のやり取りをする何らかの方法です。この投稿での実装はこれらの問題両方に取り組みます。ではそれぞれ順に見ていきましょう。

So, to have CollectionView support in Silverlight we need two things – an implementation of the ICollectionView interface, and some way for the controls in our UI to interact with it (without code in the View’s code-behind file!). The implementation in this post tackles both of these issues. Let’s take a look at each in turn.

The CollectionViewModel Class

サンプルコード内のCollectionViewModelクラスはICollectionViewインターフェイスを実装し、下層にあるコレクションモデルの選択、フィルタリング、ソート、グルーピングをサポートするのに必要とされるロジックを実装します。私が‘CollectionViewModel’という名前を選んだのは、第一に、これがコレクションに対するViewModelであること、第二に、WPFとSilverlightの既存のクラスと衝突しないためです。

The CollectionViewModel class in the sample code implements the ICollectionView interface and the required logic to support selection, filtering, sorting and grouping for an underlying collection model. I chose the name ‘CollectionViewModel’ because 1) it’s a ViewModel for collections!!, and 2) so that it wouldn’t clash horribly with any existing classes in WPF or Silverlight.

このクラスの実装についてのいくつかのメモ:

  • 下層にあるIEnumerable<T>型のコレクションへのViewModelを提供するのに使うことが出来るのはジェネリッククラスです。
  • 主にINotifyCollectionChangedインターフェイスを一から実装する必要がないように、また、View内のControlがこのCollectionViewModelクラスに対してバインドされるのを簡単にするために、CollectionViewModelクラスはObservableCollection<T>から派生します。
  • このクラスをWPFで使うこともできるでしょう。しかし私はまだテストしていません。もし試されたなら、どうなったか教えてください。
  • このクラスはWPFのListCollectionViewクラスとおおよそ同等のものです。しかしクラスの階層と実装するインターフェイスにおいていくつかの小さな違いがあります。
  • フィルタリング、ソート、グルーピングの機能のために、Linq表現を使用しています。私は自分で一からこの機能を実装したくありませんでしたし、加えて、Linq表現で遊んでみたかったので、これは良い機会に思えたのです。この実装が効率的であるのか、多少なりとも動的Linq表現のベストプラクティスを表しているのかは私はよく分かりません…。 CollectionViewModelクラスは、Viewのコードビハインド、あるいは親のViewModel内でのコマンド無しでのUIを通じた前後の選択、ソート、グルーピングなどを出来るようにするたくさんの共通コマンドを実装しています(以下を参照ください)。

Some notes on the implementation of this class:

  • It’s a generic class and can be used to provide a ViewModel for any underlying collection of type IEnumerable<T>.
  • It derives from ObservableCollection<T>, mainly so that I didn’t have to implement the INotifyCollectionChanged interface from the ground up, and to make it easy for the controls in the View to bind against it.
  • You should be able to use this class in WPF, but I haven’t test it yet. If you try it, please let me know how it goes.
  • This class is roughly the equivalent of the ListCollectionView class in WPF, but there are some minor differences in the class hierarchy and the interfaces that it implements.
  • It uses Linq Expressions to implement the filtering, sorting and grouping functionality. I really didn’t want to implement this functionality myself from the ground up, plus I wanted to play with Linq Expressions so this seemed like a good opportunity. I am not sure that the implementation is efficient or in any way represents best practice for dynamic Linq Expressions though…
  • The CollectionViewModel class implements a number of common commands (see below) that allow next/previous selection and sorting and grouping through the UI, without requiring View code-behind or commands in a parent ViewModel.

では、このクラスがサンプルアプリケーションでどのように使われているか見ていきましょう。

Let’s see how this class is used in the sample application.

サンプルアプリケーションはショッピングカタログを表示し、ユーザーはカタログ内の項目のフィルタリング、ソート、グルーピングをすることが出来ます。アプリケーションはCatalogViewModelにバインドされた一つのCatalogViewを持ちます。CatalogViewはCatalogViewModelのCatalogプロパティにバインドされたListBoxコントロールを持ちます。このプロパティはICollectionView型で、単純にCollectionViewModelクラスのインスタンスを返します。

The sample displays a shopping catalog and lets the user filter, sort, and group the items in the catalog. The app has a single CatalogView that’s bound to a CatalogViewModel. The CatalogView has a ListBox control that’s bound to the Catalog property of the CatalogViewModel. This property is of type ICollectionView and simply returns an instance of the CollectionViewModel class.

CatalogViewはこのように定義されます。

The CatalogView is defined like this:

<UserControl x:Class="Helix.Samples.CollectionViewModel.CatalogView" ...>
    <UserControl.DataContext>
        <h:CatalogViewModel />
    </UserControl.DataContext>
    ...
    <ListBox ItemsSource="{Binding Catalog}" ...>
    </ListBox>
    ...
</UserControl>

CatalogViewModelはこのようになります。

The CatalogViewModel looks like this:

public class CatalogViewModel : INotifyPropertyChanged
{
    private ObservableCollection<CatalogItem> _catalogModel;
    private ICollectionView _catalogViewModel;
 
    public CatalogViewModel()
    {
        _catalogModel = new ObservableCollection<CatalogItem>();
        ...
        _catalogViewModel = new CollectionViewModel<CatalogItem>( _catalogModel );
    }

    public ICollectionView Catalog
    {
        get { return _catalogViewModel; }
    }
    ...
}

2、3点メモ:CatalogViewModelはViewによって作成され、Viewのデータコンテキストとしてセットされます(これはView-First composition パターンのシンプルな実装方法です)。CatalogViewModelのコンストラクタはモデルを作成し、それをダミーデータで埋めます。実際のアプリケーションでは、モデルはウェブサービスによって装着され、データアクセスサービスコンポーネントを通じて作成されるでしょう。

A couple of things to note: The CatalogViewModel is created by the view and set as the View’s data context (this is a simple way to implement the View-First composition pattern). The CatalogViewModel’s constructor creates the model and fills it with dummy data. In a real application, the model would likely be populated via a web service and created through a data access service component.

ベールの下には Under The Covers

では、どのようにCollectionViewModelは動くのでしょうか?CollectionViewModelは単にICollectionViewインターフェイスを実装しただけなので、十分にシンプルなクラスです。唯一の複雑さはフィルタリング、ソート、グルーピングの内部実装だけです。

So how does the CollectionViewModel work? In a way, it’s a simple enough class because it really just implements the ICollectionView interface. The only complication is the internal implementation of the filtering, sorting and grouping.

ICollectionViewインターフェイスの利用者はフィルタや、一つかそれ以上のソートやグルーピングの評価基準を設定することが出来ます。Linqは明らかにこの種のことに優れていますが、実行時に一つかそれ以上の判断基準をを持ちうるので、下層にあるコレクションに対して簡単なLinqクエリを書くことはできません。Linq表現を使ってオンザフライで動的クエリを構築しなければなりません。それで、例えば複数のソート基準がある場合は、クエリは下層にあるコレクションへの‘SortBy + n*(ThenBy)’スタイルのクエリと一致します。あなたが複数レベルでのグルーピングを必要としたかは分かりませんが、ICollectionViewはサポートしているので、可能だとは思うのです…。

The consumer of the ICollectionView interface can specify a filter, or one or more sorting or grouping criteria. Linq is clearly good at this kind of stuff, but since there can be one or more criteria at runtime we can’t just write a straightforward Linq query against the underlying collection. We have to build a dynamic query on the fly using Linq Expressions. So if we have multiple sort criteria, for example, the query equates to a ‘SortBy + n*(ThenBy)’ style query on the underlying collection. I’m not sure whether you’d ever want multiple levels of grouping, but the ICollectionView supports it so it’s possible I guess…

Viewとの統合 Interacting With The View

はい、半分まで来ましたね。ここまで説明したコードを実行すれば、カタログの項目がListBox内に表示されるでしょう。そして、プログラムによってフィルタリング、ソート、グルーピングの評価基準を加えることが出来るでしょう。しかし、ListBox内の項目を選択した時、CollectionViewModelのCurrentItemプロパティとCurrentPositionプロパティは更新されません。加えて、ユーザーにどうフィルタリング、ソート、グルーピングをするか選ばせるUIを提供したいならば、Viewのコードビハインドファイルに、テストするには汚いコードをたくさん加えなければなりません。うんざりだ!

OK – we’re about half way there. If you run the code described above, you’ll get the catalog items to show up in the ListBox, and you can programmatically add filtering, sorting and grouping criteria, but as you select items in the ListBox the CurrentItem & CurrentPosition properties on the CollectionViewModel won’t be updated. In addition, if you want to provide UI that allows the user to choose how to filter, sort or group, you’ll likely have to add lots of nasty to test code to the code-behind file of the View. Blecch!

選択された項目のトラッキング Selected Item Tracking

次のゴールは、ユーザーがListBox内の項目を選択した場合、CollectionViewModelの現在の項目が更新されるよう、またその逆も成り立つよう、ListBoxを下層にある、ListBoxがバインドされたCollectionViewModelにリンクさせることです。これはコードビハインド内でフックすることでも実現できますが、XAML内で宣言的にこの動作を指定出来た方がもっと良いでしょう。ゴールはView内のコードビハインドを減らすか無くすかであることを忘れないでください。

The next goal is to link the ListBox to the underlying CollectionViewModel that it is bound to, so that as the user selects items in the ListBox the current item in the CollectionViewModel is updated, and vice versa. We could do this hook-up in code behind but it would be much better if we could specify this behavior declaratively in the XAML. Remember, the goal is to reduce or eliminate any code behind in the View.

添付プロパティはSilverlightにおける一般的な拡張メカニズムです。添付プロパティは、他のコントロールが何らかの方法で設定できるよう、単純な値をコントロールと連携させるのに一番よくつかわれています。Grid.Column添付プロパティを思い浮かべて下さい。その親Gridコントロールが適切に子コントロールを配置できるよう、それはcolumnの値を子コントロールに割り当てるために使われます。

Attached Properties are a popular extensibility mechanism in Silverlight. They are used most often to associate a simple value to a control so that some other control can lay it out in some way – think of the Grid.Column attached property, which is used to assign a column value to a control so that its parent grid control can lay it out appropriately.

添付プロパティはあなたが考えていたより、実際はより一層パワフルであることが明らかになりました。添付プロパティは添付されたコントロールの動作や見た目を変更するものだと定義することが出来ます。この種の添付プロパティはしばしば添付ビヘイビア、あるいは単にビヘイビアと呼ばれます。サンプルは‘SynchronizeCollectionViewBehavior‘と呼ばれるビヘイビアを実装していまして、これは現在選択された項目を尊重しながらListBoxコントロールとCollectionViewModelの同期を保つよう、ListBoxコントロールをCollectionViewModelに接続させます。

It turns out that attached properties are actually much more powerful than you might think. You can define an attached property that alters the behavior or appearance of the control to which it is attached. This kind of attached properties are often called attached behaviors, or just behaviors. The sample implements a behavior called ‘SynchronizeCollectionViewBehavior‘ that links the ListBox control to the CollectionViewModel so that the two are kept in sync with respect to the currently selected item.

ビヘイビアはこう使います。

You use the behavior like this.

<ListBox ItemsSource="{Binding Catalog}" ...
         h:SelectionChanged.SynchronizeCollectionView="{Binding Catalog}"/>

このコードを今実行すると、ListBoxの項目を選択した場合、CollectionViewModel内の現在の項目は更新されます。また、プログラムからCollectionViewModelの項目を選択した場合は(例えばMoveCurrentToPositionメソッドを使って)、 ListBox内の一致する項目が選択されます。そしてこれら全てはコードビハインドなしで実現されます!

If you run the code now, as you select items in the ListBox, the current item in the CollectionViewModel is updated. And if you programmatically select an item on the CollectionViewModel (say using the MoveCurrentToPosition method) the corresponding item in the ListBox is selected. And all with no code-behind!

ベールの下には Under The Covers

これはどのように働くのでしょうか?SelectionChangedクラス(Selector.SelectionChanged.csファイル内)はSynchronizeCollectionViewという添付プロパティを定義します。この添付プロパティによって開発者はソースであるICollectionViewオブジェクトが対象のコントロールと同期されるよう指定できます。このクラスと、このクラスが定義する添付プロパティは、上に示したSelectionChanged.SynchronizeCollectionView添付プロパティ構文を定義します。この命名規約は後でCommandに使っているのと同じで‘event.action’パターンに従います。

How does this work? The SelectionChanged class (in the Selector.SelectionChanged.cs file) defines an attached property SynchronizeCollectionView. This attached property allows the developer to specify the source ICollectionView object to be synchronized with the target control. This class and the attached property it defines together define the SelectionChanged.SynchronizeCollectionView attached property syntax shown above. This naming convention follows an ‘event.action’ pattern that we’ll also be using for commands below.

XAMLを通じてこの値がセットされた時、SynchronizeCollectionViewBehaviorクラスのインスタンスが作成され、ターゲットコントロールに添付されます。それが添付された時、このビヘイビアは対象のコントロールのSelectionChangedイベントを購読し(それで、それはCollectionViewModelをユーザーがUIを操作したときに更新することが出来るのです)、また、ソースであるCollectionViewModelのCurrentChangedイベントを購読します(それで下層のCollectionViewModelが変化したときに、それは対象のコントロールのUIを更新することが出来ます)。以下のダイアグラムはこれがどのように働くかを示しています。

When this value is set via XAML, an instance of the SynchronizeCollectionViewBehavior class is created and ‘attached’ to the target control. When it is attached, this behavior subscribes to the target control’s SelectionChanged event (so it can update the CollectionViewModel as the user interacts with the UI) and subscribes to the CurrentChanged event on the source CollectionViewModel (so it can update the target control’s UI as the underlying CollectionViewModel is changed). The following diagram shows how this works.

SyncBehavior

再利用機会を増やすために、SynchronizeCollectionViewBehaviorクラスはSelectionChangedBehaviorBaseクラスから派生します。そしてSelectionChangedBehaviorBaseクラスはビヘイビア基本クラスから派生します。これら二つの基本クラスを使えば、他のSelectionChangedビヘイビアやもっと一般的なビヘイビアを実装することが出来ます。

To improve re-use opportunities, the SynchronizeCollectionViewBehavior class derives from a SelectionChangedBehaviorBase class, which in turn derives from a Behavior base class. You can implement other SelectionChanged behaviors, or more general behaviors, using these two base classes.

コマンド Commands

さて、もう我々はListBoxとCollectionViewModelの選択項目の同期を済ませました。次はCommandに移りましょう。我々のサンプルアプリケーションでは、Viewはユーザーに在庫のあるアイテムだけを表示し、説明や価格によるソート、レーティングによるグルーピングを許すUIを提供します。ユーザーは同様にリスト内の前後の項目をボタンのクリックにより、選択することが出来ます。ViewはUIを定義しますが、我々はビヘイビアは我々がテスト可能なViewModel内で定義されるのを望みます。それゆえ課題はどのようにView内のUIを、Viewのコードビハインド無しでViewModel内に定義された符合するするビヘイビアに繋ぐかです。

So now that we have the ListBox and CollectionViewModel selected items synchronized, we can turn our attention to commands. In our sample application, the View provides UI that allows the user to show in-stock items only, or to sort by description or price, or to group by rating. The user can also select the next or previous items in the list by clicking buttons. The View defines the UI but we want the behavior to be defined in the ViewModel where we can test it. So the challenge is how to connect the UI in the View to the corresponding behavior defined in the ViewModel(s) without having any code-behind in the View?

Prismはこの問題に対して、上で示したのと全く同じ添付プロパティ/ビヘイビアのパターンを使うことで解決策を提供しています。我々はあれを、コントロールをViewのViewModel内で定義されたコマンドハンドラに繋ぐために使うことが出来ます。PrismはButtonBaseコントロールがデータバインディングを通じてコマンドハンドラに繋ぐために使うことが出来るClick.Commandビヘイビアを提供します。ユーザーがカタログ内を前後に行ったり来たりするのに使うことのできるボタンのために、Click.Command添付ビヘイビアをそれらに対してこのようにシンプルにセットすることが出来る。

Prism provides a solution to this problem using the exact same attached property/behavior pattern described above. We can use that to hookup controls to command handlers defined in the View’s ViewModel. Prism provides a Click.Command behavior which can be used to hook any ButtonBase control (essentially all controls that fire a Click event) to a command handler through data binding. For the buttons that allow the user to step forwards or backwards through the catalog, we can simply set the Click.Command attached behavior on them like this:

<Button Content="Next"
    p:Click.Command="{Binding Path=Catalog.SelectNextCommand}"/>

前/後のCommandはカタログに対するCollectionViewModelに実際に実装されていることに注意して下さい。これらはCollectionViewModelに持たせておくと実に便利なジェネリックコマンドで、ユーザーがコレクションを走査したい場合は、どんなシナリオでも役に立つでしょう。

Note that the Next and Previous commands are actually implemented on the CollectionViewModel for the Catalog. These are pretty useful generic commands to have on a CollectionViewModel and can be useful in any scenario where the user wants to step through the collection.

CollectionViewModelクラスは他に二つの便利はCommandを実装します。SortByとGroupByです。これらのCommandは文字列パラメタを取り、ViewがシンプルなソートとグルーピングをViewのViewModel内のコード無しで素早く簡単に出来るようにします。サンプルはこれら二つのコマンドをこのように使っています。

The CollectionViewModel class implements two other useful commands – SortBy and GroupBy. These commands take a string parameter and allow the View to perform simple sorting and grouping quickly and easily without requiring any code in the View’s ViewModel. The sample uses these two commands like this:

<Button Content="Sort By Description"
    p:Click.Command="{Binding Path=Catalog.SortByCommand}"
    p:Click.CommandParameter="Description" />

PrismはViewModelのCommandにボタンをバインディングするための独創的な解決策を提供します。適切な添付プロパティを定義することによって、これを色々なコントロールやイベントをサポートするように拡張することをが出来ます。これをどうすれば良いかを示すために、サンプルアプリケーションは、チェックボックスをユーザーがViewをフィルタし、在庫のアイテムだけが表示されるようするために使っています。そのチェックボックスコントロールはCheckedCommandBehaviorクラスを使い、ViewModelのFilterInStockコマンドにアタッチされています。これを使うことで、チェックボックスをこのようにCommandにフックすることが出来ます。

Prism provides an out-of-the box solution for binding buttons to commands on a ViewModel. You can extend this to support any control or event by defining a suitable attached property. To show you how to do this, the sample application uses a check box to allow the user to filter the view so that only items that are in stock are shown. The check box control is attached to the ViewModel’s FilterInStock command using a CheckedCommandBehavior class. Using this, we can hook-up the check box to the command like this:

<CheckBox Content="Show In Stock Only"
    h:Checked.Command="{Binding FilterInStockCommand}"/>

全ての統合 Putting It All Together

以下のダイアグラムはサンプルアプリケーションがどう統合されているかを示しています。

The following diagram shows how the sample app is put together.

CollectionViewModel Sample

ViewのListBoxをComboBoxや(December Releaseの)DataGrid、選択ベースのどのコントロールにも変更することが出来、アプリケーションの残りの部分はそのまま動くでしょう。ViewのXAMLは実にシンプルで複雑なトリガや心配すべきバインディング表現は存在しません。同様に、アプリケーションのコードをみても、同じく実にシンプルです。全ての複雑なコードは再利用可能なクラスにカプセル化されています。これによりUIデザイナーはViewModelの上にエクスペリエンスを創るのを非常に簡単に出来、またViewModelはアプリケーション開発者によって独立して開発、テストをすることが出来ます。

You can switch out the ListBox in the View for a ComboBox or a DataGrid (December Release) or any Selector based control and the rest of the application will just work. The XAML in the View is pretty simple and there are no complicated triggers or binding expressions to worry about. Similarly, if you look at the application’s code, it’s also very simple – all of the complicated code is encapsulated in re-usable classes . This makes it very easy for a UI designer to create an experience on top of a ViewModel, which can be independently developed and tested by an application developer.

Helixについてのメモ A Note On Helix

あなたは、サンプルアプリケーションには、より汎用的なクラスがHelixフォルダ内にあることに気づくことでしょう。私はこれらを次のリリースではHelixライブラリに移すつもりです。

You’ll notice that the more general purpose classes in the sample application are in the Helix folder. I’ll be moving these into the Helix library for its next release…

MSDN Magazine 2月号のM-V-VMパターンについての記事の訳(WPF のための MODEL-VIEW-VIEWMODEL (MVVM) デザイン パターン)が昨日公開されましたね。なかなか読みごたえがありました。さて、今回のエントリでは、同じくM-V-VMパターンについてを解説した、David Hill’s WebLogDavid Hill’s WebLog : The ViewModel Patternという記事を訳してみました。

-訳ここから-

ViewModelパターン(より一般的にはModel-View-ViewModelパターンと呼ばれていますが、それではとても長ったらしく、また私は無精なのです)はWPFとSilverlightの開発者にとってますます人気のあるパターンになってきました。これは、そのシンプルさ、フレキシブルさ、そしてWPFとSilverlightによる最初からのサポートによるものです。ViewModelパターンはビジネスロジック、プレゼンテーションロジック、UIロジックを分離するのを助け、多くの開発とデザインの問題においても役立ちます。

The ViewModel pattern (more formally called the Model-View-ViewModel pattern, but that’s way too long winded and I’m lazy) is an increasingly popular pattern with WPF and Silverlight developers. This is largely due to its simplicity and flexibility and its inherent support in WPF and Silverlight. It helps to separate business, presentation and UI logic which in turn helps with a whole host of development and design issues.

アプリケーションロジックとユーザーインターフェイスの間がきれいに分割出来ているということは、ユーザーインターフェイスから独立してユニットテストが出来るということを意味します。UIを通じてのアプリケーションロジックの自動テストは複雑になりがちで、準備に時間もかかります。加えて、ユーザーインターフェイスとアプリケーションロジック部分を同時にテストする場合は二つの間でエラーを切り分けるのも難しくなりがちです。

Having a clean separation between your application’s user interface and underlying application logic means that you can unit test it independently of the user interface. Automated testing of application logic through the UI can be complex and time consuming to setup. In addition, it can be difficult to isolate errors in the user interface or application logic portions of your application when they are both tested at the same time.

ViewModelパターンを使うもう一つの重要な利点は、アプリケーション開発者はアプリケーションロジックに注力し、同時にユーザーインターフェイスデザイナはアプリケーションの見た目に注力することが出来ることです。これにより、アプリケーションのルックアンドフィールを下層のアプリケーションロジックから分離して決めることができ、保守、(ユーザビリティのフィードバックによる)変更、カスタマイズを容易にします。

Another key benefit of using the ViewModel pattern is that it allows application developers to focus on application logic while user interface designers can focus on the visual aspects of the application. This allows the look and feel of the application to be defined separately from the underlying application logic, making it easier to maintain, modify (say as a result of usability feedback) or to customize.

ViewModelパターンは、同様にアプリケーションを理解、保守しやすくします。ViewModelパターンにより、コードの再利用の機会が増えます。同じアプリケーションでの別の部分でのアプリケーションロジックの再利用が可能になります。ユーザーインターフェイスを特定のユーザーのロール、言語に合わせることが可能になります。アプリケーションロジックを同じアプリケーションのデスクトップ版とRIA版とで共有することが可能になります。これらのすべてのケースで、見た目が非常に違っていても、下層にあるアプリケーションロジックは同じです。

The ViewModel pattern also makes the application easier to understand and maintain. It also provides more opportunities for code re-use, allowing application logic to be re-used in different parts of the same application, or allowing the user interface to be tailored to specific user roles or locales, or allowing application logic to be shared between desktop and RIA versions of the same application. In all of these cases, the underlying application logic is the same though its visual representation may be quite different.

テーマのバリエーション Variations On A Theme

世の中にはViewModelパターンについてのたくさんの記事(お気に入りのものはこの記事の最後に挙げました)があり、それらはパターンの定義の仕方においてすべて少しづつ違うように見えます。もちろんこれは全て無理からぬ話です。パターンは潜在的な解決策を描く一つのただのやり方であり、実際に使う局面では状況に一番合うようにパターンを変更することが必要です。私はいわゆる「デザインパターン専門家」が参加している(常に激しく争っている)議論を見たことがあります。彼らはパターンのあるべきより良い細目について論争していましたが、「退屈で役に立たない」とだけ言っておきましょう。問題なのは、根本にあるパターンの魂と原則が理解され、適用されることであり、細かいことは殆ど意味を持ちません。

There are many posts and articles out there that describe the ViewModel pattern (my favorite ones are listed at the end of this post), and they all seem to differ slightly in how they define or describe the pattern. This is, of course, totally reasonable. Patterns are just a way to describe a potential solution to a design problem and specific scenarios will call for a pattern to be modified to best suit the situation. I find the (invariably heated) debates that the so-called ‘design pattern experts’ engage in as they argue about the finer details of a pattern to be, let’s just say, tedious and unhelpful. What matters is that the spirit and the underlying principles of the pattern are understood and applied – the details don’t matter nearly as much.

ViewModelパターンは分離されたプレゼンテーションパターンのカテゴリに入るたくさんのパターンの一つに過ぎません。本来、ViewModelパターンはPresentation Modelパターンのバリエーションの一つです(実際、ViewModelパターンは本当はPresentation Modelパターンと同じであると言う人も居ます)。あなたの考え方が何であれ、私はパターンの必要不可欠な機能であると私が信じる物について説明します。次の記事では、私が最近触っている、collection-based modelにViewModelパターンを適用したコードをお見せしましょう。

The ViewModel pattern is just one of many patterns that fall into the ‘Separated Presentation Pattern‘ bucket. Essentially, the ViewModel pattern is a variation of the Presentation Model pattern (actually, some would say that the ViewModel pattern it is in fact the same as the Presentation Model pattern). Whatever your point of view, I’m going to describe what I believe are the essential features of the pattern. In my next post I’ll show you some code that I’ve been playing with recently that applies the ViewModel pattern to collection-based models.

ViewModelの概要 ViewModel In A Nutshell

では、何がViewModelパターンの必要不可欠な機能なのでしょうか?私はViewModelパターンをこのように表現しました:

So what are the essential features of the ViewModel pattern? I describe the ViewModel pattern like this:

ViewModelパターンでは、UIとUIロジックはViewの中にカプセル化されます。Viewはプレゼンテーションのロジックと状態をカプセル化するViewModelを監視します。ViewModelは同様にModelと情報のやり取りをし、ModelとViewの間の代理人として振舞います。

In the ViewModel pattern, the UI and any UI logic are encapsulated in a View. The View observes a ViewModel which encapsulates presentation logic and state. The ViewModel in turn interacts with the Model and acts as an intermediary between it and the View.

ViewModel

分割されたプレゼンテーションパターンは全て、アプリケーションロジックとUIの様々な特色を異なるクラス、コンポーネントに配置することでアプリケーションロジックからUIを分離することを助けるために存在します。様々なパターン間の違いは二つのことに起因します。クラスに割り振られた役割と責任、そしてそれらクラスがどう情報をやり取りするか、です。ではViewModelパターンをこれら二つの観点からみてみましょう:

All of the separated presentation patterns exist to help separate UI from application logic by placing the various flavors of application logic and the UI into distinct classes or components. The differences between the various patterns are largely due to two things: the roles and responsibilities assigned to these classes, and how these classes interact with each other. Let’s look at the ViewModel pattern in these two respects:

役割と責任 Roles & Responsibilities

ViewModelパターンのクラスの役割と責任はこのように特徴づけられます:

The roles and responsibilities of the classes in the ViewModel pattern can be characterized like this:

  • ViewModelはプレゼンテーションロジックと状態をカプセル化する。プレゼンテーションロジックはここではアプリケーションのユースケース(あるいはユーザーストーリー、ユーザータスク、ワークフロー、etc.)に影響するアプリケーションロジックと定義され、アプリケーションの論理的な動作や構造を決定します。ViewModelはコントロールのコードビハインドでは決してありません。プレゼンテーションロジックは視覚的表現がどうなるか知るべきではなく、どんなユーザーインターフェイスの実装からも独立しているべきです。再利用機会を最大化するために、ViewModelは特定のUIクラス、要素、動作(アニメーションなど)に対して参照を持つべきではありません。UI基本クラスから派生するべきではありません(理想的にはPOCOであるべきです)。(もちろん)ユニットテスト可能で、 全くUI無しでも機能するべきです。
  • Modelはビジネスロジックとデータをカプセル化する。ビジネスロジックはここではアプリケーションデータの検索と管理に影響し、データの整合性と有効性を確保するいくつかのビジネスルールを確実に課すためのアプリケーションロジックと定義されます。 再利用機会を最大化するために、モデルはユースケース/ユーザーストーリー特有の動作やアプリケーションロジックを含むべきではありません。
  • ViewはUIとUIロジックをカプセル化する。Viewはアプリケーションの具体的なユーザーインターフェイスを定義します。Viewは典型的にはViewModelにバインドされたときによく動くようにデザインされた、ただのコントロールです。その特有の役割は、ユーザーに、最も適切な方法で下層にあるアプリケーションのデータとロジックを表現するユーザーインターフェイスで表示すること、そして、ユーザーの入力を受けて、それをどのようにモデルの中の下層にあるデータとアプリケーションの状態に影響させるかを定義しているViewModelに転送することです。幾つかのケースでは、Viewはコードビハインドファイルに、UIロジック―ユーザーインターフェイスの実装に固有のアプリケーションロジック―を含むことができます。しかしながら、そのようなUI固有のコードはテストが難しく、出来る限り避けるべきです。もちろん、ViewがXAMLでは宣言的に記述できない固有の動作を持つ時もあり、Viewのコードビハインドにいくらかコードがあっても差し支えありません。しかし、このコードはView内のUIコントロールの操作だけに注力すべきで、プレゼンテーションロジックを実装するべきではありません。
  • The ViewModel encapsulates presentation logic and state. Presentation logic is defined here as the application logic that is concerned with the application’s use-cases (or user stories, user tasks, workflow, etc) and defines the logical behavior and structure of the application. The ViewModel is NOT a control’s code behind. Presentation logic should have no idea what its visual representation will be like and should be independent of any specific user interface implementation. To maximize re-use opportunities, the ViewModel should have no reference to any specific UI classes, elements, controls or behavior (like animations). It should not derive from any UI base class (ideally it should be a POCO). It should (of course!) be unit testable and be able to function without any UI at all.
  • The Model encapsulates business logic and data. Business logic is defined here as any application logic that is concerned with the retrieval and management of application data and for making sure that any business rules that ensure data consistency and validity are imposed. To maximize re-use opportunities, Models should not contain any use-case or user-story specific behavior or application logic.
  • The View encapsulates UI and UI logic. Views define the specific user interface for a piece of the application. Views are typically just controls that are designed to work well when bound to a ViewModel. Their specific role is to present the user with a user interface that represents the application’s underlying data and logic in the most appropriate way, and to interpret user input gestures and forward them to the ViewModel that then defines how they affect the underlying data and application state in the Model. In some cases the view may contain UI logic – application logic that is specific to the user interface implementation – in the view’s code-behind file. However, such UI specific code is difficult to test and should be avoided as much as possible. Of course, there will be times when the View will have specific behavior that cannot be expressed declaratively in XAML so there may be some code in the View’s code-behind, but this code should be specifically focused on managing the UI controls in the View and should not implement presentation logic.

情報のやり取り Interactions

次に考えなければならないのは、お互いがどのように情報のやり取りをするかです。ここで重要なのは、ViewとViewModel間の双方向の情報のやり取りの理解です。ViewModelパターンの本質的特徴はViewがViewModelを監視することであり、それはViewModelがViewへの直接の参照を持たないことを意味します。ViewModelはステートマシンのように動作し、Viewの責任は適切な方法で状態を描くこと、受け取ったユーザー入力をもとに状態に影響させることです。

The next thing to consider is how they interact with each other. The key here is to understand the two-way interaction between the View and the ViewModel. I think the essential feature of the ViewModel pattern is that the View is an observer of the ViewModel, which means that the ViewModel doesn’t have a direct reference to the View. The ViewModel acts like a state machine and the View’s responsibility is to render the state in whatever way is appropriate, and to influence that state by interpreting user input gestures.

Viewは典型的にはViewModelが公開するプロパティを通じてViewModelの状態を引き出し、ViewModelの状態変化はViewが購読するイベントと通知を通じて連絡されます。逆方向には、Viewはユーザー入力を受け取り、ViewModelの状態を変化させることでViewModelと情報をやり取りします。これは様々な方法で行われえます。Viewは単にViewModelの公開するプロパティに値をセットすることも出来ます。より複雑な状態変化はViewModelによって公開されているアクションメソッドを通じて実現できます。

The View typically extracts state from the ViewModel through properties that the ViewModel provides and changes of state in the ViewModel are communicated through events and notifications that the View consumes. In the other direction, the View captures user input gestures and interacts with the ViewModel to change its state. This can happen in a number of ways. The View can simply set a value on a property provided by the ViewModel. More complex state changes can be achieved through action methods that are exposed by the ViewModel.

WPFとSilverlightにおけるViewModelパターン The ViewModel Pattern in WPF and Silverlight

ここまでのViewModelのパターンの説明で、私がWPFやSilverlightの特定の機能について言及していないことに気付かれたかもしれません。これは意図的なもので、パターンはテクノロジーに関知すべきではないからです。ViewModelパターンを解説する記事の多くはViewModelパターンを直接サポートするWPFやSilverlightの特定の機能に焦点を当てます。実際、これらの機能はViewModelパターンをサポートするのに素晴らしい仕事をしてくれ、そしてそのことはパターンからそれらの機能を切り離すことを困難にしています。

You may have noticed that in the description of the ViewModel pattern above I don’t make any mention of any specific WPF or Silverlight frameworks features. This is intentional since a pattern should be technology agnostic. Most of the posts and articles that describe the ViewModel pattern focus on the specific features of WPF and Silverlight that directly support the ViewModel pattern. In fact, these features do such a great job of supporting the ViewModel pattern that it becomes difficult to separate them from the pattern itself.

では、それらの機能と、それらの機能がどのようにViewModelパターンをサポートしているかをみていきましょう。

Let’s look at some of these features and see how they support the ViewModel pattern…

Data Binding: WPFとSilverlightのデータバインディングはViewをViewModelにバインドするのを容易にします。データバインディングはXAMLで宣言的に定義され、とてもフレキシブルです。ほとんどのものをほとんどのものにバインドすることができます。Viewのデータバインディングがそのプロパティとイベントを解決するために、ViewのDataContextはViewModelを参照します。双方向のデータバインディングはViewのプレゼンテーションのための、ユーザのUI操作の結果としてのViewModelの状態の更新のためのViewModelの状態の検索の世話をします。ViewModelによって発生された変更通知は、ViewModelの状態が変化したときは何時でもViewが自動的に更新できるようにします。

Data Binding: WPF and Silverlight data binding make it easy to bind a View to a ViewModel. Data bindings can be defined declaratively in XAML and are very flexible – you can pretty much bind anything to anything. The View’s DataContext references the ViewModel so that the View’s data bindings resolve to its properties and events. Two way data binding takes care of retrieving state from the ViewModel for presentation in the View, and for updating state in the ViewModel as a result of the user’s interaction with the UI. Change notifications fired by the ViewModel allow the view to be automatically updated whenever the ViewModel’s state changes.

Commands:データバインディングはプロパティとして公開される状態のためにViewとViewModelの間の双方向の情報のやり取りをサポートするための素晴らしいメカニズムを提供しますが、ViewModelはしばしばユーザーがUIを通じて始められるAction―注文の送信や経費報告書の承認など―を表すメソッドを公開します。Commandはこれに対して良い解決策を与えます。Commandによって、Viewのコントロールは、UIロジック接着コードなしでユーザーが実行できるActionと簡単に連携することが可能になります。また、Actionの視覚表現、即ちUIがCommand、即ちハンドラの実際の実装からきれいに分離されるのも可能にします。WPFはWPFより(訳注:silverlight?)一層広範囲にわたるCommandのサポートを持ちますが、基本的なメカニズムは ViewとViewModelがきれいに情報のやり取りをできるように使われることができます。

Commands: While data binding provides a great mechanism for supporting two-way interaction between the View and the ViewModel for state exposed as properties, the ViewModel will often provide methods that represent actions that the user can initiate through the UI – submit an order, approve an expense report, etc. Commands provide a good solution for this. They allow controls in the View to be easily associated with actions that the user can carry out without requiring any UI logic glue code, and they allow the visual representation of the action (i.e. the UI) to be cleanly separated from the actual implementation of the command (i.e. the handler). WPF has much more extensive support for commands than WPF, but the basic mechanism can be used to allow a View and a ViewModel to interact cleanly.

Data Templates:WPFとSilverlightのデータテンプレートの機能はViewを完全に宣言的に定義することを可能にします。UIロジック(コードビハインド)を必要とせず、純粋なUIであることは、Viewにとって凄く良いことです。データテンプレートはコントロールと、連携したバインディングを含みます。テンプレートが適用されると、そのDataContextはテンプレートとしているオブジェクト(この場合はViewModel)にセットされ、バインディングが支配するようになります。データテンプレートはあなたにViewModelのプレゼンテーションロジックに注力することが出来るようにし、ViewModelの「スキニング」をBlendのようなツールをテンプレートを構築するのに使えるUIデザイナーに、いかなるコードも書いて貰うことなしに託せるようにします。WPFは暗黙のデータテンプレートをサポートするので、データテンプレートをViewModel型と連携させることができますし、システムがその型のViewModelを見ることが出来る場合はいつでもデータテンプレートは自動的に適用されます。Silverlightはまだ暗黙のデータテンプレートをサポートしていませんが、個々のコントロールにデータテンプレートを指定してやることは可能です。最後に、WPFデータテンプレートセレクタはシステムがユーザーの役割やロケーションについて責任を負うカスタムロジックに基づいてデータテンプレートを選択することを可能にします。

Data Templates: The Data Template feature of WPF and Silverlight allow you to define a View entirely declaratively. This is great for Views that require no UI logic (no code behind) and are ‘pure’ UI. Data templates contain controls and their associated bindings. When a template is applied, its DataContext is set to the object that it’s templating (in our case, the ViewModel) and the bindings take over. Data Templates allow you to focus on the presentation logic in the ViewModel and leave the ‘skinning’ of your ViewModel to a UI designer who can use a tool such as Blend to built the template without having to write any code whatsoever. WPF supports implicit data templates so you can associate a data template with a ViewModel type and wherever the system sees a ViewModel of that type, the data template will automatically get applied. Silverlight doesn’t yet support implicit data templates, but you can manually specify the data template for individual controls. Finally, WPF Data Template Selectors allow the system to choose a data template based on custom logic, say to account for user roles or location.

CollectionViews:WPFのCollectionViewは実際ViewModelの良い例でしょう。それらは特にコレクションベースのModelをサポートし、フィルタリング、ソート、グルーピング、選択をサポートするプレゼンテーションのロジックと状態をカプセル化します。それらはデータバインディングを通じてViewによって容易に使用され、また、UIから独立しており、ユニットテストをサポートします。Silverlightはまだこの分野を十分にサポートしていませんが、ICollectionViewインターフェイスを提供しており、同等の物を実装することは出来るでしょう。(これが次のエントリのテーマなので、引き続き見ていてね!)

CollectionViews: I think WPF’s CollectionViews are actually good examples of ViewModels. They specifically support models that are collection based and they encapsulate presentation logic and state to support filtering, sorting, grouping and selection. They are easily consumed by Views through data binding, are independent of the UI and support unit testing. Silverlight doesn’t yet have as much support in this area, but it does provide the ICollectionView interface so you can implement something equivalent (this is the subject of my next post so stay tuned!!).

WPFとSilverlightには他にもたくさんの機能があります。例えばStyle, Visual State Manager, Trigger等。これらは間接的にViewModelパターンを支援しますが、ここまで説明してきたものが一番よく支援します。これらの機能すべてはViewがViewModelときれいに情報のやり取りをするのを助けるのに役立ちます。私は「きれいに」という言葉を、特に、全てを統合するのに必要なUI固有のコードの量を減らすこと、という意味で使っています。

There are many other features in WPF and Silverlight – such as Styles, Visual State Manager, Triggers*, etc – that indirectly support the ViewModel pattern but the ones described above are the big ones. All of these features work together to help the View interact cleanly with the ViewModel. By ‘cleanly’ I specifically mean that they reduce the amount of UI specific code that is required to glue everything together.

* 付け加えておきますと、私はtriggerの大ファンではありません。triggerは視覚的な挙動を定義するためのいくつかの状況ではとても便利ですが、プレゼンテーションロジックをViewに定義するよう(間違って)使うこともでき、それは良くありません。私はVisual State Managerの方が視覚的挙動を定義するのに優れたメカニズムだと思います。

* As a side note, I am not a big fan of triggers. They are very useful in some situations for defining visual behavior, but they can also be (mis-)used to define presentation logic in the view which is bad. I find the Visual State Manager a much better mechanism for defining visual behavior…

正しいコードを正しい場所に The Right Code in the Right Place

要約すると、正しいコードを正しい場所に置くのが、WPFとSilverlightアプリケーションをフレキシブルに構築し、再加速させるために、ViewModelパターンを活用するキーとなります。あるコードがどこまでやるかを判断するのはしばしば難しくなりますが、しかしやっているうちに簡単にもなりますし、WPFとSilverlightが提供する機能はこのプロセスを劇的に単純化するのを助けます。

To summarize, getting the right code in the right place is the key to leveraging the ViewModel pattern to build flexible, robust WPF and Silverlight applications. Deciding where a specific piece of code should go can sometimes be difficult, but it gets easier with practice and the features that WPF and Silverlight provides help simplify this process dramatically.

ViewModelについてもっと More Stuff On ViewModel

私がWeb上で見つけたもっとも役立ち興味深い記事をいくつか挙げておきます。

Here’s a short of list posts and articles on the web that I’ve found the most useful and interesting

oO——Oo

Silverlightでのナビゲーションについての私の次の記事を熱心に待っている方々へ。この記事と私の前のview-firstとpresenter-firstの複合についての記事は、本当は、あのシリーズのパート2aと2bです。 私はこれらの記事で描いたコンセプトに基づいて構築し、navigationとViewModelパターンがどのように相互作用をするかについて特に注目したいと思います。時間がかかっていて申し訳ありません。しかし上手くいけば待つ価値のあるものとなるでしょう:-)

For those of you who are keenly awaiting my next post on navigation in Silverlight, this post and my previous post on view-first and presenter-first composition are really parts 2a and 2b of that series. I’ll be building on the concepts that I’ve described in these posts and focusing specifically on how navigation and the ViewModel pattern interact. Sorry it’s taken so long, but hopefully it’ll be worth the wait :-)

最近WPFアプリケーションの設計で頭を悩ませていまして、どのような設計をしたものかと色々と記事を物色しているのですが、どうやらModel-View-ViewModelパターンというのが良いみたいですね。そこでとりあえずThe Orbifoldというサイトの、WPF patterns : MVC, MVP or MVVM or…?というエントリを勉強がてら訳しながら読んでみました。自分も英語が得意なわけではないので、ミスがありましたらご指摘をお願いします。

2009-02-21追記:もう一つ記事を訳してみました:M-V-VMパターンについてのエントリを訳してみた2 原題:「David Hill’s WebLog : The ViewModel Pattern」 – SharpLab.

―訳ここから―

導入 Introduction

XAMLの登場以来、WindowsアプリケーションのためにMVCアーキテクチャを概念化を試みる上で、事態は少しややこしくなりました。ウェブとデスクトップのギャップは狭まってきており、WPF全体が様々な可能性を道具箱に加えました。使うべきものは?model-view-controlerd?それともmodel-view-presenter?model-view-viewmodelと呼ばれる新しいパラダイムでしょうか?

Since XAML things have become a bit complicated in trying to conceptualize MVC architectures for Windows applications. The gap between web and win is narrowing and the whole WPF thing adds diverse possibilities to one’s toobox. What to use? Model-view-controler, the model-view-presenter or the new paradigm called model-view-viewmodel?

私は一体全体それが何なのかを理解することを試みました。そしてこれは私が見つけ出したものです。

I have tried to understand what it’s all about and this is what I found out.

何が問題なのか? What’s the problem?

WPFは幾つかのことを変化させました。

WPF has changed a few things:

  • 宣言型プログラミング(即ちXAML)によって、特にデータバインディングにおいて、たくさんの方法が用意され、多くのコードが節約されました。別の分野は、例えば、アニメーションとスタイリングです。以前はC#でプログラムされる必要があったそれらが、今ではXAMLの中でトリガとイベントで可能です。
  • MVCパターンにおいて、ViewはModelからのデータを表示していますが、今やXAMLがこの仕事を行い、時々、表示を更新するのにValueConverterやObservableCollectionのようなものを必要とします。
  • WPFにおけるユーザーコントロールはアプリケーション内部で使われるときにオーバーライドされることができるデフォルトのgeneric.xamlスタイルを定義することが出来、即ち既存のコントロールへのスタイル付けはModelの表示に柔軟性を加える新しい機能です。
  • 新しく追加されたICommand、XAMLのトリガ、アニメーション、イベントのバブリング、トンネリングを通じて、XAMLは物事を変更するため、ユーザーのアクションに応えるための様々なメカニズムを可能にします。
  • through declarative programming (i.e. XAML) a lot of plumbing is created for you which especially in the context of databinding saves a lot of code. Another area is e.g. animations and styling; what previously had to be programmed in C# is now possible inside XAML through triggers or events.
  • in the MVC pattern the View is presenting the data from the Model but now XAML does this job and sometimes need things like value converters or the ObservableCollection to update the presentation
  • user controls in WPF allow you to define a default generic.xaml style which can be overriden when used inside an application, i.e. styling of existing controls is a new feature which adds flexibility to the presentation of the Model
  • XAML allows a wide variety of mechanisms to change things or to react to user actions; through the ICommand that is now defined in the framework, through triggers in XAML, through animations, through event bubbling or tunneling (which is also new)

MVVMアーキテクチャ The MVVM architecture

概観 Overview

MVVM

DataModel

DataModelはWPFによって使いやすいようにデータを公開する責任を負います。そのすべてのPublicなAPIはUIスレッドにおいてのみで呼ばれる必要があります。INotifyPropertyChangedとINotifyCollectionChangedインターフェイスを適切に実装する必要があります。データを取ってくるコストが高い場合は、コストの高い操作を取り去り、UIスレッドを決してブロックしないようにします。ブロックすることは悪です!同様にデータを新鮮に保ち、複数のソースからのデータを組み合わせるのにも使えます。これらのクラスは容易にユニットテスト出来ます。

DataModel is responsible for exposing data in a way that is easily consumable by WPF. All of its public APIs must be called on the UI thread only. It must implement INotifyPropertyChanged and/or INotifyCollectionChanged as appropriate. When data is expensive to fetch, it abstracts away the expensive operations, never blocking the UI thread (that is evil!). It also keeps the data “live” and can be used to combine data from multiple sources. These sorts of classes are fairly straightforward to unit test.

ViewModel

ViewModelはアプリケーションにおけるViewのためのModelです(当然ですが)。Viewに関係のあるデータを公開し、通常Commandsと共にViewのためのbehaviorを公開します。ModelはアプリケーションのViewに対して極めて特化していますが、いずれのWPFクラスのサブクラスでもなく、バインドされるUIについての想定もしません。ViewModelは実際のUIから分離されているので、これらのクラスは同様に比較的容易にユニットテストできます。

A ViewModel is a model for a view in the application (duh!). It exposes data relevant to the view and exposes the behaviors for the views, usually with Commands. The model is fairly specific to a view in the application, but does not subclass from any WPF classes or make assumptions about the UI that will be bound to it. Since they are separate from the actual UI, these classes are also relatively straightforward to unit test.

View

Viewはアプリケーションのビューの後ろにある実際のUIです。我々が使うパターンはそのViewModelにviewのDataContextをセットする必要があります。これがバインディングを通じてViewModelに到達するのを容易にします。また、WPFのDataTemplate/Data patternにも合致します。理想を言えば、Viewはコードビハインド無しのXAMLだけで実装できるでしょう。添付プロパティはこれに非常に役に立つでしょう。

A View is the actual UI behind a view in the application. The pattern we use is to set the DataContext of a view to its ViewModel. This makes it easy to get to the ViewModel through binding. It also matches the DataTemplate/Data pattern of WPF. Ideally, the view can be implemented purely as Xaml with no code behind. The attached property trick comes in very handy for this.

DataModelとViewModelの間の線引きはあいまいになりがちです。DataModelは時折DataTemplateと共に表示され、それは我々がViewModelを使うやり方と殆ど変わりません。しかしながら、実際にはその区別は通常意味があります。私は同様に多くのレイヤでしばしば合成が行われることも指摘しなければなりません。ViewModelは他のViewModelやDataModelを構成するかもしれません。また、DataModelはほかのDataModelによって構成されるかもしれません。

The lines between DataModels and ViewModels can be blurry. DataModels are often shown in the UI with some DataTemplate, which isn’t really so different than the way we use ViewModels. However, the distinction usually makes sense in practice. I also want to point out that there’s often composition at many layers. ViewModels may compose other ViewModels and DataModels. And, DataModels may be composed of other DataModels.

ViewModelはViewのModelです。DataObject(Model)のプロパティをViewObject(View)のプロパティにデータバインドしたいけれども、たまに、変換や計算がために、ModelのCLRプロパティを直接バインドすることができないことがあります。そこでViewModelの出番です。ViewModelは計算済み、変換済みのModelからの値を公開するので、このプロパティをViewのプロパティに直接バインドすることが出来るようになります。

The ViewModel is a model of the view. That means: You want to DataBind a property from your DataObject (model) to a property from your ViewObject (view) but you sometimes cannot bind directly to a CLR property of the model (because of converting or calculating). This is when ViewModel comes into play. It propagates the already calculated or converted value from your model, so you can bind this property directly to the view property.

Model-View-ViewModelアーキテクチャの一番の要点はデータ(Model)の上に、データのコンセプトをデータのビュー(View)のコンセプトにより近付けてマッピングする非視覚的なコンポーネントの別のレイヤ(ViewModel)があることのようです。Viewがバインドする先はViewModelであり、Modelに直接バインドするのではありません。

The main thrust of the Model/View/ViewModel architecture seems to be that on top of the data (”the Model”), there’s another layer of non-visual components (”the ViewModel”) that map the concepts of the data more closely to the concepts of the view of the data (”the View”). It’s the ViewModel that the View binds to, not the Model directly.

The model

INotifyPropertyChangedを使うことで、変更をスタックをバブリングさせて伝えることができます。UIスレッドにプロパティの変化を分離スレッドから処理させるメソッドがあるにも関わらず、PublicなメソッドがUIスレッドにあるべき理由は、ModelはUIをブロックしうる、時間のかかる、或いは非同期のを呼び出すこともあり得るからです。詳しくはDispatcherオブジェクトとWPFスレッドモデルのドキュメントを参照ください。DispatcherのBeginInvoke()メソッドによってバックグラウンドで実行する場合は優先度を指定するパラメータに気を付けてください。Dispatcherがbusyでないとき、SystemIdleは特に興味深いです。

Using the INotifyPropertyChanged you can bubble changes up the stack. The reason that public methods should be on the UI thread is because the model could call long running or async stuff which would block the UI, though there are methods to let the UI thread handle property changes from a separate thread. See the doc on the Dispatcher object and the WPF threading model for more on this. Note in this context that you can let things happen in the background by means of the BeginInvoke() method of the Dispatcher and the paramter that specifies the priority. The SystemIdle in particular is interesting to be used when the Dispatcher is not busy.

downloadにあるDataModelはDan Crevierのサンプルの真似で、Modelの抽象基本クラスとして役立つでしょう。

The DataModel you can find in the download is mimiced from the Dan Crevier’s sample and can serve as an abstract base class for your own models.

Dispatcher things

DispatcherTimerはDispatcherループの最初毎に再評価されます。

The DispatcherTimer is reevaluated at the top of every Dispatcher loop.

Timerは時間間隔に正確なタイミングで実行されることが保証されていませんが、時間間隔より前に実行されないことは保証されています。これはDispatcherTimer操作が他の操作と同様にDispatcherキューに配置されているからです。DispatcherTimer操作が実行されるタイミングはキューにある別のジョブとその優先度に依存します。

Timers are not guaranteed to execute exactly when the time interval occurs, but are guaranteed to not execute before the time interval occurs. This is because DispatcherTimer operations are placed on the Dispatcher queue like other operations. When the DispatcherTimer operation executes is dependent on the other jobs in the queue and their priorities.

WPFアプリケーションでSystem.Timers.Timerが使われた場合、System.Timers.TimerがUIスレッドとは別のスレッドで実行されることは注目すべきことです。UIスレッドのオブジェクトにアクセスするにはInvokeかBeginInvokeを使ってUIスレッドのDispatcherに操作を渡す必要があります。System.Timers.Timerの例としては、System Timer sampleのthe Disable Command Sourceを参照してください。System.Timers.TimerではなくDispatcherTimerを使う理由はDispatcherTimerがDispatcherと同じスレッドで実行され、DispatcherTimer上でDispatcherPriorityがセットされうるからです。

If a System.Timers.Timer is used in a WPF application, it is worth noting that the System.Timers.Timer runs on a different thread then the user interface (UI) thread. In order to access objects on the user interface (UI) thread, it is necessary to post the operation onto the Dispatcher of the user interface (UI) thread using Invoke or BeginInvoke. For an example of using a System.Timers.Timer, see the Disable Command Source Via System Timer sample. Reasons for using a DispatcherTimer opposed to a System.Timers.Timer are that the DispatcherTimer runs on the same thread as the Dispatcher and a DispatcherPriority can be set on the DispatcherTimer.

DispatcherTimerは何時でもオブジェクトのメソッドがタイマにバインドされた時はオブジェクトを生かし続けます。

A DispatcherTimer will keep an object alive whenever the object’s methods are bound to the timer.

それで、WPFのUIの中で物事をスケジュールする正しいやり方はこのようになります。

So, the right way to schedule things inside the WPF UI is something like;

private DispatcherTimer _timer; 
timer = new DispatcherTimer(DispatcherPriority.Background); 
timer.Interval = TimeSpan.FromMinutes(5); 
timer.Tick += delegate { ScheduleUpdate(); }; 
timer.Start();

タイマはUIのdispatcherに結びついたスレッドに暗黙的に挿入されます。

the timer is injected implicitly in the thread associated to the dispatcher of the UI.

References

 

―訳おわり―

DispatcherTimerってM-V-VMパターンとどう関係してくるんだろう?

M-V-VMパターンについての具体的な記事を見たい場合は、M-V-VM « Karl On WPF – .NetというページにM-V-VMについて書かれた記事が纏まっているので、これも参照すると良いかもしれません。また、Patterns: WPF Apps With The Model-View-ViewModel Design Patternという凄く面白そうな記事がMSDN Magazineの2009-02号に出ているので、少し待てばこれの日本語訳も出てくるでしょう。ちなみにこのMSDN Magagineの記事の著者はCrack.NETを作られた方でもあるようで、Crack.NETはM-V-VMパターンの良いサンプルコードになっているらしいです。

DEC
30

Expression Blend

Published:2008-12-30 21:34:08 UTC

でWPFと戯れています。ベクタグラフィックス面白い。

image

OCT
8

Expression Blend SP1 Preview

Published:2008-10-08 20:35:11 UTC
WPF

Expression StudioをDream Sparkプログラムを利用してインストールした後で、Expression Blend2 SP1 Previewをインストールしてみた。

image

さて、何をしたものか…。

MAY
2

Expression Studio2

Published:2008-05-02 20:37:33 UTC
WPF

Expression Studio2がリリースされたらしい。もう少し、もう少し待とう。C#の学び直しとか、PHPとActionScript3.0の学習とか、色々やりたいことが山積みなので・・・。

このスイートは「Expression Web 2」「Expression Blend 2」「Expression Design 2」「Expression Media 2」「Expression Encoder 2」を含み、Visual Studio 2005および2008と連係する。Microsoftのメディア再生プラグインSilverlightに対応したコンテンツや、Windows Vista、.NET Framework 3.5対応のアプリケーションも作成できる。

この最新版は100を超える新機能を搭載しており、例えばExpression Web 2ではPHPサポートとAdobe Photoshopインポートを追加し、Blend 2では頂点アニメーションを加えている。またデジタル資産管理ソリューションのMedia 2ではRAWフォーマット、位置情報タグ、Microsoft Office 2007およびMicrosoft Office for Mac 2008を新たにサポートしている。

MS、Webデザインスイート「Expression Studio」の新版リリース -ITmedia News

APR
23

XBAP on Lunascape Sidebar

Published:2008-04-23 23:07:41 UTC
WPF

先日購入したVisual Studio 2008、そのまま放置しておくのも余りにもったいないので、とりあえず.NET Framework3.0の目玉機能の一つである、WPFを活用したプログラムを習作として作ってみることにした。とはいえ、とりたてて作りたいスタンドアロンアプリケーションがあるわけでもなく、やはりいつものように、アドオン開発をすることにした。ターゲットはLunascape。

Lunascapeのサイドバー(というよりはDockだが)用アドオンを開発するには、

  • スクリプトサイドバープラグインフレームワークを利用してJavaScript+XSLTで開発
  • C++でゴリゴリ開発

という二通りのアプローチが存在する。前者に必要な知識はWebページ制作技術の延長上にあるのに対し、後者はC++によるWindowsプログラミングの知識が必要になり、難易度も高い。
前にLunascapeプラグインコンテストに出品した作品では、JavaScript+XSLTを使用して開発したのだが、やはりC++でつくられたアドオンに対して表現力、実行速度の面で圧倒的に不利であり、当時からC#でアドオンを開発できればなぁと考えていた。

そこで今回は、かなりトリッキーな方法ではあるが、JavaScript+XSLTでプラグインとしてのベースを作成し、その上にXBAP(WPFアプリをブラウザ上でホストする方法。SilverLightとはまた別)でプラグインとしての機能を実装することにした。

XBAPの作成

image2 image3

新しい技術を試す際は、やはりHello Worldプログラムを作成してみなければ!ということで、早速作ってみる。「WPFブラウザアプリケーション」という項目を選択して新しいプロジェクトを作成し、とりあえず適当にTextBlockを並べた上で、そのイベントで、

private void textBlock1_MouseDown(object sender, MouseButtonEventArgs e) {
        FileInfo info = new FileInfo("C:\\Users\\shiroica\\Desktop\\ConfoundApriFoolsDay.user.js");
        MessageBox.Show(info.LastAccessTime.ToString());
}

ローカル上のファイルの最終アクセス日時を表示するよう設定。これはセキュリティ機構がどのように働くかをチェックするため。

で、実際にビルドしてみてイベントを発動させてみると、しっかりセキュリティ関係の実行時エラー。WBAPは部分信頼で実行されるらしいから、やはり無理かとも思ったが、プロジェクトのプロパティから完全信頼に設定することで、エラー無しで走らせることができるようになった。

がしかし・・・

LunascapeのサイドバーのなかにうまくXBAPがホストできない><。なんでー?

このprojectはまた今度・・・。

追記

サイドバーの中はインターネットゾーン扱いの筈だから、それで完全信頼を必要とするXBAPが動かなかった気がしてきた。