特集
» 2010年12月14日 00時00分 公開

特集:Visual Studio 2010で社内C/Sシステム開発(前編):Visual Studio 2010でユーザー・インターフェイス開発 (2/3)

[一色政彦,デジタルアドバンテージ]

■Gridパネルへの各種コントロールの追加

ヘッダーバーとフッターバーの作成

 次に、作成が比較的簡単なヘッダーバーとフッターバーを作成していこう。

ヘッダーバーへのGridパネルの配置

 まずヘッダーバーでは、現在ログインしているユーザーの名前を表示する(TextBlockコントロール使用)。その右端では、ログイン・ユーザーを切り替えられるコンボボックス(=ComboBoxコントロール)を配置する。この2つをレイアウトするため、Gridパネルを使う。要するに、Gridパネルの中の1つのセルに、もう1つGridパネルを配置する入れ子関係にする。WPFでは、このような入れ子にしたレイアウト・デザインがよく用いられる。

 「Gridパネルを新規に追加するには、WPFデザイナへドラッグ&ドロップ」といきたいところなのだが、Gridパネル内の目的のセルにコントロールを追加するには少しコツが必要だ(この方法は、後述の「【コラム】Gridパネルのセルにコントロールを追加する方法」で説明する)。そこで本稿では、XAMLエディタ側にドラッグ&ドロップする。

 XAMLエディタの「</r:Ribbon>」(=<r:Ribbon>要素の閉じタグ)の下に改行を入れ、次の画面のように、そこに[ツールボックス]の[コモン WPF コントロール]タブから「Grid」をドラッグ&ドロップする(もしくは手動で記述してもよい)。

ヘッダーバー用のGridパネルの追加(XAMLエディタ)

対象のセル領域に要素を指定

 追加された「<Grid />」というコード部分を選択する(もしくは、タグ・ナビゲータや[ドキュメント アウトライン]ウィンドウで、先ほど追加したGridパネルを選択する)。この状態で、[プロパティ]ウィンドウを開き(メニューバーから[表示]−[プロパティ ウィンドウ]を実行)、次の画面のように、要素の名前「HeaderBar」を指定したうえで、[Grid.ColumnSpan]添付プロパティに「2」を、[Grid.Row]添付プロパティ(=行番号)に「1」(=0始まりなので2行目)を設定する。

対象のセル領域に要素を指定([プロパティ]ウィンドウ)

 なお、目的のプロパティを探す場合には、上の画面のように、検索ボックスにプロパティ名の一部(この例では「Grid」)を入力すれば、素早く見つけ出すことができる。これは大活躍する機能なので、ぜひ覚えておいてほしい。

【コラム】Gridパネルのセルにコントロールを追加する方法

 Gridパネル内の目的のセルにコントロールを追加するには少しコツが必要だ。その手順を、複数のスクリーン・キャプチャ画像で示す。以下は、Gridパネルの2行目の1〜2列目に、別のGridパネル(以降、新コントロール)を追加しているところ(つまり、上記の手順をドラッグ&ドロップにより実現したもの)。


[ツールボックス]から新コントロールを2行1列目にドラッグ&ドロップ
この例では複数のセルに新コントロールが貼り付けられた
右下をドラッグしてサイズを変更し、新コントロールを2行目の1〜2列目の中に収める
(なお、左上の[+]部分をドラッグすると、新コントロールをほかのセルに移動させられる)
新コントロールの位置やサイズを変更することで、
Grid.ColumnSpan添付プロパティやGrid.Row添付プロパティが自動的に設定される
最後に、新コントロールを右クリックして、
表示されるコンテキスト・メニューから[レイアウトのリセット]−[すべて]を実行。
これにより、サイズや位置などレイアウトに関するプロパティが消去されるため、
2行目の1〜2列目全体に新コントロールが広がる


2行目の1〜2列目に新コントロールをドラッグ&ドロップにより追加する手順


グラデーションがかかった背景色の設定

 WPFっぽい(今風の)外観に仕上げるには、わずかなグラデーションをかけるのがお勧めだ。自然界では、上方に太陽や蛍光灯があるので白っぽく明るく、下方が濃い色で暗い。ナチュラルな外観に仕上げるには、この自然界の法則に従ったグラデーションをかけるとよい。

 ここでは、上方に白色、下方に濃い水色のグラデーションをヘッダーバーの背景色として設定しよう。

 これには、[プロパティ]ウィンドウで[Background]プロパティを指定する(次の画面を参照)。プロパティ値の入力領域をクリックすると、その下に色選択ツールが表示されるので、[グラデーション ブラシ]ボタンと[水平方向のグラデーション]ボタンをクリックする。色の開始ポイントをクリックして、色名のコンボボックスで「White」を選択する。次に色の終了ポイントをクリックして、RGB値のそれぞれの値を入力する。今回は、[R]=「188」、[G]=「240」、[B]=「252]、[A]=「255」を指定する([A]はアルファ値で、透明度を表す)。

色選択ツールによるグラデーションの指定([プロパティ]ウィンドウ)
[プロパティ]ウィンドウの[Background]プロパティなどで値の入力領域をクリックすると、その下に色選択ツールが表示される。
  (1)[グラデーション ブラシ]ボタン。ちなみに、左端は[Null ブラシ]ボタンで、その隣が[純色ブラシ]ボタン。
  (2)[水平方向のグラデーション]ボタン。
  (3)色の開始ポイント。
  (4)色名のコンボボックス。
  (5)色の終了ポイント。
  (6)[境界の挿入]ボタン。色のポイントは開始、終了だけでなく、中間地点も設定できる。中間地点の色も指定することで、途中で色の流れが変化する複雑なグラデーションを作成することが可能だ。追加したポイントは、枠外にドラッグ&ドロップすることで削除できる。

プロパティ値をリソースとして抽出

 こうやって指定したグラデーション部分(=グラデーション・ブラシ)のXAMLコードを、XAMLエディタで見てみると少し長いことが分かる。グラデーション・ブラシなどは、リソースとしてコントロールの外部に分離することができる。分離することで、ほかのコントロールで同じグラデーション・ブラシを再利用できるなどのメリットも生まれる。そこで、ここではBackgroundプロパティに指定したグラデーション・ブラシをリソースとして抽出しよう。

 この作業も、[プロパティ]ウィンドウから簡単に行える。具体的に、[Background]プロパティを右クリックして、表示されるコンテキスト・メニューから[値をリソースに抽出]をクリック。これにより、[リソースの作成]ダイアログが表示されるので、[キー]欄に任意の名前(この例では「BackgroundHeaderBar」)を指定して、[OK]ボタンをクリックする。

グラデーション・ブラシをリソースとして抽出([プロパティ]ウィンドウ)

 その結果、次のようなコードが出力される。<r:RibbonWindow.Resources>要素は、RibbonWindowオブジェクトのResourcesプロパティを意味している。つまり、メインのウィンドウのResourcesプロパティにLinearGradientBrushオブジェクトが指定されていることが分かる。

<r:RibbonWindow.Resources>
  <LinearGradientBrush x:Key="BackgroundHeaderBar"
                       EndPoint="0.5,1" StartPoint="0.5,0">
    <GradientStop Color="White" Offset="0" />
    <GradientStop Color="#FFBCF0FC" Offset="1" />
  </LinearGradientBrush>
</r:RibbonWindow.Resources>

抽出されたグラデーション・ブラシのXAMLコード

 一方、<Grid>要素のBackgroundプロパティの値(抽出したリソースのキー名は「BackgroundHeaderBar」)は、StaticResource(=静的リソース)のマークアップ拡張機能を用いて、次のようなコードで指定されている

<Grid ……省略……
  Background="{StaticResource BackgroundHeaderBar}">

[プロパティ]ウィンドウでの列の設定

 次に、ヘッダーバーのGridパネルを、1行2列にする。列幅は、下記の表のとおり。

メニュー列 *
コンテンツ列 200
ヘッダーバーのGridパネルの各列のサイズ(=Widthプロパティ)の値

 列を追加するには、[プロパティ]ウィンドウで[ColumnDefinitions]プロパティの[...]ボタンをクリックする。そこで表示される[コレクション エディタ: ColumnDefinitions]ダイアログで[追加]ボタンをクリックし、<ColumnDefinition>要素を2つ追加する。左側の[項目の選択]一覧で2つ目の「ColumnDefinition」を選択したまま、右側の[プロパティ]ペインで[Width]プロパティに「200」を設定する(1つ目の<ColumnDefinition>要素のWidthプロパティはデフォルト値の「*」のままにする)。これにより、右端の200px(ピクセル)が固定され、左側がその残りで動的な幅サイズになる。最後に[OK]ボタンをクリックしてダイアログを閉じる。

各列へのコントロールの追加

 ヘッダーバーのGridパネルの1行1列目にTextBlockコントロールを、1行2列目にComboBoxコントロールを追加する。この方法は、前述のGridパネルの追加と同じなので割愛する(入れ子になった内側の<Grid>要素内に配置する必要があるので、追加位置には注意してほしい)。各コントロールに設定するプロパティは、下記の表にまとめた。

コントロール プロパティ
TextBlock 名前(Name) UserName
Text <ユーザー名>
Margin 3
FontWeight Bold
FontSize 20
ComboBox 名前(Name) UserSelection
Margin 3
ヘッダーバーの各コントロールのプロパティ値
通常、C#の変数名の頭文字は小文字にするが、WPFリボン・アプリケーションのテンプレートが大文字の名前を出力していたため、本稿のサンプル全体で各要素のNameプロパティ(=変数名)の頭文字を大文字に統一した。

【コラム】TextBlock/TextBox/Labelの根本的な違い

 「テキストブロック(TextBlock)」が初耳の開発者もいるかもしれないので簡単に紹介しよう。

 TextBlockコントロールは、TextBoxコントロールと違い、入力のできない表示専用のコントロールである。「それなら、Labelコントロールと同じなのではないか?」という疑問があるだろうが、この両者にも違いがある。

 実は、TextBlockコントロールは厳密にはコントロールではない。Controlクラス(System.Windows.Controls名前空間)を継承していないのだ。このため、通常のコントロールの動作をしてくれない。例えばコントロールを無効化(IsEnabled = False)しても、表示がグレーにならない。

 逆にいえば、Labelコントロールはコントロールとして動作する。例えば、アクセス・キー(=[Alt]キー+[A]〜[Z]もしくは数字などのいずれかのキーでコントロールにアクセスできる機能)に対応している。ちなみに、WPFのアクセス・キーは「_A」のようにアンダースコアを利用する(Windowsフォームの場合は「&A」のようにする)。


 以上で、ヘッダーバー部分のUIは完成だ。

フッターバーへのStatusBarコントロールの配置

 続いて、フッターバーにStatusBarコントロールを配置する。

 詳しい手順の説明は、もう必要がないだろう。[ツールボックス]の[すべての WPF コントロール]タブから「StatusBar」をドラッグ&ドロップする(もしくは手動で記述してもよい)。一番外側の<Grid>要素の、一番下に配置するよう、配置場所には気を付けてほしい。

 そして、各種プロパティを設定する。設定内容は下記の表のとおり。

プロパティ
名前(Name) FooterBar
Grid.Row 3
Grid.ColumnSpan 2
Background ・グラデーション・ブラシ
・水平方向のグラデーション
・開始ポイント:R=240/G=240/B=240/A=255
・終了ポイント:R=193/G=193/B=193/A=255
Items <StatusBarItem>要素を1つ(詳細後述)
ヘッダーバーの各コントロールのプロパティ値

 Backgroundプロパティに指定したグラデーション・ブラシは、先ほどと同じようにリソースとして抽出しよう。本稿では、キー名を「BackgroundFooterBar」にした。

 Itemsプロパティを設定するには、[プロパティ]ウィンドウで[Items]プロパティの[...]ボタンをクリックする。そこで表示される[コレクション エディタ: Items]ダイアログで[追加]ボタンをクリックし、<StatusBarItem>要素を1つ追加する。左側の[項目の選択]一覧でその「StatusBarItem」を選択したまま、右側の[プロパティ]ペインで[Content]プロパティに「Ready」を設定する。最後に[OK]ボタンをクリックしてダイアログを閉じる。これにより、フッターバーに「Ready」と表示される。

 なお、このフッターバーは、簡易な情報をユーザーに通知するための領域として利用する。

メニュー領域とコンテンツ領域の境界線の作成

 ようやく、メイン部分のUI実装に入る。まずはメニュー領域。

メニュー領域の境界線の作成

 メニュー領域の回りは、境界が分かりやすいように灰色の枠(以降、境界線)を作成する。これには、Borderコントロールを利用する。この場合も[ツールボックス]からドラッグ&ドロップして(追加位置は<StatusBar>要素の上)、下記の表のプロパティを設定する。なお、C#やVB(Visual Basic)のコードから境界線にアクセスすることはないため、「名前(Name)」は設定しなくてよい。

プロパティ
Grid.Row 2
BorderThickness 1
BorderBrush 純色ブラシ、色:R=188/G=188/B=188/A=255
Background ・グラデーション・ブラシ
・水平方向のグラデーション
・開始ポイント:R=246/G=246/B=246/A=255
・終了ポイント:R=190/G=190/B=190/A=255
メニュー領域の各コントロールのプロパティ値

 Backgroundプロパティに指定したグラデーション・ブラシは、先ほどと同じようにリソースとして抽出しよう。本稿では、キー名を「BackgroundMenu」にした。

コンテンツ領域の境界線の作成

 コンテンツ領域も、先ほどのメニュー領域と同様に、Borderコントロールで境界線を作成する。プロパティ値は、下記の表のとおり。

プロパティ
Grid.Row 2
Grid.Column 1
BorderThickness 1
BorderBrush ・純色ブラシ
・色:R=188/G=188/B=188/A=255
コンテンツ領域の各コントロールのプロパティ値

 なお、メニュー領域とコンテンツ領域のBorderBrushプロパティの値は共通しているので、リソースとして抽出した方がよいケースがある。そうすることで、リソースの1個所を変えるだけで、両方の色を一度に変更できるようになるからだ。しかし本稿では、コードをシンプルにするために、この手順は省いた。

メニュー領域の作成

 それでは、メニュー領域の内部、つまり実際のメニューを作成していこう。

StackPanelパネルの追加

 メニュー項目は、上から下に順番に項目が並ぶようにしたいので、StackPanelパネルを配置する。なお、WPFデザイナでは、前述したようにGridパネルの個別のセルにドラッグ&ドロップすることはできないのだが、それ以外のドラッグ&ドロップには対応している。ここで行うBorderコントロールへのドラッグ&ドロップも行える。

 そこでここでは、[ツールボックス]の[コモン WPF コントロール]タブから「StackPanel」を、WPFデザイナのBorderコントロールの上にドラッグ&ドロップすればよい。追加したら、サイズを調整するために、WPFデザイナ上でStackPanelパネルを右クリックして、表示されるコンテキスト・メニューから[レイアウトのリセット]−[すべて]を実行する(次の画面を参照)。この作業により、サイズや位置などレイアウトに関するプロパティが消去されるため、Borderコントロール全体にStackPanelパネルが広がる。

StackPanelパネルのサイズのリセット(WPFデザイナ)

 StackPanelパネルの名前(Name)は、[プロパティ]ウィンドウなどで「StackPanelMenu」と指定しておこう。

StackPanelパネルへのExpanderコントロールの追加

 StackPanelパネル内には、冒頭で示した下記のメニューを縦に並べる。

【一般ユーザー向けのメニュー】
・お弁当発注

【お弁当当番向けのメニュー】
・お弁当登録
・お弁当一覧
・ユーザー登録
・ユーザー管理

 「一般ユーザー向け」と「お弁当当番向け」のカテゴリ分けは、Expanderコントロールで実現する。Expanderコントロールは、領域を折りたたんだり、展開したりする機能を提供する。

 そこで、(WPFデザイナへのドラッグ&ドロップにより)Expanderコントロールを2つ追加しよう。プロパティ設定は下記のとおり。

コントロール プロパティ
Expander Name ExpanderUser
Header みんなのメニュー
IsExpanded True
Expander Name ExpanderAdmin
Header お弁当当番のメニュー
Height 150
各Expanderコントロールのプロパティ値

ExpanderコントロールへのStackPanelパネルの追加

 各Expanderコントロールの内部には、最初から<Grid>要素が追加されているが、この要素は不要なため削除する。これには、次の画面のようにWPFデザイナでExpanderコントロールのコンテンツ部分をクリックして選択し、[Delete]キーを押せばよい。

各Expanderコントロールの内部にあるGridパネルの削除(WPFデザイナ)

 削除したら、同じ位置にStackPanelパネルをドラッグ&ドロップにより配置し、右クリック・メニューから[レイアウトのリセット]−[すべて]を実行する。

 この作業を、2つのExpanderコントロール(ExpanderUserとExpanderAdmin)で行う。追加した各StackPanelパネルのプロパティ設定は、下記のとおり。

コントロール プロパティ
StackPanel Name StackPanelUser
StackPanel Name StackPanelAdmin
各Expanderコントロールのプロパティ値

StackPanelパネルへのToggleButtonコントロールの追加

 次に、StackPanelUserに1つのButtonコントロール、StackPanelAdminに4つのButtonコントロールを、ドラッグ&ドロップにより配置し右クリック・メニューから[レイアウトのリセット]−[すべて]を実行する。すべてのボタンが追加し終わったら、各Expanderコントロールの右クリック・メニューから[レイアウトのリセット]−[すべて]を実行する。

 ここで使用したいメニュー・ボタンは、通常のボタンではなく、メニュー項目が開かれている間はずっと押し下げられた(=チェックされた)状態になるボタンだ。これを実現するのが、ToggleButtonコントロールである。そこで、XAMLコードで下記の一括置換を行ってほしい(先頭に「<」を含めているのは、誤ってほかのコードまで書き換えてしまわないため)。

  1. 置換前:<Button
  2. 置換後:<ToggleButton

 各ToggleButtonコントロールのプロパティ設定は、すべての上から順に次の表のとおりに行う。

コントロール プロパティ
ToggleButton Name MenuObentoOrder
Content お弁当発注
IsChecked True
ToggleButton Name MenuObentoEntry
Content お弁当登録
ToggleButton Name MenuObentoList
Content お弁当一覧
ToggleButton Name MenuUserEntry
Content ユーザー登録
ToggleButton Name MenuUserList
Content ユーザー一覧
各ToggleButtonコントロールのプロパティ値

 また、すべてのToggleButtonコントロールに下記のプロパティ設定を行う。一括してプロパティ設定を行うには、[ドキュメント アウトライン]ウィンドウで[Ctrl]キーを押したまま、各ToggleButtonコントロールを次々とクリックして複数選択したうえで、プロパティ設定を行えばよい。

プロパティ
Focusable False
Padding 1,10
Margin 2
Background Nullブラシ(=「{x:Null}」)
ClickMode Press
各ToggleButtonコントロールのプロパティ値

 ウィンドウの高さが不足してメニューの表示領域が足りなくなったら、RibbonWindowウィンドウのHeightプロパティに「550」を設定しておこう。

 取りあえず、ここまでの内容でビルドしよう。次の画面は現状のアプリを実行したところだ。当然、メニュー・ボタンをクリックしても、何も起きないし、メニュー・ボタンが複数選択されてしまう結果となる。

現状のアプリを実行したところ(未完成)

 さらにメニュー部分の作り込みは必要だが、その前に必要なUI要素をすべてそろえたいので、コンテンツ領域を作成しよう。

コンテンツ領域の作成

 コンテンツ領域では、「メニューを選択すると、それに合わせて内容を変化させる」という仕様にする。この仕様を実現する単純な方法は、メニューの数だけユーザー・コントロールを用意しておき、メニューが切り替えられるたびに、それらのユーザー・コントロールをコンテンツ領域に当てはめることだ。この目的に使えるのが、ContentControlコントロールである。

 ContentControlコントロールは、そのContentプロパティに指定された要素の内容を表示する機能を持つ。つまり、ContentControlコントロールのContentプロパティに、ユーザー・コントロールのオブジェクトを指定すれば表示が切り替わるわけだ。

 ContentControlコントロールを追加するには、[ツールボックス]の[すべての WPF コントロール]タブから「ContentControl」を、WPFデザイナの右側のBorderコントロールの上にドラッグ&ドロップすればよい。配置したら、右クリック・メニューから[レイアウトのリセット]−[すべて]を実行する。ContentControlコントロールの名前(Name)は、[プロパティ]ウィンドウなどで「ContentControlMain」と指定しておこう。

リボン・タブの追加

 コンテンツ領域が切り替わると、それに対応するリボンが表示されるようにしよう。

 その仕組みとして本稿では、各コンテンツ領域用のリボン・タブ(合計5つ)をあらかじめ用意しておき、該当するメニューが選択されていないときは非表示に(=Visibilityプロパティに「Hidden」を設定)しておくことにしよう。ひな型コードで生成されているリボン・タブ(HomeTab)はXAMLコードを書き直し、新たに残りの4つのリボン・タブを追加する。

 WPFリボン・コントロール用のWPFデザイナでは、まだ高度なデザイン処理が行えないようだ。従って、XAMLエディタでコードを手動で追記する。具体的には、<r:Ribbon>要素内に、下記のコードを追記すればよい。

<r:RibbonTab x:Name="TabObentoOrder" Header="基本操作">
  <r:RibbonGroup x:Name="Group1" Header="Group1">
    <r:RibbonButton x:Name="Button1" Label="Button1"
                    LargeImageSource="Images\LargeIcon.png" />
  </r:RibbonGroup>
</r:RibbonTab>

<r:RibbonTab x:Name="TabObentoEntry" Header="基本操作"
             Visibility="Hidden">
  <r:RibbonGroup x:Name="Group2" Header="Group2">
    <r:RibbonButton x:Name="Button2" Label="Button2"
                    LargeImageSource="Images\LargeIcon.png" />
  </r:RibbonGroup>
</r:RibbonTab>

<r:RibbonTab x:Name="TabObentoList" Header="基本操作"
             Visibility="Hidden">
  <r:RibbonGroup x:Name="Group3" Header="Group3">
    <r:RibbonButton x:Name="Button3" Label="Button3"
                    LargeImageSource="Images\LargeIcon.png" />
  </r:RibbonGroup>
</r:RibbonTab>

<r:RibbonTab x:Name="TabUserEntry" Header="基本操作"
             Visibility="Hidden">
  <r:RibbonGroup x:Name="Group4" Header="Group4">
    <r:RibbonButton x:Name="Button4" Label="Button4"
                    LargeImageSource="Images\LargeIcon.png" />
  </r:RibbonGroup>
</r:RibbonTab>

<r:RibbonTab x:Name="TabUserList" Header="基本操作"
             Visibility="Hidden">
  <r:RibbonGroup x:Name="Group5" Header="Group5">
    <r:RibbonButton x:Name="Button5" Label="Button5"
                    LargeImageSource="Images\LargeIcon.png" />
  </r:RibbonGroup>
</r:RibbonTab>

5つのリボン・タブを実装するXAMLコード

 次のページでは、メニューが選択されるとコンテンツ領域を切り替える処理を実装していく。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。