Xamarin.Forms のリソース ディクショナリ
ResourceDictionary
は、Xamarin.Forms アプリケーションによって使用されるリソースのリポジトリです。 ResourceDictionary
に保存される一般的なリソースには、スタイル、コントロール テンプレート、データ テンプレート、色、コンバーターなどがあります。
XAML では、ResourceDictionary
に保存されるリソースは、StaticResource
または DynamicResource
マークアップ拡張機能を使用して参照し、要素に適用できます。 C# では、リソースを ResourceDictionary
で定義し、文字列ベースのインデクサーを使用して参照し、要素に適用することもできます。 ただし、C# では共有オブジェクトをフィールドまたはプロパティとして保存し、最初にディクショナリから取得しなくても直接アクセスできるため、ResourceDictionary
を使用する利点はほとんどありません。
XAML でリソースを作成する
すべての VisualElement
派生オブジェクトには Resources
プロパティがあります。これは、リソースを含めることができる ResourceDictionary
です。 同様に、Application
派生オブジェクトには Resources
プロパティがあります。これは、リソースを含めることができる ResourceDictionary
です。
Xamarin.Forms アプリケーションは、Application
から派生したクラスのみを格納しますが、ページ、レイアウト、コントロールなどの、VisualElement
から派生する多くのクラスをしばしば利用します。 これらのオブジェクトはいずれも、その Resources
プロパティを、リソースを含む ResourceDictionary
に設定できます。 特定の ResourceDictionary
をどこに配置するかで、リソースを使用できる範囲が変わります。
Button
やLabel
などのビューにアタッチされたResourceDictionary
内のリソースは、その特定のオブジェクトのみに適用できます。StackLayout
やGrid
などのレイアウトにアタッチされたResourceDictionary
内のリソースは、レイアウトとそのレイアウトのすべての子に適用できます。- ページ レベルで定義された
ResourceDictionary
のリソースは、ページとその子に適用できます。 - アプリケーション レベルで定義された
ResourceDictionary
内のリソースは、アプリケーション全体に適用できます。
暗黙的なスタイルを除き、リソース ディクショナリの各リソースには、x:Key
属性で定義された一意の文字列キーが必要です。
次の XAML は、App.xaml ファイルでアプリケーション レベルの ResourceDictionary
で定義されたリソースを示します。
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResourceDictionaryDemo.App">
<Application.Resources>
<Thickness x:Key="PageMargin">20</Thickness>
<!-- Colors -->
<Color x:Key="AppBackgroundColor">AliceBlue</Color>
<Color x:Key="NavigationBarColor">#1976D2</Color>
<Color x:Key="NavigationBarTextColor">White</Color>
<Color x:Key="NormalTextColor">Black</Color>
<!-- Implicit styles -->
<Style TargetType="{x:Type NavigationPage}">
<Setter Property="BarBackgroundColor"
Value="{StaticResource NavigationBarColor}" />
<Setter Property="BarTextColor"
Value="{StaticResource NavigationBarTextColor}" />
</Style>
<Style TargetType="{x:Type ContentPage}"
ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor"
Value="{StaticResource AppBackgroundColor}" />
</Style>
</Application.Resources>
</Application>
この例では、リソース ディクショナリは、Thickness
リソース、複数の Color
リソース、および 2 つの暗黙的な Style
リソースを定義します。 クラス App
の詳細については、「Xamarin.Forms の App クラス」を参照してください。
Note
明示的な ResourceDictionary
タグの間にすべてのリソースを配置することも有効です。 ただし、Xamarin.Forms 3.0 以降、ResourceDictionary
タグは必要ありません。 代わりに、ResourceDictionary
オブジェクトが自動的に作成されて、Resources
のプロパティと要素のタグの間にリソースを直接挿入できます。
XAML でリソースを使用する
各リソースには、x:Key
属性を使用して指定されたキーがあります。これは ResourceDictionary
のディクショナリ キーになります。 このキーが、StaticResource
または DynamicResource
マークアップ拡張機能で ResourceDictionary
のリソースを参照するために使用されます。
StaticResource
マークアップ拡張と DynamicResource
マークアップ拡張は、どちらもディクショナリ キーを使用してリソース ディクショナリの値を参照するという点で似ています。 ただし、StaticResource
マークアップ拡張は 1 つのディクショナリ検索を実行しますが、DynamicResource
マークアップ拡張はディクショナリ キーへのリンクを保持します。 そのため、キーに関連付けられているディクショナリ エントリが置き換えられると、変更がビジュアル要素に適用されます。 これにより、実行時のリソースの変更をアプリケーションで行うことができます。 マークアップ拡張機能の詳細については、「XAML マークアップ拡張」を参照してください。
次の XAML の例は、リソースを使用し、StackLayout
内に追加のリソースの定義も行う方法を示しています。
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResourceDictionaryDemo.HomePage"
Title="Home Page">
<StackLayout Margin="{StaticResource PageMargin}">
<StackLayout.Resources>
<!-- Implicit style -->
<Style TargetType="Button">
<Setter Property="FontSize" Value="Medium" />
<Setter Property="BackgroundColor" Value="#1976D2" />
<Setter Property="TextColor" Value="White" />
<Setter Property="CornerRadius" Value="5" />
</Style>
</StackLayout.Resources>
<Label Text="This app demonstrates consuming resources that have been defined in resource dictionaries." />
<Button Text="Navigate"
Clicked="OnNavigateButtonClicked" />
</StackLayout>
</ContentPage>
この例では、ContentPage
オブジェクトは、アプリケーション レベルのリソース ディクショナリで定義される暗黙的なスタイルを使用します。 StackLayout
オブジェクトは、アプリケーション レベルのリソース ディクショナリで定義される PageMargin
リソースを使用しますが、Button
オブジェクトは StackLayout
リソース ディクショナリで定義される暗黙的なスタイルを使用します。 これで、次のスクリーンショットのような結果になります。
重要
単一のページに固有のリソースを、アプリケーション レベルのリソース ディクショナリに含めないようにする必要があります。そのようなリソースは、ページが必要とするときではなく、アプリケーションの起動時に解析されるためです。 詳細については、「アプリケーション リソース ディクショナリのサイズを減らす」を参照してください。
リソースの検索動作
次の検索プロセスは、リソースが StaticResource
または DynamicResource
マークアップ拡張で参照されている場合に発生します。
- 要求されたキーは、プロパティを設定する要素について (存在する場合)、リソース ディクショナリ内で確認されます。 要求されたキーが見つかった場合、その値が返され、検索プロセスが終了します。
- 一致するものが見つからない場合、検索プロセスはビジュアル ツリーを上方向に検索し、各親要素のリソース ディクショナリを確認します。 要求されたキーが見つかった場合、その値が返され、検索プロセスが終了します。 このプロセスは、ルート要素に到達するまで続けられます。
- ルート要素で一致するものが見つからない場合は、アプリケーション レベルのリソース ディクショナリを調べます。
- それでも一致するものが見つからない場合は、
XamlParseException
がスローされます。
したがって、XAML パーサーは、StaticResource
または DynamicResource
マークアップ拡張を検出すると、ビジュアル ツリーを上向きに移動して一致するキーを検索し、見つけた最初の一致を使用します。 この検索がページで終了してもキーがまだ見つからない場合、XAML パーサーは App
オブジェクトにアタッチされている ResourceDictionary
を検索します。 それでもキーが見つからない場合は、例外がスローされます。
リソースをオーバーライドする
リソースがキーを共有する場合、ビジュアル ツリーで下位に定義されているリソースは、上位に定義されているリソースよりも優先されます。 たとえば、アプリケーション レベルで AppBackgroundColor
リソースを AliceBlue
に設定すると、Teal
に設定されたページ レベルの AppBackgroundColor
リソースによってオーバーライドされます。 同様に、ページ レベルの AppBackgroundColor
リソースは、コントロール レベルの AppBackgroundColor
リソースによってオーバーライドされます。
スタンドアロンのリソース ディクショナリ
ResourceDictionary
から派生したクラスは、スタンドアロンの XAML ファイルに含めることもできます。 これで、XAML ファイルをアプリケーション間で共有できます。
そのようなファイルを作成するには、新しいコンテンツ ビューまたはコンテンツ ページ項目をプロジェクトに追加します (ただし、C# ファイルのみを含む コンテンツ ビューやコンテンツ ページではないもの)。 分離コード ファイルを削除し、XAML ファイルで基底クラスの名前を ContentView
または ContentPage
から ResourceDictionary
に変更します。 さらに、ファイルのルート タグから x:Class
属性を削除します。
次の XAML の例は、MyResourceDictionary.xaml という名前の ResourceDictionary
を示しています
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<DataTemplate x:Key="PersonDataTemplate">
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}"
TextColor="{StaticResource NormalTextColor}"
FontAttributes="Bold" />
<Label Grid.Column="1"
Text="{Binding Age}"
TextColor="{StaticResource NormalTextColor}" />
<Label Grid.Column="2"
Text="{Binding Location}"
TextColor="{StaticResource NormalTextColor}"
HorizontalTextAlignment="End" />
</Grid>
</ViewCell>
</DataTemplate>
</ResourceDictionary>
この例では、ResourceDictionary
には、DataTemplate
型のオブジェクトである単一のリソースが含まれます。 MyResourceDictionary.xaml は、別のリソース ディクショナリにマージすることで使用できます。
既定では、リンカーの動作がすべてのアセンブリをリンクするように設定されると、リンカーはスタンドアロン XAML ファイルをリリース ビルドから削除します。 スタンドアロンの XAML ファイルがリリース ビルドに残るようにするには、次のようにします。
スタンドアロン XAML ファイルを含むアセンブリにカスタムの
Preserve
属性を追加します。 詳細については、「コードの維持」を参照してください。アセンブリ レベルで次のように
Preserve
属性を設定します。[assembly:Preserve(AllMembers = true)]
リンクの詳細については、「Xamarin.iOS アプリをリンクする」と「Android でのリンク」を参照してください。
マージされたリソース ディクショナリ
マージされたリソース ディクショナリとは、1 つ以上の ResourceDictionary
オブジェクトを別の 1 つの ResourceDictionary
に結合したものです。
ローカル リソース ディクショナリをマージする
リソースを含む XAML ファイルのファイル名に Source
プロパティが設定されている ResourceDictionary
オブジェクトを作成することで、ローカルの ResourceDictionary
ファイルを別の ResourceDictionary
にマージできます。
<ContentPage ...>
<ContentPage.Resources>
<!-- Add more resources here -->
<ResourceDictionary Source="MyResourceDictionary.xaml" />
<!-- Add more resources here -->
</ContentPage.Resources>
...
</ContentPage>
この構文では、MyResourceDictionary
クラスはインスタンス化されません。 代わりに、XAML ファイルを参照します。 そのため、Source
プロパティを設定するときに分離コード ファイルは必要ありません。x:Class
属性は MyResourceDictionary.xaml ファイルのルート タグから削除できます。
重要
Source
プロパティは XAML からのみ設定できます。
他のアセンブリからリソース ディクショナリをマージする
ResourceDictionary
は、ResourceDictionary
の MergedDictionaries
プロパティに追加することで、別の ResourceDictionary
にマージすることもできます。 この手法を使用すると、リソース ディクショナリが存在するアセンブリに関係なく、リソース ディクショナリをマージできます。 外部アセンブリからリソース ディクショナリをマージするには、ResourceDictionary
が EmbeddedResource に設定されたビルド アクションを持ち、分離コード ファイルを持ち、ファイルのルート タグに x:Class
属性を定義する必要があります。
警告
ResourceDictionary
クラスでは MergedWith
プロパティも定義されます。 ただし、このプロパティは非推奨となり、使用されなくなりました。
次のコード例は、ページ レベル ResourceDictionary
の MergedDictionaries
コレクションに追加される 2 つのリソース ディクショナリを示します。
<ContentPage ...
xmlns:local="clr-namespace:ResourceDictionaryDemo"
xmlns:theme="clr-namespace:MyThemes;assembly=MyThemes">
<ContentPage.Resources>
<ResourceDictionary>
<!-- Add more resources here -->
<ResourceDictionary.MergedDictionaries>
<!-- Add more resource dictionaries here -->
<local:MyResourceDictionary />
<theme:LightTheme />
<!-- Add more resource dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Add more resources here -->
</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>
この例では、同じアセンブリのリソース ディクショナリと外部アセンブリのリソース ディクショナリが、ページ レベルのリソース ディクショナリにマージされます。 さらに、MergedDictionaries
プロパティ要素タグ内の他の ResourceDictionary
オブジェクトや、それらのタグの外部にある他のリソースを追加することもできます。
重要
ResourceDictionary
の中で、MergedDictionaries
プロパティ要素タグは 1 つだけですが、ResourceDictionary
オブジェクトは必要な数を配置できます。
マージされた ResourceDictionary
リソースが同じ x:Key
属性値を共有するときに Xamarin.Forms が使用するリソースの優先順位は次のとおりです。
- リソース ディクショナリに対してローカルなリソース。
MergedDictionaries
コレクションを介してマージされたリソース ディクショナリに含まれるリソースは、逆の順序でMergedDictionaries
プロパティに一覧表示されます。
Note
アプリケーションに複数の大規模なリソース ディクショナリが含まれている場合、リソース ディクショナリの検索は計算負荷の高いタスクになる可能性があります。 そのため、不要な検索を回避するには、アプリケーション内の各ページで、ページに適したリソース ディクショナリのみを使用する必要があります。