Share via


建立 XAML 標記延伸

在程式設計層級上,XAML 標記延伸是實作 或 IMarkupExtension<T> 介面的IMarkupExtension類別。 您可以在 GitHub 存放庫的 MarkupExtensions 目錄中探索標準標記延伸的Xamarin.Forms原始程式碼。

您也可以從 或 IMarkupExtension<T>衍生IMarkupExtension來定義自己的自訂 XAML 標記延伸。 如果標記延伸取得特定類型的值,請使用泛型形式。 這是有數個 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 的 Alpha 元件。 類別衍生自 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>必須包含兩ProvideValue個方法,一個傳回 Color ,另一個傳回 object,但第二個方法只能呼叫第一個IMarkupExtension方法。

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

請注意,當 是 XML 標記時 HslColorExtension ,四個屬性會設定為屬性,但在大括弧之間出現時,四個屬性會以逗號分隔,不含引號。 、 SL 預設H值為 0,預設值A為 1,因此如果您想要將這些屬性設定為預設值,則可以省略這些屬性。 最後一個範例顯示一個範例,其中亮度為0,這通常會產生黑色,但Alpha色板為0.5,因此它是半透明,而且在頁面的白色背景顯示灰色:

HSL 色彩示範

用於存取位圖的標記延伸

的 自變數 ProvideValue 是實作 介面的物件 IServiceProvider ,該介面定義於 .NET System 命名空間中。 這個介面有一個成員,一個以 Type 自變數命名GetService的方法。

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 變數,您必須知道特定內容中有哪些可用的服務類型。 瞭解這項功能的最佳方式是研究 GitHub 上存放庫之 MarkupExtensions 資料夾中Xamarin.Forms現有 XAML 標記延伸的原始程式碼。 請注意,某些類型的服務在 內部 Xamarin.Forms。

在某些 XAML 標記延伸中,這項服務可能很有用:

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

介面 IProvideValueTarget 會定義兩個屬性和 TargetObjectTargetProperty。 在類別中ImageResourceExtension取得這項資訊時,TargetObjectImageTargetProperty ,而且 是 BindableProperty 的屬性Image物件Source。 這是已設定 XAML 標記延伸的屬性。

具有 GetService 自變數的 typeof(IProvideValueTarget) 呼叫實際上會傳回 型 SimpleValueTargetProvider別 的物件,該物件定義於 命名空間中 Xamarin.Forms.Xaml.Internals 。 如果您將 的 GetService 傳回值轉換成該型別,您也可以存取 ParentObjects 屬性,這是包含 Image 元素、 Grid 父代和 ImageResourceDemoPage 父系的 Grid陣列。

結論

XAML 標記延伸透過擴充從各種來源設定屬性的能力,在 XAML 中扮演重要角色。 此外,如果現有的 XAML 標記延伸未完全提供您需要的內容,您也可以自行撰寫。