2017/7/19

Xamarin.Forms 教學系列文(十九.參 - 2)TableView - TableSection 隱藏 & Menu



學習目標
  • 整段 Section 隱藏和顯示的方法 
  • Table 也可以當作 Menu 使用!

接續前一小節,我們有個簡單的功能還未完成,
就是開啟 Programmer switch 開關時,才能去點選 Language 和 Platform 這兩個屬性,反之則不能點選。

要完成這個功能,我們要藉由 TableView 本身能放置多組的 TableSection 的特性,並直接隱藏或顯示整組 TableSection

然而...隱藏 TableSection 這件事 無法用 XAML 處理,需借助 C# 才能完成...


接續上一小節的範例,
但我們將 Language 和 Platform 這兩個 PickerCell,移放到一個新的 TableSection 內:

ConditionalSection
*OnPlatform 寫法已更改,請參考此篇文章

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ConditionalSection"
             xmlns:toolkit="clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="ConditionalSection.ConditionalSectionPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    
    <StackLayout>
        <TableView x:Name="tableView"
                   Intent="Form">
            
            <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" />
                    <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 x:Name="isProgrammerSwitch"
                                Text="Are you a programmer?"
                                On="{Binding IsProgrammer}" />
                </TableSection>

                <!-- 新增一個 TableSection-->
                <TableSection x:Name="programmerInfoSection"
                              Title="Programmer Information">
                    
                    <toolkit:PickerCell Label="Language:"
                                        Title="Language"
                                        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>
                    
                    <toolkit:PickerCell Label="Platform:"
                                        Title="Platform"
                                        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>

.cs 控制 programmerInfoSection 的隱藏或顯示:
public partial class ConditionalSectionPage : ContentPage
{
    public ConditionalSectionPage()
    {
        InitializeComponent();

        // 設定 tableView 的 BindingContext
        ProgrammerInformation programmerInfo = new ProgrammerInformation();
        tableView.BindingContext = programmerInfo;

        // 先將要控制的 TableSection 移除
        tableView.Root.Remove(programmerInfoSection);

        // 等待 IsProgrammer 屬性更改時.
        programmerInfo.PropertyChanged += (sender, args) =>
        {
            if (args.PropertyName == "IsProgrammer")
            {
                if (programmerInfo.IsProgrammer &&
                tableView.Root.IndexOf(programmerInfoSection) == -1)
                {
                    // 將 TableSection 加入 TableView
                    tableView.Root.Add(programmerInfoSection);
                }
                if (!programmerInfo.IsProgrammer &&
                        tableView.Root.IndexOf(programmerInfoSection) != -1)
                {
                    // 移除 TableSection 
                    tableView.Root.Remove(programmerInfoSection);
                }
            }
        };
    }
}

可以注意一下 programmerInfo.PropertyChanged +=  第 15 行屬性發生改變時的事件 這寫法,
第一次看到這個寫法覺得滿新奇的... 原來 Xamarin 可以這樣寫...

執行結果:
switch 關閉
switch 開啟

* 貼心提醒:每個 TableSection 都可以擁有 自己的 BindingContext 喔!
* 可以發現 windows 沒有作用...

TableView Menu

這功能算是... TableView 的變化吧,
我們將 TableView 的 Cell 都只放置事件就好,很簡單的就可以形成一個選單了...

這就是為什麼 TextCell 和 ImageCell 會有 Command CommandParameter 屬性,想當然可以將之綁定於寫好的 commands,執行我們要做的事件,或是導覽去別的頁面。

底下來看範例,TableView 有四個 TextCell,分別可以控制 BoxView 左、上、右、下 移動
*OnPlatform 寫法已更改,請參考此篇文章

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MenuCommands.MenuCommandsPage"
             x:Name="page">

    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    
    <StackLayout>
        <TableView Intent="Menu"
                   VerticalOptions="Fill"
                   BindingContext="{x:Reference page}">
            
            <TableRoot>
                <TableSection Title="Move the Box">
                    <!--綁定 MoveCommand 並傳入參數-->
                    <TextCell Text="Left"
                              Command="{Binding MoveCommand}"
                              CommandParameter="left" />

                    <TextCell Text="Up"
                              Command="{Binding MoveCommand}"
                              CommandParameter="up" />

                    <TextCell Text="Right"
                              Command="{Binding MoveCommand}"
                              CommandParameter="right" />

                    <TextCell Text="Down"
                              Command="{Binding MoveCommand}"
                              CommandParameter="down" />
                </TableSection>
            </TableRoot>
        </TableView>

        <AbsoluteLayout BackgroundColor="Maroon"
                        VerticalOptions="FillAndExpand">
            
            <BoxView x:Name="boxView"
                     Color="Blue"
                     AbsoluteLayout.LayoutFlags="All"
                     AbsoluteLayout.LayoutBounds="0.5, 0.5, 0.2, 0.2" />
        </AbsoluteLayout>
    </StackLayout>
</ContentPage>

.cs
值得一提的是,ContentPage 的 BindingContext 就是自己,所以可以直接綁定自己 .cs 內的 Command。
public partial class MenuCommandsPage : ContentPage
{
    int xOffset = 0; // ranges from -2 to 2
    int yOffset = 0; // ranges from -2 to 2

    public MenuCommandsPage()
    {
        // 初始化 Command
        MoveCommand = new Command(ExecuteMove, CanExecuteMove);
        InitializeComponent();
    }

    public ICommand MoveCommand { private set; get; }

    void ExecuteMove(string direction)
    {
        switch (direction)
        {
            case "left": xOffset--; break;
            case "right": xOffset++; break;
            case "up": yOffset--; break;
            case "down": yOffset++; break;
        }

        ((Command)MoveCommand).ChangeCanExecute();

        // 移動 boxView 在 AbsoluteLayout 內的位置
        AbsoluteLayout.SetLayoutBounds(boxView,
            new Rectangle((xOffset + 2) / 4.0,
            (yOffset + 2) / 4.0, 0.2, 0.2));
    }

    bool CanExecuteMove(string direction)
    {
        // 當碰到邊界時就不能再移動
        switch (direction)
        {
            case "left": return xOffset > -2;
            case "right": return xOffset < 2;
            case "up": return yOffset > -2;
            case "down": return yOffset < 2;
        }
        return false;
    }
}

執行結果:


*當然,也可以放在 master/detail page 內作為選單使用。




沒有留言:

張貼留言