Стиль приложений с помощью XAML
Приложения пользовательского интерфейса многоплатформенных приложений .NET (.NET MAUI) часто содержат несколько элементов управления с одинаковым внешним видом. Например, приложение может иметь несколько Label экземпляров с одинаковыми параметрами шрифта и параметрами макета:
<Label Text="These labels"
HorizontalOptions="Center"
VerticalOptions="Center"
FontSize="18" />
<Label Text="are not"
HorizontalOptions="Center"
VerticalOptions="Center"
FontSize="18" />
<Label Text="using styles"
HorizontalOptions="Center"
VerticalOptions="Center"
FontSize="18" />
В этом примере каждый Label объект имеет одинаковые значения свойств для управления внешним видом текста, отображаемого объектом Label. Однако настройка внешнего вида каждого отдельного элемента управления может быть повторяющейся и подверженной ошибке. Вместо этого можно создать стиль, определяющий внешний вид, а затем применить к необходимым элементам управления.
Общие сведения о стилях
Приложение можно стильировать с помощью Style класса для группировки коллекции значений свойств в один объект, который затем можно применить к нескольким визуальным элементам. Это помогает уменьшить повторяющуюся разметку и позволяет легко изменять внешний вид приложений.
Хотя стили предназначены в основном для приложений на основе XAML, их также можно создать в C#:
- Style Объекты, созданные в XAML, обычно определяются в ResourceDictionary
Resources
коллекции элементов управления, страницы илиResources
коллекции приложения. - Style Объекты, созданные в C#, обычно определяются в классе страницы или в классе, к которому можно получить глобальный доступ.
От того, где определен стиль (Style), зависит, где его можно использовать:
- Style экземпляры, определенные на уровне элемента управления, могут применяться только к элементу управления и к его дочерним элементам.
- Style Экземпляры, определенные на уровне страницы, могут применяться только к странице и к его дочерним элементам.
- Style экземпляры, определенные на уровне приложения, могут применяться во всем приложении.
Каждый Style объект содержит коллекцию одного или нескольких Setter объектов, каждый из которых Setter имеет значение Property
и a Value
. Property
— это имя привязываемого свойства элемента, к которому применяется стиль, а Value
— это применяемое к этому свойству значение.
Каждый Style объект может быть явным или неявным:
- Явный Style объект определяется путем указания
TargetType
значения иx:Key
значения, а также путем задания свойства целевого элемента Style ссылке.x:Key
Дополнительные сведения см. в разделе "Явные стили". - Неявный Style объект определяется путем указания только объекта
TargetType
. Затем Style объект будет автоматически применен ко всем элементам этого типа. Однако подклассы подклассов неTargetType
применяются Style автоматически. Дополнительные сведения см. в разделе "Неявные стили".
При создании Style свойство TargetType
является обязательным. В следующем примере показан явный стиль:
<Style x:Key="labelStyle" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
</Style>
Для применения целевого Styleобъекта должен быть VisualElement объект, соответствующий TargetType
значению Styleсвойства:
<Label Text="Demonstrating an explicit style" Style="{StaticResource labelStyle}" />
Стили ниже в иерархии представлений имеют приоритет над теми, кто определен выше. Например, установка Style набора на Label.TextColor
Red
уровне приложения будет переопределена стилем на уровне страницы, на который устанавливается значение Label.TextColor
Green
. Аналогичным образом стиль уровня страницы переопределяется стилем уровня элемента управления. Кроме того, если Label.TextColor
задано непосредственно в свойстве элемента управления, это имеет приоритет над любыми стилями.
Стили не реагируют на изменения свойств и остаются неизменными в течение длительности приложения. Однако приложения могут динамически реагировать на изменения стиля во время выполнения с помощью динамических ресурсов. Дополнительные сведения см. в разделе "Динамические стили".
Явные стили
Чтобы создать страницу Style на уровне страницы, ResourceDictionary необходимо добавить его на страницу, а затем в него можно включить ResourceDictionaryодно или несколько Style объявлений. Объект Style делается явным путем предоставления его объявления x:Key
атрибута, который дает ему описательный ключ в ResourceDictionary. Затем явные стили должны применяться к определенным визуальным элементам, задав их Style свойства.
В следующем примере показаны явные стили в страницах и применены к объектам страницыResourceDictionaryLabel:
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="labelRedStyle"
TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
<Setter Property="TextColor" Value="Red" />
</Style>
<Style x:Key="labelGreenStyle"
TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
<Setter Property="TextColor" Value="Green" />
</Style>
<Style x:Key="labelBlueStyle"
TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
<Setter Property="TextColor" Value="Blue" />
</Style>
</ContentPage.Resources>
<StackLayout>
<Label Text="These labels"
Style="{StaticResource labelRedStyle}" />
<Label Text="are demonstrating"
Style="{StaticResource labelGreenStyle}" />
<Label Text="explicit styles,"
Style="{StaticResource labelBlueStyle}" />
<Label Text="and an explicit style override"
Style="{StaticResource labelBlueStyle}"
TextColor="Teal" />
</StackLayout>
</ContentPage>
В этом примере ResourceDictionary определяются три стиля, которые явно задаются на объектах страницы Label . Каждый Style используется для отображения текста в другом цвете, а также настройки размера шрифта, а также параметров горизонтального и вертикального макета. Каждый из них Style применяется к другому Label , задав свойства Style с помощью расширения разметки StaticResource
. Кроме того, в то время как последний Label имеет Style набор, он также переопределяет TextColor
свойство на другое Color значение.
Неявные стили
Чтобы создать страницу Style на уровне страницы, ResourceDictionary необходимо добавить его на страницу, а затем в него можно включить ResourceDictionaryодно или несколько Style объявлений. Объект Style создается неявным путем указания атрибута x:Key
. Затем стиль будет применен к визуальным элементам области, которые соответствуют TargetType
точно, но не к элементам, производным от TargetType
значения.
В следующем примере кода показан неявный стиль страницы и применен к объектам страницыResourceDictionaryEntry:
<ContentPage ...>
<ContentPage.Resources>
<Style TargetType="Entry">
<Setter Property="HorizontalOptions" Value="Fill" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="BackgroundColor" Value="Yellow" />
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="TextColor" Value="Blue" />
</Style>
</ContentPage.Resources>
<StackLayout>
<Entry Text="These entries" />
<Entry Text="are demonstrating" />
<Entry Text="implicit styles," />
<Entry Text="and an implicit style override"
BackgroundColor="Lime"
TextColor="Red" />
<local:CustomEntry Text="Subclassed Entry is not receiving the style" />
</StackLayout>
</ContentPage>
В этом примере ResourceDictionary определяется один неявный стиль, который неявно задан на объектах Entry страницы. Используется Style для отображения синего текста на желтом фоне, а также настройки других параметров внешнего вида. Он Style добавляется в страницу ResourceDictionary без указания атрибута x:Key
. Поэтому применяется Style ко всем Entry объектам неявно, так как они соответствуют TargetType
свойству точного Style . Однако этот Style объект не применяется к CustomEntry
объекту, который является подклассом Entry. Кроме того, четвертый Entry переопределяет BackgroundColor
и TextColor
свойства стиля на разные Color значения.
Применение стиля к производным типам
Свойство Style.ApplyToDerivedTypes
позволяет применять стиль к элементам управления, производным от базового типа, на который TargetType
ссылается свойство. Таким образом, если задать это свойство, чтобы true
один стиль был предназначен для нескольких типов, при условии, что типы являются производными от базового типа, указанного в свойстве TargetType
.
В следующем примере показан неявный стиль, который задает цвет фона Button экземпляров красным:
<Style TargetType="Button"
ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor"
Value="Red" />
</Style>
Размещение этого стиля на уровне ResourceDictionary страницы приведет к применению ко всем Button объектам на странице, а также к любым элементам управления, производным от Button. Однако если ApplyToDerivedTypes
свойство осталось незамеченным, стиль будет применяться только к Button объектам.
Глобальные стили
Стили можно определить глобально, добавив их в словарь ресурсов приложения. Эти стили можно использовать во всем приложении и помочь избежать дублирования стилей на страницах и элементах управления.
В следующем примере показан определенный Style на уровне приложения:
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Styles"
x:Class="Styles.App">
<Application.Resources>
<Style x:Key="buttonStyle" TargetType="Button">
<Setter Property="HorizontalOptions"
Value="Center" />
<Setter Property="VerticalOptions"
Value="CenterAndExpand" />
<Setter Property="BorderColor"
Value="Lime" />
<Setter Property="CornerRadius"
Value="5" />
<Setter Property="BorderWidth"
Value="5" />
<Setter Property="WidthRequest"
Value="200" />
<Setter Property="TextColor"
Value="Teal" />
</Style>
</Application.Resources>
</Application>
В этом примере ResourceDictionary определяется один явный стиль, buttonStyle
который будет использоваться для задания внешнего вида Button объектов.
Примечание.
Глобальные стили могут быть явными или неявными.
В следующем примере показана страница, используюющая buttonStyle
объекты страницы Button :
<ContentPage ...>
<StackLayout>
<Button Text="These buttons"
Style="{StaticResource buttonStyle}" />
<Button Text="are demonstrating"
Style="{StaticResource buttonStyle}" />
<Button Text="application styles"
Style="{StaticResource buttonStyle}" />
</StackLayout>
</ContentPage>
Наследование стилей
Стили могут наследовать от других стилей, чтобы уменьшить дублирование и включить повторное использование. Это достигается путем задания Style.BasedOn
свойства существующему Style. В XAML это можно сделать, задав BasedOn
свойству StaticResource
расширение разметки, которое ссылается на ранее созданное расширение Style.
Стили, наследуемые от базового стиля, могут включать Setter экземпляры для новых свойств или использовать их для переопределения наборов из базового стиля. Кроме того, стили, наследуемые от базового стиля, должны нацелены на тот же тип или тип, производный от типа, наследуемого базовым стилем. Например, если базовый стиль предназначен View для объектов, стили, основанные на базовом стиле, могут целевые View объекты или типы, производные от View класса, например Label и Button объектов.
Стиль может наследоваться только от стилей на том же уровне или выше в иерархии представлений. Это означает следующее.
- Стиль уровня приложения может наследовать только от других стилей уровня приложения.
- Стиль уровня страницы может наследоваться от стилей уровня приложения и других стилей уровня страницы.
- Стиль уровня элемента управления может наследоваться от стилей уровня приложения, стилей на уровне страницы и других стилей уровня управления.
В следующем примере показано явное наследование стиля:
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="baseStyle"
TargetType="View">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
</Style>
</ContentPage.Resources>
<StackLayout>
<StackLayout.Resources>
<Style x:Key="labelStyle"
TargetType="Label"
BasedOn="{StaticResource baseStyle}">
<Setter Property="FontSize" Value="18" />
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="TextColor" Value="Teal" />
</Style>
<Style x:Key="buttonStyle"
TargetType="Button"
BasedOn="{StaticResource baseStyle}">
<Setter Property="BorderColor" Value="Lime" />
<Setter Property="CornerRadius" Value="5" />
<Setter Property="BorderWidth" Value="5" />
<Setter Property="WidthRequest" Value="200" />
<Setter Property="TextColor" Value="Teal" />
</Style>
</StackLayout.Resources>
<Label Text="This label uses style inheritance"
Style="{StaticResource labelStyle}" />
<Button Text="This button uses style inheritance"
Style="{StaticResource buttonStyle}" />
</StackLayout>
</ContentPage>
В этом примере объекты целевых View baseStyle
объектов и задают HorizontalOptions
свойства.VerticalOptions
Параметр baseStyle
не задан непосредственно для элементов управления. Вместо этого и buttonStyle
наследуется от него, labelStyle
задав дополнительные значения привязываемого свойства. Затем labelStyle
объекты задаются на и Label Button.buttonStyle
Внимание
Неявный стиль может быть производным от явного стиля, но явный стиль не может быть производным от неявного стиля.
Динамические стили
Стили не реагируют на изменения свойств и остаются неизменными в течение длительности приложения. Например, после назначения Style визуального элемента одно из Setter объектов изменяется, удаляется или добавляется новое Setter , изменения не будут применены к визуальному элементу. Однако приложения могут динамически реагировать на изменения стиля во время выполнения с помощью динамических ресурсов.
DynamicResource
Расширение разметки аналогично StaticResource
расширению разметки в том, что оба используют ключ словаря для получения значения изResourceDictionary. Однако при выполнении StaticResource
одного поиска словаря DynamicResource
сохраняется ссылка на ключ словаря. Поэтому при замене записи словаря, связанной с ключом, изменение применяется к визуальному элементу. Это позволяет вносить изменения в стиле среды выполнения в приложении.
В следующем примере показаны динамические стили:
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="baseStyle"
TargetType="View">
<Setter Property="VerticalOptions" Value="Center" />
</Style>
<Style x:Key="blueSearchBarStyle"
TargetType="SearchBar"
BasedOn="{StaticResource baseStyle}">
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="PlaceholderColor" Value="Blue" />
</Style>
<Style x:Key="greenSearchBarStyle"
TargetType="SearchBar">
<Setter Property="FontAttributes" Value="None" />
<Setter Property="PlaceholderColor" Value="Green" />
</Style>
</ContentPage.Resources>
<StackLayout>
<SearchBar Placeholder="SearchBar demonstrating dynamic styles"
Style="{DynamicResource blueSearchBarStyle}" />
</StackLayout>
</ContentPage>
В этом примере SearchBar объект использует DynamicResource
расширение разметки для задания именованного Style blueSearchBarStyle
. Затем SearchBar его Style определение может быть обновлено в коде:
Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];
В этом примере blueSearchBarStyle
определение обновляется для использования значений greenSearchBarStyle
из определения. При выполнении этого кода будет обновлено, чтобы использовать объекты, SearchBar определенные в greenSearchBarStyle
.Setter
Наследование динамического стиля
Производный стиль от динамического стиля не может быть достигнут с помощью Style.BasedOn
свойства. Вместо этого Style класс включает BaseResourceKey
свойство, которое можно задать для ключа словаря, значение которого может динамически измениться.
В следующем примере показано наследование динамического стиля:
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="baseStyle"
TargetType="View">
<Setter Property="VerticalOptions" Value="Center" />
</Style>
<Style x:Key="blueSearchBarStyle"
TargetType="SearchBar"
BasedOn="{StaticResource baseStyle}">
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="TextColor" Value="Blue" />
</Style>
<Style x:Key="greenSearchBarStyle"
TargetType="SearchBar">
<Setter Property="FontAttributes" Value="None" />
<Setter Property="TextColor" Value="Green" />
</Style>
<Style x:Key="tealSearchBarStyle"
TargetType="SearchBar"
BaseResourceKey="blueSearchBarStyle">
<Setter Property="BackgroundColor" Value="Teal" />
<Setter Property="CancelButtonColor" Value="White" />
</Style>
</ContentPage.Resources>
<StackLayout>
<SearchBar Text="SearchBar demonstrating dynamic style inheritance"
Style="{StaticResource tealSearchBarStyle}" />
</StackLayout>
</ContentPage>
В этом примере SearchBar объект использует StaticResource
расширение разметки для ссылки на именованный Style tealSearchBarStyle
объект. При этом Style задаются некоторые дополнительные свойства и используется BaseResourceKey
свойство для ссылки blueSearchBarStyle
. DynamicResource
Расширение разметки не требуется, так как tealSearchBarStyle
не изменится, за исключением Style производных от него расширений. tealSearchBarStyle
Поэтому сохраняет ссылку на blueSearchBarStyle
и обновляется при изменении базового стиля.
Определение можно обновить в коде blueSearchBarStyle
:
Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];
В этом примере blueSearchBarStyle
определение обновляется для использования значений greenSearchBarStyle
из определения. При выполнении этого кода будет обновлено, чтобы использовать объекты, SearchBar определенные в greenSearchBarStyle
.Setter
Классы стилей
Классы стилей позволяют применять несколько стилей к элементу управления, не прибегая к наследованию стилей.
Класс стиля можно создать, задав Class
для свойства значение, Style string
представляющее имя класса. Преимуществом этого предложения является определение явного стиля с помощью атрибута x:Key
, что к атрибуту может применяться несколько классов стилей VisualElement.
Внимание
Несколько стилей могут совместно использовать одно и то же имя класса, если они предназначены для разных типов. Это позволяет нескольким классам стилей, которые одинаково именованы, для целевых типов.
В следующем примере показаны три BoxView класса стиля и VisualElement класс стиля:
<ContentPage ...>
<ContentPage.Resources>
<Style TargetType="BoxView"
Class="Separator">
<Setter Property="BackgroundColor"
Value="#CCCCCC" />
<Setter Property="HeightRequest"
Value="1" />
</Style>
<Style TargetType="BoxView"
Class="Rounded">
<Setter Property="BackgroundColor"
Value="#1FAECE" />
<Setter Property="HorizontalOptions"
Value="Start" />
<Setter Property="CornerRadius"
Value="10" />
</Style>
<Style TargetType="BoxView"
Class="Circle">
<Setter Property="BackgroundColor"
Value="#1FAECE" />
<Setter Property="WidthRequest"
Value="100" />
<Setter Property="HeightRequest"
Value="100" />
<Setter Property="HorizontalOptions"
Value="Start" />
<Setter Property="CornerRadius"
Value="50" />
</Style>
<Style TargetType="VisualElement"
Class="Rotated"
ApplyToDerivedTypes="true">
<Setter Property="Rotation"
Value="45" />
</Style>
</ContentPage.Resources>
</ContentPage>
В этом примере классы стилей Separator
Rounded
и Circle
классы стилей задают BoxView свойства определенным значениям. Класс Rotated
стилей имеет тип TargetType
VisualElement, который означает, что он может применяться только к VisualElement экземплярам. Однако его ApplyToDerivedTypes
свойство имеет true
значение , которое гарантирует, что оно может применяться к любым элементам управления, производным от VisualElementтаких элементов управления, как BoxView. Дополнительные сведения о применении стиля к производного типа см. в разделе "Применение стиля к производным типам".
Классы стилей можно использовать, задав StyleClass
свойство элемента управления, которое имеет тип IList<string>
, в список имен классов стилей. Классы стилей будут применены, если тип элемента управления соответствует TargetType
классам стиля.
В следующем примере показаны три BoxView экземпляра, каждый из которых имеет разные классы стилей:
<ContentPage ...>
<ContentPage.Resources>
...
</ContentPage.Resources>
<StackLayout>
<BoxView StyleClass="Separator" />
<BoxView WidthRequest="100"
HeightRequest="100"
HorizontalOptions="Center"
StyleClass="Rounded, Rotated" />
<BoxView HorizontalOptions="Center"
StyleClass="Circle" />
</StackLayout>
</ContentPage>
В этом примере первый BoxView стиль должен быть разделителем линий, а третий BoxView — циклическим. BoxView Второй имеет два класса стиля, примененные к нему, которые дают ему округленные угловые и поворот его 45 градусов:
Внимание
К элементу управления можно применять несколько классов стилей, так как StyleClass
свойство имеет тип IList<string>
. При этом классы стилей применяются в порядке возрастания списка. Таким образом, если несколько классов стилей задают идентичные свойства, свойство в классе стилей, которое находится в самой высокой позиции списка, будет иметь приоритет.