リソース ディクショナリ

.NET Multi-platform App UI (.NET MAUI) ResourceDictionary は、.NET MAUI アプリで使用されるリソースのリポジトリです。 ResourceDictionary に格納される一般的なリソースには、スタイル、コントロール テンプレート、データ テンプレート、コンバーター、色などあります。

ResourceDictionary に格納される XAML リソースは、StaticResource または DynamicResource マークアップ拡張機能を使用して参照し、要素に適用できます。 C# では、リソースを ResourceDictionary で定義し、文字列ベースのインデクサーを使用して参照し、要素に適用することもできます。

ヒント

Visual Studio では、.NET MAUI ResourceDictionary (XAML) 項目テンプレートを使用して、分離コード ファイルでサポートされる XAML ベースの ResourceDictionary ファイルを、プロジェクトに追加できます。

リソースを作成する

すべての VisualElement 派生オブジェクトには Resources プロパティがあります。これは、リソースを含めることができる ResourceDictionary です。 同様に、Application 派生オブジェクトには Resources プロパティがあります。これは、リソースを含めることができる ResourceDictionary です。

.NET MAUI アプリには、Application から派生するクラスを 1 つだけ含めることができますが、多くの場合、ページ、レイアウト、ビューなど、VisualElement から派生する多くのクラスを使用します。 これらのオブジェクトはいずれも、その Resources プロパティを、リソースを含む ResourceDictionary に設定できます。 特定の ResourceDictionary をどこに配置するかで、リソースを使用できる範囲が変わります。

  • ButtonLabel などのビューにアタッチされた ResourceDictionary のリソースは、その特定のオブジェクトにのみ適用できます。
  • StackLayoutGrid などのレイアウトにアタッチされた ResourceDictionary のリソースは、レイアウトとそのレイアウトのすべての子に適用できます。
  • ページ レベルで定義された ResourceDictionary のリソースは、ページとその子に適用できます。
  • アプリケーション レベルで定義された ResourceDictionary のリソースは、アプリケーション全体に適用できます。

暗黙的なスタイルを除き、リソース ディクショナリの各リソースには、x:Key 属性で定義された一意の文字列キーが必要です。

次の XAML は、App.xaml ファイルでアプリケーション レベルの ResourceDictionary で定義されたリソースを示します。

<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             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>

        <!-- Images -->
        <x:String x:Key="BackgroundImage">background</x:String>
        <x:String x:Key="MenuIcon">menu.png</x:String>
        <x:String x:Key="SearchIcon">search.png</x:String>

        <!-- Implicit styles -->
        <Style TargetType="NavigationPage">
            <Setter Property="BarBackgroundColor"
                    Value="{StaticResource NavigationBarColor}" />
            <Setter Property="BarTextColor"
                    Value="{StaticResource NavigationBarTextColor}" />
        </Style>

        <Style TargetType="ContentPage"
               ApplyToDerivedTypes="True">
            <Setter Property="BackgroundColor"
                    Value="{StaticResource AppBackgroundColor}" />
        </Style>

    </Application.Resources>
</Application>

この例では、リソース ディクショナリで、Thickness リソース、複数の Color リソース、2 つの暗黙的な Style リソースを定義しています。

重要

Resources プロパティ要素タグの間に直接リソースを挿入すると、ResourceDictionary オブジェクトが自動的に作成されます。 ただし、オプションの ResourceDictionary タグの間にすべてのリソースを配置することも有効です。

リソースを使用する

各リソースには、x:Key 属性を使用して指定されたキーがあります。これは ResourceDictionary のディクショナリ キーになります。 キーは、StaticResource または DynamicResource XAML マークアップ拡張を使用して ResourceDictionary のリソースを参照するために使用されます。

StaticResource マークアップ拡張と DynamicResource マークアップ拡張は、どちらもディクショナリ キーを使用してリソース ディクショナリの値を参照するという点で似ています。 ただし、StaticResource マークアップ拡張は 1 つのディクショナリ検索を実行しますが、DynamicResource マークアップ拡張はディクショナリ キーへのリンクを保持します。 そのため、キーに関連付けられているディクショナリ エントリが置き換えられると、変更がビジュアル要素に適用されます。 これにより、アプリ内でランタイム リソースの変更を行うことができます。 マークアップ拡張の詳細については、「XAML マークアップ拡張」をご覧ください。

次の XAML の例は、リソースを使用する方法を示します。また、StackLayout で追加のリソースを定義します。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ResourceDictionaryDemo.MainPage"
             Title="Main page">
    <StackLayout Margin="{StaticResource PageMargin}"
                 Spacing="6">
        <StackLayout.Resources>
            <!-- Implicit style -->
            <Style TargetType="Button">
                <Setter Property="FontSize" Value="14" />
                <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 resource dictionary resources.

重要

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

リソースの検索動作

次の検索プロセスは、リソースが StaticResource または DynamicResource マークアップ拡張で参照されている場合に発生します。

  • 要求されたキーは、プロパティを設定する要素について (存在する場合)、リソース ディクショナリ内で確認されます。 要求されたキーが見つかった場合、その値が返され、検索プロセスが終了します。
  • 一致するものが見つからない場合、検索プロセスはビジュアル ツリーを上方向に検索し、各親要素のリソース ディクショナリを確認します。 要求されたキーが見つかった場合、その値が返され、検索プロセスが終了します。 このプロセスは、ルート要素に到達するまで続けられます。
  • ルート要素で一致するものが見つからない場合は、アプリケーション レベルのリソース ディクショナリを調べます。
  • それでも一致するものが見つからない場合は、XamlParseException がスローされます。

したがって、XAML パーサーは、StaticResource または DynamicResource マークアップ拡張を検出すると、ビジュアル ツリーを上向きに移動して一致するキーを検索し、見つけた最初の一致を使用します。 この検索がページで終了してもキーがまだ見つからない場合、XAML パーサーは App オブジェクトにアタッチされている ResourceDictionary を検索します。 それでもキーが見つからない場合は、例外がスローされます。

リソースをオーバーライドする

リソースがキーを共有する場合、ビジュアル ツリーで下位に定義されているリソースは、上位に定義されているリソースよりも優先されます。 たとえば、アプリケーション レベルで AppBackgroundColor リソースを AliceBlue に設定すると、Teal に設定されたページ レベルの AppBackgroundColor リソースによってオーバーライドされます。 同様に、ページ レベルの AppBackgroundColor リソースは、レイアウトまたはビュー レベルの AppBackgroundColor リソースによってオーバーライドされます。

スタンドアロンのリソース ディクショナリ

ResourceDictionary は、分離コード ファイルでサポートされていないスタンドアロンの XAML ファイルとして作成することもできます。 スタンドアロンの ResourceDictionary を作成するには、.NET MAUI ResourceDictionary (XAML) 項目テンプレートを使用して新しい ResourceDictionary ファイルをプロジェクトに追加し、分離コード ファイルを削除します。 次に、XAML ファイルで、ファイルの先頭付近にある ResourceDictionary タグから x:Class 属性を削除します。 さらに、XML ヘッダーの後に <?xaml-comp compile="true" ?> を追加して、XAML がコンパイルされるようにします。

Note

スタンドアロンの ResourceDictionary には、MauiXaml のビルド アクションが必要です。

次の XAML の例は、MyResourceDictionary.xaml という名前のスタンドアロンの ResourceDictionary を示します。

<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
    <DataTemplate x:Key="PersonDataTemplate">
        <ViewCell>
            <Grid RowSpacing="6"
                  ColumnSpacing="6">
                <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 は、別のリソース ディクショナリにマージすることで使用できます。

リソース ディクショナリをマージする

リソース ディクショナリは、1 つ以上の ResourceDictionary オブジェクトを別の 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 ファイルのルート タグから削除できます。

重要

ResourceDictionary.Source プロパティは XAML からのみ設定できます。

他のアセンブリからリソース ディクショナリをマージする

ResourceDictionary は、ResourceDictionaryMergedDictionaries プロパティに追加することで、別の ResourceDictionary にマージすることもできます。 この手法を使用すると、リソース ディクショナリが存在するアセンブリに関係なく、リソース ディクショナリをマージできます。 外部アセンブリからリソース ディクショナリをマージするには、ResourceDictionary でビルド アクションを MauiXaml に設定し、分離コード ファイルを作成し、ファイルのルート タグに 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:DefaultTheme />
                <!-- Add more resource dictionaries here -->
            </ResourceDictionary.MergedDictionaries>
            <!-- Add more resources here -->
        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

この例では、同じアセンブリのリソース ディクショナリと外部アセンブリのリソース ディクショナリが、ページ レベルのリソース ディクショナリにマージされます。 さらに、MergedDictionaries プロパティ要素タグ内の他の ResourceDictionary オブジェクトや、それらのタグの外部にある他のリソースを追加することもできます。

重要

ResourceDictionary の中で、MergedDictionaries プロパティ要素タグは 1 つだけですが、ResourceDictionary オブジェクトは必要な数を配置できます。

マージされた ResourceDictionary リソースが同じ x:Key 属性値を共有する場合、.NET MAUI では次のリソースの優先順位が使用されます。

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

ヒント

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

コードから XAML ベースのリソース ディクショナリを使用する

XAML で定義されるリソース ディクショナリは、ResourceDictionary が分離コード ファイルによってサポートされている場合、コード内で使用できます。 Visual Studio では、分離コード ファイルによってサポートされる XAML ベースの ResourceDictionary ファイルを、.NET MAUI ResourceDictionary (XAML) 項目テンプレートによってプロジェクトに追加できます。

Screenshot of resource dictionaries backed by code-behind.

分離コード ファイルによってサポートされる XAML ベースのリソース ディクショナリは、リソース ディクショナリの MergedDictionaries コレクションに追加することで C# から使用できます。

Resources.MergedDictionaries.Add(new MyMauiApp.Resources.Styles.MyColors());
Resources.MergedDictionaries.Add(new MyMauiApp.Resources.Styles.MyStyles());

コードからキーでリソースにアクセスする

他のディクショナリと同様に、コードからリソース ディクショナリ内のリソースにアクセスできます。

この例では、ページのリソース ディクショナリからリソースを取得して適用する方法を示します。

// Retrieve the Primary color value which is in the page's resource dictionary
var hasValue = Resources.TryGetValue("Primary", out object primaryColor);

if (hasValue)
{
    myLabel.TextColor = (Color)primaryColor;
}

これは、コードからリソースを取得できない場合に .NET MAUI が KeyNotFoundException をスローしないようにする推奨されるアプローチです。 これは、マージされたリソース ディクショナリが XAML ファイルで定義されるリソースとインライン リソースで構成されている場合に発生する可能性があります。 詳細については、「GitHub issue #11214」をご覧ください。

Note

コードからアプリ全体のリソースを取得するには、App.Current.Resources リソース ディクショナリにアクセスします。