Udostępnij za pośrednictwem


Tworzenie rozszerzeń struktury znaczników XAML

Na poziomie programowym rozszerzenie znaczników XAML to klasa, która implementuje IMarkupExtension interfejs lub IMarkupExtension<T> . Kod źródłowy standardowych rozszerzeń znaczników opisanych poniżej można eksplorować w katalogu Xamarin.Forms MarkupExtensions repozytorium GitHub.

Istnieje również możliwość zdefiniowania własnych niestandardowych rozszerzeń znaczników XAML przez wyprowadzenie z lub IMarkupExtension IMarkupExtension<T>. Użyj formularza ogólnego, jeśli rozszerzenie znaczników uzyskuje wartość określonego typu. Jest to przypadek z kilkoma Xamarin.Forms rozszerzeniami znaczników:

  • TypeExtension pochodzi z IMarkupExtension<Type>
  • ArrayExtension pochodzi z IMarkupExtension<Array>
  • DynamicResourceExtension pochodzi z IMarkupExtension<DynamicResource>
  • BindingExtension pochodzi z IMarkupExtension<BindingBase>
  • ConstraintExpression pochodzi z IMarkupExtension<Constraint>

IMarkupExtension Dwa interfejsy definiują tylko jedną metodę o nazwie ProvideValue:

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

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

Ponieważ IMarkupExtension<T> pochodzi z IMarkupExtension i zawiera new słowo kluczowe w , ProvideValuezawiera obie ProvideValue metody.

Bardzo często rozszerzenia znaczników XAML definiują właściwości, które przyczyniają się do zwracanej wartości. (Oczywistym wyjątkiem jest NullExtension, w którym ProvideValue po prostu zwraca wartość null. Metoda ProvideValue ma jeden argument typu IServiceProvider , który zostanie omówiony w dalszej części tego artykułu.

Rozszerzenie znaczników do określania koloru

Następujące rozszerzenie znaczników XAML umożliwia konstruowanie Color wartości przy użyciu składników odcieni, nasycenia i jasności. Definiuje cztery właściwości dla czterech składników koloru, w tym składnik alfa zainicjowany do 1. Klasa pochodzi z IMarkupExtension<Color> , aby wskazać wartość zwracaną Color :

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);
    }
}

Ponieważ IMarkupExtension<T> pochodzi z IMarkupExtensionklasy , klasa musi zawierać dwie ProvideValue metody, jeden, który zwraca i drugi, który zwraca Color objectwartość , ale druga metoda może po prostu wywołać pierwszą metodę.

Strona pokazu koloru HSL przedstawia różne sposoby wyświetlania HslColorExtension w pliku XAML w celu określenia koloru elementu 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>

Zwróć uwagę, że jeśli HslColorExtension jest tagiem XML, cztery właściwości są ustawiane jako atrybuty, ale gdy pojawia się między nawiasami klamrowymi, cztery właściwości są oddzielone przecinkami bez znaków cudzysłowu. Wartości domyślne dla Hwartości , Si L to 0, a wartość A domyślna to 1, więc te właściwości można pominąć, jeśli chcesz, aby zostały ustawione na wartości domyślne. W ostatnim przykładzie pokazano przykład, w którym jasność wynosi 0, co zwykle powoduje kolor, ale kanał alfa wynosi 0,5, więc jest pół przezroczysty i jest szary na białym tle strony:

Pokaz kolorów HSL

Rozszerzenie znaczników na potrzeby uzyskiwania dostępu do map bitowych

Argument ProvideValue to obiekt, który implementuje IServiceProvider interfejs zdefiniowany w przestrzeni nazw platformy .NET System . Ten interfejs ma jeden element członkowski, metodę o nazwie GetService z argumentem Type .

W ImageResourceExtension poniższej IServiceProvider klasie przedstawiono jedno możliwe użycie obiektu i GetService uzyskanie IXmlLineInfoProvider obiektu, który może dostarczyć informacje o wierszach i znakach wskazujące miejsce wykrycia określonego błędu. W takim przypadku zgłaszany jest wyjątek, gdy właściwość nie została ustawiona Source :

[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 Jest pomocny, gdy plik XAML musi uzyskać dostęp do pliku obrazu przechowywanego jako zasób osadzony w projekcie biblioteki .NET Standard. Używa Source właściwości do wywoływania metody statycznej ImageSource.FromResource . Ta metoda wymaga w pełni kwalifikowanej nazwy zasobu, która składa się z nazwy zestawu, nazwy folderu i nazwy pliku oddzielonego kropkami. Drugi argument metody ImageSource.FromResource zawiera nazwę zestawu i jest wymagany tylko dla kompilacji wydania na platformie UWP. Niezależnie od tego, należy wywołać z zestawu zawierającego mapę bitową, co oznacza, ImageSource.FromResource że to rozszerzenie zasobu XAML nie może być częścią biblioteki zewnętrznej, chyba że obrazy znajdują się również w tej bibliotece. (Zobacz Artykuł Embedded Images (Obrazy osadzone) zawiera więcej informacji na temat uzyskiwania dostępu do map bitowych przechowywanych jako zasoby osadzone).

Mimo że ImageResourceExtension właściwość wymaga ustawienia właściwości, Source właściwość jest wskazywana w atrybucie Source jako właściwość content klasy. Oznacza to, że Source= część wyrażenia w nawiasach klamrowych może zostać pominięta. Na stronie Image Pokaz zasobu obrazu elementy pobierają dwa obrazy przy użyciu nazwy folderu i nazwy pliku rozdzielone kropkami:

<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>

Oto uruchomiony program:

Pokaz zasobu obrazu

Dostawcy usług

Używając argumentu IServiceProvider do ProvideValue, rozszerzenia znaczników XAML mogą uzyskać dostęp do przydatnych informacji o pliku XAML, w którym są używane. Jednak aby pomyślnie użyć argumentu IServiceProvider , musisz wiedzieć, jakiego rodzaju usługi są dostępne w określonych kontekstach. Najlepszym sposobem poznania tej funkcji jest zbadanie kodu źródłowego istniejących rozszerzeń znaczników XAML w folderze MarkupExtensions w repozytorium w Xamarin.Forms usłudze GitHub. Należy pamiętać, że niektóre typy usług są wewnętrzne w usłudze Xamarin.Forms.

W niektórych rozszerzeniach znaczników XAML ta usługa może być przydatna:

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

Interfejs IProvideValueTarget definiuje dwie właściwości i TargetObject TargetProperty. Gdy te informacje są uzyskiwane w ImageResourceExtension klasie, TargetObject jest Image elementem i TargetProperty jest obiektem BindableProperty właściwości Source Image. Jest to właściwość, na której ustawiono rozszerzenie znaczników XAML.

Wywołanie GetService z argumentem typeof(IProvideValueTarget) faktycznie zwraca obiekt typu SimpleValueTargetProvider, który jest zdefiniowany w Xamarin.Forms.Xaml.Internals przestrzeni nazw. Jeśli rzutujesz wartość zwracaną GetService dla tego typu, możesz również uzyskać dostęp do właściwości, która jest tablicą ParentObjects zawierającą Image element, Grid element nadrzędny i ImageResourceDemoPage element nadrzędny obiektu Grid.

Podsumowanie

Rozszerzenia znaczników XAML odgrywają istotną rolę w języku XAML, rozszerzając możliwość ustawiania atrybutów z różnych źródeł. Ponadto jeśli istniejące rozszerzenia znaczników XAML nie udostępniają dokładnie potrzebnych elementów, możesz również napisać własne.