Partie 3. Extensions de balisage XAML
Les extensions de balisage XAML constituent une fonctionnalité importante en XAML qui permet aux propriétés d’être définies sur des objets ou des valeurs référencés indirectement à partir d’autres sources. Les extensions de balisage XAML sont particulièrement importantes pour le partage d’objets et le référencement des constantes utilisées dans une application, mais elles trouvent leur plus grand utilitaire dans les liaisons de données.
Extensions de balisage XAML
En général, vous utilisez XAML pour définir des propriétés d’un objet à des valeurs explicites, telles qu’une chaîne, un nombre, un membre d’énumération ou une chaîne convertie en valeur en arrière-plan.
Toutefois, parfois, les propriétés doivent plutôt référencer des valeurs définies ailleurs, ou qui peuvent nécessiter un peu de traitement par le code au moment de l’exécution. À ces fins, les extensions de balisage XAML sont disponibles.
Ces extensions de balisage XAML ne sont pas des extensions XML. XAML est entièrement légal XML. Ils sont appelés « extensions », car ils sont soutenus par du code dans des classes qui implémentent IMarkupExtension
. Vous pouvez écrire vos propres extensions de balisage personnalisées.
Dans de nombreux cas, les extensions de balisage XAML sont instantanément reconnaissables dans les fichiers XAML, car elles apparaissent en tant que paramètres d’attribut délimités par des accolades : { et }, mais parfois les extensions de balisage apparaissent dans le balisage en tant qu’éléments conventionnels.
Ressources partagées
Certaines pages XAML contiennent plusieurs vues avec des propriétés définies sur les mêmes valeurs. Par exemple, la plupart des paramètres de propriété pour ces Button
objets sont les mêmes :
<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>
Si l’une de ces propriétés doit être modifiée, vous préférerez peut-être apporter la modification une seule fois au lieu de trois fois. S’il s’agissait d’un code, vous utiliseriez probablement des constantes et des objets statiques en lecture seule pour vous aider à maintenir ces valeurs cohérentes et faciles à modifier.
En XAML, une solution populaire consiste à stocker ces valeurs ou objets dans un dictionnaire de ressources. La VisualElement
classe définit une propriété nommée Resources
de typeResourceDictionary
, qui est un dictionnaire avec des clés de type et des valeurs de type object
string
. Vous pouvez placer des objets dans ce dictionnaire, puis les référencer à partir du balisage, tout en XAML.
Pour utiliser un dictionnaire de ressources sur une page, incluez une paire de balises d’élément de Resources
propriété. Il est plus pratique de les placer en haut de la page :
<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>
Il est également nécessaire d’inclure explicitement des ResourceDictionary
balises :
<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>
Les objets et valeurs de différents types peuvent maintenant être ajoutés au dictionnaire de ressources. Ces types doivent être instanciables. Ils ne peuvent pas être des classes abstraites, par exemple. Ces types doivent également avoir un constructeur sans paramètre public. Chaque élément nécessite une clé de dictionnaire spécifiée avec l’attribut x:Key
. Par exemple :
<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>
Ces deux éléments sont des valeurs du type LayoutOptions
de structure, et chacune a une clé unique et une ou deux propriétés définies. Dans le code et le balisage, il est beaucoup plus courant d’utiliser les champs statiques de LayoutOptions
, mais ici, il est plus pratique de définir les propriétés.
À présent, il est nécessaire de définir les propriétés et VerticalOptions
les HorizontalOptions
propriétés de ces boutons sur ces ressources, et cela s’effectue avec l’extension StaticResource
de balisage XAML :
<Button Text="Do this!"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />
L’extension StaticResource
de balisage est toujours délimitée par des accolades et inclut la clé de dictionnaire.
Le nom StaticResource
le distingue de DynamicResource
, qui Xamarin.Forms prend également en charge. DynamicResource
est destiné aux clés de dictionnaire associées aux valeurs susceptibles de changer pendant l’exécution, tandis qu’elles StaticResource
accèdent aux éléments du dictionnaire une seule fois lorsque les éléments de la page sont construits.
Pour la BorderWidth
propriété, il est nécessaire de stocker un double dans le dictionnaire. XAML définit facilement des balises pour les types de données courants comme x:Double
et 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>
Vous n’avez pas besoin de le mettre sur trois lignes. Cette entrée de dictionnaire pour cet angle de rotation ne prend qu’une seule ligne :
<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>
Ces deux ressources peuvent être référencées de la même façon que les LayoutOptions
valeurs :
<Button Text="Do this!"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="Red"
FontSize="24" />
Pour les ressources de type Color
, vous pouvez utiliser les mêmes représentations sous forme de chaîne que celles que vous utilisez lors de l’attribution directe d’attributs de ces types. Les convertisseurs de type sont appelés lors de la création de la ressource. Voici une ressource de type Color
:
<Color x:Key="textColor">Red</Color>
Souvent, les programmes définissent une FontSize
propriété sur un membre de l’énumération NamedSize
, par Large
exemple . La FontSizeConverter
classe fonctionne en arrière-plan pour la convertir en valeur dépendante de la plateforme à l’aide de la Device.GetNamedSized
méthode. Toutefois, lors de la définition d’une ressource de taille de police, il est plus judicieux d’utiliser une valeur numérique, illustrée ici sous la forme d’un x:Double
type :
<x:Double x:Key="fontSize">24</x:Double>
À présent, toutes les propriétés à l’exception Text
sont définies par les paramètres de ressource :
<Button Text="Do this!"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />
Il est également possible d’utiliser OnPlatform
dans le dictionnaire de ressources pour définir différentes valeurs pour les plateformes. Voici comment un OnPlatform
objet peut faire partie du dictionnaire de ressources pour différentes couleurs de texte :
<OnPlatform x:Key="textColor"
x:TypeArguments="Color">
<On Platform="iOS" Value="Red" />
<On Platform="Android" Value="Aqua" />
<On Platform="UWP" Value="#80FF80" />
</OnPlatform>
Notez qu’il OnPlatform
obtient à la fois un x:Key
attribut, car il s’agit d’un objet dans le dictionnaire et d’un x:TypeArguments
attribut, car il s’agit d’une classe générique. Les iOS
attributs et UWP
les Android
attributs sont convertis en Color
valeurs lorsque l’objet est initialisé.
Voici le fichier XAML complet final avec trois boutons accédant à six valeurs partagées :
<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>
Les captures d’écran vérifient le style cohérent et le style dépendant de la plateforme :
Bien qu’il soit le plus courant de définir la Resources
collection en haut de la page, gardez à l’esprit que la Resources
propriété est définie par VisualElement
, et que vous pouvez avoir Resources
des collections sur d’autres éléments de la page. Par exemple, essayez d’en ajouter un dans StackLayout
cet exemple :
<StackLayout>
<StackLayout.Resources>
<ResourceDictionary>
<Color x:Key="textColor">Blue</Color>
</ResourceDictionary>
</StackLayout.Resources>
...
</StackLayout>
Vous découvrirez que la couleur de texte des boutons est désormais bleue. En fait, chaque fois que l’analyseur XAML rencontre une StaticResource
extension de balisage, il recherche l’arborescence visuelle et utilise le premier ResourceDictionary
qu’il rencontre contenant cette clé.
L’un des types d’objets les plus courants stockés dans les dictionnaires de ressources est celui Xamarin.FormsStyle
qui définit une collection de paramètres de propriété. Les styles sont abordés dans les styles de l’article.
Parfois, les développeurs se demandent s’ils peuvent placer un élément visuel tel que Label
ou Button
dans un ResourceDictionary
. Bien qu’il soit sûrement possible, il n’a pas beaucoup de sens. L’objectif du projet ResourceDictionary
est de partager des objets. Un élément visuel ne peut pas être partagé. La même instance ne peut pas apparaître deux fois sur une seule page.
The x:Static Markup Extension
Malgré les similitudes de leurs noms, x:Static
et StaticResource
sont très différentes. StaticResource
retourne un objet à partir d’un dictionnaire de ressources tout en x:Static
accédant à l’un des éléments suivants :
- un champ statique public
- une propriété statique publique
- un champ de constante publique
- un membre d’énumération.
L’extension StaticResource
de balisage est prise en charge par les implémentations XAML qui définissent un dictionnaire de ressources, tandis qu’elle x:Static
est une partie intrinsèque de XAML, car le x
préfixe révèle.
Voici quelques exemples qui montrent comment x:Static
référencer explicitement des champs statiques et des membres d’énumération :
<Label Text="Hello, XAML!"
VerticalOptions="{x:Static LayoutOptions.Start}"
HorizontalTextAlignment="{x:Static TextAlignment.Center}"
TextColor="{x:Static Color.Aqua}" />
Jusqu’à présent, ce n’est pas très impressionnant. Mais l’extension de x:Static
balisage peut également référencer des champs statiques ou des propriétés à partir de votre propre code. Par exemple, voici une AppConstants
classe qui contient des champs statiques que vous pouvez utiliser sur plusieurs pages dans une application :
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;
}
}
}
}
Pour référencer les champs statiques de cette classe dans le fichier XAML, vous aurez besoin d’un moyen d’indiquer dans le fichier XAML où se trouve ce fichier. Pour ce faire, utilisez une déclaration d’espace de noms XML.
Rappelez-vous que les fichiers XAML créés dans le cadre du modèle XAML standard Xamarin.Forms contiennent deux déclarations d’espace de noms XML : une pour accéder aux Xamarin.Forms classes et une autre pour référencer des balises et des attributs intrinsèques au code XAML :
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Vous aurez besoin de déclarations d’espace de noms XML supplémentaires pour accéder à d’autres classes. Chaque déclaration d’espace de noms XML supplémentaire définit un nouveau préfixe. Pour accéder aux classes locales à la bibliothèque .NET Standard d’application partagée, telles que AppConstants
les programmeurs XAML utilisent souvent le préfixe local
. La déclaration d’espace de noms doit indiquer le nom de l’espace de noms CLR (Common Language Runtime), également appelé nom d’espace de noms .NET, qui est le nom qui apparaît dans une définition C# namespace
ou dans une using
directive :
xmlns:local="clr-namespace:XamlSamples"
Vous pouvez également définir des déclarations d’espace de noms XML pour les espaces de noms .NET dans n’importe quel assembly référencé par la bibliothèque .NET Standard. Par exemple, voici un sys
préfixe pour l’espace de noms .NET System
standard, qui se trouve dans l’assembly netstandard . Comme il s’agit d’un autre assembly, vous devez également spécifier le nom de l’assembly, dans ce cas netstandard :
xmlns:sys="clr-namespace:System;assembly=netstandard"
Notez que le mot clé clr-namespace
est suivi d’un signe deux-points, puis du nom de l’espace de noms .NET, suivi d’un point-virgule, du mot cléassembly
, d’un signe égal et du nom de l’assembly.
Oui, un signe deux-points suit clr-namespace
mais un signe égal suit assembly
. La syntaxe a été définie de cette façon délibérément : la plupart des déclarations d’espace de noms XML font référence à un URI qui commence un nom de schéma d’URI tel que http
, qui est toujours suivi d’un signe deux-points. La clr-namespace
partie de cette chaîne est destinée à imiter cette convention.
Ces deux déclarations d’espace de noms sont incluses dans l’exemple StaticConstantsPage . Notez que les BoxView
dimensions sont définies sur etMath.E
, mais mises à Math.PI
l’échelle par un facteur de 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>
La taille du résultat BoxView
par rapport à l’écran dépend de la plateforme :
Autres extensions de balisage standard
Plusieurs extensions de balisage sont intrinsèques au code XAML et prises en charge dans les Xamarin.Forms fichiers XAML. Certains d’entre eux ne sont pas utilisés très souvent, mais sont essentiels quand vous en avez besoin :
- Si une propriété a une valeur différente
null
par défaut, mais que vous souhaitez la définirnull
sur , définissez-la sur l’extension de{x:Null}
balisage. - Si une propriété est de type
Type
, vous pouvez l’affecter à unType
objet à l’aide de l’extension{x:Type someClass}
de balisage . - Vous pouvez définir des tableaux en XAML à l’aide de l’extension de
x:Array
balisage. Cette extension de balisage a un attribut obligatoire nomméType
qui indique le type des éléments dans le tableau. - L’extension de
Binding
balisage est abordée dans la partie 4. Principes de base de la liaison de données. - L’extension de
RelativeSource
balisage est abordée dans les liaisons relatives.
Extension de balisage ConstraintExpression
Les extensions de balisage peuvent avoir des propriétés, mais elles ne sont pas définies comme des attributs XML. Dans une extension de balisage, les paramètres de propriété sont séparés par des virgules et aucun guillemet n’apparaît dans les accolades.
Cela peut être illustré avec l’extension de Xamarin.Forms balisage nommée ConstraintExpression
, qui est utilisée avec la RelativeLayout
classe. Vous pouvez spécifier l’emplacement ou la taille d’une vue enfant en tant que constante, ou par rapport à un parent ou à une autre vue nommée. La syntaxe du fichier ConstraintExpression
vous permet de définir la position ou la taille d’une vue à l’aide d’une Factor
propriété d’une autre vue, plus un Constant
. Tout ce qui est plus complexe que cela nécessite du code.
Voici un exemple :
<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>
La leçon la plus importante que vous devez suivre à partir de cet exemple est la syntaxe de l’extension de balisage : aucun guillemet ne doit apparaître dans les accolades d’une extension de balisage. Lorsque vous tapez l’extension de balisage dans un fichier XAML, il est naturel de placer les valeurs des propriétés entre guillemets. Résistez à la tentation !
Voici le programme en cours d’exécution :
Résumé
Les extensions de balisage XAML indiquées ici fournissent une prise en charge importante des fichiers XAML. Mais peut-être l’extension de balisage XAML la plus précieuse est Binding
, qui est abordée dans la prochaine partie de cette série, partie 4. Principes de base de la liaison de données.