Condividi tramite


Creazione di estensioni di markup XAML

A livello di codice, un'estensione di markup XAML è una classe che implementa l'interfaccia IMarkupExtension o IMarkupExtension<T> . È possibile esplorare il codice sorgente delle estensioni di markup standard descritte di seguito nella directory MarkupExtensions del Xamarin.Forms repository GitHub.

È anche possibile definire estensioni di markup XAML personalizzate derivando da IMarkupExtension o IMarkupExtension<T>. Utilizzare il formato generico se l'estensione di markup ottiene un valore di un particolare tipo. Questo è il caso di diverse estensioni di Xamarin.Forms markup:

  • TypeExtension deriva da IMarkupExtension<Type>
  • ArrayExtension deriva da IMarkupExtension<Array>
  • DynamicResourceExtension deriva da IMarkupExtension<DynamicResource>
  • BindingExtension deriva da IMarkupExtension<BindingBase>
  • ConstraintExpression deriva da IMarkupExtension<Constraint>

Le due IMarkupExtension interfacce definiscono un solo metodo, denominato ProvideValue:

public interface IMarkupExtension
{
    object ProvideValue(IServiceProvider serviceProvider);
}

public interface IMarkupExtension<out T> : IMarkupExtension
{
    new T ProvideValue(IServiceProvider serviceProvider);
}

Poiché IMarkupExtension<T> deriva da IMarkupExtension e include la new parola chiave in ProvideValue, contiene entrambi i ProvideValue metodi.

Molto spesso, le estensioni di markup XAML definiscono proprietà che contribuiscono al valore restituito. L'eccezione ovvia è NullExtension, in cui ProvideValue restituisce nullsemplicemente . Il ProvideValue metodo ha un singolo argomento di tipo IServiceProvider che verrà illustrato più avanti in questo articolo.

Estensione di markup per specificare il colore

L'estensione di markup XAML seguente consente di costruire un Color valore usando componenti hue, saturazione e luminosità. Definisce quattro proprietà per i quattro componenti del colore, incluso un componente alfa inizializzato su 1. La classe deriva da IMarkupExtension<Color> per indicare un Color valore restituito:

public class HslColorExtension : IMarkupExtension<Color>
{
    public double H { set; get; }

    public double S { set; get; }

    public double L { set; get; }

    public double A { set; get; } = 1.0;

    public Color ProvideValue(IServiceProvider serviceProvider)
    {
        return Color.FromHsla(H, S, L, A);
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
    {
        return (this as IMarkupExtension<Color>).ProvideValue(serviceProvider);
    }
}

Poiché IMarkupExtension<T> deriva da IMarkupExtension, la classe deve contenere due ProvideValue metodi, uno che restituisce e un altro che restituisce Color object, ma il secondo metodo può semplicemente chiamare il primo metodo.

La pagina HSL Color Demo mostra diversi modi in cui HslColorExtension è possibile visualizzare in un file XAML per specificare il colore di un oggetto BoxView:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MarkupExtensions"
             x:Class="MarkupExtensions.HslColorDemoPage"
             Title="HSL Color Demo">

    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="BoxView">
                <Setter Property="WidthRequest" Value="80" />
                <Setter Property="HeightRequest" Value="80" />
                <Setter Property="HorizontalOptions" Value="Center" />
                <Setter Property="VerticalOptions" Value="CenterAndExpand" />
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>
        <BoxView>
            <BoxView.Color>
                <local:HslColorExtension H="0" S="1" L="0.5" A="1" />
            </BoxView.Color>
        </BoxView>

        <BoxView>
            <BoxView.Color>
                <local:HslColor H="0.33" S="1" L="0.5" />
            </BoxView.Color>
        </BoxView>

        <BoxView Color="{local:HslColorExtension H=0.67, S=1, L=0.5}" />

        <BoxView Color="{local:HslColor H=0, S=0, L=0.5}" />

        <BoxView Color="{local:HslColor A=0.5}" />
    </StackLayout>
</ContentPage>

Si noti che quando HslColorExtension è un tag XML, le quattro proprietà vengono impostate come attributi, ma quando viene visualizzata tra parentesi graffe, le quattro proprietà sono separate da virgole senza virgolette. I valori predefiniti per H, Se L sono 0 e il valore predefinito di A è 1, pertanto tali proprietà possono essere omesse se si desidera che siano impostate su valori predefiniti. L'ultimo esempio mostra un esempio in cui la luminosità è 0, che normalmente restituisce nero, ma il canale alfa è 0,5, quindi è metà trasparente e appare grigio sullo sfondo bianco della pagina:

Demo dei colori HSL

Estensione di markup per l'accesso alle bitmap

L'argomento di ProvideValue è un oggetto che implementa l'interfaccia IServiceProvider , definita nello spazio dei nomi .NET System . Questa interfaccia ha un membro, un metodo denominato GetService con un Type argomento.

La ImageResourceExtension classe illustrata di seguito mostra un possibile utilizzo di e GetService per ottenere un IXmlLineInfoProvider oggetto in grado di IServiceProvider fornire informazioni sulla riga e sul carattere che indicano dove è stato rilevato un errore specifico. In questo caso, viene generata un'eccezione quando la Source proprietà non è stata impostata:

[ContentProperty("Source")]
class ImageResourceExtension : IMarkupExtension<ImageSource>
{
    public string Source { set; get; }

    public ImageSource ProvideValue(IServiceProvider serviceProvider)
    {
        if (String.IsNullOrEmpty(Source))
        {
            IXmlLineInfoProvider lineInfoProvider = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider;
            IXmlLineInfo lineInfo = (lineInfoProvider != null) ? lineInfoProvider.XmlLineInfo : new XmlLineInfo();
            throw new XamlParseException("ImageResourceExtension requires Source property to be set", lineInfo);
        }

        string assemblyName = GetType().GetTypeInfo().Assembly.GetName().Name;
        return ImageSource.FromResource(assemblyName + "." + Source, typeof(ImageResourceExtension).GetTypeInfo().Assembly);
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
    {
        return (this as IMarkupExtension<ImageSource>).ProvideValue(serviceProvider);
    }
}

ImageResourceExtension è utile quando un file XAML deve accedere a un file di immagine archiviato come risorsa incorporata nel progetto di libreria .NET Standard. Usa la Source proprietà per chiamare il metodo statico ImageSource.FromResource . Questo metodo richiede un nome di risorsa completo, costituito dal nome dell'assembly, dal nome della cartella e dal nome file separato da punti. Il secondo argomento del ImageSource.FromResource metodo fornisce il nome dell'assembly ed è necessario solo per le build di versione in UWP. Indipendentemente dal fatto che ImageSource.FromResource sia necessario chiamare dall'assembly che contiene la bitmap, il che significa che questa estensione della risorsa XAML non può far parte di una libreria esterna, a meno che le immagini non si trovino anche in tale libreria. (Vedere il Articolo Immagini incorporate per altre informazioni sull'accesso alle bitmap archiviate come risorse incorporate.

Anche se ImageResourceExtension richiede che la Source proprietà sia impostata, la Source proprietà viene indicata in un attributo come proprietà content della classe . Ciò significa che la Source= parte dell'espressione tra parentesi graffe può essere omessa. Nella pagina Demo risorsa immagine gli Image elementi recuperano due immagini usando il nome della cartella e il nome file separato da punti:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MarkupExtensions"
             x:Class="MarkupExtensions.ImageResourceDemoPage"
             Title="Image Resource Demo">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Image Source="{local:ImageResource Images.SeatedMonkey.jpg}"
               Grid.Row="0" />

        <Image Source="{local:ImageResource Images.FacePalm.jpg}"
               Grid.Row="1" />

    </Grid>
</ContentPage>

Ecco il programma in esecuzione:

Demo delle risorse immagine

Provider di servizi

Usando l'argomento IServiceProvider su ProvideValue, le estensioni di markup XAML possono ottenere l'accesso a informazioni utili sul file XAML in cui vengono usate. Tuttavia, per usare correttamente l'argomento IServiceProvider , è necessario conoscere il tipo di servizi disponibili in particolari contesti. Il modo migliore per comprendere questa funzionalità consiste nello studiare il codice sorgente delle estensioni di markup XAML esistenti nella cartella MarkupExtensions nel Xamarin.Forms repository su GitHub. Tenere presente che alcuni tipi di servizi sono interni a Xamarin.Forms.

In alcune estensioni di markup XAML questo servizio potrebbe essere utile:

 IProvideValueTarget provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;

L'interfaccia IProvideValueTarget definisce due proprietà e TargetObject TargetProperty. Quando queste informazioni vengono ottenute nella ImageResourceExtension classe , TargetObject è e TargetProperty è Image un BindableProperty oggetto per la Source proprietà di Image. Questa è la proprietà in cui è stata impostata l'estensione di markup XAML.

La GetService chiamata con un argomento di typeof(IProvideValueTarget) restituisce effettivamente un oggetto di tipo SimpleValueTargetProvider, definito nello spazio dei Xamarin.Forms.Xaml.Internals nomi . Se si esegue il cast del valore restituito di GetService a tale tipo, è anche possibile accedere a una proprietà, ovvero una ParentObjects matrice che contiene l'elemento Image , l'elemento Grid padre e l'elemento ImageResourceDemoPage padre dell'oggetto Grid.

Conclusione

Le estensioni di markup XAML svolgono un ruolo fondamentale in XAML estendendo la possibilità di impostare attributi da un'ampia gamma di origini. Inoltre, se le estensioni di markup XAML esistenti non forniscono esattamente ciò di cui hai bisogno, puoi anche scrivere il tuo.