2017/3/4

Xamarin.Forms 教學系列文(七)XAML vs. code


學習目標
  • XAML 與 code 的差異
  • 屬性標籤(Property-element syntax)
  • OnPlatform in XAML 
  • 可省略的 Content property

XAML 是微軟拿來設計 Windows Phone 前端用,一種衍生自 XML 的標籤式語法

最早出現於一般桌面 windows 應用程式~ 感謝 布陋閣 指證。

標籤式語法的好處就是關注點分離 (Separation of concerns,SOC):

將邏輯從前端分離出來,可以讓前端設計師程式設計師專注於他們要做的事情上 (幫台灣的全端工程師QQ)。 

另一個好處是較好維護,畢竟 Visual Elements 是以巢狀的方式去堆疊,而標籤語法維護巢狀時是相對容易的。

不過,XAML 有許多無法辦到的事情,例如迴圈、流程控制…等邏輯控制。
另外,Visual Studio 還沒支援拖曳物件設計前端 (或許未來吧?),截至目前還是直接寫 code。


底下先來看 C# 寫成 XAML 的例子,

這是一個包含許多屬性的 Label:
new Label 
{     
    Text = "Hello from Code!",     
    IsVisible = true,     
    Opacity = 0.75,     
    HorizontalTextAlignment = TextAlignment.Center,     
    VerticalOptions = LayoutOptions.CenterAndExpand,     
    TextColor = Color.Blue, 
    BackgroundColor = Color.FromRgb(255, 128, 128),     
    FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),     
    FontAttributes = FontAttributes.Bold | FontAttributes.Italic 
};


當我們改用 XAML 會變成:
<Label Text="Hello from XAML!"        
       IsVisible="True"        
       Opacity="0.75"        
       HorizontalTextAlignment="Center"        
       VerticalOptions="CenterAndExpand"        
       TextColor="Blue"        
       BackgroundColor="#FF8080"        
       FontSize="Large"        
       FontAttributes="Bold,Italic" /> 

上面範例可以注意到,FontSize 在 XAML 裡只要設定屬性值為 "Large",而在 C# 要用 Device.GetNamedSize() 方法才能取得並設定字體大小。

XAML 讓程式碼看起來更簡潔了!



屬性標籤 (Property-element syntax)

C# - 將 Label 放在 Frame 內如下:
new Frame 
{     
    OutlineColor = Color.Accent,     
    HorizontalOptions = LayoutOptions.Center,     
    VerticalOptions = LayoutOptions.Center,     
    Content = new Label     
    {         
        Text = "Greetings, Xamarin.Forms!"     
    } 
}; 

改成 XAML:
<Frame OutlineColor="Accent" HorizontalOptions="Center" VerticalOptions="Center">     
    <Frame.Content>         
        <Label Text="Greetings, Xamarin.Forms!" />     
    </Frame.Content> 
</Frame>   
要注意的是 Frame 內使用了一個 Frame.Content 標籤,並把 Label 標籤包在 Frame.Content 內。

這種巢狀屬性的寫法都稱作 Property-element,所有的物件屬性都能寫成 property element。

例如:
<Frame HorizontalOptions="Center">     
    <Frame.VerticalOptions>         
        Center     
    </Frame.VerticalOptions>     
    <Frame.OutlineColor>         
        Accent     
    </Frame.OutlineColor>     
    <Frame.Content>         
        <Label>            
            <Label.Text>                 
            Greetings, Xamarin.Forms!             
            </Label.Text>         
         </Label>     
    </Frame.Content> 
</Frame> 

或想將 LayoutOptions 拉出來也可以:
<Frame>
    <Frame.HorizontalOptions>
        <LayoutOptions>
            <LayoutOptions.Alignment>
                Center
            </LayoutOptions.Alignment>
            <LayoutOptions.Expands>
                False
            </LayoutOptions.Expands>
        </LayoutOptions>
    </Frame.HorizontalOptions>
    …
</Frame>
當然我們沒必要這樣寫造成世界大亂...

但當你想將 View Element 放入另一個物件時就很好用,

例如底下將 BoxView 放入 StackLayout.Children 內:
<StackLayout>
    <StackLayout.Children>
        <StackLayout Orientation="Horizontal">
            <StackLayout.Children>
                <BoxView Color="Red" />
                <Label Text="Red" VerticalOptions="Center" />
            </StackLayout.Children>
        </StackLayout>
        
        <StackLayout Orientation="Horizontal">
            <StackLayout.Children>
                <BoxView Color="Green" />
                <Label Text="Green" VerticalOptions="Center" />
            </StackLayout.Children>
        </StackLayout>
        
        <StackLayout Orientation="Horizontal">
            <StackLayout.Children>
                <BoxView Color="Blue" />
                <Label Text="Blue" VerticalOptions="Center" />
            </StackLayout.Children>
        </StackLayout>
    </StackLayout.Children>
</StackLayout>



特定平台 (OnPlatform)

早在第二章處理 Padding 時,c# 就能針對不同平台來給予不同的值:
public partial class ScaryColorListPage : ContentPage 
{     
    public ScaryColorListPage()     
    {         
        Padding = Device.OnPlatform(new Thickness(0, 20, 0, 0),                                     
        new Thickness(0),                                     
        new Thickness(0)); 
        InitializeComponent();     
    } 
} 


這東西也能轉成 XAML 的寫法如下:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              
             x:Class="ScaryColorList.ScaryColorListPage">          
    <ContentPage.Padding>         
        <OnPlatform x:TypeArguments="Thickness"                     
                    iOS="0, 20, 0, 0" />     
        </ContentPage.Padding>          
    <ContentPage.Content>         
       …     
    </ContentPage.Content> 
</ContentPage>



可被省略的 Content property 

先看一下這支程式 (很醜):
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="ScaryColorList.ScaryColorListPage">
    <ContentPage.Content>
        <StackLayout>
            <StackLayout.Children>
                <Frame OutlineColor="Accent">
                    <Frame.Content>
                        <StackLayout Orientation="Horizontal">
                            <StackLayout.Children>
                                <BoxView Color="Red" />
                                <Label Text="Red" VerticalOptions="Center" />
                            </StackLayout.Children>
                        </StackLayout>
                    </Frame.Content>
                </Frame>
                
                <Frame OutlineColor="Accent">
                    <Frame.Content>
                        <StackLayout Orientation="Horizontal">
                            <StackLayout.Children>
                                <BoxView Color="Green" />
                                <Label Text="Green" VerticalOptions="Center" />
                            </StackLayout.Children>
                        </StackLayout>
                    </Frame.Content>
                </Frame>
                
                <Frame OutlineColor="Accent">
                    <Frame.Content>
                        <StackLayout Orientation="Horizontal">
                            <StackLayout.Children>
                                <BoxView Color="Blue" />
                                <Label Text="Blue" VerticalOptions="Center" />
                            </StackLayout.Children>
                        </StackLayout>
                    </Frame.Content>
                </Frame>
            </StackLayout.Children>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>


在 XAML 內其實可以把 ConetnPage.Content 或是 StackLayout.Children 或是 Frame.Content 這些標籤全部省略 (好看且更直覺):
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="ScaryColorList.ScaryColorListPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    <StackLayout>
        <Frame OutlineColor="Accent">
            <StackLayout Orientation="Horizontal">
                <BoxView Color="Red" />
                <Label Text="Red" VerticalOptions="Center" />
            </StackLayout>
        </Frame>
        
        <Frame OutlineColor="Accent">
            <StackLayout Orientation="Horizontal">
                <BoxView Color="Green" />
                <Label Text="Green" VerticalOptions="Center" />
            </StackLayout>
        </Frame>
        
        <Frame OutlineColor="Accent">
            <StackLayout Orientation="Horizontal">
                <BoxView Color="Blue" />
                <Label Text="Blue" VerticalOptions="Center" />
            </StackLayout>
        </Frame>
    </StackLayout>
</ContentPage>

這些屬性標籤通稱 Content property,在 XAML 內皆可省略

文字在 XAML 內的特性

若要在 Label 內放入文字,最簡單的做法就是放到 Text 屬性內:
<Label VerticalOptions="CenterAndExpand"  Text="Single lines of text are easy." /> 

也可以把 Text 屬性拆出來:
<Label VerticalOptions="CenterAndExpand">
    <Label.Text>
           Text can also be content of the Text property.
    </Label.Text>
</Label>

Label.Text 是 Content Property,因此可省略:
<Label VerticalOptions="CenterAndExpand">
    Text can also be content of the Text property.
</Label>


當我們放入多行文字時,會因為放置在屬性內或是內容內而有不同的特性,

屬性內:
<Label VerticalOptions="CenterAndExpand" 
       Text= "Perhaps the best way to define a paragraph of 
       uniformly formatted text is by setting the Text 
       property as an attribute and left justifying 
       the block of text in the XAML file. End-of-line 
       characters are converted to a space character." />

內容內:
<Label VerticalOptions="CenterAndExpand">
    Text as content has the curse 
    Of breaks at each line's close. 
    That's a format great for verse 
    But not the best for prose.
</Label>

結果:

可以發現,若是把文字放在 Label 的屬性內換行會被單格空白取代掉。

而若是把文字放在 Label 的內容內,會保留文字內的空白和換行


還記得 Label 內有個 Span 屬性,能在同一 Label 內給予不同段落文字的設定,當然也能寫成 XAML 如下:
<Label VerticalOptions="CenterAndExpand">                 
    <Label.FormattedText>                     
        <FormattedString>                         
            <Span Text="A single line with " />                         
            <Span Text="bold" FontAttributes="Bold" />                         
            <Span Text=" and " />                          
            <Span Text="italic" FontAttributes="Italic" />                         
            <Span Text=" and " />                         
            <Span Text="large" FontSize="Large" />                         
            <Span Text=" text." />                    
        </FormattedString>                 
    </Label.FormattedText>             
</Label> 



6 則留言:

  1. 看到這句真是 中肯(幫台灣的全端工程師QQ) QQ

    回覆刪除
  2. XAML 最早出現時是微軟拿來設計 Windows Phone 前端用,一種衍生自 XML 的標籤式語法。

    XAML 在古早弄一般桌面 windows 應用程式就在用了,那時候連網站開發的 "前端" "後端" 名詞都還沒出現
    https://msdn.microsoft.com/zh-tw/library/mt149842(v=vs.110).aspx

    回覆刪除
    回覆
    1. 感謝指證和補充喔~ 修正一下內容~

      刪除
  3. 罗根你好,我想请问一下有关文字上下间距的问题。一段文字上下的间距总是显示的特别拥挤,我想调整上下间距使其显得更加美观,请问该怎么处理?谢谢。

    回覆刪除
    回覆
    1. http://www.loganedge.tw/2017/08/xamarinforms-linespacinglabel-label.html
      直接幫你做了篇教學喔!
      試試看,有問題再來一起討論!

      刪除