2017/5/16

Xamarin.Forms 教學系列文(十五.貳)互動元件 -- Switch & CheckBox




學習目標
  • Switch
  • CheckBox - Custom View

Switch 是 Xamarin.Forms 內作為開關,回傳布林值 (true | false) 的元件。

要注意的是 Xamarin.Forms 沒有提供 CheckBox,需自行用 ContentView 製作並編寫屬性

本小節最後會附上 範例程式 並讓大家更熟悉 自訂元件 的寫法。


Swtich

Switch 只有定義一個屬性 - IsToggled,值為布林,當值改變時會去觸發 Toggled 事件。

原文書有提到一個 Xamarin.Forms 的特性,

若設定 Switch 的 x:Name 為 switch,
但由於 switch 是 C# 的關鍵字,在 C# 挑選時會很聰明的自動轉成 @switch


這範例放兩個 Switch 去控制下方 Label 的斜體與粗體:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="SwitchDemo.SwitchDemoPage">
    
    <StackLayout Padding="10, 0">
        <StackLayout HorizontalOptions="Center"
                     VerticalOptions="CenterAndExpand">
            
            <!--First Switch,斜體開關-->
            <StackLayout Orientation="Horizontal"
                         HorizontalOptions="End">
                <Label Text="Italic: "
                       VerticalOptions="Center" />
                <Switch Toggled="OnItalicSwitchToggled"
                        VerticalOptions="Center" />
            </StackLayout>

            <!--Second Switch,粗體開關-->
            <StackLayout Orientation="Horizontal"
                         HorizontalOptions="End">
                <Label Text="Boldface: "
                       VerticalOptions="Center" />
                <Switch Toggled="OnBoldSwitchToggled"
                        VerticalOptions="Center" />
            </StackLayout>
            
        </StackLayout>
        
        <Label x:Name="label"
               Text="Just a little passage of some sample text that can be formatted
                        in italic or boldface by toggling the two Switch elements."
               FontSize="Large"
               HorizontalTextAlignment="Center"
               VerticalOptions="CenterAndExpand" />

    </StackLayout>
</ContentPage>

當 Toggled 事件觸發時,可藉由 ToogleEventArgs 取得其
    public partial class SwitchDemoPage : ContentPage
    {
        public SwitchDemoPage()
        {
            InitializeComponent();
        }

        void OnItalicSwitchToggled(object sender, ToggledEventArgs args)
        {
            if (args.Value)
            {
                label.FontAttributes |= FontAttributes.Italic;
            }
            else
            {
                label.FontAttributes &= ~FontAttributes.Italic;
            }
        }

        void OnBoldSwitchToggled(object sender, ToggledEventArgs args)
        {
            if (args.Value)
            {
                label.FontAttributes |= FontAttributes.Bold;
            }
            else
            {
                label.FontAttributes &= ~FontAttributes.Bold;
            }
        }
    }


執行結果:


本範例要注意的是排版的問題,通常在放置時會將上下的 Switch 做垂直對齊,視覺上會較好。

程式的部分,先將一對 Label 和 Switch 放入 StackLayout 內,並將 StackLayout 的 HorizontalOptions 設為 End (靠右),就能讓 Switch 對齊了~


CheckBox

CheckBox 比較常見於網頁上的使用,通常是中間空白的方框,點擊後以打勾或是 X 填滿,同樣回傳布林值。

但 Xamarin.Forms 沒有現成的 CheckBox 可用,得自己手動製作一個~

Xaml,先擺好框框與文字,還有點擊時要執行的動作:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Xamarin.FormsBook.Toolkit.CheckBox">
    <StackLayout Orientation="Horizontal">
        <Label x:Name="boxLabel" Text="&#x2610;" />
        <Label x:Name="textLabel" />
    </StackLayout>
    <ContentView.GestureRecognizers>
        <TapGestureRecognizer Tapped="OnCheckBoxTapped" />
    </ContentView.GestureRecognizers>
</ContentView>

Label 用 Unicode 可以顯示稱為 Ballot Box 的字元,有以下三種:
  • \u2610 空白方框
  • \u2611 中間打勾的方框
  • \u2612 中間 X 的方框

前端放好元件後,後端需要設定其屬性事件,加入三個 BindableProperty
關於 BindableProperty 的介紹在原文書第 11 章,本系列先跳過,可能會等後面教學打完再回頭補齊。
  • Text
  • FontSize
  • IsChecked
IsCheckedChanged 事件。


.cs,要注意的是 FontSize 屬性 [TypeConverter] 的用法,用來轉換 Xaml 傳進來的字串值如 "Large " 至 特定的文字大小:
namespace Xamarin.FormsBook.Toolkit
{
    public partial class CheckBox : ContentView
    {
        // 文字內容
        public static readonly BindableProperty TextProperty =
        BindableProperty.Create(
        "Text",
        typeof(string),
        typeof(CheckBox),
        null,
        propertyChanged: (bindable, oldValue, newValue) =>
        {
            ((CheckBox)bindable).textLabel.Text = (string)newValue;
        });

        // 文字大小
        public static readonly BindableProperty FontSizeProperty =
        BindableProperty.Create(
        "FontSize",
        typeof(double),
        typeof(CheckBox),
        Device.GetNamedSize(NamedSize.Default, typeof(Label)),
        propertyChanged: (bindable, oldValue, newValue) =>
        {
            CheckBox checkbox = (CheckBox)bindable;
            checkbox.boxLabel.FontSize = (double)newValue;
            checkbox.textLabel.FontSize = (double)newValue;
        });

        // 是否勾選
        public static readonly BindableProperty IsCheckedProperty =
        BindableProperty.Create(
        "IsChecked",
        typeof(bool),
        typeof(CheckBox),
        false,
        propertyChanged: (bindable, oldValue, newValue) =>
        {
            // 設定空白框或打勾
            CheckBox checkbox = (CheckBox)bindable;
            checkbox.boxLabel.Text = (bool)newValue ? "\u2611" : "\u2610";
            
            // 觸發事件
            checkbox.CheckedChanged?.Invoke(checkbox, (bool)newValue);
        });

        public event EventHandler CheckedChanged;
        public CheckBox()
        {
            InitializeComponent();
        }

        public string Text
        {
            set { SetValue(TextProperty, value); }
            get { return (string)GetValue(TextProperty); }
        }

        [TypeConverter(typeof(FontSizeConverter))]
        public double FontSize
        {
            set { SetValue(FontSizeProperty, value); }
            get { return (double)GetValue(FontSizeProperty); }
        }

        public bool IsChecked
        {
            set { SetValue(IsCheckedProperty, value); }
            get { return (bool)GetValue(IsCheckedProperty); }
        }

        // TapGestureRecognizer 觸發的事件.
        void OnCheckBoxTapped(object sender, EventArgs args)
        {
            IsChecked = !IsChecked;
        }
    }
}


製作好後就拿來用看看吧,範例功能跟 Switch Demo 一樣,更改 Label 的斜體與粗體:

XAML,最上方記得引用 CheckBox 的 namespace
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit="clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="CheckBoxDemo.CheckBoxDemoPage">
    
    <StackLayout Padding="10, 0">
        <StackLayout HorizontalOptions="Center"
                     VerticalOptions="CenterAndExpand">

            <toolkit:CheckBox Text="Italic"
                              FontSize="Large"
                              CheckedChanged="OnItalicCheckBoxChanged" />
            <toolkit:CheckBox Text="Boldface"
                              FontSize="Large"
                              CheckedChanged="OnBoldCheckBoxChanged" />
        </StackLayout>
        
        <Label x:Name="label"
               Text="Just a little passage of some sample text that can be formatted
                        in italic or boldface by toggling the two custom CheckBox views."
               FontSize="Large"
               HorizontalTextAlignment="Center"
               VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

.cs:
public partial class CheckBoxDemoPage : ContentPage
{
    public CheckBoxDemoPage()
    {
        InitializeComponent();
    }

    void OnItalicCheckBoxChanged(object sender, bool isChecked)
    {
        if (isChecked)
        {
            label.FontAttributes |= FontAttributes.Italic;
        }
        else
        {
            label.FontAttributes &= ~FontAttributes.Italic;
        }
    }

    void OnBoldCheckBoxChanged(object sender, bool isChecked)
    {
        if (isChecked)
        {
            label.FontAttributes |= FontAttributes.Bold;
        }
        else
        {
            label.FontAttributes &= ~FontAttributes.Bold;
        }
    }
}


執行結果:





沒有留言:

張貼留言