Muokkaa

Jaa


Resource dictionaries

A .NET Multi-platform App UI (.NET MAUI) ResourceDictionary is a repository for resources that are used by a .NET MAUI app. Typical resources that are stored in a ResourceDictionary include styles, control templates, data templates, converters, and colors.

XAML resources that are stored in a ResourceDictionary can be referenced and applied to elements by using the StaticResource or DynamicResource markup extension. In C#, resources can also be defined in a ResourceDictionary and then referenced and applied to elements by using a string-based indexer.

Tip

In Visual Studio, a XAML-based ResourceDictionary file that's backed by a code-behind file can be added to your project by the .NET MAUI ResourceDictionary (XAML) item template.

Create resources

Every VisualElement derived object has a Resources property, which is a ResourceDictionary that can contain resources. Similarly, an Application derived object has a Resources property, which is a ResourceDictionary that can contain resources.

A .NET MAUI app can contain only a single class that derives from Application, but often makes use of many classes that derive from VisualElement, including pages, layouts, and views. Any of these objects can have its Resources property set to a ResourceDictionary containing resources. Choosing where to put a particular ResourceDictionary impacts where the resources can be used:

  • Resources in a ResourceDictionary that is attached to a view, such as Button or Label, can only be applied to that particular object.
  • Resources in a ResourceDictionary attached to a layout, such as StackLayout or Grid, can be applied to the layout and all the children of that layout.
  • Resources in a ResourceDictionary defined at the page level can be applied to the page and to all its children.
  • Resources in a ResourceDictionary defined at the application level can be applied throughout the app.

With the exception of implicit styles, each resource in resource dictionary must have a unique string key that's defined with the x:Key attribute.

The following XAML shows resources defined in an application level ResourceDictionary in the App.xaml file:

<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>

In this example, the resource dictionary defines a Thickness resource, multiple Color resources, and two implicit Style resources.

Important

Inserting resources directly between the Resources property-element tags automatically creates a ResourceDictionary object. However, it's also valid to place all resources between optional ResourceDictionary tags.

Consume resources

Each resource has a key that is specified using the x:Key attribute, which becomes its dictionary key in the ResourceDictionary. The key is used to reference a resource from the ResourceDictionary with the StaticResource or DynamicResource XAML markup extension.

The StaticResource markup extension is similar to the DynamicResource markup extension in that both use a dictionary key to reference a value from a resource dictionary. However, while the StaticResource markup extension performs a single dictionary lookup, the DynamicResource markup extension maintains a link to the dictionary key. Therefore, if the dictionary entry associated with the key is replaced, the change is applied to the visual element. This enables runtime resource changes to be made in an app. For more information about markup extensions, see XAML markup extensions.

The following XAML example shows how to consume resources, and also define an additional resource in a 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>

In this example, the ContentPage object consumes the implicit style defined in the application level resource dictionary. The StackLayout object consumes the PageMargin resource defined in the application level resource dictionary, while the Button object consumes the implicit style defined in the StackLayout resource dictionary. This results in the appearance shown in the following screenshot:

Consuming resource dictionary resources.

Important

Resources that are specific to a single page shouldn't be included in an application level resource dictionary, as such resources will then be parsed at app startup instead of when required by a page. For more information, see Reduce the application resource dictionary size.

Resource lookup behavior

The following lookup process occurs when a resource is referenced with the StaticResource or DynamicResource markup extension:

  • The requested key is checked for in the resource dictionary, if it exists, for the element that sets the property. If the requested key is found, its value is returned and the lookup process terminates.
  • If a match isn't found, the lookup process searches the visual tree upwards, checking the resource dictionary of each parent element. If the requested key is found, its value is returned and the lookup process terminates. Otherwise the process continues upwards until the root element is reached.
  • If a match isn't found at the root element, the application level resource dictionary is examined.
  • If a match still isn't found, a XamlParseException is thrown.

Therefore, when the XAML parser encounters a StaticResource or DynamicResource markup extension, it searches for a matching key by traveling up through the visual tree, using the first match it finds. If this search ends at the page and the key still hasn't been found, the XAML parser searches the ResourceDictionary attached to the App object. If the key still isn't found, an exception is thrown.

Override resources

When resources share keys, resources defined lower in the visual tree will take precedence over those defined higher up. For example, setting an AppBackgroundColor resource to AliceBlue at the application level will be overridden by a page level AppBackgroundColor resource set to Teal. Similarly, a page level AppBackgroundColor resource will be overridden by a layout or view level AppBackgroundColor resource.

Stand-alone resource dictionaries

A ResourceDictionary can also be created as a stand-alone XAML file that isn't backed by a code-behind file. To create a stand-alone ResourceDictionary, add a new ResourceDictionary file to the project with the .NET MAUI ResourceDictionary (XAML) item template and delete its code-behind file. Then, in the XAML file remove the x:Class attribute from the ResourceDictionary tag near the start of the file. In addition, add <?xaml-comp compile="true" ?> after the XML header to ensure that the XAML will be compiled.

A ResourceDictionary can also be created as a stand-alone XAML file that isn't backed by a code-behind file. To create a stand-alone ResourceDictionary, add a new ResourceDictionary file to the project with the .NET MAUI ResourceDictionary (XAML) item template and delete its code-behind file. Then, in the XAML file remove the x:Class attribute from the ResourceDictionary tag near the start of the file. By default, a stand-alone ResourceDictionary has its XAML compiled, unless <?xaml-comp compile="false" ?> is specified after the XML header.

Note

A stand-alone ResourceDictionary must have a build action of MauiXaml.

The following XAML example shows a stand-alone ResourceDictionary named MyResourceDictionary.xaml:

<?xml version="1.0" encoding="UTF-8" ?>
<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>

In this example, the ResourceDictionary contains a single resource, which is an object of type DataTemplate. MyResourceDictionary.xaml can be consumed by merging it into another resource dictionary.

Merge resource dictionaries

Resource dictionaries can be combined by merging one or more ResourceDictionary objects into another ResourceDictionary.

Merge local resource dictionaries

A local ResourceDictionary file can be merged into another ResourceDictionary by creating a ResourceDictionary object whose Source property is set to the filename of the XAML file with the resources:

<ContentPage ...>
    <ContentPage.Resources>
        <!-- Add more resources here -->
        <ResourceDictionary Source="MyResourceDictionary.xaml" />
        <!-- Add more resources here -->
    </ContentPage.Resources>
    ...
</ContentPage>

This syntax does not instantiate the MyResourceDictionary class. Instead, it references the XAML file. For that reason, when setting the Source property, a code-behind file isn't required, and the x:Class attribute can be removed from the root tag of the MyResourceDictionary.xaml file.

Important

The ResourceDictionary.Source property can only be set from XAML.

Merge resource dictionaries from other assemblies

A ResourceDictionary can also be merged into another ResourceDictionary by adding it into the MergedDictionaries property of the ResourceDictionary. This technique allows resource dictionaries to be merged, regardless of the assembly in which they reside. Merging resource dictionaries from external assemblies requires the ResourceDictionary to have a build action set to MauiXaml, to have a code-behind file, and to define the x:Class attribute in the root tag of the file.

Warning

The ResourceDictionary class also defines a MergedWith property. However, this property has been deprecated and should no longer be used.

The following code example shows two resource dictionaries being added to the MergedDictionaries collection of a page level ResourceDictionary:

<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>

In this example, a resource dictionary from the same assembly, and a resource dictionary from an external assembly, are merged into the page level resource dictionary. In addition, you can also add other ResourceDictionary objects within the MergedDictionaries property-element tags, and other resources outside of those tags.

Important

There can be only one MergedDictionaries property-element tag in a ResourceDictionary, but you can put as many ResourceDictionary objects in there as required.

When merged ResourceDictionary resources share identical x:Key attribute values, .NET MAUI uses the following resource precedence:

  1. The resources local to the resource dictionary.
  2. The resources contained in the resource dictionaries that were merged via the MergedDictionaries collection, in the reverse order they are listed in the MergedDictionaries property.

Tip

Searching resource dictionaries can be a computationally intensive task if an app contains multiple, large resource dictionaries. Therefore, to avoid unnecessary searching, you should ensure that each page in an application only uses resource dictionaries that are appropriate to the page.

Consume a XAML-based resource dictionary from code

Resource dictionaries that are defined in XAML can be consumed in code, provided that the ResourceDictionary is backed by a code-behind file. In Visual Studio, XAML-based ResourceDictionary files that are backed by code-behind files can be added to your project by the .NET MAUI ResourceDictionary (XAML) item template:

Screenshot of resource dictionaries backed by code-behind.

XAML-based resource dictionaries that are backed by code-behind files can then be consumed from C# by adding them to the MergedDictionaries collection of the resource dictionary:

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

Access resources by key from code

You can access resources in a resource dictionary from code like any other dictionary.

The following example shows how to retrieve and apply a resource from a page's resource dictionary:

// 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;
}

This is the recommended approach that ensures that .NET MAUI won't throw a KeyNotFoundException if it's unable to retrieve a resource from code. This can occur when a merged resource dictionary is made up of resources defined in a XAML file, and inline resources. For more information, see GitHub issue #11214.

Note

To retrieve app-wide resources from code, access the App.Current.Resources resource dictionary.