Создание расширений разметки XAML

На программном уровне расширение разметки XAML — это класс, реализующий IMarkupExtension или IMarkupExtension<T> интерфейс. Исходный код стандартных расширений разметки, описанных ниже в каталогеXamarin.Forms MarkupExtensions репозитория GitHub.

Кроме того, можно определить собственные расширения разметки XAML, производные от IMarkupExtension или IMarkupExtension<T>. Используйте универсальную форму, если расширение разметки получает значение определенного типа. Это касается нескольких Xamarin.Forms расширений разметки:

  • TypeExtension происходит от IMarkupExtension<Type>.
  • ArrayExtension происходит от IMarkupExtension<Array>.
  • DynamicResourceExtension происходит от IMarkupExtension<DynamicResource>.
  • BindingExtension происходит от IMarkupExtension<BindingBase>.
  • ConstraintExpression происходит от IMarkupExtension<Constraint>.

Два IMarkupExtension интерфейса определяют только один метод с именем ProvideValue:

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

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

Так как IMarkupExtension<T> он является производным от IMarkupExtension и включает new ключевое слово, ProvideValueон содержит оба ProvideValue метода.

Очень часто расширения разметки XAML определяют свойства, которые вносят вклад в возвращаемое значение. (Очевидное исключение — это NullExtension, в котором ProvideValue просто возвращается null.) Метод ProvideValue имеет один аргумент типа IServiceProvider , который будет обсуждаться далее в этой статье.

Расширение разметки для указания цвета

Следующее расширение разметки XAML позволяет создавать Color значение с помощью компонентов оттенка, насыщенности и яркости. Он определяет четыре свойства для четырех компонентов цвета, включая альфа-компонент, инициализированный до 1. Класс является производным от IMarkupExtension<Color> указания возвращаемого 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);
    }
}

Так как IMarkupExtension<T> производный от IMarkupExtensionкласса должен содержаться два ProvideValue метода, одно Color возвращает и другое, которое возвращает object, но второй метод может просто вызвать первый метод.

На странице демонстрации цвета HSL показаны различные способы, которые могут отображаться в XAML-файле, чтобы HslColorExtension указать цвет для 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>

Обратите внимание, что при HslColorExtension использовании XML-тега четыре свойства задаются как атрибуты, но при отображении между фигурными скобками четыре свойства разделяются запятыми без кавычки. Значения по умолчанию для H, Sи L равны 0, а значение A по умолчанию равно 1, поэтому эти свойства могут быть опущены, если они задают значения по умолчанию. В последнем примере показан пример, в котором светимость составляет 0, что обычно приводит к черному цвету, но альфа-канал равен 0,5, поэтому он является полупрозрачным и отображается серым на белом фоне страницы:

Демонстрация цвета HSL

Расширение разметки для доступа к растровым картам

Аргументом ProvideValue является объект, реализующий IServiceProvider интерфейс, который определен в пространстве имен .NET System . Этот интерфейс содержит один член, метод с именем GetService аргумента Type .

В ImageResourceExtension приведенном ниже классе показано одно возможное использование IServiceProvider и GetService получение IXmlLineInfoProvider объекта, который может предоставлять сведения о строке и символах, указывающих, где обнаружена определенная ошибка. В этом случае возникает исключение, если 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 полезно, если ФАЙЛ XAML должен получить доступ к файлу изображения, хранящейся в качестве внедренного ресурса в проекте библиотеки .NET Standard. Он использует Source свойство для вызова статического ImageSource.FromResource метода. Для этого метода требуется полное имя ресурса, состоящее из имени сборки, имени папки и имени файла, разделенного точками. Второй аргумент метода ImageSource.FromResource предоставляет имя сборки и требуется только для сборки выпуска в UWP. Независимо от того, следует вызывать из сборки, ImageSource.FromResource содержащей растровое изображение, что означает, что это расширение ресурса XAML не может быть частью внешней библиотеки, если изображения также не находятся в этой библиотеке. (См. раздел Дополнительные сведения о доступе к растровым изображениям, хранящимся в виде внедренных ресурсов.

Хотя ImageResourceExtension требуется Source задать свойство, Source свойство указывается в атрибуте в качестве свойства содержимого класса. Это означает, что Source= часть выражения в фигурных скобках может быть опущена. На странице "Демонстрация ресурсов изображения" элементы извлекает два изображения с помощью имени папки и имени файла, Image разделенного точками:

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

Вот работающая программа:

Демонстрация ресурсов образа

Поставщики услуг

С помощью аргумента IServiceProviderProvideValueрасширения разметки XAML могут получить доступ к полезным сведениям о XAML-файле, в котором они используются. Но для успешного IServiceProvider использования аргумента необходимо знать, какие службы доступны в определенных контекстах. Лучший способ получить представление об этой функции заключается в изучении исходного кода существующих расширений разметки XAML в папке MarkupExtensions в Xamarin.Formsрепозитории на GitHub. Помните, что некоторые типы служб являются внутренними Xamarin.Forms.

В некоторых расширениях разметки XAML эта служба может оказаться полезной:

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

Интерфейс IProvideValueTarget определяет два свойства и TargetObjectTargetProperty. При получении этих сведений ImageResourceExtension в классе является ImageBindablePropertyTargetProperty объектом для Source свойства . TargetObjectImage Это свойство, в котором задано расширение разметки XAML.

Вызов GetService с аргументом typeof(IProvideValueTarget) фактически возвращает объект типа SimpleValueTargetProvider, который определяется в Xamarin.Forms.Xaml.Internals пространстве имен. При приведение возвращаемого значения GetService этого типа также можно получить к ParentObjects свойству, который является массивом, содержащим Image элемент, Grid родительский и ImageResourceDemoPage родительский элемент Grid.

Заключение

Расширения разметки XAML играют важную роль в XAML, расширяя возможность задавать атрибуты из различных источников. Кроме того, если существующие расширения разметки XAML не предоставляют точно необходимые компоненты, вы также можете написать собственные.