はじめに
今回はSilverlightアプリケーションで階層構造の表示・選択などに利用されるコントロールTreeViewコントロールと、Infragisticsの高機能コントロール群であるNetAdvantage for SilverlightにあるXamDataTreeコントロールの利用方法について解説します。本稿では、まずそれらの基本的な利用方法について説明します。
対象読者
Silverlightを利用したアプリケーション開発に必要となるXAMLやコンテンツモデル、データバインディング、テンプレートなどのSilverlightの基礎的な知識を有する方。
SilverlightにおけるC#,Visual BasicなどのマネージAPIに関する知識をお持ちの方。
必要環境
Visual Studio 2010もしくはVisual Web Developer 2010 Express Edition、Silverlight 4 Tools for Visual Studio, XamDataTreeの利用にはNetAdvantage for Silverlight Line of Business, NetAdvantage for .NETまたはNet Advantage Ultimateが必要となります。
なお、今回のサンプルはWindows 7 Ultimate 64bit環境でVisual Studio 2010 UltimateとSilverlight 4 Tools for Silverlight、NetAdvantage Ultimate 2010 Volume 3を利用しました。
TreeViewコントロールについて
Silverlightにはさまざまなデータを表現するための多くのコントロールが存在しています。今回紹介するTreeViewコントロールはその名前のとおり、階層構造となる各データ・オブジェクトをTree形式で表現することができるコントロールです。
データ構造の中でも階層構造を持つデータは一般的に複雑な構造となりやすく、整理整頓を意識したデータ作成を行わないと、データ構造の可読性は低くなりやすい傾向にあります。
こういった、階層構造を持つデータ構造・オブジェクト構造を表現する方法としてTreeViewのようなコントロールを利用することで、ユーザーにとって必要のないデータを一時表示・非表示の制御を行えるようにし、これらの構造の見通しをよくすることができるという効能もあることから、さまざまな場面で利用されます。
しかし、階層構造を持つデータを取り扱うコントロールのため、データ構造とコントロール内のデータ設定を行うポイントとのマッピング方法をきちんと押さえておくことが重要です。
XamDataTreeコントロールについて
XamDataTreeコントロールは、Infragisticsで開発されたSilverlight上で利用できる高度なコントロール群であるNetAdvantage for Silverlight LOB, NetAdvantage for .NETもしくはNetAdvantage Ultimateに含まれるコントロールです。
今回の記事で紹介するこれらのコントロールはInfragisticsのホームページより20日間フル機能を試すことができるトライアル版のダウンロードが可能です。それ以外にも公開されたヘルプやサンプルなどが用意されているので、本記事で興味を持たれた方はぜひダウンロードしてお試しください。
今回の記事ではSilverlightのSDKに含まれる標準のTreeViewコントロールの利用方法と、XamDataTreeの利用方法を解説し、それぞれどのような特徴があるのかを紹介したいと思います。
コンテンツモデル
まず、Silverlightのコンテンツモデルのおさらいからはじめましょう。
Silverlightではユーザーインターフェースとして表示・入力そしてそれらの振る舞いの定義を行うためのさまざまな種類のコントロールが用意されています。
これらの中から表示機能を持ったものに注目するとテキスト・イメージ・動画など、それぞれ決まった特定のオブジェクトを表示するためのコントロール群や、ユーザーが用意した特定のエンティティクラスやその配列のコレクション、そしてレイアウトコントロールのような、コントロールそのものを組み合わせて配置・表示するためのコントロールなど多岐にわたって用意されています。
これらの指定されたオブジェクト(コンテンツ)を表示するコントロールということで、これらを総称して「コンテンツモデルのコントロール」と呼んでいます。
コンテンツモデルと一口に言っても、当然、コントロールによって取り扱うコンテンツの種類は異なります。これらのコンテンツの種類をざっくりと分類すると以下の表のような形になります。
取り扱うコンテンツの種類 | 主要なコントロール |
テキスト | TextBlock, TextBoxなど |
単一の要素 | Button, CheckBox, RadioButtonなど |
単一の要素とヘッダー | TabItemなど |
オブジェクトのコレクション | ComboBox, ListBoxなど |
オブジェクトのコレクションとヘッダー | TreeViewItemなど |
UI要素(コントロール) | Canvas, Grid, StackPanelなど |
こういったコンテンツモデルのコントロール群はそれぞれ指定された任意のコンテンツ(データ)を表示するため、それぞれコンテンツプロパティとよばれるプロパティを持っています。
例えば、TextBoxコントロールであればTextプロパティ、ButtonコントロールであればContentプロパティ、TabItemであればHeaderプロパティとContentプロパティといったプロパティがそれに当たります。
各コントロールは、コンテンツプロパティに指定された各データを何らかの形で表示するなど取り扱いができるように実装されています。
今日紹介するTreeViewコントロールや、XamDataTreeコントロールも同様、コンテンツモデルのコントロールになるため、そのコンテンツとして階層構造を持ったオブジェクトを指定することになります。
しかし、階層構造を持つデータという特性からオブジェクトの各ノードの表現方法や取扱い方法が少々複雑になります。
まず、TreeViewコントロールやXamDataTreeコントロールが取り扱うコンテンツは「オブジェクトのコレクション」を取り扱うコントロールということになります。
このオブジェクトのコレクションを取り扱うコントロールの多くが親クラスとして「ItemsControl」というコントロールを継承しています。つまり、ItemsControlは複数のItem(コンテンツ)を取り扱うことができるコントロールで、コレクションに定義された各Itemを表示するための機能を持っています。
TreeViewコントロールもこのItemsControlを継承しており、それらの機能を有しています。また、XamDataTreeでもその一部の機能は同様に利用できます。
それではItemsControlについて少し見てみましょう。
ItemsControl
さて、先ほどご説明したとおり、今回の本題の一つ目であるTreeViewコントロールはItemsControlと呼ばれるクラスの派生クラスになります。
ItemsControlは前述したコンテンツモデルのコントロールの中でも「項目のコレクション」となっていることから分かるように配列やList<T>などのコレクションオブジェクトを取り扱うことができるコントロールです。
そのため、コレクションを取り扱うためのコンテンツプロパティとして、ItemsプロパティとItemsSourceプロパティが用意されています。それぞれの利用方法について順番に説明します。
Itemsプロパティ
ItemsプロパティはItemCollection型で、その名前のとおりItemsControlに表示する任意オブジェクトのコレクションを格納・管理するプロパティとなっています。
つまり、ItemsControlに対して、任意オブジェクトの追加・参照・削除が行えます。
ItemCollectionクラスのAddメソッド, Removeメソッドなどを使って個々のデータの追加・削除が行えます。
それでは早速TreeViewコントロールを使って、Itemsプロパティによる、追加・削除を行うサンプルプログラムを見てみましょう。
- サンプルプログラム1: 01-TreeViewItemsProperty.zip
<UserControl x:Class="_01_TreeViewItemsProperty.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <Grid x:Name="LayoutRoot" Background="White"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Button Content="Add" Name="btnAdd" Margin="5" Click="btnAdd_Click" /> <Button Content="Remove" Grid.Column="1" Name="btnRemove" Margin="5" Click="btnRemove_Click" /> <sdk:TreeView Name="treeView1" Grid.Row="1" Grid.ColumnSpan="2" Margin="5" /> </Grid> </UserControl>
また、Buttonコントロールのイベントハンドラの実装が必要となりますので、コードビハインドは以下のように実装します。
/// <summary> /// Addボタンイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnAdd_Click(object sender, RoutedEventArgs e) { //ItemsプロパティのAddメソッド treeView1.Items.Add(string.Format("Item {0}", treeView1.Items.Count)); } /// <summary> /// Removeボタンイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnRemove_Click(object sender, RoutedEventArgs e) { //カウンターが0となれば実行しない if (treeView1.Items.Count <= 0) return; //Itemsプロパティのデータの削除 treeView1.Items.RemoveAt(treeView1.Items.Count - 1); }
実行結果は以下のような形になります。
まだ、単一のコレクションオブジェクトを追加しただけなので、階層表示は行われませんが、Addボタン、Removeボタンをクリックすることで、ItemsControlの項目の追加・削除が行われる様子は確認できると思います。
ItemsSourceプロパティ
前述のように1つずつオブジェクトをItemsプロパティに追加する以外にもすでに存在する配列やListなどのコレクションオブジェクトをコントロールに直接指定するプロパティが用意されています。それがItemsSourceプロパティです。
ItemsSourceプロパティにはIEnumerableインターフェースを実装したコレクションオブジェクトを指定できます。
IEnumerableインターフェースは.NETでの配列の親クラスであるArrayクラスやSystem.Collection名前空間などにある主要なコレクションオブジェクトで実装されていることから、これらのコレクションオブジェクトはItemsSourceプロパティにそのまま指定できます。
本日の本題であるTreeViewコントロール、XamTreeDataコントロールはいずれもItemsSourceプロパティを持っています。
それでは、早速コレクションオブジェクトを表示するためのサンプルプログラムを見てみましょう。
こちらのサンプルプログラムはXamDataTreeを利用していますので、プロジェクトで「参照の追加」するなど、関連コンポーネントを追加する必要がありますが、先ほどご紹介したNetAdvantageはインストールされているとツールボックスに表示されますので、ドラッグ&ドロップだけで簡単にXAMLに追加することができます。
最終的には以下のようにTreeViewコントロール、XamDataTreeコントロールを追加し、UserControlへLoadedイベントを追加します。
- サンプルプログラム2: 02-ItemsSource.zip
<UserControl x:Class="_02_ItemsSource.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:ig="http://schemas.infragistics.com/xaml" Loaded="UserControl_Loaded"> <Grid x:Name="LayoutRoot" Background="White"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <sdk:TreeView Name="treeView1" /> <ig:XamDataTree Grid.Column="1" Name="xamDataTree1" /> </Grid> </UserControl>
Loadedイベントハンドラは以下のようにLINQを使ってstring型のコレクションオブジェクトを生成し、各コントロールのItemsSourceプロパティに設定します。
private void UserControl_Loaded(object sender, RoutedEventArgs e) { var stringItems = Enumerable.Range(1, 10).Select(i => string.Format("Item {0}", i)); treeView1.ItemsSource = stringItems; xamDataTree1.ItemsSource = stringItems; }
実行結果は以下のとおりです。
それぞれのコントロールの描画方法が異なることから若干表示されるイメージが異なりますが、いずれも同じように各項目を表示できます。
また、XamDataTreeコントロールでは、特定のエンティティクラスなどを指定する場合、DisplayMemberPathプロパティにプロパティパスを設定することで表示項目を指定できます。
それではサンプルとして以下のようなエンティティクラスを定義します。
- サンプルプログラム3: 03-DisplayMemberPath.zip
public class Person { public string Name { get; set; } private ObservableCollection<Person> _children = new ObservableCollection<Person>(); public ObservableCollection<Person> Children { get { return _children; } } }
そして、XAMLは以下のように作成します。
<UserControl x:Class="_03_DisplayMemberPath.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:ig="http://schemas.infragistics.com/xaml" Loaded="UserControl_Loaded"> <Grid x:Name="LayoutRoot" Background="White"> <ig:XamDataTree Grid.Column="1" Name="xamDataTree1" DisplayMemberPath="PersonName" /> </Grid> </UserControl>
そして、前のサンプル同様、Loadedイベントに対して上記エンティティクラスを表示するために以下のようにコーディングします。
private ObservableCollection<Person> persons; private void UserControl_Loaded(object sender, RoutedEventArgs e) { persons = new ObservableCollection<Person> { new Person { PersonName = "しるば太郎" }, new Person { PersonName = "しるば次郎" }, new Person { PersonName = "しるば三郎" }, new Person { PersonName = "しるば四郎" }, new Person { PersonName = "しるば五郎" } }; xamDataTree1.ItemsSource = persons; }
実行結果は以下のように表示されます。
階層構造の表示
ここまでは階層構造を持たない単一のコレクションオブジェクトのデータを設定し、表示しましたが、TreeViewコントロールやXamDataTreeコントロールは本来階層構造を表示するためのコントロールなので、いよいよ階層構造を表示してみましょう。
ItemsControlは設定されたコレクションオブジェクトの個々のItemを表示するために、個々のItem表示用のコントロールを生成して表示に利用します。生成するコントロールは親となるコントロールによって異なります。
例えばListBoxコントロールの場合はListBoxItemコントロール、ComboBoxの場合はComboBoxItem、そしてTreeViewコントロールの場合はTreeViewItemというコントロールが生成されます。
つまり、最終的にはItemsSourceに設定されたオブジェクトの要素の数だけTreeViewItemコントロールが追加されることになります。
TreeViewItemコントロールもコンテンツモデルのコントロールで、こちらはHeaderdItemsControlというコントロールの種類でオブジェクトのコレクションとヘッダーを取り扱うコントロールとなっています。
そのため、TreeViewItemには通常のItemsControlと同じItemsプロパティ、そしてItemsSourceプロパティに加えてヘッダーを表示するためHeaderプロパティが用意されています。
つまり、TreeViewコントロールは複数のTreeViewItemを保持することができて、コンテンツとして設定することで階層構造を表現することができます。
それでは早速、コンテンツにTreeViewItemコントロールを利用して階層構造の表示を行ってみましょう。
- サンプルプログラム4: 04-TreeViewItem.zip
<UserControl x:Class="_04_TreeViewItem.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <Grid x:Name="LayoutRoot" Background="White"> <!-- TreeViewコントロール --> <sdk:TreeView Name="treeView1"> <sdk:TreeViewItem Header="階層構造1"> <sdk:TreeViewItem Header="階層構造2-1"> <sdk:TreeViewItem Header="階層構造3-1" /> <sdk:TreeViewItem Header="階層構造3-2" /> </sdk:TreeViewItem> <sdk:TreeViewItem Header="階層構造2-2" /> <sdk:TreeViewItem Header="階層構造2-3" /> <sdk:TreeViewItem Header="階層構造2-4" /> </sdk:TreeViewItem> </sdk:TreeView> </Grid> </UserControl>
実行結果は以下のとおりです。
TreeViewItemは個々のデータ表示にContentPresenterを利用して表示しています。つまり、通常の文字だけではなく、各項目の表示をコントロールで表現できます。
以下のようなXAMLを設定することで各項目をカスタマイズすることもできます。
- サンプルプログラム5: 05-TreeViewItemUseControl.zip
<UserControl x:Class="_05_TreeViewItemUseControl.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <Grid x:Name="LayoutRoot" Background="White"> <sdk:TreeView Name="treeView1"> <sdk:TreeViewItem Header="テキストブロック"> <TextBlock Text="テキストブロック1" /> <TextBlock Text="テキストブロック2" /> <TextBlock Text="テキストブロック3" /> </sdk:TreeViewItem> <sdk:TreeViewItem Header="ボタンコントロール"> <Button Content="ボタン1" /> <Button Content="ボタン2" /> <Button Content="ボタン3" /> </sdk:TreeViewItem> <sdk:TreeViewItem Header="チェックボックスコントロール"> <CheckBox Content="チェックボックス1"/> <CheckBox Content="チェックボックス2"/> <CheckBox Content="チェックボックス3"/> </sdk:TreeViewItem> </sdk:TreeView> </Grid> </UserControl>
実行結果は以下のとおりです。
階層構造を持つデータの表示
XamDataTreeコントロールでは階層構造データを取り扱うためにGlobalNodeLayoutsプロパティが定義されています。
このプロパティにはNodeLayoutオブジェクトを複数に定義することができます。
NodeLayoutオブジェクトはあらかじめ各種データに対する表示方法を以下のように定義しておくことができます。
NodeLayoutプロパティ | 設定する値 |
Key | NodeLayoutオブジェクトの特定に利用される一意の識別子を設定 |
TargetTypeName | 表示を行うノードの型(クラス) |
DisplayMemberPath | 表示対象となる項目のプロパティパス |
そして、ItemsSourceプロパティに対して実際のデータがセットされた際、XamDataTreeコントロールはそのデータの階層構造を解析し、発見した各ノードの型に対応するNodeLayoutオブジェクトをGlobalNodeLayoutsプロパティ内に発見すると、それを利用して階層構造の各Nodeを表示します。
あらためてこれらを整理すると以下のような形になります。
それでは実際にLIST5で定義したエンティティクラスを使って階層構造を持つデータの表示を行ってみましょう。
まず、エンティティクラスを利用して、最初に以下のような階層構造データをLoadedイベントでコーディングします。
- サンプルプログラム6: 06-TreeData.zip
private ObservableCollection<Person> persons; private void UserControl_Loaded(object sender, RoutedEventArgs e) { persons = new ObservableCollection<Person> { new Person { PersonName = "しるば太郎", Children = { new Person { PersonName = "しるば子太郎"} } }, new Person { PersonName = "しるば次郎", Children = { new Person {PersonName = "しるば子次郎"}, new Person {PersonName = "しるば子三郎", Children = { new Person {PersonName = "しるば孫太郎"}} }, } }, new Person { PersonName = "しるば三郎" }, new Person { PersonName = "しるば四郎" }, new Person { PersonName = "しるば五郎" } }; xamDataTree1.ItemsSource = persons; }
上記のコードのほとんどはPersonクラスを使って階層構造を持つデータの作成であり、XamDataTreeコントロールにはItemsSourceプロパティにデータを設定したのみです。
次にXAMLです。
GlobalNodeLayoutsプロパティにPerson型に対応するNodeLayoutを定義します。そして、階層の最上階層を表示するためのDisplayMemberPathも併せて設定します。
<ig:XamDataTree Name="xamDataTree1" DisplayMemberPath="PersonName" > <ig:XamDataTree.GlobalNodeLayouts> <ig:NodeLayout Key="ChildrenLayout" TargetTypeName="Person" DisplayMemberPath="PersonName"/> </ig:XamDataTree.GlobalNodeLayouts> </ig:XamDataTree>
実行結果は以下のようになります。
もし、階層構造のメンバーに複数のデータ型が存在する場合、GlobalNodeLayoutsにその型に対応するNodeLayoutを追加するのみで複雑な階層構造も簡単にマッピングできます。
おわりに
今回は階層構造を表示するためのコントロールTreeViewコントロールとXamDataTreeコントロールを紹介しました。
今回の記事の中で、TreeViewコントロールを使った階層構造のデータを表示する方法に触れていませんでしたが、TreeViewコントロールは残念ながらXamDataTreeのようにプロパティ設定だけでデータに対してのマッピングを行う機能はありません。
TreeViewコントロールで特定エンティティクラスとして定義された階層構造を持つオブジェクトの表示を行うには各Templateのカスタマイズが必要となります。
次回は、これらのTemplateをはじめとするTreeViewおよびXamDataTreeのカスタマイズの方法について説明したいと思います。