はじめに
リスト項目を階層状に表示できるツリービューコントロールは何かと便利なコントロールですが、リストの動作はノードの展開と折りたたみしかありません。例えば、ユーザーが自由にリスト項目の順序を入れ替えられれば、アプリケーションの幅はぐっと広がるのではないでしょうか。
ComponentOne Studio for Silverlightに収録されているC1TreeViewコントロールは、階層ツリーを作成する際、好きな画像をアイコンに設定でき、文字色を選択してカラフルに設定できます。さらに、リスト項目をドラッグ&ドロップで入れ替えることもできます。
今回は、このC1TreeViewコントロールを使って、見た目が綺麗で、ドラッグ&ドロップもできるツリービューを持ったSilverlightアプリケーションを作ってみました。
対象読者
Visual Basic/Visual C# 2010を使ってプログラムを作ったことのある人。また、SilverlightおよびXAMLに対する基礎的な知識が必要になります。
必要な環境
Visual Basic 2010、Visual C# 2010、Visual Studio 2010でプログラムが作れる環境。また、Silverlight 4 Tools for Visual Studio 2010をインストールしていることが必須条件です。
なお、本プログラムはWindows Vista上で動作するVisual Studio 2010およびSilverlight 4 Tools for Visual Studio 2010を使用して作成し、Internet Explorer 8で動作確認を行っています(※注意)。
今回の記事から、Visual Studio 2010+Silverlight 4 Tools for Visual Studio 2010の開発環境になっています。
これまでのVisual Studio 2008+Silverlight 3 Tools for Visual Studio 2008の環境では、サンプルソースは動作しないので注意してください。
サンプルソースを実行するには、C#、VBともに次の設定を行う必要があるため、注意してください(※○○にはcsまたはvbのいずれかが入ります)。
- ソリューションエクスプローラでASP.NET Webサイトプロジェクト(sl_TreeView2011_○○.Web)を右クリックし、[スタートアッププロジェクトに設定]を選択
- ソリューションエクスプローラでsl_TreeView2011_○○TestPage.aspxを右クリックし、[スタートページに設定]を選択
コンポーネントのインストール
ComponentOne Studio for Silverlightを使用する方は、Visual Studio、Visual Basic、Visual C#の開発環境にComponentOne Studio Enterpriseをインストールする必要があります。
インストーラは、グレープシティのWebページからダウンロードできます。製品ページの[申込フォーム]をクリックし、グレープシティのWebサイトへ必要情報を登録すると、添付トライアルライセンスキーファイルとダウンロードサイトを記載したE-Mailが送られてきますので、ここからダウンロードします。制限事項などの詳細については、インストーラに同梱されているリリースノートを参照ください。
C1TreeViewコントロールについて
TreeView for Silverlightは、データ項目を階層的に表示するコントロールです。WPFやWindowsフォーム、Silverlightに標準で装備されているTreeViewコントロールに似ていますが、標準コントロールよりもさらに多い、次のような機能を備えています。
ノードのドラッグ&ドロップ
C1TreeViewコントロールは、ツリー内のドラッグ&ドロップ操作をサポートします。AllowDragDropプロパティを「true」に設定するだけで、ツリー内のノードをドラッグして並べ替えることができます。また、ドラッグ&ドロップの操作中にイベントを発生し、このイベントを使って一部のノードのドラッグを禁止したり、一部のノードがドロップ先にならないように指定できます。
キーボードによる移動
カーソルキーを使用して、ノード間の移動や、ノードの展開・折りたたみができます。また、自動検索機能を使用して、特定のノードをすばやく簡単に見つけることができます。
ノードのカスタマイズ
リスト項目となっているノードヘッダーはコンテンツ要素なので、任意の種類の要素をホストできます。画像、チェックボックスなど、アプリケーションに必要なあらゆる要素を追加できます。
Silverlight Toolkitテーマのサポート
ExpressionDark、ExpressionLight、WhistlerBlue、RainierOrange、ShinyBlue、BureauBlackなど、よく使用されているMicrosoft Silverlight Toolkitテーマが組み込みでサポートされており、それを使ってUIにスタイルを追加できます。
C1TreeViewクラスは、次の2つの要素からなる、1つのStackPanelです。
- 実際のノードを表すヘッダー
- 別のStackPanelからなる本体
各リスト項目はC1TreeViewItemクラスになっており、それぞれにHeaderオブジェクトを持ちます。このHeaderオブジェクト内にStackPanelコントロールを組み込むことで、リスト項目にImage、TextBlock、CheckBoxなどのコントロールを組み込むことができます。C1TreeViewクラスは、C1TreeViewItemクラスを入れ子状に配置することで、リスト項目を階層状に表示します。
C1TreeViewコントロールのAllowDragDropプロパティを「true」に設定すると、ノード、ノードの間、または1つのツリーから別のツリーにドラッグ&ドロップできます。
実行時にユーザーがノードをクリックすると、そのノードは自動的に選択中としてマークされ、C1TreeViewコントロールにSelectionChangedイベントが発生します。現在選択中の項目を取得するには、SelectedItemプロパティを使用します。ノードの操作では、マウスとキーボードによる移動をサポートします。それぞれの操作とコントロールのアクションは、次表のようになります。
| アクション | マウスコマンド |
| ノードの展開 | ノード名の左側にあるプラス記号をクリック |
| ノードの折りたたみ | ノード名の左側にあるマイナス記号をクリック |
| ノードの選択 | ノード名をクリック |
| アクション | キーボードコマンド |
| ノードの展開 | [+]キー |
| ノードの折りたたみ | [-]キー |
| ノードを上に移動 | [↑]キー |
| ノードを下に移動 | [↓]キー |
| 複数ノードの選択 | [Ctrl]キー+クリック |
今回は、このC1TreeViewコントロールの機能の中で、ドラッグ&ドロップによるリスト項目の移動を使ったアプリケーションを作成します。
Webページの作成
では、Webページを作成してみましょう。まず、グリッドを1行2列に設定します。左側の列にC1TreeViewコントロールを、右側の列にTextBoxコントロールを配置します。C1TreeViewコントロールの設定は、この後説明します。
Silverlightプロジェクトの作成
まずは、Silverlightプロジェクトを作成します。Visual Studio 2010 Silverlight Tools 4.0をインストールし、Visual Studioで新しいプロジェクトを作成すると、[プロジェクトの種類]に[Silverlight]が追加されています。これを選んで[テンプレート]から[Silverlightアプリケーション]を選びます。
「新しいSilverlightアプリケーション」というダイアログボックスが表示されるので、[新しいWebプロジェクトの種類]でリストから[ASP.NET Webサイト]を選びます。
プロジェクトが作成され、新しいWebサイトに「MainPage.xaml」が作られてXAMLのコードが表示されます。このXAMLコードに、以下の名前空間を追加します。
xmlns:c1="http://schemas.componentone.com/winfx/2006/xaml"
この名前空間の追加によって、XAMLでComponentOne Studio for Silverlightの各コントロールを使うことができるようになります。プロジェクトに次の参照を追加します。
C1.Silverlight.dll
全体のレイアウト作成
今回は、「交通違反の反則金」を表示するアプリケーションを作成します。反則行為の種類をC1TreeViewコントロールで表示し、リスト項目をクリックすると、反則金がTextBoxコントロールで表示されます。
グリッドとC1TreeViewコントロールの作成
まず、グリッドを設定します。「ColumnDefinitions」プロパティを使用し、2列のグリッドにします。
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="136*"/>
<ColumnDefinition Width="264*"/>
</Grid.ColumnDefinitions>
</Grid>
続いて、C1TreeViewコントロールを記述します。入力候補がリストで表示されるので、順番に選んでいきます。
<c1:C1TreeView></c1:C1TreeView>
<c1:C1TreeView>を、次のXAMLに書き換えます。なお、Marginプロパティは、Visual StudioのデザイナでC1TreeViewコントロールの位置を変えると値が変化します。また、C1TreeViewコントロールのSelectionChangedイベントを使うため、このイベントハンドラを作成しておきます。リストのドラッグ&ドロップを有効にするために、「AllowDragDrop」プロパティを「True」にセットします。これで、C1TreeViewコントロールのすべてのノードがドラッグ&ドロップで移動可能になります(※注意)。
<c1:C1TreeView x:Name="TreeView1" AllowDragDrop="True" Margin="42,41,20,165"
FontSize="14" SelectionChanged="TreeView1_SelectionChanged">
</c1:C1TreeView>
個々のノードのドラッグ&ドロップ操作の可否を設定したい場合は、それぞれのノードのC1TreeViewItemクラスの「AllowDragDrop」プロパティを使用します。
ツリーの階層を作成する
次に、ツリーの階層を作成します。トップレベルの階層と次の階層で、以下のようなリスト項目を作成します。
-
反則行為の種類
- 速度超過
- 積載物重量制限超過
- 信号無視
- 免許証不携帯
- 追越し違反
C1TreeViewコントロールの中に、C1TreeViewItemクラスを入れ子状にして記述していきます。リスト項目の文字列やアイコンはHeaderプロパティを使用して組み込みます。
トップレベルの階層は「反則行為の種類」で「大見出し」にあたる部分になり、C1TreeViewコントロールの中にC1TreeViewItemクラスを作成します。
<c1:C1TreeView x:Name="TreeView1" AllowDragDrop="True" Margin="42,41,20,165"
FontSize="14" SelectionChanged="TreeView1_SelectionChanged">
<c1:C1TreeViewItem Foreground="SteelBlue" >
</c1:C1TreeViewItem>
</c1:C1TreeView>
そして、C1TreeViewItemクラスの中に、さらに次の階層となるC1TreeViewItemクラスを作成します。
<c1:C1TreeViewItem Foreground="SteelBlue" > <c1:C1TreeViewItem Foreground="DarkGreen"> </c1:C1TreeViewItem> </c1:C1TreeViewItem>
ここでは、第2階層に5つの項目を組み込むため、XAMLのコードは次のようになります。
<c1:C1TreeView x:Name="TreeView1" AllowDragDrop="True" Margin="42,41,20,165"
FontSize="14" SelectionChanged="TreeView1_SelectionChanged">
<c1:C1TreeViewItem Foreground="SteelBlue" >
<!-- グループ 1 -->
<c1:C1TreeViewItem Foreground="DarkGreen">
</c1:C1TreeViewItem>
<!-- グループ 2 -->
<c1:C1TreeViewItem Foreground="Coral">
</c1:C1TreeViewItem>
<!-- グループ 3 -->
<c1:C1TreeViewItem Foreground="DeepPink">
</c1:C1TreeViewItem>
<!-- グループ なし -->
<c1:C1TreeViewItem x:Name="Item12" Foreground="DarkViolet">
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item13" Foreground="mediumblue">
</c1:C1TreeViewItem>
</c1:C1TreeViewItem>
</c1:C1TreeView>
ヘッダー部にアイコンと項目名を設定する
続いて、作成したノードにアイコンと項目名を設定していきます。これは、各C1TreeViewItemオブジェクトのHeaderプロパティを利用しますが、テキストだけのリストであれば、Headerプロパティに項目名を設定するだけです。
<c1:C1TreeViewItem Header="反則行為の種類" />
テキストとアイコンを組み込むというように、複数のコントロールを使用したい場合は、StackPanelにImageコントロールとTextBlockコントロールを組み込みます。
<c1:C1TreeViewItem Foreground="SteelBlue" >
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/List_BulletsHS.png"/>
<TextBlock Text=" 反則行為の種類"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
Imageコントロールを使う場合は、プロジェクトの「ClientBin」フォルダに使用するイメージ画像を追加しておきます(ここでは、アプリケーション全体で7つのイメージ画像を使用するためまとめて追加)。
同様に、第2階層の5つのノードも同じように、StackPanel、Image、TextBlockコントロールを使ってリスト項目を作成します。
<!-- グループ 1 -->
<c1:C1TreeViewItem Foreground="DarkGreen">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/book_reportHS.png"/>
<TextBlock Text=" 速度超過"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
第3階層のノードを設定する
ここまでできたら、後は簡単です。第2階層の3つに、さらにもう1階層下のノードを作成します。そして、それぞれのC1TreeViewItemクラスの中に、さらにC1TreeViewItemクラスを作成していきます。アイコンやテキストの設定方法も同様で、C1TreeViewItemクラスの中にC1TreeViewItemクラスを作っていけば、どんどん深い階層のノードを作成できます。
<!-- グループ 1 -->
<c1:C1TreeViewItem Foreground="DarkGreen">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/book_reportHS.png"/>
<TextBlock Text=" 速度超過"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
<c1:C1TreeViewItem x:Name="Item1" >
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 高速道路35以上40未満"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item2" >
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 高速道路30以上35未満"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
.........
.........
</c1:C1TreeViewItem>
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="136*"/>
<ColumnDefinition Width="264*"/>
</Grid.ColumnDefinitions>
<c1:C1TreeView x:Name="TreeView1" AllowDragDrop="True" Margin="42,41,20,165"
FontSize="14" SelectionChanged="TreeView1_SelectionChanged">
<c1:C1TreeView.Effect>
<DropShadowEffect BlurRadius="5" Color="DarkGray" />
</c1:C1TreeView.Effect>
<c1:C1TreeViewItem Foreground="SteelBlue" >
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/List_BulletsHS.png"/>
<TextBlock Text=" 反則行為の種類"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
<!-- グループ 1 -->
<c1:C1TreeViewItem Foreground="DarkGreen">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/book_reportHS.png"/>
<TextBlock Text=" 速度超過"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
<c1:C1TreeViewItem x:Name="Item1" >
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 高速道路35以上40未満"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item2" >
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 高速道路30以上35未満"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item3">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 25以上30未満"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item4">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 20以上25未満"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item5">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 15以上20未満"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item6">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 15未満"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
</c1:C1TreeViewItem>
<!-- グループ 2 -->
<c1:C1TreeViewItem Foreground="Coral">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/FillDownHS.png"/>
<TextBlock Text=" 積載物重量制限超過"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
<c1:C1TreeViewItem x:Name="Item7">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 普通等10割以上"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item8">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 5割以上10割未満"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item9">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Flag_redHS.png"/>
<TextBlock Text=" 5割未満"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
</c1:C1TreeViewItem>
<!-- グループ 3 -->
<c1:C1TreeViewItem Foreground="DeepPink">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/RadialChartHS.png"/>
<TextBlock Text=" 信号無視"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
<c1:C1TreeViewItem x:Name="Item10" Foreground="IndianRed">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/RadialChartHS.png"/>
<TextBlock Text=" 信号無視(赤色等)違反"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item11" Foreground="Indigo">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/RadialChartHS.png"/>
<TextBlock Text=" 信号無視(点滅)違反"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
</c1:C1TreeViewItem>
<!-- グループ なし -->
<c1:C1TreeViewItem x:Name="Item12" Foreground="DarkViolet">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/BreakpointHS.png"/>
<TextBlock Text=" 免許証不携帯"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
<c1:C1TreeViewItem x:Name="Item13" Foreground="mediumblue">
<c1:C1TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/DoubleRightArrowHS.png"/>
<TextBlock Text=" 追越し違反"/>
</StackPanel>
</c1:C1TreeViewItem.Header>
</c1:C1TreeViewItem>
</c1:C1TreeViewItem>
</c1:C1TreeView>
TextBoxコントロールとイベントハンドラの処理
最後に、反則金を表示するTextBoxコントロールをグリッドの右側に配置し、イベントハンドラにその処理を作成します。
<TextBox Grid.Column="1" FontSize="14" Name="TextBox1" Height="104" HorizontalAlignment="Left"
Margin="21,44,0,0" VerticalAlignment="Top" Width="207" AcceptsReturn="True">
<TextBox.Effect>
<DropShadowEffect BlurRadius="5" Color="DarkGray" />
</TextBox.Effect>
</TextBox>
イベントハンドラの処理では、リスト項目が選択されると、C1TreeViewコントロールのSelectedItemプロパティに選択されたC1TreeViewItemオブジェクトへの参照が格納されます。ここでは、各C1TreeViewItemオブジェクトに名前を付けているため、そのNameプロパティでどのノードが選択されたのかを識別し、TextBoxコントロールへの表示を振り分けています。
なお、すべてのC1TreeViewItemオブジェクトに名前を付けていないので、名前のないノード(C1TreeViewItemオブジェクト)が選択されると「NullReferenceException」というエラーが発生します。そのため、Tryステートメントを使ってエラー回避の処理を組み込んでいます。
Imports C1.Silverlight
Partial Public Class MainPage
Inherits UserControl
Public Sub New()
InitializeComponent()
End Sub
Private Sub TreeView1_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs)
Try
Dim si As C1TreeViewItem = TreeView1.SelectedItem
Dim kingaku As String = ""
If si.Name = "" Then
Exit Sub
End If
Select Case si.Name
Case "Item1"
kingaku = 35
Case "Item2"
kingaku = 25
Case "Item3"
kingaku = 18
Case "Item4"
kingaku = 15
Case "Item5"
kingaku = 12
Case "Item6"
kingaku = 9
Case "Item7"
kingaku = 35
Case "Item8"
kingaku = 30
Case "Item9"
kingaku = 25
Case "Item10"
kingaku = 9
Case "Item11"
kingaku = 7
Case "Item12"
kingaku = 3
Case "Item13"
kingaku = 9
End Select
TextBox1.Text = "普通車の反則金額は" + kingaku + "千円"
Catch ex As NullReferenceException
End Try
End Sub
End Class
using C1.Silverlight;
namespace sl_TreeView2011_cs
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void TreeView1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
String si = TreeView1.SelectedItem.Name;
String kingaku = "";
if (si == "")
{
return;
}
switch (si)
{
case "Item1":
kingaku = "35";
break;
case "Item2":
kingaku = "25";
break;
case "Item3":
kingaku = "18";
break;
case "Item4":
kingaku = "15";
break;
case "Item5":
kingaku = "12";
break;
case "Item6":
kingaku = "9";
break;
case "Item7":
kingaku = "35";
break;
case "Item8":
kingaku = "30";
break;
case "Item9":
kingaku = "25";
break;
case "Item10":
kingaku = "9";
break;
case "Item11":
kingaku = "7";
break;
case "Item12":
kingaku = "3";
break;
case "Item13":
kingaku = "9";
break;
}
TextBox1.Text = "普通車の反則金額は" + kingaku + "千円";
}
catch (NullReferenceException ex)
{
}
}
}
}
まとめ
今回は、C1TreeViewコントロールを使ってアイコンと文字色を組み合わせ、見栄えの良い綺麗なツリーを作成してみました。リスト項目がドラッグ&ドロップで入れ替えられるようになると、例えば、組織図を階層ツリーにしてメンバーの入れ替えをシミュレートしたり、ユーザーごとにリストの順序を入れ替える、などという形でアプリケーションを作ることができます。標準のTreeViewコントロールでは物足りないという方は、C1TreeViewコントロールを使ってみてはいかがでしょうか。




![[Silverlightアプリケーション]を選ぶ](http://cz-cdn.shoeisha.jp/static/images/article/5985/5985_fig5.gif)
