2017/3/17

Xamarin.Forms 教學系列文(十.壹)擴充標籤 - 靜態 & 資源檔使用


學習目標
  • x:Static - XAML 類 C# 再來一發
  • Resource - StaticResource - 可重複利用的擴充標籤

Xamarin 一直積極的要讓 XAML 做一些 C# 能做的事情,例如這一章節要講的擴充標籤  (markup extensions)。

在 C# 內要指派給 triangle.Angle1 值的話可以如下:
triangle.Angle1 = 45; 
triangle.Angle1 = 180 * radians / Math.PI; 
triangle.Angle1 = angles[i]; 
triangle.Angle1 = animator.GetCurrentAngle();

只要 結果回傳 是浮點數 (double) 就好。

但你沒辦法在 XAML 內寫上這種鬼東西的:

<triangle.Angle1>
    180 * radians / Math.PI
</triangle.Angle1>
讓哥懷念起 MVC 的 Razor 真是方便...
 鑒於以上原因,Xamarin 在 XAML 2009 規格內定義了 擴充標籤 (markup extensions),讓我們在編寫 XAML 時能有更多的彈性。


擴充標籤有以下三大類型:

1. 前面帶 x : 的擴充標籤:
  • x : Static
  • x : Reference
  • x : Type
  • x : Null
  • x : Array

2. 跟資料面有關係的:
  • StaticResource
  • DynamicResource
  • Binding

3. 或是給 RelativeLayout 用的:
  • ConstrainExpression

從第一個 x:Static 來介紹 - 

x:Static

先來看一下這 Label:
<Label Text="Just some text" 
       BackgroundColor="Accent" 
       TextColor="Black" 
       FontAttributes="Italic" 
       VerticalOptions="Center" 
       HorizontalTextAlignment="Center" />

Label 的五個屬性值都是 字串
其實執行時,這些字串都是先透過 C # 的 TryParse (轉換) 後才給予他們真正的意義。

可以使用 x:Static 改寫成:
<Label Text="Just some text" 
       BackgroundColor="{x:Static Color.Accent}" 
       TextColor="{x:Static Color.Black}" 
       FontAttributes="{x:Static FontAttributes.Italic}"
       VerticalOptions="{x:Static LayoutOptions.Center}" 
       HorizontalTextAlignment="{x:Static TextAlignment.Center}" />

這兩個比較下來很清楚的知道 x:Static 的功能,就是在 XAML 內使用 C# 的變數。

但以上的改寫其實沒啥意義...

介紹有用一點的程式,我們可以先在 C# 定義好變數和其值:
namespace SharedStatics
{
    static class AppConstants
    {
        public static Color LightBackground = Color.Yellow;
        public static Color DarkForeground = Color.Blue;
        public static double NormalFontSize = 18;
        public static double TitleFontSize = 1.4 * NormalFontSize;
        public static double ParagraphSpacing = 10;
        public const FontAttributes Emphasis = FontAttributes.Italic;
        public const FontAttributes TitleAttribute = FontAttributes.Bold;
        public const TextAlignment TitleAlignment = TextAlignment.Center;
    }
}

接著在 XAML 內使用其變數:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:local="clr-namespace:SharedStatics" 
             x:Class="SharedStatics.SharedStaticsPage" 
             BackgroundColor="{x:Static local:AppConstants.LightBackground}">

    <StackLayout Padding="10, 0" 
                 Spacing="{x:Static local:AppConstants.ParagraphSpacing}">

要注意的是在 ContentPage 內有先引用 xmlns:local="clr-namespace:SharedStatics"


另外,因為 Math Enviroment 在 .NET System namespace 內有定義,可以在引用 namespace 後使用其靜態變數如 Math.PI
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:sys="clr-namespace:System;assembly=mscorlib" 
             x:Class="SystemStatics.SystemStaticsPage">

         <!--Math.PI-->
        <Button Text=" Button with &#x03C0; border width " 
                BorderWidth="{x:Static sys:Math.PI}" 
                HorizontalOptions="Center" 
                VerticalOptions="CenterAndExpand">
            
            <Button.BackgroundColor>
                <OnPlatform x:TypeArguments="Color" Android="#404040" />
            </Button.BackgroundColor>
            
            <Button.BorderColor>
                <OnPlatform x:TypeArguments="Color" Android="White" WinPhone="Black" />
            </Button.BorderColor>
            
        </Button>

        <Label VerticalOptions="CenterAndExpand"
               HorizontalTextAlignment="Center"
               FontSize="Medium">

            <!--Environment.NewLine-->
            <Label.FormattedText>
                <FormattedString>
                    <Span Text="Three lines of text" />
                    <Span Text="{x:Static sys:Environment.NewLine}" />
                    <Span Text="separated by" />
                    <Span Text="{x:Static sys:Environment.NewLine}" />
                    <Span Text="Environment.NewLine" FontSize="Medium" FontAttributes="Italic" />
                    <Span Text=" strings" />
                </FormattedString>
            </Label.FormattedText>
            
        </Label>
    </StackLayout>
</ContentPage>

執行結果:


Resource dictionaries

上面看到 C# 內建立全域變數,並在 XAML 內使用其值的範例。

Xamarin 也想將這件事情搬來 XAML 做 ... 所以提供了 <ResourceDictionary> 這樣的擴充標籤,讓我們在 XAML 內建立共用的變數資源

使用 Resource dictionaries 時又分成 StaticResource 和 DynamicResource,差在一個讀取靜態XAML dictionaries,後一個由 .cs 指派 dictionaries 時使用。

StaticResource

假設你要在 StackLayout 內建立三個相似的 Button,你可能會這麼寫:
    <StackLayout>

        <!--First Button-->
        <Button Text=" Carpe diem " 
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand" 
                BorderWidth="3" 
                TextColor="Red"
                FontSize="Large">
            
            <Button.BackgroundColor>
                <OnPlatform x:TypeArguments="Color" Android="#404040" />
            </Button.BackgroundColor>
            
            <Button.BorderColor>
                <OnPlatform x:TypeArguments="Color" Android="White" WinPhone="Black" />
            </Button.BorderColor>
        </Button>

        <!--Second Button-->
        <Button Text=" Sapere aude "
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand" 
                BorderWidth="3" 
                TextColor="Red" 
                FontSize="Large">
            
            <Button.BackgroundColor>
                <OnPlatform x:TypeArguments="Color" Android="#404040" />
            </Button.BackgroundColor>
            
            <Button.BorderColor>
                <OnPlatform x:TypeArguments="Color" Android="White" WinPhone="Black" />
            </Button.BorderColor>
        </Button>

        <!--Third Button-->
        <Button Text=" Discere faciendo " 
                HorizontalOptions="Center" 
                VerticalOptions="CenterAndExpand" 
                BorderWidth="3"
                TextColor="Red" 
                FontSize="Large">
            
            <Button.BackgroundColor>
                <OnPlatform x:TypeArguments="Color" Android="#404040" />
            </Button.BackgroundColor>
            
            <Button.BorderColor>
                <OnPlatform x:TypeArguments="Color" Android="White" WinPhone="Black" />
            </Button.BorderColor>
        </Button>
    </StackLayout>

很多屬性的值是 重複 的,執行結果如下:


此時我們就能在 (Visual Elements).Resource 內撰寫 <ResourceDictionoary> 標籤,來製作 可重複利用 的資源檔:
    <ContentPage.Resources>
        <ResourceDictionary>
            <LayoutOptions x:Key="horzOptions">Center</LayoutOptions>
            <LayoutOptions x:Key="vertOptions" Alignment="Center" Expands="True" />
            <x:Double x:Key="borderWidth">3</x:Double>
            <Color x:Key="textColor">Red</Color>
            <OnPlatform x:Key="backgroundColor" x:TypeArguments="Color" Android="#404040" />
            <OnPlatform x:Key="borderColor" x:TypeArguments="Color" Android="White" WinPhone="Black" />
            <x:String x:Key="fontSize">Large</x:String>
        </ResourceDictionary>
    </ContentPage.Resources>
要注意 ResourceDictionary 內每個資源都有 x:Key 這屬性,且同一層級下的 x:Key 不能重複

接著使用 StaticResource 改寫屬性
    <Button Text=" Discere faciendo " 
            HorizontalOptions="{StaticResource horzOptions}" 
            VerticalOptions="{StaticResource vertOptions}" 
            BorderWidth="{StaticResource borderWidth}" 
            TextColor="{StaticResource textColor}"
            BackgroundColor="{StaticResource backgroundColor}"
            BorderColor="{StaticResource borderColor}" 
            FontSize="{StaticResource fontSize}" />

這樣三顆 Button 重複的屬性值,我們只要在 ResourceDictionary 寫一次就能使用,當然也便於維護。

Resource 可以儲存各種物件,像是把一顆 Button 放進去:
    <ContentPage.Resources>
        <ResourceDictionary>
            <Button x:Key="button" 
                Text="Shared Button?" 
                HorizontalOptions="Center" 
                VerticalOptions="CenterAndExpand"
                FontSize="Large" />
        </ResourceDictionary>
    </ContentPage.Resources>

但是!!! 同一顆按鈕是不能出現兩次的… 所以以下寫法會出錯:
    <StackLayout>
        <StaticResourceExtension Key="button" />
        <StaticResourceExtension Key="button" />
    </StackLayout>

關於這種多個元件需要同樣的 屬性 時,會在 12章 Style 有說明。




沒有留言:

張貼留言