Ресурсы и стили

Ресурсы

Последнее обновление: 18.12.2022

Для совместного использования различными элементами общих компонентов в .NET MAUI применяются ресурсы. В данном случае под ресурсами понимаются не вспомогательные файлы - изображений и т.д., которые используются в приложении, а логические ресурсы, которые определяются в коде C# или XAML.

Определение ресурсов

В качестве ресурса можно определить любой объект. Все ресурсы помещаются в объект ResourceDictionary. У каждого визуального объекта, например, ContentPage или Button, имеется свойство Resources, которое как раз хранит объект ResourceDictionary. Например, определим несколько ресурсов:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Color x:Key="textColor">#004D40</Color>
            <Color x:Key="backColor">#80CBC4</Color>
            <x:Double x:Key="margin">10</x:Double>
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout Padding="10">
        <Button Text="iOS" TextColor="{StaticResource Key=textColor}"
      BackgroundColor="{StaticResource Key=backColor}" Margin="{StaticResource Key=margin}" />
        <Button Text="Android" TextColor="{StaticResource textColor}"
      BackgroundColor="{StaticResource backColor}" Margin="{StaticResource margin}" />
    </StackLayout>
</ContentPage>

Каждый ресурс должен иметь ключ, задаваемый с помощью атрибута x:Key. Это своего рода уникальный идентификатор ресурса. Например:

<Color x:Key="textColor">#004D40</Color>

Здесь в качестве ресурса определяется объект Color. Этот объект имеет ключ textColor и значение #004D40.

Чтобы обратиться к этому ресурсу в коде, надо использовать расширение StaticResource:

TextColor="{StaticResource Key=textColor}"

Свойство Key через ключ ресурса будет ссылаться на данный ресурс. Стоит отметить, что само слово Key можно не указывать:

TextColor="{StaticResource textColor}"

Таким образом, здесь определяется и применяется три ресурса: textColor (для цвета текста), backColor (для цвета фона) и margin (для размера внешних отступов). То есть в итоге получится следующая страница:

Статические ресурсы в .NET MAUI и C#

При этом важно, что на данный ресурс могут ссылаться сразу несколько элементов. Например, мы захотели создать общий для всех кнопок цвет фона. И в этом случае проще определить цветовой ресурс, чем присваивать свойству BackgroundColor у каждой кнопки конкретный цвет. А в случае, если мы захотим изметь цвет кнопок на другой, то не надо будет менять свойство BackgroundColor у всех кнопок. Достаточно будет поменять значение ресурса.

Уровни ресурсов

Ресурсы могут определяться на трех уровнях:

  • На уровне отдельного элемента управления. Такие ресурсы могут применяться ко всем вложенным элементам, которые определены внутри этого элемента

  • На уровне всей страницы. Такие ресурсы могут применяться ко всем элементам на странице

  • На уровне всего приложения. Эти ресурсы доступны из любого места и из любой страницы приложения.

  • На уровне всего приложения (глобальные ресурсы)

Когда парсер XAML встречает в разметке расширения StaticResource или DynamicResource, он сначала ищет соответствующий ресурс по ключу в словаре ресурсов текущего элемента. Если в ресурсах элемента отсутствует ресурс с указанным ключом, то парсер поднимается выше по дереву элементов и ищет ресурс в ресурсах элементов-контейнеров, затем в ресурсах текущей страницы и в конце в ресурсах приложения. Поиск прекращается, когда когда найден первый попавшийся ресурс, который соответствует ключу. Если парсер нигде не смог найти ресурс, то он генерирует исключение XamlParseException.

Ресурсы на уровне элемента

Выше в примере ресурсы определялись а уровне страницы. Теперь опустимся на уровень ниже и определим их на уровне элемента StackLayout:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage">
    <StackLayout Padding="10">
        <StackLayout.Resources>
            <ResourceDictionary>
                <Color x:Key="textColor">#004D40</Color>
                <Color x:Key="backColor">#80CBC4</Color>
                <x:Double x:Key="margin">10</x:Double>
            </ResourceDictionary>
        </StackLayout.Resources>
        <Button Text="iOS" TextColor="{StaticResource Key=textColor}"
      BackgroundColor="{StaticResource Key=backColor}" Margin="{StaticResource Key=margin}" />
        <Button Text="Android" TextColor="{StaticResource Key=textColor}"
      BackgroundColor="{StaticResource Key=backColor}" Margin="{StaticResource Key=margin}" />
    </StackLayout>
</ContentPage>

По сути результат в данном случае будет от же самый, так как все элементы все равно определены внутри StackLayout. Ресурсы элемента будут доступны данному элементу и всем вложенным в него элементам. Подобным образом можно определять ресурсы и для других элементов, например, для отдельной кнопки.

Ресурсы приложения

Для определения общих для всего приложения ресурсов в проекте присутствует файл App.xaml, который связан с основным файлом приложения App.xaml.cs. По умолчанию файл App.xaml уже содержит некоторые ресурсы:

<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:HelloApp"
             x:Class="HelloApp.App">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/Styles/Colors.xaml" />
                <ResourceDictionary Source="Resources/Styles/Styles.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
Глобальные ресурсы и словари ресурсов ResourceDictionary в App.xaml в .NET MAUI и C#

Здесь словарь ресурсов ResourceDictionary содержит элемент <ResourceDictionary.MergedDictionaries> с помощью которого объединяются другие словари в один. В данном случае определяются внешние словари, которые расположены в проектах во внешних файлах по пути "Resources/Styles/Colors.xaml" и "Resources/Styles/Styles.xaml".

Добавим наши собственные глобальные ресурсы и для этого изменим файл App.xaml следующим образом:

<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:HelloApp"
             x:Class="HelloApp.App">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/Styles/Colors.xaml" />
                <ResourceDictionary Source="Resources/Styles/Styles.xaml" />
                <ResourceDictionary>
                    <Color x:Key="textColor">#C0392B</Color>
                    <Color x:Key="backColor">#fab1a0</Color>
                    <x:Double x:Key="margin">10</x:Double>
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Для объединения нашего словаря с другими он добавляется в виде объекта ResourceDictionary в элемент <ResourceDictionary.MergedDictionaries>. То есть здесь определены все те же ресурсы, что и ранее, только теперь они будут доступны для любого элемента на любой стрнице внутри приложения.

И в этом случае мы просто можем их использовать:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage">
    <StackLayout Padding="10">
        <Button Text="iOS" TextColor="{StaticResource Key=textColor}"
      BackgroundColor="{StaticResource Key=backColor}" Margin="{StaticResource Key=margin}" />
        <Button Text="Android" TextColor="{StaticResource Key=textColor}"
      BackgroundColor="{StaticResource Key=backColor}" Margin="{StaticResource Key=margin}" />
    </StackLayout>
</ContentPage>
Объединение глобальных ресурсов в .NET MAUI и C#

Управление ресурсами в коде C#

Аналогичный пример использования ресурсов выглядел бы следующим образом:

namespace HelloApp
{
    class StartPage : ContentPage
    {
        public StartPage()
        {
            Color textColor = Color.FromArgb("#004D40");
            Color backColor = Color.FromArgb("#80CBC4");
            int margin = 10;

            // определяем словарь ресурсов
            ResourceDictionary resDict = new ResourceDictionary();
            // добавляем ресурсы в словарь
            resDict.Add("textColor", textColor);
            resDict.Add("backColor", backColor);
            resDict.Add("margin", margin);
            // устанавливаем словарь ресурсов для текущей страницы
            this.Resources = resDict;

            Button iosButton = new Button { Text = "iOS" };
            // получаем ресурс из словаря
            iosButton.TextColor = (Color)Resources["textColor"];
            iosButton.BackgroundColor = (Color)Resources["backColor"];
            iosButton.Margin = (int)Resources["margin"];

            Button androidButton = new Button { Text = "Android" };
            androidButton.TextColor = (Color)Resources["textColor"];
            androidButton.BackgroundColor = (Color)Resources["backColor"];
            androidButton.Margin = (int)Resources["margin"];

            Content = new StackLayout
            {
                Padding = 10,
                Children = { iosButton, androidButton }
            };
        }
    }
}

В данном случае опреляется словаоь ресурсов уровня страницы. Для управления ресурсами применяются методы и свойства ResourceDictionary:

  • Add(string key, object resource): добавляет объект с ключом key в словарь, причем в словарь можно добавить любой объект, главное ему сопоставить ключ

  • Remove(string key): удаляет из словаря ресурс с ключом key

Чтобы найти ресурс в словаре, достаточно обратиться по ключу:

androidButton.TextColor = (Color)Resources["textColor"];

Так как словарь ресурсов хранит объекты типа object, то при получении ресурса его надо привести к нужному типу.

Динамические ресурсы

Ресурсы могут быть статическими и динамическими. Рассмотрим разницу на примере:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Color x:Key="textColor">#1976D2</Color>
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout>
        <Button Text="Изменить" BackgroundColor="white" 
                WidthRequest="120"
                TextColor="{StaticResource textColor}"
                Clicked="ColorChange" />
    </StackLayout>
</ContentPage>

В файле кода C# добавим обработчик для кнопки:

namespace HelloApp;

public partial class MainPage : ContentPage
{
	public MainPage()
	{
		InitializeComponent();
	}
    private void ColorChange(object sender, EventArgs e)
    {
        Color textColor = (Color)Resources["textColor"];
        Resources["textColor"] = textColor.ToHex() == "#1976D2" ? 
            Color.FromArgb("#00838F") : Color.FromArgb("#1976D2");
    }
}

То есть здесь у кнопки цвет текста привязан к ресурсу "textColor". В обработчике кнопки мы пытаемся поменять значение ресурса - если цвет "#1976D2", то меняем на "#00838F" и наоборот. Однако после запуска приложения и нажатия на кнопку кнопка не поменяет цвет текста, так как ресурс "textColor" установлен как статический. То есть после установки он уже не изменяется.

И чтобы изменение срабатывало, данный ресурс надо установить как динамический. Для этого вместо StaticResource используется расширение DynamicResource. Изменим определение кнопки следующим образом:

<Button Text="Изменить" BackgroundColor="white" 
    WidthRequest="120"
    TextColor="{DynamicResource textColor}"
    Clicked="ColorChange" />

Весь остальной код остается без изменений.

Динамические ресурсы в .NET MAUI и C#

Стоит отметить, что если DynamicResource не найдет нужного ресурса по ключу, то никакой ошибки не возникнет, и приложение также будет работать.

Установка динамических ресурсов в коде C#

Для установки динамического ресурса в коде C# применяется метод SetDynamicResource():

button1.SetDynamicResource(Button.TextColorProperty, "resourceKey");

Первый параметр - свойство элемента, а второй - ключ ресурса. Например:

namespace HelloApp
{
    class StartPage : ContentPage
    {
        public StartPage()
        {
            ResourceDictionary resDict = new ResourceDictionary();
            // добавляем ресурсы в словарь
            resDict.Add("textColor", Color.FromArgb("#1976D2"));

            // устанавливаем словарь ресурсов
            this.Resources = resDict;

            Button androidButton = new Button 
            { 
                Text = "Android", 
                WidthRequest=120,
                BackgroundColor = Colors.White
            };
            // устанавливаем динамический ресурс
            androidButton.SetDynamicResource(Button.TextColorProperty, "textColor");
            androidButton.Clicked += ColorChange;

            Content = new StackLayout
            {
                Padding = 20,
                Children = { androidButton }
            };
        }
        private void ColorChange(object sender, EventArgs e)
        {
            Color textColor = (Color)Resources["textColor"];
            Resources["textColor"] = textColor.ToHex() == "#1976D2" ?
                Color.FromArgb("#00838F") : Color.FromArgb("#1976D2");
        }
    }
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850