このエントリは、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…