Стиль приложений с помощью 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, обычно определяются в ResourceDictionaryResources коллекции элементов управления, страницы или 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.TextColorRed уровне приложения будет переопределена стилем на уровне страницы, на который устанавливается значение Label.TextColorGreen. Аналогичным образом стиль уровня страницы переопределяется стилем уровня элемента управления. Кроме того, если 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>

В этом примере объекты целевых ViewbaseStyle объектов и задают HorizontalOptions свойства.VerticalOptions Параметр baseStyle не задан непосредственно для элементов управления. Вместо этого и buttonStyle наследуется от него, labelStyle задав дополнительные значения привязываемого свойства. Затем labelStyle объекты задаются на и LabelButton.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 расширение разметки для задания именованного StyleblueSearchBarStyle. Затем 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 расширение разметки для ссылки на именованный StyletealSearchBarStyleобъект. При этом Style задаются некоторые дополнительные свойства и используется BaseResourceKey свойство для ссылки blueSearchBarStyle. DynamicResource Расширение разметки не требуется, так как tealSearchBarStyle не изменится, за исключением Style производных от него расширений. tealSearchBarStyle Поэтому сохраняет ссылку на blueSearchBarStyle и обновляется при изменении базового стиля.

Определение можно обновить в коде blueSearchBarStyle :

Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];

В этом примере blueSearchBarStyle определение обновляется для использования значений greenSearchBarStyle из определения. При выполнении этого кода будет обновлено, чтобы использовать объекты, SearchBar определенные в greenSearchBarStyle.Setter

Классы стилей

Классы стилей позволяют применять несколько стилей к элементу управления, не прибегая к наследованию стилей.

Класс стиля можно создать, задав Class для свойства значение, Stylestring представляющее имя класса. Преимуществом этого предложения является определение явного стиля с помощью атрибута 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>

В этом примере классы стилей SeparatorRoundedи Circle классы стилей задают BoxView свойства определенным значениям. Класс Rotated стилей имеет тип TargetTypeVisualElement, который означает, что он может применяться только к 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 градусов:

Screenshot of BoxViews styled with style classes.

Важно!

К элементу управления можно применять несколько классов стилей, так как StyleClass свойство имеет тип IList<string>. При этом классы стилей применяются в порядке возрастания списка. Таким образом, если несколько классов стилей задают идентичные свойства, свойство в классе стилей, которое находится в самой высокой позиции списка, будет иметь приоритет.