WisdomSoft - for your serial experiences.

7.7 リソース

WPF では、オブジェクトの階層に対して任意のデータを格納するリソース辞書の仕組みを提供しています。リソース辞書は任意のオブジェクトを鍵とするキーバリューで構成され、FrameworkElement オブジェクトは自分が所属する階層の親をたどってリソースを検索できます。

7.7.1 リソースの管理

リソースという言葉には様々な意味が含まれますが、WPF においてリソースとはプログラムの中で使われるデータ的な性質のオブジェクトです。通常は、テキストやイメージ、音声、アイコン、ブラシ、その他の様々な情報がリソースであると考えることができます。WPF では、このようなデータをフレームワークの中で効率的に管理することができる基盤を提供しています。

実際にはあらゆるオブジェクトがリソースになる事ができますが、通常はグラフィカルな要素に対してデータとして作用するオブジェクトをリソースと指します。リソースは、リソースを使用するアプリケーションと関連付けられるため、関連付けを管理する仕組みが必要になります。

WPF では、リソースを管理するための System.Windows.ResourceDictionary クラスが提供されています。

System.Windows.ResourceDictionary クラス
[LocalizabilityAttribute(LocalizationCategory.Ignore)] 
public class ResourceDictionary : IDictionary, ICollection, IEnumerable, 
    INameScope, ISupportInitialize, IUriContext

ResourceDictionary クラス事態は複雑なクラスではありません。実装しているインターフェイスを見ても理解することができますが、このクラスはリソースを保存する単純な辞書クラスです。キーとなるオブジェクトと、リソースを内部で関連付けます。

このクラスのコンストラクタは、パラメータを受け取りません。

ResourceDictionary クラスのコンストラクタ
public ResourceDictionary ()

ResourceDictionary に、リソースとなるオブジェクトを追加するには Add() メソッドを使います。

ResourceDictionary クラス Add() メソッド
public void Add (Object key, Object value)

key にはキーとなるオブジェクトを、value にはキーに関連付けられるオブジェクトを指定します。

追加したオブジェクトを取得するには、インデクサを使います。

ResourceDictionary クラスのインデクサ
public Object this [Object key] { get; set; }

key に、取得したいオブジェクトに関連付けられているキーを指定します。インデクサは、キーに関連付けられているオブジェクトを返します。

ResourceDictionary に追加されているオブジェクトの数を取得するには Count プロパティを使います。

Count プロパティ
public int Count { get; }

Count プロパティは、ResourceDictionary オブジェクトが保有しているオブジェクトの数を返します。

これらのメソッドやプロパティは、すべて辞書オブジェクトを表す IDictionary インターフェイスや ICollection インターフェイスで宣言されているものなので、辞書クラスを扱ったことがあれば容易に理解できます。

コード1
using System;
using System.Windows;
using System.Windows.Controls;

class Test {
	[STAThread]
	public static void Main() {
		ResourceDictionary rd = new ResourceDictionary();
		rd.Add("Test1", "Kitty on your lap");
		rd.Add("Test2", "Dote up a cat");

		TextBlock textBlock = new TextBlock();
		textBlock.FontSize = 30;
		textBlock.Text = "Test1=" + rd["Test1"] + "\nTest2=" + rd["Test2"];

		Window wnd = new Window();
		wnd.Content = textBlock;

		Application app = new Application();
		app.Run(wnd);
	}
}
実行結果
コード1 実行結果

コード1は、ResourceDictionary オブジェクトを生成して、"Test1" という名前と "Test2" という名前のキーで、文字列をリソースとして追加しています。この文字列は、そのまま表示する TextBlock オブジェクトのテキストとして設定しています。

7.7.2 オブジェクトのリソース

プログラムで ResourceDictionary をインスタンス化する必要はほとんどありません。FramelwrokElement を継承する WPF の描画要素は Resources プロパティから自分自身の ResourceDictionary を提供しています。

FrameworkElement クラス Resources プロパティ
public ResourceDictionary Resources { get; set; }

このプロパティは FramelwrokElement クラスのものなので、WPF コントロールは全てリソースを保有する仕組みを持っていることになります。様々な形でデータを管理することができるプログラミング言語のコードではリソースを積極的に利用する場面は少ないかもしれません。このリソースの仕組みは、XAML でデザインを行うときに能力を発揮します。XAML は、スタイルやテンプレートなどのリソースを変数として保存する仕組みを持たないためです。

コード2
using System;
using System.Windows;
using System.Windows.Media;

class Test {
	[STAThread]
	public static void Main() {
		Window wnd = new Window();
		wnd.Resources.Add("TextColor", Brushes.Red);
		wnd.Resources.Add("Size", 30.0);

		wnd.Content = "Kitty on your lap";
		wnd.Foreground = (Brush)wnd.Resources["TextColor"];
		wnd.FontSize = (double)wnd.Resources["Size"];

		Application app = new Application();
		app.Run(wnd);
	}
}
実行結果
コード2 実行結果

コード2は表示する Window オブジェクトの Resources プロパティに、前景色とフォントサイズを表すリソースを保存しています。その後、これらのリソースを取得してウィンドウの Foreground プロパティと FontSize プロパティにそれぞれ設定しています。

FrameworkElement オブジェクトに設定されているリソースは、ただの辞書ではありません。FrameworkElement クラスでは、リソースを検索する FindResource() メソッドを提供しています。このメソッドを使うことで、自分自身にリソースが存在していなくても、その親の FrameworkElement オブジェクトのリソースを検索します。

FrameworkElement クラス FindResource() メソッド
public Object FindResource (Object resourceKey)

resourceKey には、検索するリソースに関連付けられているキーを指定します。このメソッドを使うことで、複数のオブジェクトに適用させたいリソースを親 FrameworkElement に設定するだけで共有することができます。

コード3
using System;
using System.Windows;
using System.Windows.Controls;

class Test {
	[STAThread]
	public static void Main() {
		Button button = new Button();
		TextBlock textBlock = new TextBlock();		

		StackPanel panel = new StackPanel();
		panel.Resources.Add("Text", "Kitty on your lap");
		panel.Children.Add(button);
		panel.Children.Add(textBlock);

		button.Content = (string)button.FindResource("Text");
		textBlock.Text = (string)textBlock.FindResource("Text");

		Window wnd = new Window();
		wnd.Content = panel;

		Application app = new Application();
		app.Run(wnd);
	}
}
実行結果
コード3 実行結果

コード3は、button オブジェクトや textBlock オブジェクトが表示するテキストをリソースから取得しています。しかし、それぞれのオブジェクト自身の Resources プロパティにはリソースを設定していません。しかし、FindResource() メソッドからリソースを検索しているため、親である StackPanel のリソースも検索されています。このプログラムの場合は、StackPanel のリソースからテキストを取得しています。もし、StackPanel にも該当のリソースが存在しない場合は、さらにその親を検索します。