Xamarin.Forms のリソース ディクショナリ

Download Sampleサンプルのダウンロード

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 をどこに配置するかで、リソースを使用できる範囲が変わります。

  • ButtonLabel などのビューにアタッチされた ResourceDictionary 内のリソースは、その特定のオブジェクトのみに適用できます。
  • StackLayoutGrid などのレイアウトにアタッチされた 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 リソース ディクショナリで定義される暗黙的なスタイルを使用します。 これで、次のスクリーンショットのような結果になります。

Consuming ResourceDictionary Resources

重要

単一のページに固有のリソースを、アプリケーション レベルのリソース ディクショナリに含めないようにする必要があります。そのようなリソースは、ページが必要とするときではなく、アプリケーションの起動時に解析されるためです。 詳細については、「アプリケーション リソース ディクショナリのサイズを減らす」を参照してください。

リソースの検索動作

次の検索プロセスは、リソースが 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 ファイルがリリース ビルドに残るようにするには、次のようにします。

  1. スタンドアロン XAML ファイルを含むアセンブリにカスタムの Preserve 属性を追加します。 詳細については、「コードの維持」を参照してください。

  2. アセンブリ レベルで次のように 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 は、ResourceDictionaryMergedDictionaries プロパティに追加することで、別の ResourceDictionary にマージすることもできます。 この手法を使用すると、リソース ディクショナリが存在するアセンブリに関係なく、リソース ディクショナリをマージできます。 外部アセンブリからリソース ディクショナリをマージするには、ResourceDictionaryEmbeddedResource に設定されたビルド アクションを持ち、分離コード ファイルを持ち、ファイルのルート タグに x:Class 属性を定義する必要があります。

警告

ResourceDictionary クラスでは MergedWith プロパティも定義されます。 ただし、このプロパティは非推奨となり、使用されなくなりました。

次のコード例は、ページ レベル ResourceDictionaryMergedDictionaries コレクションに追加される 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 が使用するリソースの優先順位は次のとおりです。

  1. リソース ディクショナリに対してローカルなリソース。
  2. MergedDictionaries コレクションを介してマージされたリソース ディクショナリに含まれるリソースは、逆の順序で MergedDictionaries プロパティに一覧表示されます。

Note

アプリケーションに複数の大規模なリソース ディクショナリが含まれている場合、リソース ディクショナリの検索は計算負荷の高いタスクになる可能性があります。 そのため、不要な検索を回避するには、アプリケーション内の各ページで、ページに適したリソース ディクショナリのみを使用する必要があります。

他の Xamarin ビデオは、Channel 9 および YouTube でご覧いただけます。