3. část: Rozšíření značek XAML

Download Sample Stažení ukázky

Rozšíření značek XAML představují důležitou funkci XAML, která umožňuje nastavit vlastnosti na objekty nebo hodnoty, na které se odkazuje nepřímo z jiných zdrojů. Rozšíření značek XAML jsou obzvláště důležitá pro sdílení objektů a odkazování na konstanty používané v celé aplikaci, ale v datových vazbách najdou jejich největší nástroj.

Rozšíření značek XAML

Obecně platí, že xaml slouží k nastavení vlastností objektu na explicitní hodnoty, jako je řetězec, číslo, člen výčtu nebo řetězec, který je převeden na hodnotu na pozadí.

Někdy však vlastnosti musí místo toho odkazovat na hodnoty definované někde jinde nebo které mohou vyžadovat malé zpracování kódem za běhu. Pro tyto účely jsou k dispozici rozšíření značek XAML.

Tato rozšíření značek XAML nejsou rozšíření XML. XAML je zcela právní XML. Nazývají se "rozšíření", protože jsou podporovány kódem ve třídách, které implementují IMarkupExtension. Můžete napsat vlastní rozšíření značek.

V mnoha případech jsou rozšíření značek XAML okamžitě rozpoznatelná v souborech XAML, protože se zobrazují jako nastavení atributů oddělená složenými závorkami: { a }, ale někdy se rozšíření značek zobrazují ve značkách jako konvenční prvky.

Sdílené prostředky

Některé stránky XAML obsahují několik zobrazení s vlastnostmi nastavenými na stejné hodnoty. Například mnoho nastavení vlastnosti pro tyto Button objekty je stejné:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <StackLayout>
        <Button Text="Do this!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />

        <Button Text="Do that!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />

        <Button Text="Do the other thing!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />

    </StackLayout>
</ContentPage>

Pokud je potřeba některou z těchto vlastností změnit, můžete změnu raději provést jen jednou, a ne třikrát. Pokud by to byl kód, pravděpodobně byste používali konstanty a statické objekty jen pro čtení, které vám pomůžou zachovat takové hodnoty konzistentní a snadno upravit.

Jedním z oblíbených řešení v JAZYCE XAML je uložení takových hodnot nebo objektů ve slovníku prostředků. Třída VisualElement definuje vlastnost s názvem Resources typu ResourceDictionary, což je slovník s klíči typu string a hodnoty typu object. Objekty můžete vložit do tohoto slovníku a pak na ně odkazovat z revizí, a to vše v XAML.

Pokud chcete použít slovník prostředků na stránce, zahrňte dvojici značek elementů Resources vlastností. Nejpohodlnější je umístit je do horní části stránky:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>

    </ContentPage.Resources>
    ...
</ContentPage>

Je také nutné explicitně zahrnout ResourceDictionary značky:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>
        <ResourceDictionary>

        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

Nyní lze do slovníku prostředků přidat objekty a hodnoty různých typů. Tyto typy musí být okamžité. Nemůžou být například abstraktními třídami. Tyto typy musí mít také veřejný konstruktor bez parametrů. Každá položka vyžaduje klíč slovníku zadaný atributem x:Key . Příklad:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>
        <ResourceDictionary>
            <LayoutOptions x:Key="horzOptions"
                           Alignment="Center" />

            <LayoutOptions x:Key="vertOptions"
                           Alignment="Center"
                           Expands="True" />
        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

Tyto dvě položky jsou hodnoty typu LayoutOptionsstruktury a každý má jedinečný klíč a jednu nebo dvě sady vlastností. V kódu a kódu je mnohem častější používat statická pole LayoutOptions, ale zde je vhodnější nastavit vlastnosti.

Teď je potřeba nastavit HorizontalOptions a VerticalOptions vlastnosti těchto tlačítek na tyto prostředky a to se provádí s rozšířením StaticResource značek XAML:

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="3"
        Rotation="-15"
        TextColor="Red"
        FontSize="24" />

Rozšíření StaticResource značek je vždy oddělené složenými závorkami a obsahuje klíč slovníku.

Název StaticResource jej odlišuje od DynamicResource, který Xamarin.Forms také podporuje. DynamicResource je určen pro klíče slovníku přidružené k hodnotám, které se můžou během běhu změnit, zatímco StaticResource přístup k prvkům ze slovníku jen jednou při vytváření prvků na stránce.

BorderWidth Pro vlastnost je nutné uložit dvojitou hodnotu ve slovníku. XAML pohodlně definuje značky pro běžné datové typy jako x:Double a x:Int32:

<ContentPage.Resources>
    <ResourceDictionary>
        <LayoutOptions x:Key="horzOptions"
                       Alignment="Center" />

        <LayoutOptions x:Key="vertOptions"
                       Alignment="Center"
                       Expands="True" />

        <x:Double x:Key="borderWidth">
            3
        </x:Double>
    </ResourceDictionary>
</ContentPage.Resources>

Nemusíte to dávat na tři řádky. Tato položka slovníku pro tento úhel otočení zabírá pouze jeden řádek:

<ContentPage.Resources>
    <ResourceDictionary>
        <LayoutOptions x:Key="horzOptions"
                       Alignment="Center" />

        <LayoutOptions x:Key="vertOptions"
                       Alignment="Center"
                       Expands="True" />

         <x:Double x:Key="borderWidth">
            3
         </x:Double>

        <x:Double x:Key="rotationAngle">-15</x:Double>
    </ResourceDictionary>
</ContentPage.Resources>

Tyto dva prostředky lze odkazovat stejným způsobem jako na LayoutOptions hodnoty:

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="{StaticResource borderWidth}"
        Rotation="{StaticResource rotationAngle}"
        TextColor="Red"
        FontSize="24" />

Pro prostředky typu Colormůžete použít stejné řetězcové reprezentace, které používáte při přímém přiřazování atributů těchto typů. Převaděče typů jsou vyvolány při vytvoření prostředku. Tady je prostředek typu Color:

<Color x:Key="textColor">Red</Color>

Programy často nastaví FontSize vlastnost na člen výčtu NamedSize , například Large. Třída FontSizeConverter pracuje na pozadí a převádí ji na hodnotu závislou na platformě pomocí Device.GetNamedSized metody. Při definování prostředku velikosti písma ale dává smysl použít číselnou hodnotu, která je zde uvedena x:Double jako typ:

<x:Double x:Key="fontSize">24</x:Double>

Teď jsou všechny vlastnosti kromě Text definovaných nastavením prostředků:

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="{StaticResource borderWidth}"
        Rotation="{StaticResource rotationAngle}"
        TextColor="{StaticResource textColor}"
        FontSize="{StaticResource fontSize}" />

V rámci slovníku prostředků je také možné definovat OnPlatform různé hodnoty pro platformy. Tady je postup, jak OnPlatform může být objekt součástí slovníku prostředků pro různé barvy textu:

<OnPlatform x:Key="textColor"
            x:TypeArguments="Color">
    <On Platform="iOS" Value="Red" />
    <On Platform="Android" Value="Aqua" />
    <On Platform="UWP" Value="#80FF80" />
</OnPlatform>

Všimněte si, že OnPlatform získá atribut x:Key , protože se jedná o objekt ve slovníku a x:TypeArguments atribut, protože se jedná o obecnou třídu. Atributy iOSa , AndroidUWP jsou převedeny na Color hodnoty při inicializaci objektu.

Tady je konečný kompletní soubor XAML se třemi tlačítky, která přistupují k šesti sdíleným hodnotám:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>
        <ResourceDictionary>
            <LayoutOptions x:Key="horzOptions"
                           Alignment="Center" />

            <LayoutOptions x:Key="vertOptions"
                           Alignment="Center"
                           Expands="True" />

            <x:Double x:Key="borderWidth">3</x:Double>

            <x:Double x:Key="rotationAngle">-15</x:Double>

            <OnPlatform x:Key="textColor"
                        x:TypeArguments="Color">
                <On Platform="iOS" Value="Red" />
                <On Platform="Android" Value="Aqua" />
                <On Platform="UWP" Value="#80FF80" />
            </OnPlatform>

            <x:Double x:Key="fontSize">24</x:Double>
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>
        <Button Text="Do this!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />

        <Button Text="Do that!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />

        <Button Text="Do the other thing!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />

    </StackLayout>
</ContentPage>

Snímky obrazovky ověřují konzistentní styly a styly závislé na platformě:

Styled Controls

Ačkoli je nejběžnější definovat kolekci Resources v horní části stránky, mějte na paměti, že Resources vlastnost je definována pomocí VisualElementa můžete mít Resources kolekce na jiných prvcích na stránce. Zkuste například přidat jeden do tohoto příkladu StackLayout :

<StackLayout>
    <StackLayout.Resources>
        <ResourceDictionary>
            <Color x:Key="textColor">Blue</Color>
        </ResourceDictionary>
    </StackLayout.Resources>
    ...
</StackLayout>

Zjistíte, že barva textu tlačítek je teď modrá. V podstatě, kdykoli analyzátor XAML narazí na StaticResource rozšíření značek, vyhledá vizuální strom a použije první ResourceDictionary , na které narazí obsahující tento klíč.

Jedním z nejběžnějších typů objektů uložených ve slovníkech prostředků je Xamarin.FormsStyleten, který definuje kolekci nastavení vlastností. Styly jsou popsány v článku Styly.

Vývojáři, kteří s JAZYKem XAML noví, se někdy diví, jestli můžou vložit vizuální prvek, jako Label je nebo Button do ResourceDictionary. I když je to jistě možné, nedává moc smysl. Účelem je ResourceDictionary sdílet objekty. Prvek vizuálu nelze sdílet. Stejná instance se nemůže na jedné stránce objevit dvakrát.

Rozšíření x:Static Markup

Navzdory podobnostem jejich jmen x:Static a StaticResource jsou velmi odlišné. StaticResource vrátí objekt ze slovníku prostředků, zatímco x:Static přistupuje k jedné z následujících možností:

  • veřejné statické pole
  • veřejná statická vlastnost
  • pole veřejné konstanty
  • člen výčtu.

Rozšíření StaticResource značek je podporováno implementacemi XAML, které definují slovník prostředků, zatímco x:Static je vnitřní součástí XAML, jak se předpona x odhalí.

Tady je několik příkladů, které ukazují, jak x:Static explicitně odkazovat na statická pole a členy výčtu:

<Label Text="Hello, XAML!"
       VerticalOptions="{x:Static LayoutOptions.Start}"
       HorizontalTextAlignment="{x:Static TextAlignment.Center}"
       TextColor="{x:Static Color.Aqua}" />

Zatím to není moc působivé. x:Static Rozšíření značek ale může také odkazovat na statická pole nebo vlastnosti z vlastního kódu. Tady je AppConstants například třída, která obsahuje některá statická pole, která můžete chtít použít na více stránkách v celé aplikaci:

using System;
using Xamarin.Forms;

namespace XamlSamples
{
    static class AppConstants
    {
        public static readonly Thickness PagePadding;

        public static readonly Font TitleFont;

        public static readonly Color BackgroundColor = Color.Aqua;

        public static readonly Color ForegroundColor = Color.Brown;

        static AppConstants()
        {
            switch (Device.RuntimePlatform)
            {
                case Device.iOS:
                    PagePadding = new Thickness(5, 20, 5, 0);
                    TitleFont = Font.SystemFontOfSize(35, FontAttributes.Bold);
                    break;

                case Device.Android:
                    PagePadding = new Thickness(5, 0, 5, 0);
                    TitleFont = Font.SystemFontOfSize(40, FontAttributes.Bold);
                    break;

                case Device.UWP:
                    PagePadding = new Thickness(5, 0, 5, 0);
                    TitleFont = Font.SystemFontOfSize(50, FontAttributes.Bold);
                    break;
            }
        }
    }
}

Pokud chcete odkazovat na statická pole této třídy v souboru XAML, budete potřebovat nějaký způsob, jak označit v souboru XAML, kde se tento soubor nachází. Provedete to s deklarací oboru názvů XML.

Vzpomeňte si, že soubory XAML vytvořené jako součást standardní Xamarin.Forms šablony XAML obsahují dvě deklarace oboru názvů XML: jednu pro přístup ke Xamarin.Forms třídám a druhou pro odkazování na značky a atributy vnitřní do XAML:

xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

Pro přístup k jiným třídám budete potřebovat další deklarace oboru názvů XML. Každá další deklarace oboru názvů XML definuje novou předponu. Pro přístup ke třídám místnímu ke sdílené knihovně .NET Standard aplikace, jako AppConstantsje například , programátoři XAML často používají předponu local. Deklarace oboru názvů musí obsahovat název oboru názvů CLR (Common Language Runtime), označovaný také jako název oboru názvů .NET, což je název, který se zobrazuje v definici jazyka C# namespace nebo v direktivě using :

xmlns:local="clr-namespace:XamlSamples"

Deklarace oboru názvů XML můžete také definovat pro obory názvů .NET v libovolném sestavení, na které odkazuje knihovna .NET Standard. Tady je sys například předpona standardního oboru názvů .NET System , který je ve standardním sestavení netstandard . Vzhledem k tomu, že se jedná o jiné sestavení, musíte také zadat název sestavení, v tomto případě netstandard:

xmlns:sys="clr-namespace:System;assembly=netstandard"

Všimněte si, že za tímto klíčovým slovem clr-namespace následuje dvojtečka a název oboru názvů .NET, za kterým následuje středník, klíčové slovo assembly, znaménko rovná se a název sestavení.

Ano, dvojtečka následuje clr-namespace , ale rovnítko následuje assembly. Syntaxe byla definována tímto způsobem záměrně: Většina deklarací oboru názvů XML odkazuje na identifikátor URI, který začíná název schématu identifikátoru URI, například http, který je vždy následovaný dvojtečka. Součástí clr-namespace tohoto řetězce je napodobování této konvence.

Obě tyto deklarace oboru názvů jsou součástí ukázky StaticConstantsPage . Všimněte si, že BoxView dimenze jsou nastaveny na Math.PI a Math.E, ale škálovány faktorem 100:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples"
             xmlns:sys="clr-namespace:System;assembly=netstandard"
             x:Class="XamlSamples.StaticConstantsPage"
             Title="Static Constants Page"
             Padding="{x:Static local:AppConstants.PagePadding}">

    <StackLayout>
       <Label Text="Hello, XAML!"
              TextColor="{x:Static local:AppConstants.BackgroundColor}"
              BackgroundColor="{x:Static local:AppConstants.ForegroundColor}"
              Font="{x:Static local:AppConstants.TitleFont}"
              HorizontalOptions="Center" />

      <BoxView WidthRequest="{x:Static sys:Math.PI}"
               HeightRequest="{x:Static sys:Math.E}"
               Color="{x:Static local:AppConstants.ForegroundColor}"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               Scale="100" />
    </StackLayout>
</ContentPage>

Velikost výsledného BoxView výsledku vzhledem k obrazovce je závislá na platformě:

Controls using x:Static Markup Extension

Další standardní rozšíření značek

V souborech XAML je vnitřních několik rozšíření značek a podporuje se v Xamarin.Forms souborech XAML. Některé z nich se velmi často nepoužívají, ale jsou nezbytné, pokud je potřebujete:

  • Pokud vlastnost má ve výchozím nastavení jinou hodnotu null , ale chcete ji nastavit na null, nastavte ji na {x:Null} rozšíření značek.
  • Pokud je vlastnost typu Type, můžete ji přiřadit k objektu Type pomocí rozšíření {x:Type someClass}značek .
  • Pole v XAML můžete definovat pomocí x:Array rozšíření značek. Toto rozšíření značek má požadovaný atribut, Type který označuje typ prvků v poli.
  • Rozšíření Binding značek je popsáno v části 4. Základy datových vazeb
  • Rozšíření RelativeSource značek je popsáno v relativních vazbách.

Rozšíření značek ConstraintExpression

Rozšíření značek můžou mít vlastnosti, ale nejsou nastavené jako atributy XML. V rozšíření značek jsou nastavení vlastností oddělená čárkami a ve složených závorkách se nezobrazují žádné uvozovky.

To lze ilustrovat s příponou Xamarin.Forms značek s názvem ConstraintExpression, který se používá s RelativeLayout třídou. Umístění nebo velikost podřízeného zobrazení můžete zadat jako konstantu nebo relativní k nadřazené nebo jiné pojmenované zobrazení. Syntaxe ConstraintExpression umožňuje nastavit pozici nebo velikost zobrazení pomocí Factor časů vlastnosti jiného zobrazení a navíc Constant. Cokoli složitějšího, než vyžaduje kód.

Tady je příklad:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.RelativeLayoutPage"
             Title="RelativeLayout Page">

    <RelativeLayout>

        <!-- Upper left -->
        <BoxView Color="Red"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}" />
        <!-- Upper right -->
        <BoxView Color="Green"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=1,
                                            Constant=-40}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}" />
        <!-- Lower left -->
        <BoxView Color="Blue"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=1,
                                            Constant=-40}" />
        <!-- Lower right -->
        <BoxView Color="Yellow"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=1,
                                            Constant=-40}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=1,
                                            Constant=-40}" />

        <!-- Centered and 1/3 width and height of parent -->
        <BoxView x:Name="oneThird"
                 Color="Red"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=0.33}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=0.33}"
                 RelativeLayout.WidthConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=0.33}"
                 RelativeLayout.HeightConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=0.33}"  />

        <!-- 1/3 width and height of previous -->
        <BoxView Color="Blue"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=X}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=Y}"
                 RelativeLayout.WidthConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=Width,
                                            Factor=0.33}"
                 RelativeLayout.HeightConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=Height,
                                            Factor=0.33}"  />
    </RelativeLayout>
</ContentPage>

Možná nejdůležitější lekcí, kterou byste měli vzít z této ukázky, je syntaxe rozšíření značek: V závorkách s příponou značek se nesmí objevit žádné uvozovky. Při zadávání přípony značek do souboru XAML je přirozené, že chcete uzavřít hodnoty vlastností do uvozovek. Odporujte pokušení!

Tady je spuštěný program:

Relative Layout using Constraints

Shrnutí

Zde uvedené rozšíření značek XAML poskytují důležitou podporu pro soubory XAML. Ale možná nejužitečnější rozšíření kódu XAML je Binding, které je popsáno v další části této série, část 4. Základy datových vazeb