第3回 “見た目”を決めるリソースとスタイル:連載 WPF/Silverlight UIフレームワーク入門(1/3 ページ)
WPF/Silverlightの外観をデザインするには3つの機能を活用すればよい。今回は3つの中でも根幹となるリソース、そしてスタイルを解説する。
powered by Insider.NET
WPF UIフレームワークの“見た目”に関する機能としては「スタイル」と「コントロール・テンプレート」があるが、今回はまず、スタイルを紹介したいと思う(※編集部注:連載最初で3回としていましたが、情報量が多いため、編集部による判断で「スタイル」と「コントロール・テンプレート」で分けて4回としました)。
コントロール・テンプレートは一般的にスタイルの中で使われることが多く、スタイルはほぼ間違いなく「リソース」として定義される。従って、実際にはリソース、スタイル、コントロール・テンプレートの3つをまとめて使用することが多いのだが、これらをまとめて解説してしまうと、非常に理解しづらい。そのため、ここではこれら3つをできる限り切り離して説明する。
■リソース
WPF UIフレームワークでは、リソース・ディクショナリにオブジェクトを追加しておくことで、そのオブジェクトをリソースとしてXAML上で簡単に再利用できる。なお、このリソース・ディクショナリで管理されるリソースは、.NET全般で利用可能なリソース(=主にアセンブリに埋め込む形で利用されるリソース)とは概念が異なることに注意していただきたい。
具体的な説明に入る前に、まずは単純なリソースの利用例を見てほしい。例えば、下記のように2つの<Ellipse>要素のFillプロパティに青色のブラシを設定しているXAMLがあるとする。
<StackPanel>
<Ellipse Fill="Blue" Height="150"/>
<Ellipse Fill="Blue" Height="150"/>
</StackPanel>
Ellipseは円を描画するUI要素で、Fillプロパティはその円を塗りつぶすブラシを設定するプロパティ。
このXAMLコードは、リソース・ディクショナリを利用することで下記のように書き換えられる。
<StackPanel>
<!-- リソース・ディクショナリ -->
<StackPanel.Resources>
<SolidColorBrush x:Key="BlueBrush" Color="Blue"/>
</StackPanel.Resources>
<Ellipse Fill="{StaticResource BlueBrush}" Height="150"/>
<Ellipse Fill="{StaticResource BlueBrush}" Height="150"/>
</StackPanel>
青色のSolidColorBrushオブジェクトをリソース・ディクショナリに追加する(記述方法については後述)ことで、それをリソースとして2つの<Ellipse>要素から再利用することができる。
XAMLコードだけを見ればコード量が増えて複雑になったように感じるかもしれないが、リソース・ディクショナリを使うようにしたことで、下記の2つのメリットが得られる。
- 実行時に生成されるSolidColorBrushオブジェクトが1つで済むため、メモリ・リソースを節約できる
- Ellipseの色を変更する際には、リソース部分の1個所を修正するだけでよいため、保守性が向上する
具体的な利用例とメリットを確認したところで、上記のXAMLコードの内容を理解すべく、WPF UIフレームワークが持つリソースの詳細な機能について見ていこう。
●リソースの種類
リソースは、追加先のリソース・ディクショナリの場所により、イミディエイト・リソースとアプリケーション・リソースの2種類に分けられる。
○イミディエイト・リソース
FrameworkElementオブジェクトのResourcesプロパティ(ResourceDictionary型)に宣言されたリソースは、イミディエイト・リソースと呼ばれる。
FrameworkElementクラスはUIElementの派生クラスであり、本連載で登場したコントロール、パネル、Border、TextBlockなどのすべてのUI要素は、このFrameworkElementクラスを継承している。つまり、ほぼすべてのUI要素が自身のResourcesプロパティにリソースを持つことができる。そして、イミディエイト・リソースは、リソースの宣言要素と、その子要素から参照できるという特徴を持っている。
○アプリケーション・リソース
ApplicationオブジェクトのResourcesプロパティ(ResourceDictionary型)に宣言されたリソースは、アプリケーション・リソースと呼ばれる。
Applicationクラスは、アプリケーションの実装と管理で共通に使用される機能を提供するクラスで、標準的なWPF/Silverlightのプロジェクト・テンプレートでは、App.xamlファイルとその分離コード(=コードビハインド・ファイル)で使用されている。そのため、通常は、App.xamlファイル内の<Application>要素のResourcesプロパティに宣言されたリソースが、アプリケーション・リソースとなる。アプリケーション・リソースは、アプリケーション全体から、つまりどこからでも参照できるという特徴を持っている。
どこからでも参照できるのであれば、すべてのリソースをアプリケーション・リソースとして定義した方が便利なのではと思われるかもしれないが、それは避けた方がよいだろう。なぜなら、アプリケーションの起動時に読み込まれるアプリケーション・リソースが肥大化してしまうと、アプリケーションの起動が遅くなってしまうからだ。
●リソース・キー
リソース・ディクショナリの各リソースはキーによって識別されるため、x:Key属性を使用して各リソースに対しリソース・キーを割り当てておく必要がある。
通常、リソース・キーには文字列が使用されるが、WPFの一部の機能においてはオブジェクトが使用されることがある。これについてはスタイルとコントロール・テンプレートの項で述べる。
なお、1つのリソース・ディクショナリ内においてリソース・キーは一意でなければならないが、異なるリソース・ディクショナリであれば同名のリソース・キーが存在しても構わない。
●静的リソース参照
多くの場合、リソースの参照にはStaticResourceマークアップ拡張機能を使用した静的リソース参照が使用される。マークアップ拡張機能については、前回の「データ・バインディング」でも説明している。
リソース参照では、1つのリソース・オブジェクトが複数のプロパティから参照されるという仕組みが必要になるため、マークアップ拡張機能による「既存オブジェクトの参照」のための記述方法が必要になる。具体的な記述は、下記のようにStaticResourceマークアップ拡張機能のトークンとして、参照したいリソース・キー(下記の例では「BlueBrush」)を渡すという方法を取る。
<Ellipse Fill="{StaticResource BlueBrush}"/>
WPFでは、StaticResourceマークアップ拡張機能はStaticResourceExtensionというクラスにマッピングされているため、上記の記述はStaticResourceExtensionクラスのコンストラクタのパラメータにBlueBrushというリソース・キーを指定しているのと同じ意味を持つ。
StaticResourceExtensionクラスはResourceKeyプロパティを持っているため、このプロパティにリソース・キーを設定する記述でも同様の動作となるが、StaticResourceExtensionクラスはResourceKeyプロパティ以外のプロパティを持たないことから、このような記述方法は推奨されていない。
<Ellipse Fill="{StaticResource ResourceKey=BlueBrush}"/>
この記述方法は、WPFでは可能だが、Silverlightでは使えない。
一方、SilverlightのStaticResourceマークアップ拡張機能はXAML専用となっており、StaticResourceExtensionクラスは存在しない。そのため、上記のコードのようにResourceKeyプロパティを使用した記述はサポートされていない。また同じ理由から、カーリーブラケット( { } )を用いるマークアップ拡張構文のみがサポートされ、プロパティ要素構文を用いることもできない。
なお、SilverlightではStaticResourceExtensionクラスが存在しないことから、コードによるリソース参照はResourceDictionaryクラスのインデクサ(=Itemプロパティ)を使用して、直接リソースを参照する必要がある。
●リソース検索(静的リソース参照)
静的リソース参照では、アプリケーションの起動時にリソース検索が行われ、各リソース・ディクショナリに指定されたリソース・キーが存在するかどうかが確認される。
リソース検索が行われるのは、後にも先にもこの1回だけだ。指定されたリソース・キーが見つかった場合、そのリソースに対するオブジェクト参照が、参照元のプロパティに設定される。そもそも、指定されたリソース・キーが参照可能な範囲内に存在しない場合には、コンパイル時にエラーとなる。
リソース検索は下記の順序で行われる。
- イミディエイト・リソースを確認する
1-1. リソース参照元となる要素のResourcesプロパティ(=リソース・ディクショナリ)に、要求されたキーがないか確認する
1-2. 親要素のResourcesプロパティに要求されたキーがないかどうか確認し、ない場合には、そのまた親要素に対して同様の確認を行うという動作をルート要素に到達するまで繰り返す - . アプリケーション・リソースを確認する
では、ここで1つ問題を出してみたい。下記のRectangleのFillプロパティには、いったい何色のブラシが設定されるだろうか。
<Grid>
<Grid.Resources>
<SolidColorBrush x:Key="MyBrush" Color="Red"/>
</Grid.Resources>
<Ellipse Fill="{StaticResource MyBrush}">
<Ellipse.Resources>
<SolidColorBrush x:Key="MyBrush" Color="Blue"/>
</Ellipse.Resources>
</Ellipse >
</Grid>
上記の例では、<Grid>要素のResourcesプロパティに宣言された赤色のMyBrushと、<Ellipse>要素のResourcesプロパティに宣言された青色のMyBrushの2つがイミディエイト・リソースとして存在し、<Ellipse>要素からStaticResourceマークアップ拡張機能を使用して静的リソース参照を行っている。この場合のリソース・キーの検索動作は、まず<Ellipse>要素のResourcesプロパティを確認するため、青色のMyBrushが適用されるのが正解のように思えるが、実際に試してみると青色ではなく赤色のブラシが適用される。
これまで特に説明する機会がなかったが、アプリケーションの実行時、XAMLコードの内容は上から下へ、親から子へ向かって処理(インスタンス化)が行われる。<Ellipse>要素のFillプロパティに値を設定する時点では、<Ellipse>要素の下に記述されたResourcesプロパティの内容は処理されていないため、Resourcesプロパティは空の状態である。そのため、リソース・キーの検索動作に従い、親であるGrid要素のResourcesプロパティに存在する赤いMyBrushが適用されているのである。
下記のように、プロパティ要素構文を使い、StaticResourceマークアップ拡張機能によるリソース参照を、<Ellipse>要素のResourcesプロパティよりも下に記述することで、<Ellipse>要素のResourcesプロパティに宣言された青色のMyBrushが適用されるようになる。ただし、前述のとおり、このようなプロパティ要素構文を使ったStaticResourceマークアップ拡張機能の記述は、WPFでのみサポートされる。
<Grid>
<Grid.Resources>
<SolidColorBrush x:Key="MyBrush" Color="Red"/>
</Grid.Resources>
<Ellipse>
<Ellipse.Resources>
<SolidColorBrush x:Key="MyBrush" Color="Blue"/>
</Ellipse.Resources>
<Ellipse.Fill>
<StaticResource ResourceKey="MyBrush"/>
</Ellipse.Fill>
</Ellipse>
</Grid>
実際に必要となるケースは少ないが、WPFでのみ、静的リソース参照のほかにDynamicResourceマークアップ拡張機能を使用した動的リソース参照が用意されているので、次にこれについて紹介しておこう。
Copyright© Digital Advantage Corp. All Rights Reserved.