XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書

2019/04/26

使用 StackLayout 配合 BindableLayout.ItemTemplateSelector 建立一個動態資料樣板的呈現的效果

使用 StackLayout 配合 BindableLayout.ItemTemplateSelector 建立一個動態資料樣板的呈現的效果

在 Xamarin.Forms 3.5 版本推出的時候,有一個相當好用的功能,那就是 BindableLayout ,有興趣的人可以參考 Xamarin.Forms 3.5: A Little Bindable Love 這篇文章,只要是版面配置繼承於 Layout ,都可以使用 BindableLayout 這個附加屬性 Attached Property,這包括了:AbsoluteLayuot, FlexLayout, Grid, RelativeLayout, StackLayout。
在這篇文章中,將要來練習使用 StackLayout 這個版面配置,但是不會在 StackLayout 版面配置內指定這些仔檢視,而是透過 BindableLayout.ItemsSource 來指定要顯示在 StackLayout 內的子檢視 View 的物件,並且每個子檢視的樣貌都會不相同,會依據當時該物件值來決定要顯示甚麼樣貌,也就是要顯示哪個 DataTemplate,所以,這裡將會使用 BindableLayout.ItemTemplateSelector
該文件的專案原始碼可以透過 GitHub 來取得

建立一個 使用 StackLayout 專案

  • 開啟 Visual Studio 2019 程式
  • 當 Visual Studio 2019 開始 視窗 出現之後,請點選左下角的 [建立新專案] 選項
  • 當 [建立新專案] 對話窗出現之後,請在中間最上方的搜尋文字輸入盒中輸入 [prism] 關鍵字,搜尋所有與 Prism 有關的專案樣板
  • 請選擇 [Prism Blank App (Xamarin.Forms)] 這個專案樣板
  • 當出現 [設定新的專案] 對話窗,請在 [專案名稱] 輸入 [XF3003]
  • 最後點選該對話窗右下方的 [建立] 按鈕
  • 現在將會看到 [PRISM PROJECT WIZARD] 對話窗,請勾選 ANDROID, iOS, UWP 三個行動裝置平台,接著在底下 [Container] 下拉選單,選擇 Unity 項目
  • 最後,點選 [CREATE PROJECT] 按鈕,以便產生 Xamarin.Forms 專案

安裝需要用到的 PropertyChanged.Fody NuGet 套件

  • 當這個 Xamarin.Forms 專案建立成功之後,請在該方案中,找到 Xamarin.Forms 使用的專案(這是一個 .NET Standard 類別庫,簡稱為 SCL ),請在該專案中,使用滑鼠右擊 [相依性] 節點,選擇 [管理 NuGet 套件] 選項
  • 在 [NuGet: XXX] 視窗中,點選 [瀏覽] 標籤頁次,並且在下方的搜尋文字輸入盒中,輸入 [propertychanged.fody] 關鍵字,搜尋出這個 NuGet 套件
  • 當出現 [PropertyChanged.Fody] NuGet 套件,請點選該套件,並且點選右方的 [安裝] 按鈕,將這個套件安裝到 Xamarin.Forms 專案內
  • 請查看 Xamarin.Forms 專案內,並沒有 [FodyWeavers.xml] 這個檔案,因此,使用滑鼠右擊 Xamarin.Forms 專案節點,選擇 [建置] 選項
  • 當建置完成之後,在這個 Xamarin.Forms 專案內將會出現 [FodyWeavers.xml] 檔案

升級 Xamarin.Forms 套件到大於 3.5 板本以上

因為 BindableLayout 將會在 Xamarin.Forms 3.5 版才有支援,因此,現在這個時間點,將會升級到最新的 3.6 版本
  • 使用滑鼠右擊方案節點(方案總管最上方的那個節點),選擇 [管理方案的 NuGet 套件]
  • 點選 [更新] 標籤頁次
  • 勾選該標籤頁次內的所有項目
  • 點選右上方的更新按鈕,就可以升級這些套件到最新版本了

建立資料模型

  • 滑鼠右擊 Xamarin.Forms 專案,選擇 [加入] > [新增資料夾]
  • 將新增資料夾的名稱設定為 [Models]
  • 滑鼠右擊剛剛建立的 [Models] 資料夾,選擇 [加入] > [類別]
  • 在 [新增項目] 對話窗下方的 [名稱] 欄位中,輸入 [ItemBlock]
  • 點選右下方的 [新增] 按鈕
  • 將底下程式碼填入到這個新建立的類別檔案內
C Sharp / C#
namespace XF3003.Models
{
    public enum ItemBlockTypeEnum
    {
        Label,
        BoxView,
        Entry,
    }
    public class ItemBlock
    {
        public ItemBlockTypeEnum ShowViewType { get; set; }
        public string LabelText { get; set; }
        public int CountIndex { get; set; }
    }
}

建立資料樣板選擇器的類別

  • 滑鼠右擊 Xamarin.Forms 專案,選擇 [加入] > [新增資料夾]
  • 將新增資料夾的名稱設定為 [DataTemplateSelectors]
  • 滑鼠右擊剛剛建立的 [DataTemplateSelectors] 資料夾,選擇 [加入] > [類別]
  • 在 [新增項目] 對話窗下方的 [名稱] 欄位中,輸入 [MyItemTemplateSelector]
  • 點選右下方的 [新增] 按鈕
  • 將底下程式碼填入到這個新建立的類別檔案內
這個 MyItemTemplateSelector 類別將需要繼承 DataTemplateSelector 這個類別,並且要覆寫實作出 OnSelectTemplate 這個方法,而這個方法的主要作用在於會接收一個型別為 object 的 item 物件,該物件就是當時要顯示在螢幕上的物件,這個時候,可以根據該物件屬性值,決定當時要顯示出哪個資料樣板 DataTemplate,如此,就可以做出動態顯示不同檢視項目的效果了。
在這個類別類,將會宣告三個 DataTemplate 屬性,這三個 DataTemplate 屬性將會在該頁面的 XAML 中來宣告與指定,因此,在這裡將不會有任何的定義。
C Sharp / C#
public class MyItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate LabelDataTemplate { get; set; }
    public DataTemplate BoxViewDataTemplate { get; set; }
    public DataTemplate EntryDataTemplate { get; set; }
    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    {
        ItemBlock foo = (ItemBlock)item;
        if (foo.ShowViewType == ItemBlockTypeEnum.Label)
        {
            return LabelDataTemplate;
        }
        else if (foo.ShowViewType == ItemBlockTypeEnum.BoxView)
        {
            return BoxViewDataTemplate;
        }
        else
        {
            return EntryDataTemplate;
        }
    }
}

建立使用 BindableLayout.ItemTemplateSelector 的頁面與商業邏輯

  • 在 [Views] 資料夾內,打開 [MainPage.xaml] 檔案
  • 修正使用底下的 XAML 語言宣告
在這裡將會看到在 StackLayout 版面配置內,並沒有指定任何的子檢視,而是透過 BindableLayout.ItemsSource 來指定要顯示的物件,而該物件要顯示那些 XAML 檢視項目,這裡是透過 BindableLayout.ItemTemplateSelector 來決定。
設計技巧就是要使用到 XAML 的 資源 Resource,在此會使用 ContentPage.Resources 來宣告這些資源;在前面提到過的三種資料樣板 LabelDataTemplate BoxViewDataTemplate EntryDataTemplate 將會在這裡進行定義,另外,也需要建立一個 XAML 命名空間 xmlns:LocalDataTemplates="clr-namespace:XF3003.DataTemplateSelectors" ,將會透過該命名空間可以參考到上面所設計的 MyItemTemplateSelector 類別,也會在這裡分別指定該類別中的三個屬性所要參考的物件值。
xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:LocalDataTemplates="clr-namespace:XF3003.DataTemplateSelectors"
             x:Class="XF3003.Views.MainPage"
             Title="StackLayout的ItemTemplateSelector 應用練習">

    <ContentPage.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="labelTemplate">
                <Grid>
                    <Label Text="{Binding LabelText}"/>
                </Grid>
            </DataTemplate>
            <DataTemplate x:Key="boxViewTemplate">
                <Grid>
                    <BoxView Color="LightPink"/>
                </Grid>
            </DataTemplate>
            <DataTemplate x:Key="entryTemplate">
                <Grid>
                    <Entry Placeholder="{Binding LabelText}"/>
                </Grid>
            </DataTemplate>
            <LocalDataTemplates:MyItemTemplateSelector 
                x:Key="myItemTemplateSelector"
                LabelDataTemplate="{StaticResource labelTemplate}"
                BoxViewDataTemplate="{StaticResource boxViewTemplate}"
                EntryDataTemplate="{StaticResource entryTemplate}"/>
        </ResourceDictionary>
    </ContentPage.Resources>

    <Grid>
        <StackLayout
            Orientation="Vertical"
            BindableLayout.ItemsSource="{Binding myItemList}"
            BindableLayout.ItemTemplateSelector="{StaticResource myItemTemplateSelector}">
        </StackLayout>
    </Grid>
</ContentPage>
  • 在 [ViewModels] 資料夾內,打開 [MainPageViewModel.xaml] 檔案
  • 修正使用底下的 C# 程式碼
C Sharp / C#
namespace XF3003.ViewModels
{
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    using XF3003.Models;

    public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public ObservableCollection<ItemBlock> myItemList { get; set; } = new ObservableCollection<ItemBlock>();
        private readonly INavigationService navigationService;

        public MainPageViewModel(INavigationService navigationService)
        {
            this.navigationService = navigationService;

        }

        public void OnNavigatedFrom(INavigationParameters parameters)
        {
        }

        public void OnNavigatedTo(INavigationParameters parameters)
        {
            myItemList.Add(new ItemBlock()
            {
                ShowViewType = ItemBlockTypeEnum.Label,
                LabelText = "User Account"
            });
            myItemList.Add(new ItemBlock()
            {
                ShowViewType = ItemBlockTypeEnum.Entry,
                LabelText = "Please Enter Account"
            });
            myItemList.Add(new ItemBlock()
            {
                ShowViewType = ItemBlockTypeEnum.Label,
                LabelText = "User Password"
            });
            myItemList.Add(new ItemBlock()
            {
                ShowViewType = ItemBlockTypeEnum.Entry,
                LabelText = "Please Enter Password"
            });
            myItemList.Add(new ItemBlock()
            {
                ShowViewType = ItemBlockTypeEnum.BoxView,
            });
        }

        public void OnNavigatingTo(INavigationParameters parameters)
        {
        }

    }
}

執行結果




沒有留言:

張貼留言