XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

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

2017/11/15

Xamarin.Forms / .NET Standard 體驗之旅 3 : 使用 PCLStorage 元件進行檔案的讀取與寫入

在 Xamarin.Forms / .NET Standard 體驗之旅 3的文章中,我們將要來測試這個最常用的 NuGet 套件:PCLStorage。
我們從這個套件在 NuGet 官網上的內容可以看的出來,這個 NuGet 套件本身是支援 PCL 可攜式類別庫的,那麼,這樣的 PCL 套件,是否可以能夠在 .NET Standard 標準類別庫,並且提供給 Xamarin.Android / Xamarin.iOS / UWP 平台系統來使用,我們來測試看看。
PCLStorage NuGet

測試範例專案原始碼

這篇文章中的測試範例專案原始碼,您可以從這裡取得 https://github.com/vulcanlee/xamarin-forms-develop-notes-example/tree/master/NETStdPCLStorage

開始建立專案

請點選功能表 [檔案] > [新增] > [專案],此時,您會看到底下的 [新增專案] 對話視窗。
我們點選 Prism Template Pack 提供的 Xamarin.Forms 專用的專案樣板,請點選 [已安裝] > [Visual C#] > [Prism] > [Prism Blank App (Xamarin.Forms)]。
接著在名稱欄位輸入該專案名稱後,點選 [確定] 按鈕,以建立此練習專案。
然後,在 [PRISM PROJEC WIZAD] 對話窗中,選擇您要跨平台的作業系統,容器 (Container) 這裡,我個人喜歡與習慣使用 Prism,您可以選擇您自己要用的容器,最後,點選 [CREATE PROJECT] 按鈕。
.NET Standard Xamarin.Forms Project

安裝 PCLStorage NuGet 套件

在這裡,請使用滑鼠右建,點選剛剛建立好的方案節點,選擇 [管理方案的 NuGet 套件] 項目。
在 [瀏覽] 標籤頁次上,輸入 PCLStorage 這個關鍵字,搜尋出這個套件。這個 PCLStorage 套件圖示為一個綠色瓶子樣子。
從下圖中,PCLStorage 套件本身僅支援 PCL Profile259,並且不支援 .NET Standard 標準類別庫,不過,沒有關係,我們嘗試繼續安裝下去,因此,在右方區域,勾選 [NETStdPCLStorage] 這個 .NET Standard 專案(您可以不用勾選原生專案)
.NET Standard Xamarin.Forms Project
.NET Standard Xamarin.Forms Project
當我們點選 [安裝] 按鈕之後,會看到 Visual Studio 會提示您這樣會安裝那些相依性的套件。
.NET Standard Xamarin.Forms Project

修正 View / ViewModel 開始使用 Acr.UserDialogs

安裝好套件之後,讓我們打開 MainPage.xaml 檔案,修改XAML內容。
這裡,我們僅在 StackLayout 版片配置內,加入3個按鈕項目,並且設定 Command 屬性,要綁定 ViewModel 內類型為 DelegateCommand 的特定物件,這三個按鈕分別會將文字輸入盒輸入的文字進行儲存到指定檔案中、清空文字輸入盒、從檔案中將內容讀取出來,並且置換掉文字輸入盒的內容。
透過這三個按鈕,我們可以確認資料是否真的有擁有儲存在手機檔案中,當然,您也可以於儲存到檔案中之後,關閉這個程式,接著重新啟動,看看是否可以從檔案中讀取出來。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="NETStdPCLStorage.Views.MainPage"
             Title="{Binding Title}">

    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
        <Label Text="Welcome to Xamarin Forms and Prism!" />
        <Entry Text="{Binding MyEntry}"/>
        <Button Text="Save" Command="{Binding SaveCommand}"/>
        <Button Text="Clean" Command="{Binding CleanCommand}"/>
        <Button Text="Load" Command="{Binding LoadCommand}"/>
    </StackLayout>

</ContentPage>
接著我們打開 MainPageViewModel.cs 檔案,修正 ViewModel 的商業邏輯程式碼。
這裡進行檔案文字內容的讀取與寫入的程式碼,全部都是從 PCLStorage 官方網站上複製下來的,我們在此就不再多做說明。
namespace NETStdPCLStorage.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {


        #region MyEntry
        private string _MyEntry = "";
        /// <summary>
        /// MyEntry
        /// </summary>
        public string MyEntry
        {
            get { return this._MyEntry; }
            set { this.SetProperty(ref this._MyEntry, value); }
        }
        #endregion

        public DelegateCommand SaveCommand { get; set; }
        public DelegateCommand CleanCommand { get; set; }
        public DelegateCommand LoadCommand { get; set; }

        public MainPageViewModel(INavigationService navigationService)
            : base(navigationService)
        {
            Title = "Main Page";

            SaveCommand = new DelegateCommand(async () =>
            {
                IFolder rootFolder = FileSystem.Current.LocalStorage;
                IFolder folder = await rootFolder.CreateFolderAsync("MySubFolder",
                    CreationCollisionOption.OpenIfExists);
                IFile file = await folder.CreateFileAsync("answer.txt",
                    CreationCollisionOption.ReplaceExisting);
                await file.WriteAllTextAsync(MyEntry);
            });
            CleanCommand = new DelegateCommand(() =>
            {
                MyEntry = "";
            });
            LoadCommand = new DelegateCommand(async () =>
            {
                IFolder rootFolder = FileSystem.Current.LocalStorage;
                IFolder folder = await rootFolder.CreateFolderAsync("MySubFolder",
                    CreationCollisionOption.OpenIfExists);
                IFile file = await folder.GetFileAsync("answer.txt");
                MyEntry = await file.ReadAllTextAsync();
            });
        }
    }
}

修正 Android 專案,進行 UserDialogs 的初始化

在這個文件中,並沒有提到需要在原生平台做任何初始化的工作,因此,我們也就不任何原生專案的修正。

進行測試

最後,設定預設起始專案為 Android 專案,並且選擇適當的模擬器或者實體手機,開始進行測試。
Acr.UserDialogs 執行結果

Xamarin.Forms / .NET Standard 體驗之旅 2 : 使用 Acr.UserDialogs 元件

由於 Prism Template Pack 已經開始支援創建出使用 .NET Standard 的共用類別庫專案,在接下來的幾篇文章,我將會針對在開發 Xamarin.Forms 專案的時候,會使用到不同需求功能的 NuGet 套件,進行測試與說明。
首先,我先針對在進行 Xamarin.Forms 專案開發時候,必備擴充 NuGet 套件 Acr.UserDialogs 進行測試,當然,在使用任何套件之前,強烈建議一定要先觀看官方的使用說明文件,因為,大部分的 Xamarin.Forms 擴充套件,都須做些適度的初始化設定工作。

測試範例專案原始碼

這篇文章中的測試範例專案原始碼,您可以從這裡取得

安裝最新的 Prism Template Pack

首先,請先確認您有安裝或者更新到最新的 Prism Template Pack 這個 Visual Studio 擴充功能,您可以點選 Visual Studio 2017 功能表的 [工具] > [擴充功能與更新] 選項,看到底下 [擴充功能與更新] 對話視窗,確認有安裝這個擴充 Prism Template Pack 功能。
今天有更新到 2.0.7 版本,所以,我們將會使用今天更新的最新 Prism Template Pack 擴充功能套件。
Prism Template Pack 2.0.7

開始建立專案

請點選功能表 [檔案] > [新增] > [專案],此時,您會看到底下的 [新增專案] 對話視窗。
我們點選 Prism Template Pack 提供的 Xamarin.Forms 專用的專案樣板,請點選 [已安裝] > [Visual C#] > [Prism] > [Prism Blank App (Xamarin.Forms)]。
接著在名稱欄位輸入該專案名稱後,點選 [確定] 按鈕,以建立此練習專案。
.NET Standard Xamarin.Forms Project
然後,在 [PRISM PROJEC WIZAD] 對話窗中,選擇您要跨平台的作業系統,容器 (Container) 這裡,我個人喜歡與習慣使用 Prism,您可以選擇您自己要用的容器,最後,點選 [CREATE PROJECT] 按鈕。
.NET Standard Xamarin.Forms Project

安裝 Acr.UserDialogs NuGet 套件

在這裡,請使用滑鼠右建,點選剛剛建立好的方案節點,選擇 [管理方案的 NuGet 套件] 項目。
在 [瀏覽] 標籤頁次上,輸入 Acr.UserDialogs 這個關鍵字,搜尋出這個套件。
從下圖中,我們看到搜尋出兩個 Acr.UserDialogs 相關套件,我們點選第一個,並且在右方區域,勾選 [NETStdUserDialogs] 這個 .NET Standard 專案(您可以不用勾選原生專案)
.NET Standard Xamarin.Forms Project
我們可以查看到這個 Acr.UserDialogs 套件,已經有支援 .NET Standard,也就是說,原則上,您所使用的 NuGet 套件,最好是能夠有支援 .NET Sandard 平台,使用上較不會遇到一些問題,若該套件還是僅支援 PCL ,這裡還是建議您先建立一個測試專案,測試在 .NET Standard 環境下使用是否有遇到任何問題。
.NET Standard Xamarin.Forms Project
當我們點選 [安裝] 按鈕之後,會看到 Visual Studio 會提示您這樣會安裝那些相依性的套件。
.NET Standard Xamarin.Forms Project

修正 View / ViewModel 開始使用 Acr.UserDialogs

安裝好套件之後,讓我們打開 MainPage.xaml 檔案,修改XAML內容。
這裡,我們僅在 StackLayout 版片配置內,加入一個按鈕項目,並且設定 Command 屬性,要綁定 ViewModel 內類型為 DelegateCommand 的物件 LoadCommand
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="NETStdUserDialogs.Views.MainPage"
             Title="{Binding Title}">

    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
        <Label Text="Welcome to Xamarin Forms and Prism!" />
        <Button Text="Load" Command="{Binding LoadCommand}"/>
    </StackLayout>

</ContentPage>
接著我們打開 MainPageViewModel.cs 檔案,修正 ViewModel 的商業邏輯程式碼。
我們透過了 UserDialogs.Instance 這個物件,取得現在可用的 UserDialogs 物件,並且要顯示出 Loading 這個對話窗,我們會暫停兩秒鐘之後,這個對話窗便會消失掉。
namespace NETStdUserDialogs.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {
        public DelegateCommand LoadCommand { get; set; }
        public MainPageViewModel(INavigationService navigationService) 
            : base (navigationService)
        {
            Title = "Main Page";
            LoadCommand = new DelegateCommand(async () =>
            {
                using (var dlg = UserDialogs.Instance.Loading("Test Progress"))
                {
                    await Task.Delay(2000);
                }
            });
        }
    }
}

修正 Android 專案,進行 UserDialogs 的初始化

如同前面強調的,使用 Xamarin.Forms 第三方套件的時候,務必要觀看使用說明,所以在 Acr.UserDialogs網頁中,有提到,我們需要在 Android 專案內進行初始化設定。
在 Android 專案內,找到 MainActivity.cs 檔案,打開這個檔案。
請在 [OnCreate] 方法內,加入這行 UserDialogs.Init(this); 敘述
protected override void OnCreate(Bundle bundle)
{
    TabLayoutResource = Resource.Layout.Tabbar;
    ToolbarResource = Resource.Layout.Toolbar;

    base.OnCreate(bundle);

    UserDialogs.Init(this);

    global::Xamarin.Forms.Forms.Init(this, bundle);
    LoadApplication(new App(new AndroidInitializer()));
}

進行測試

最後,設定預設起始專案為 Android 專案,並且選擇適當的模擬器或者實體手機,開始進行測試。
使用 Acr.UserDialogs 相關對話窗的好處就是,如同底下測試結果,您會看到的是,這些對話窗將會是全螢幕顯示的。
Acr.UserDialogs 執行結果 Acr.UserDialogs 執行結果

2017/11/13

Xamarin.Forms 用 MVVM 開發的好處? Prism 和 cross-platform 開發(優劣)差異? (我發現寫在 ViewModel 內 程式碼要多好多)

剛剛收到一位學員提問的問題 XF用mvvm開發的好處? prism 和 cross-platform 開發(優劣)差異? (我發現寫在viewmodel 內 程式碼要多好多)
這個問題有相當多的 Xamarin.Forms 開發者都會有這樣的問題,我先簡單地描述問題中的各個技術名詞
在 Xamarin.Forms 中,所有的 UI 可以透過 XAML 宣告標記語言來描述出來;而在 XAML 定義 UI的過程中,我們是無法直接在 XAML 中撰寫商業邏輯到這個語言中(您也可以選擇不用 XAML,直接使用 C#來定義UI)。想要在每個頁面撰寫出各個商業邏輯處理程序與方法,您有兩種選擇 Code Behind / MVVM (Model View ViewModel)
而 Code Behind 的開發方式,則是非常舊的開發技術,那是之前在 Windows Forms 那個時候的開發技術採用的,它個優點是簡單、方便,缺點是它的頁面與商業邏輯程式代碼綁在一起,想要共用或分享上,會有些困難。
在進行 XAML 這樣 UI App 開發的時候(如 Xamarin.Forms),我們都會採用 MVVM 的開發方式 (若您在進行原生 Android / iOS 專案開發的時候,也是可以使用 MVVM 的開發方式,而不是使用這兩個原生平台提供的 MVC 開發方式)。
採用 MVVM 的開發方式,可以讓您的 XAML 頁面(View) 與該頁面的商業處理邏輯( ViewModel )徹底切隔開來,具有關注點分離的好處;也就是說,不會因為您變更了 XAML 內容,就導致 App 無法正常運作,反之亦然。因為商業邏輯都放在 ViewModel 內(與 UI 毫無關係),因此,您可以在這裡進行自動單元測試,確保 App 品質。
Prism 是一個開發框架,它幫助你建立一個具有相依性注入容器的 Bootstrap 相關程式碼,事先幫您建立好,讓您可以輕鬆的使用這些功能:ViewModel 自動綁定、導航服務、XAML行為擴充功能、事件聚合器、相依性管理服務、頁面對話窗等等(若您有使用過 Code Behind來開發過 App,而您又使用過 Prism 這些功能,您將會愛上 Prism 的這些功能所帶來的好處)。
採用預設專案樣板的 cross-platform 開發,您也是可以選擇搭配 MVVM 的開發方式或者使用 Code Behind 的開發方式;可是對於許多再進行 MVVM 開發方式的時候,會用到的許多需求,您都需要自行擴充與設計,例如 INotifyPropertyChanged INPC 這樣的機制,你需要寫出一個這樣的基底類別或者在每個 ViewModel 實作出這個介面等等。
當然,不論您使用 Prism 或者是 cross-platform 開發方式,您也可以全部都使用 Code Behind 開發您的 Xamarin.Forms App(完全不使用 MVVM 方式,使用事件綁定的方式),也有人喜歡使用 C# 來寫出整個 Xamarin.Forms App (也就是不使用任何 XAML 標記宣告語言),這些開發技術都是可以選擇的。不過,在使用 Code Behind 開發的時候,您需要去處理一些其他的問題,才能夠讓您的 App 開發上更加靈活。
還有一點我建議採用 MVVM 架構開發方式的理由是,當您遇到問題,而在網路上尋找問題解答的時候,大部分都會提供 MVVM 的解決方案(當然也會有 Code Behind的解決方式),不過,網路上相當多的 Xamarin.Forms 高手,都是採用 MVVM + Prism 的方式開發,對於這些高手寫的文章與提出的解決方案,對於同樣採用這樣開發技術的人,是更有加分的好處。
最後,青菜蘿蔔各有所好,使用您覺得好的開發方式,黑貓白貓,會抓老鼠的就是好貓,只要能夠開發出您要的 App 的技術,就是您要的技術。(若有遇到問題且專案全部都使用 Code Behind 開發,很抱歉,我無法對您提供任何有用的幫助)
若您一定要問我,Vulcan 您會選擇哪個方式,我會選擇 Prism + MVVM

2017/11/12

Xamarin.Forms / .NET Standard 體驗之旅 1 : 使用 Prism Template Pack 2.0 與 .NET Stardard 標準類別庫之建立 Xamarin.Forms App 搶先體驗

在今天,2017/11/12,Prism Template Pack 推出 2.0 版本,在這個新的版本所建立出來的 Xamarin.Forms 專案,將會使用 .NET Standard 標準類別庫來取代原先使用的 PCL Portable Class Library 可攜式類別庫,可是,當替換掉舊有的PCL類別庫,對於我們在開發 Xamarin.Forms 專案上會有甚麼新問題會發生呢?
在這裡,我們將實際使用 Prism Template Pack 2.0 的專案樣板,實際建立一個 Xamarin.Forms 專案,並且在這個專案中實作出各種頁面導航的應用,看看會有甚麼樣的變化。

升級 Prism Template Pack

首先,我們先要把您的 Visual Studio 2017 的 Prism Template Pack 擴充功能進行升級到 2.0 版本。
在這裡要特別注意, Prism Template Pack 2.0 版本將會僅僅支援 Visual Studio 2017,若您使用別的 Visual Studio 版本,您可以忽略掉這篇文章。
請確認您的 Prism Template Pack 已經升級了2.0版本
Prism Template Pack 2.0

使用 Prism Template Pack 2.0 建立 Xamarin.Forms 專案

  • 打開 Visual Studio 2017
  • 從功能表中選取 [檔案] > [新增] > [專案]
  • 在 [新增專案] 對話窗中,選擇 [已安裝] > [Visual C#] > [Prism] > [Prism Blank App (Xamarin.Forms)]
  • 在名稱欄位輸入 MyPrismPack2
    Prism Template Pack 2.0 New Project
  • 在 [PRISM PROJECT WIZARD] 對話窗中,勾選您要建立的跨平台類型,接著在 [Container] 容器下拉選單中,選擇 [Unity] 這個項目,最後,點選 [CREATE PROJECT]
    Prism 支援不同的 DI 套件,因此,您可以選擇您喜歡與常用的相依性注入套件名稱。
    Prism Template Pack 2.0 New Project
  • 稍微等候一段時間,這個 Xamarin 專案將會建立成功
  • 首先,我們來查看 MyPrismPack2 這個專案的屬性,使用滑鼠右擊這個專案,選擇 [屬性] 選項,您會看到類似下圖的畫面,表示這個專案真的是使用 .NET Standard 2.0 作為目標 Framework
    查看專案屬性
    同時,您也會從方案總管中看到了 MyProjectPack2 專案的相依性節點,有看到黃色警告圖示,而您可以從 Visual Studio 2017 [錯誤清單] 視窗中看到這兩個錯誤訊息,不過,在這裡我們可以暫時忽略掉這兩個警告訊息,因為,在您開發 Xamarin.Forms 跨平台專案的過程中,這兩個警告訊息,是不會對您造成任何問題的。
    一些警告訊息
NU1701    已使用 '.NETFramework,Version=v4.6.1' 而非專案目標架構 '.NETStandard,Version=v2.0' 還原了套件 'CommonServiceLocator 1.3.0'。此套件與您的專案並非完全相容。    MyPrismPack2    D:\Vulcan\Projects\MyPrismPack2\MyPrismPack2\MyPrismPack2\MyPrismPack2.csproj        

NU1701    已使用 '.NETFramework,Version=v4.6.1' 而非專案目標架構 '.NETStandard,Version=v2.0' 還原了套件 'Unity 4.0.1'。此套件與您的專案並非完全相容。    MyPrismPack2    D:\Vulcan\Projects\MyPrismPack2\MyPrismPack2\MyPrismPack2\MyPrismPack2.csproj

建置與測試

  • 滑鼠右擊專案節點 MyPrismPack2.Android,選擇 [建置] 選項,若建置成功,將會看到底下訊息輸出。
正在還原 NuGet 封裝...
為避免 NuGet 在建置期間還原封裝,請開啟 [Visual Studio 選項] 對話方塊,並按一下 [封裝管理員] 節點,然後取消核取 [允許 NuGet 在建置期間下載遺漏的封裝]。
1>------ 已開始建置: 專案: MyPrismPack2, 組態: Debug Any CPU ------
1>D:\Vulcan\Projects\MyPrismPack2\MyPrismPack2\MyPrismPack2\MyPrismPack2.csproj : warning NU1701: 已使用 '.NETFramework,Version=v4.6.1' 而非專案目標架構 '.NETStandard,Version=v2.0' 還原了套件 'CommonServiceLocator 1.3.0'。此套件與您的專案並非完全相容。
1>D:\Vulcan\Projects\MyPrismPack2\MyPrismPack2\MyPrismPack2\MyPrismPack2.csproj : warning NU1701: 已使用 '.NETFramework,Version=v4.6.1' 而非專案目標架構 '.NETStandard,Version=v2.0' 還原了套件 'Unity 4.0.1'。此套件與您的專案並非完全相容。
1>MyPrismPack2 -> D:\Vulcan\Projects\MyPrismPack2\MyPrismPack2\MyPrismPack2\bin\Debug\netstandard2.0\MyPrismPack2.dll
1>專案 "MyPrismPack2.csproj" 建置完成。
2>------ 已開始建置: 專案: MyPrismPack2.Android, 組態: Debug Any CPU ------
2>  MyPrismPack2.Android -> D:\Vulcan\Projects\MyPrismPack2\MyPrismPack2\MyPrismPack2.Android\bin\Debug\MyPrismPack2.Android.dll
2>  沒有辦法解決 "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 和 "mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" 之間的衝突。任意選擇 "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"。
========== 建置: 2 成功、0 失敗、0 最新、0 略過 ==========
  • 設定這個 MyPrismPack2.Android 專案為預設起始專案,並且開始執行這個專案,我們將會看到這個專案是可以順利執行的。
    .NET Standard 之 Xamarin.Android 執行結果

進行建立其他功能

接下來,我們會在這個專案建立兩個頁面,一個是 ListPage 另外一個是 Detail Page,我們將會模擬一個清單頁面 CRUD 簡單應用,看看這樣我們平常在實作開發 Xamarin.Forms 專案功能,在現在更換成 .NET Standard 2.0 之後,會不會有甚麼變化?
採用 .NET Standard 之 Xamarin.Android
採用 .NET Standard 之 Xamarin.Android
採用 .NET Standard 之 Xamarin.Android

使用.NET Standard開發 Xamarin.Forms App的測試過程

使用 Prism Template Pack 2.0 的心得

  • 由於 Prism Template Pack 2.0 擴充功能僅能夠在 Visual Studio 2017 上安裝,因此,想要使用這個功能的,必須要使用 Visual Studio 2017
  • Prism Template Pack 2.0 的共用類別庫已經轉換成為 .NET Standard 標準類別庫,因此,對於之前在 PCL 上可以使用的相關第三方套件,強烈建議您需要先行做些測試,確保這些尚未支援 .NET Standard 的第三方套件,可以正常於這個開發環境中運行
  • 這個測試範例雖然不是有很多功能,但是,這裡包含了這些功能在 .NET Standard 下是否可以正常運作的測試。
    • Prims 開發框架的各項功能
    • MVVM 的資料綁定與開發方式
    • Fody PropertyChanged 的 NuGet 套件可以正常使用
    • 頁面導航的各項操作:切換頁面、傳遞參數

2017/10/25

Xamarin.Forms 的 ListView 之延遲載入(Lazy Loading) 之設計方法

有些時候,當使用者要查詢後端資料庫的資料時候,因為所設定的查詢條件過於寬鬆,將會導致會從後端資料一次性的接收到成千上萬筆的資料,若您讓手機應用程式來接收這麼多的資料,將會有嚴重的效能上的問題出現,例如,要從網路一次讀取到這麼多的資料,會需要花點時間;要把這麼多的紀錄加入到 ListView 中,也有可能造成記憶體不足或者ListView操作延緩的現象產生。
在這個時候,我們就可以使用延遲載入 Lazy Loading 這樣的設計方法來解決此一問題。他的處理方式為,每次呼叫 Web API 的時候,後端 Web API 僅僅會回傳一定數量的紀錄(例如:最多 100 筆),當使用者捲動 ListView 的清單到最後一筆紀錄的時候,您的 Xamarin.Forms 程式,就會再度呼叫 Web API 服務,請求回傳接下來一定數量的紀錄(例如:101~200筆),並且把這些新讀取到的紀錄,顯示到 ListView 中。
想要做到這樣的功能,ListView 有提供兩個事件,ItemAppearing / ItemDisappearing 這兩個事件,可以供我們做出這樣的效果;在我們接下來的範例中,將會展示出這樣的設計方法。
我們透過了 XAML 的行為 Behavior (這裡的行為擴充,我們將會使用 Prism 所提供的擴充行為功能),設定當 ItemAppearing 事件發生的時候,會執行所對應的指定的 ViewModel 內的命令 ItemAppearingCommand,而我們在 ViewModel 內,將會設計,當這個命令 ItemAppearingCommand 被觸發的時候,並且現在所顯示的紀錄是全部紀錄的最後一筆,將會進行載入更多的紀錄到 ListView 內(這裡我們將會呼叫 Reload(fooLast.ID+1) 方法來達成)。
因此,在這個範例中,也充分展示出,如何不透過後置碼 Code Behind 的設計技術,也可以在 ViewModel 內,處理相關事件發生要處理的需求。
當然,為了簡化這個範例專案的複雜程度,我們要讀取的集合資料,並不會實際從網路呼叫 Web API 來取得,而是直接產生靜態的集合資料(若您要設計每次都從網路讀取資料,請記得要設計相關的 UX,告知使用者,現在這樣更新資料中,要不然,當在讀取更多資料的時候,手機應用程式,當時會卡卡的)。
底下是我們這個測試頁面 View 的 XAML 標記語言
我們首先宣告了一個 Prism Behavior 的命名空間 xmlns:behavior="clr-namespace:Prism.Behaviors; 接著在 ListView 內,加入一個行為設計,EventToCommandBehavior,並且指定當 ItemAppearing 事件發生的時候,就會需要執行 ViewModel 內的 ItemAppearingCommand 命令;另外,在這裡,我們使用了 EventArgsParameterPath 這個屬性,設定當觸發這個 ViewModel 內的命令時候,需要將是事件中的這個參數,傳入到命令中,這樣,我們才可以在該命令中,知道當時正在顯示的紀錄是哪一筆。
<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             prism:ViewModelLocator.AutowireViewModel="True"
             xmlns:behavior="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
             x:Class="XFListViewLazy.Views.MainPage"
             Title="MainPage">
    <Grid
      >
        <ListView
            ItemsSource="{Binding MyDatas}"
            SelectedItem="{Binding SelectedMyData}"
            HasUnevenRows="True"
            >
            <ListView.Behaviors>
                <behavior:EventToCommandBehavior
                    EventName="ItemAppearing"
                    Command="{Binding ItemAppearingCommand}"
                    EventArgsParameterPath="Item"/>
            </ListView.Behaviors>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Label Text="{Binding ID}"/>
                            <Label Text="{Binding Name}" FontSize="30"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</ContentPage>
底下是上面 View 的相對應 ViewModel
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Xamarin.Forms;
using XFListViewLazy.Models;
using XFListViewLazy.Repositories;

namespace XFListViewLazy.ViewModels
{

    public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;


        public ObservableCollection<MyModel> MyDatas { get; set; } = new ObservableCollection<MyModel>();
        public MyModel SelectedMyData { get; set; }

        private readonly INavigationService _navigationService;
        public MyRepository _myRepository { get; set; }

        public DelegateCommand<MyModel> ItemAppearingCommand { get; set; }

        public MainPageViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            _myRepository = MyRepository.GetInstance();

            ItemAppearingCommand = new DelegateCommand<MyModel>((x) =>
            {
                var fooLast = MyDatas.Last();
                if (x.ID == fooLast.ID)
                {
                    Reload(fooLast.ID+1);
                }
            });
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {
            Reload(0);
        }

        public void Reload(int last)
        {
            var foo = _myRepository.GetNext(last);
            foreach (var item in foo)
            {
                MyDatas.Add(item);
            }
        }

    }

}