2017/7/19

Xamarin.Forms 教學系列文(十九.參 - 1)TableView


學習目標
  • TableView 的屬性和寫法
  • 客製化 Cell

此章介紹 Xamarin.Forms 最後一個 Collection Views - TableView

TableView 通常拿來顯示 不同類型 的項目,而項目就是類別下的各屬性。
換個方式說,每個項目就是由屬性所呈現出來的 Cell

TableView 僅有四個屬性:
  • Intent - type of TableIntent
  • Root - type of TableRoot
  • RowHeight - type of int
  • HasUnevenRows - type of bool

Intent 屬性可以設定為 (也可不設定) 底下四個值,表示此 TableView 的用途是什麼:
  • Data - 用於顯示資料時
  • Form - 用於需要輸入資料時
  • Settings - 與 Form 很像,但通常會有預設值
  • Menu - 通常用於點選項目來觸發動作

Root 屬性對應的 XAML 為 TableRoot,底下會包一個或多個 TableSection,而 TableSection 底下會包一個或多個 Cell:
<TableView Intent="Settings">
    <TableRoot>
        <TableSection Title="Ring">
            <SwitchCell Text="New Voice Mail" />
                   <SwitchCell Text="New Mail" On="true" />
        </TableSection>
    </TableRoot>
</TableView>

但其實啊... TableView 與一個 用 ScrollView 包起來的 StackLayout,兩者之間沒有太大的差異,StackLayout 內也可以放置被綁定的 Visual Element。

不過,TableView 在規劃和安排要顯示的資訊或表單時,會更加的方便。

來看一個 Form 型態的 TableView:

一個 Form

這個 TableView 有一個 TableSection,包括五個 Cell - 四個 EntryCell 和一個 SwitchCell
那這五個 Cell 分別綁定 PersonalInformation 類別的五個屬性 Name、EmailAddress、PhoneNumber、Age、IsProgrammer。

TableRoot 物件的 Title 屬性,只有 Windows 10 Moblie 會顯示
'OnPlatform 寫法已更改,請參考此篇文章

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:EntryForm"
             x:Class="EntryForm.EntryFormPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    
    <StackLayout>
        <TableView x:Name="tableView"
                   Intent="Form">
            
            <!-- BindingContext -->
            <TableView.BindingContext>
                <local:PersonalInformation />
            </TableView.BindingContext>

            <TableRoot Title="Data Form">
                <TableSection Title="Personal Information">
                    <EntryCell Label="Name:"
                               Text="{Binding Name}"
                               Placeholder="Enter name"
                               Keyboard="Text" />
                    
                    <EntryCell Label="Email:"
                               Text="{Binding EmailAddress}"
                               Placeholder="Enter email address"
                               Keyboard="Email" />
                    
                    <EntryCell Label="Phone:"
                               Text="{Binding PhoneNumber}"
                               Placeholder="Enter phone number"
                               Keyboard="Telephone" />
                    
                    <EntryCell Label="Age:"
                               Text="{Binding Age}"
                               Placeholder="Enter age"
                               Keyboard="Numeric" />
                    
                    <SwitchCell Text="Are you a programmer?"
                                On="{Binding IsProgrammer}" />
                </TableSection>
            </TableRoot>
        </TableView>
        
        <Label x:Name="summaryLabel"
               VerticalOptions="CenterAndExpand" />
        
        <Button Text="Submit"
                HorizontalOptions="Center"
                Clicked="OnSubmitButtonClicked" />
    </StackLayout>
</ContentPage>

PersonalInformation.cs
class PersonalInformation : ViewModelBase
{
    string name, emailAddress, phoneNumber;
    int age;
    bool isProgrammer;

    public string Name
    {
        set { SetProperty(ref name, value); }
        get { return name; }
    }
    public string EmailAddress
    {
        set { SetProperty(ref emailAddress, value); }
        get { return emailAddress; }
    }
    public string PhoneNumber
    {
        set { SetProperty(ref phoneNumber, value); }
        get { return phoneNumber; }
    }
    public int Age
    {
        set { SetProperty(ref age, value); }
        get { return age; }
    }
    public bool IsProgrammer
    {
        set { SetProperty(ref isProgrammer, value); }
        get { return isProgrammer; }
    }
}

執行結果:

Submit 點下去時的動作,會在畫面最下方出現一串文字:
public partial class EntryFormPage : ContentPage
{
    public EntryFormPage()
    {
        InitializeComponent();
    }

    void OnSubmitButtonClicked(object sender, EventArgs args)
    {
        PersonalInformation personalInfo = (PersonalInformation)tableView.BindingContext;

        summaryLabel.Text = String.Format($"{personalInfo.Name} is { personalInfo.Age} years old, and has an email address " +
            $"of {personalInfo.EmailAddress}, and a phone number of {personalInfo.PhoneNumber}, and is {(personalInfo.IsProgrammer ? "" : "not ")}" +
            "a programmer.");
    }
}
*注意下取得資料的方式,是使用 tableView.BindingContext 轉成的類別去取值
同樣的方式也可以用在一般 StackLayout 的 BindingContext,做資料處理時會方便許多,不用一個一個從畫面把物件撈出來。

執行結果:


第一個範例上的表單欄位其實相當單純,當然,會有許多不純的時候...

就會需要用到底下介紹的 客製化 Cell


客製化 Cell

或許客戶需求沒那麼單純時,例如 輸入年齡 變成 挑選年齡範圍
就會需要從 Entry 的輸入方式變成 Picker 的挑選方式

以這個範例來說,讓我們先在 PersonalInformation 新增三個屬性,挑選年齡 (AgeRange),以及 挑選語言 (Language) 和 挑選平台 (Platform)
class ProgrammerInformation : ViewModelBase
{
    string name, emailAddress, phoneNumber, ageRange;
    bool isProgrammer;
    string language, platform;
    public string Name
    {
        set { SetProperty(ref name, value); }
        get { return name; }
    }
    public string EmailAddress
    {
        set { SetProperty(ref emailAddress, value); }
        get { return emailAddress; }
    }
    public string PhoneNumber
    {
        set { SetProperty(ref phoneNumber, value); }
        get { return phoneNumber; }
    }
    public string AgeRange
    {
        set { SetProperty(ref ageRange, value); }
        get { return ageRange; }
    }
    public bool IsProgrammer
    {
        set { SetProperty(ref isProgrammer, value); }
        get { return isProgrammer; }
    }
    public string Language
    {
        set { SetProperty(ref language, value); }
        get { return language; }
    }
    public string Platform
    {
        set { SetProperty(ref platform, value); }
        get { return platform; }
    }
}

今天客戶加入了三個新的需求,挑選年齡範圍、挑選平台和挑選語言,這三個需求都是要做 挑選!!

看來我們需要 Picker 的功能,但 Xamarin 在 TableView 沒給 PickerCell 這種東西怎辦?

自己刻一個出來啊!!

先把類別準備好,PickerCell 繼承自 ViewCell:
namespace Xamarin.FormsBook.Toolkit
{
    [ContentProperty("Items")]
    public partial class PickerCell : ViewCell
    {
        public static readonly BindableProperty LabelProperty =
            BindableProperty.Create(
                "Label", typeof(string), typeof(PickerCell), default(string));

        public static readonly BindableProperty TitleProperty =
            BindableProperty.Create(
                "Title", typeof(string), typeof(PickerCell), default(string));

        public static readonly BindableProperty SelectedValueProperty =
            BindableProperty.Create(
                "SelectedValue", typeof(string), typeof(PickerCell), null, 
                BindingMode.TwoWay,
                propertyChanged: (sender, oldValue, newValue) =>
                {
                    PickerCell pickerCell = (PickerCell)sender;
                    if (String.IsNullOrEmpty(newValue))
                    {
                        pickerCell.picker.SelectedIndex = -1;
                    }
                    else
                    {
                        pickerCell.picker.SelectedIndex = pickerCell.Items.IndexOf(newValue);
                    }
                });

        public PickerCell()
        {
            InitializeComponent();
        }
        public string Label
        {
            set { SetValue(LabelProperty, value); }
            get { return (string)GetValue(LabelProperty); }
        }
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }
        public string SelectedValue
        {
            get { return (string)GetValue(SelectedValueProperty); }
            set { SetValue(SelectedValueProperty, value); }
        }

        // Items property.
        public IList Items
        {
            get { return picker.Items; }
        }

        void OnPickerSelectedIndexChanged(object sender, EventArgs args)
        {
            if (picker.SelectedIndex == -1)
            {
                SelectedValue = null;
            }
            else
            {
                SelectedValue = Items[picker.SelectedIndex];
            }
        }
    }
}

接著刻 XAML:
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
          xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
          x:Class="Xamarin.FormsBook.Toolkit.PickerCell"
          x:Name="cell">
    
    <ViewCell.View>
        <StackLayout Orientation="Horizontal"
                     BindingContext="{x:Reference cell}"
                     Padding="16, 0">
            
            <Label Text="{Binding Label}"
                   VerticalOptions="Center" />
            
            <Picker x:Name="picker"
                    Title="{Binding Title}"
                    VerticalOptions="Center"
                    HorizontalOptions="FillAndExpand"
                    SelectedIndexChanged="OnPickerSelectedIndexChanged" />
        </StackLayout>
    </ViewCell.View>
</ViewCell>

燈燈,獲得 PickerCell 一枚...

最後來使用他,當然分別綁定我們新增的三個屬性:
*OnPlatform 寫法已更改,請參考此篇文章

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ConditionalCells"
             xmlns:toolkit="clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="ConditionalCells.ConditionalCellsPage">
    
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    
    <StackLayout>
        <TableView Intent="Form">

            <TableView.BindingContext>
                <local:ProgrammerInformation />
            </TableView.BindingContext>

            <TableRoot Title="Data Form">
                <TableSection Title="Personal Information">
                    <EntryCell Label="Name:"
                               Text="{Binding Name}"
                               Placeholder="Enter name"
                               Keyboard="Text" />
                    
                    <EntryCell Label="Email:"
                               Text="{Binding EmailAddress}"
                               Placeholder="Enter email address"
                               Keyboard="Email" />
                    
                    <EntryCell Label="Phone:"
                               Text="{Binding PhoneNumber}"
                               Placeholder="Enter phone number"
                               Keyboard="Telephone" />
                    
                    <!--第一個 PickerCell-->
                    <toolkit:PickerCell Label="Age Range:"
                                        Title="Age Range"
                                        SelectedValue="{Binding AgeRange}">
                        <x:String>10 - 19</x:String>
                        <x:String>20 - 29</x:String>
                        <x:String>30 - 39</x:String>
                        <x:String>40 - 49</x:String>
                        <x:String>50 - 59</x:String>
                        <x:String>60 - 99</x:String>
                    </toolkit:PickerCell>

                    <SwitchCell Text="Are you a programmer?"
                                On="{Binding IsProgrammer}" />

                    <!--第二個 PickerCell-->
                    <toolkit:PickerCell Label="Language:"
                                        Title="Language"
                                        IsEnabled="{Binding IsProgrammer}"
                                        SelectedValue="{Binding Language}">
                        <x:String>C</x:String>
                        <x:String>C++</x:String>
                        <x:String>C#</x:String>
                        <x:String>Objective C</x:String>
                        <x:String>Java</x:String>
                        <x:String>Other</x:String>
                    </toolkit:PickerCell>
                    
                    <!--第三個 PickerCell-->
                    <toolkit:PickerCell Label="Platform:"
                                        Title="Platform"
                                        IsEnabled="{Binding IsProgrammer}"
                                        SelectedValue="{Binding Platform}">
                        <x:String>iPhone</x:String>
                        <x:String>Android</x:String>
                        <x:String>Windows Phone</x:String>
                        <x:String>Other</x:String>
                    </toolkit:PickerCell>
                    
                </TableSection>
            </TableRoot>
        </TableView>
    </StackLayout>
</ContentPage>

執行結果:

有仔細看 Code 的讀者應該會發現,每個 PickerCell 的 IsEnable 都有綁定 IsProgrammer 這個屬性,
目的就是當你選擇是 Programmer 時,才能去挑選 Language 和 Platform

但執行後會發現,即使 Programmer switch 關閉,PickerCell 還是能點選...
不要問我為什麼...書上也沒寫

不過,有另一個更好的方法可以達到我們的目的,讓我們下一小節見~



2 則留言: