Для совместного использования различными элементами общих компонентов в .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
(для размера внешних отступов).
То есть в итоге получится следующая страница:
При этом важно, что на данный ресурс могут ссылаться сразу несколько элементов. Например, мы захотели создать общий для всех кнопок цвет фона. И в этом случае проще определить цветовой ресурс, чем присваивать свойству 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
содержит элемент <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>
Аналогичный пример использования ресурсов выглядел бы следующим образом:
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" />
Весь остальной код остается без изменений.
Стоит отметить, что если DynamicResource не найдет нужного ресурса по ключу, то никакой ошибки не возникнет, и приложение также будет работать.
Для установки динамического ресурса в коде 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"); } } }