XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

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

2018/02/21

2017年度,最受歡迎的Xamarin / Xamarin.Forms 文章

2017年度,最受歡迎的Xamarin / Xamarin.Forms 文章


來到新的2018 年度,回顧我剛開始寫與Xamarin 有關的部落格文章,已經超過快要有350篇以上的Xamarin 相關的文章,不知不覺就過了3個年頭了;這段期間,一起與Xamarin 工具集成長,並且不斷地整理出相關輔助設計模式和開發套件的使用方法,當然,也順利了完成許多Xamarin.Forms 方面的專案,並且開設了許多關於Xamarin.Forms 的實體教學課程,讓更多的想要從事Xamarin.Forms 專案開發的同好,可以順利地開始進行這類行動跨平台專案的開發。最後,也有著不少好友的幫忙與協助,順利獲得Xamarin MVP / Microsoft MVP 的資格。

今天是放完年假的第一天,我把去年一整年度最受歡迎的文章,彙整出來,希望大家會喜歡這些Xamarin 文章,當然,我本身在這一年,也會積極地撰寫更多實用的Xamarin 相關應用文章,並與大家一起分享。

2018/02/20

Xamarin.Forms 的頁面導航Page Navigation 之有無強制回應Modal 對話窗和導航工具列NavigationPage 的體驗

體驗不同頁面導航的效果

在我們進行Xamarin.Forms 跨平台行動專案開發的時候,原則上,我們會建立與設計許多不同的頁面,這些頁面將會使用XAML 宣告標記語言來定義出來,並且該頁面的商業處理邏輯,我們則可以使用Code-Behind 或者MVVM 的方式,將C# 程式碼撰寫在Code-Behind 檔案或者是ViewModel 檢視模型檔案內;一旦完成這些頁面與商業邏輯程式碼的開發,我們就需要使用Xamarin.Forms 提供的頁面導航Page Navigation 機制,根據使用者的需求,切換到不同頁面下,讓使用者可以根據頁面顯示內容來進行操作。
在Xamarin.Forms 中,我們通常都會透過導航頁面NavigationPage 與導航抽屜頁面MasterDetailPage 來提供這樣的使用者體驗;而在使用Prism Framwork 開發框架下,Prism 提供了導航服務物件,您可以在頁面的檢視模型ViewModel下,使用相依性注入Dependency Injection 機制,使用建構式註入方法來取得這個導航服務物件;一旦,您取得了這個物件,便可以依據使用者的需求,進行Xamarin.Forms 應用程式App 的頁面切換行為。
接下來,讓我們來進行各種不同頁面導航情境的測試。

建立要測試的Xamarin.Forms 方案與專案

請打開您的Visual Studio 2017,並且使用已經安裝好的Prism Template Pack 擴充功能所提供的Xamarin.Forms 專案樣板,幫助我們來建立一個跨平台的Xamarin.Forms 專案;一旦您完成這樣的工作,您將會得到一個方案,裡面有四個專案,其中一個是.NET Standard 標準類別庫專案,這個專案將會被另外三個專案參考引用,我們所有的Xamarin.Forms 應用程式開發工作,都會在這個專案中完成;另外三個則分別是Android / iOS / UWP 的原生專案,這三個專案將會幫助我們產生出Android / iOS / UWP 的應用程式。

建立需要用到的頁面

現在,使用滑鼠右擊Views 資料夾,選擇新增項目,接著點選對話窗左邊的Prism 項目,並且依序建立出底下需要用到的頁面
  • MDPage
    請點選Prism MasterDetailPage (Xamarin.Forms) 來建立這個導航抽屜頁面
  • HomePage
    請點選Prism ContentPage (Xamarin.Forms) 來建立這個首頁頁面
  • Page1Page / Page2Page / Page3Page
    請點選Prism ContentPage (Xamarin.Forms) 來建立這些頁面,每個頁面將會設定不同頁面名稱與背景顏色,這樣,到時候執行起來的話,便可以做出區隔,知道現在是導航到哪個頁面上了。
底下的XAML 則是HomePage 這個頁面的宣告內容
XAML Code
<?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"
             x:Class="XFNaviModal.Views.HomePage"
             Title="強制回應對話窗測試">

    <StackLayout
        HorizontalOptions="Center" VerticalOptions="Center"
        Orientation="Vertical">
        <Button
            Text="導航到下一頁(無強制回應)"
            Command="{Binding GoPage1Command}"/>
        <Button
            Text="導航到下兩頁(無強制回應)"
            Command="{Binding GoPage2Command}"/>
        <Button
            Text="導航到下一頁(有強制回應)"
            Command="{Binding GoPage1ModalCommand}"/>
        <Button
            Text="導航到下兩頁(有強制回應)"
            Command="{Binding GoPage2ModalCommand}"/>
        <Button
            Text="導航到下一頁(有強制回應且有導航頁面)"
            Command="{Binding GoPage1NaviCommand}"/>
        <Button
            Text="導航到下兩頁(有強制回應且有導航頁面)"
            Command="{Binding GoPage2NaviCommand}"/>
    </StackLayout>
</ContentPage>

設定Xamarin.Forms 專案的起始頁面

請在.NET Standard 專案中,找到App.xaml.cs 這個項目,並且打開這個節點,找到OnInitialized 這個方法,修正裡面的程式碼為底下內容;在這裡,我們將指定了這個Xamarin.Forms 專案的第一個顯示的畫面將會是一個導航抽屜頁面,而該導航抽屜頁面裡面則是由一個導航頁面與一個ContentPage 所組成。
c# code
protected override async void OnInitialized()
{
    InitializeComponent();

    await NavigationService.NavigateAsync("MDPage/NavigationPage/HomePage");
}

執行與檢視這個Xamarin.Forms 專案

現在,請切換Visual Studio 2017 的預設起始專案為Android,並且建置看看這個Android 專案,看看是否可以建置成功;一旦沒有發現錯誤,我們將可以開始執行這個專案,此時,您將會看到底下螢幕截圖出現在您的模擬器或者是手機上。
Xamarin.Forms 強制對話窗的起始畫面

準備進行頁面導航的測試

在這個測試情境中,我們分別做出三種頁面導航技巧,接著,來看看他們之間的不同,並且,也觀看和了解如何依序導航到新頁面與如何返回到就頁面上。
  • 使用非強制回應Modal 對話窗的頁面導航
    首先﹐我們先要來進行這樣非強制回應Modal 對話窗的頁面導航的測試,我們將會使用首頁頁面上的前兩個按鈕,這兩個按鈕的相對應檢視模型ViewModel 中的C# 程式碼,分別如下:
c# code
GoPage1Command = new DelegateCommand(async () =>
{
    await _navigationService.NavigateAsync("Page1Page");
});
GoPage2Command = new DelegateCommand(async () =>
{
    await _navigationService.NavigateAsync("Page1Page/Page2Page");
});
當按下了導航到下一頁(無強制回應) 這個按鈕,將會執行上述第一個按鈕命令的程式碼,此時,行動裝置上將會出現如下圖的畫面,這表示了Xamarin.Forms成功的幫我們導航到了Page1 這個頁面,而且也看到了這個頁面的名稱Title,另外,我們也看到了螢幕左上方也出現了回上頁的系統按鈕。
深度思考 想看看
當您在這個頁面的時候,請嘗試使用手勢操作,從螢幕的最左方往右滑動,看看能否將導航抽屜面板推出來。
若我們使用裝置的實體回上頁按鈕或者螢幕左上方的回上頁按鈕,都會讓我們回到了HomePage 這個頁面上。
導航到下一頁(無強制回應)
現在,讓我們點選第二個按鈕,也就是導航到下兩頁(無強制回應) 這個按鈕,將會執行上述第二個按鈕命令的程式碼,此時,行動裝置上將會出現如下圖的畫面,這表示了Xamarin.Forms 成功的幫我們導航到了Page2 這個頁面,而且也看到了這個頁面的名稱Title,另外,我們也看到了螢幕左上方也出現了回上頁的系統按鈕。
導航到下兩頁(無強制回應)
若我們使用裝置的實體回上頁按鈕或者螢幕左上方的回上頁按鈕,都會讓我們回到了Page1Page 這個頁面上,這樣的結果符合我們的預期並且也是我們通常在Xamarin.Forms 專案中所做的頁面導航技巧。
導航到下一頁(無強制回應) 回上頁結果
最後,我們在Page1 這個頁面中,點選回上頁按鈕,我們將又回到了首頁頁面。
Xamarin.Forms 強制對話窗的起始畫面
  • 使用強制回應Modal 對話窗的頁面導航
    接下來﹐我們先要來進行強制回應Modal 對話窗的頁面導航的測試,我們將會使用首頁頁面上的中間兩個按鈕,也就是第三個與第四個按鈕,這兩個按鈕的相對應檢視模型ViewModel 中的C# 程式碼,分別如下:
    若想要使用強制回應Modal 對話窗功能,您需要在_navigationService.NavigateAsync 方法的第三個參數,輸入true 的引數,這樣,這次的頁面導航動作,將會採用強制回應對話窗的導航功能。
c# code
GoPage1ModalCommand = new DelegateCommand(async () =>
{
    await _navigationService.NavigateAsync("Page1Page", null, true);
});
GoPage2ModalCommand = new DelegateCommand(async () =>
{
    await _navigationService.NavigateAsync("Page1Page/Page2Page", null, true);
});
當按下了導航到下一頁(有強制回應) 這個按鈕,將會執行上述第一個按鈕命令的程式碼,此時,行動裝置上將會出現如下圖的畫面,這表示了Xamarin.Forms成功的幫我們導航到了Page1 這個頁面,此時,我們將無法看到頁面上的頁面的名稱Title,而且,在螢幕左上方也出現了回上頁的系統按鈕。
深度思考 想看看
當您在這個頁面的時候,請嘗試使用手勢操作,從螢幕的最左方往右滑動,看看能否將導航抽屜面板推出來。
若我們使用裝置的實體回上頁按鈕,會讓我們回到了HomePage 這個頁面上。
這樣的情境應用對於有些情況下,我們需要用到全螢幕的畫面,自行來定義這個頁面的相關視覺內容,是相當實用的,因為,在這裡,我們將再也看不到了導航工具列的出現,因此,對於您想要在導航工具列上做出不同的客製化應用,您就可以自行在這個ContentPage 上,使用相關控制項做出屬於您自己的導航工具列按鈕、圖示、提醒徽章、甚至圖片。
警告 警告
不過,在使用這樣的應用情境下,您需要別的注意,在這個頁面上,您沒有左上角的回上頁按鈕可以使用,若當時您的行動裝置平台是iOS 的iPhone 或者iPad 這樣的裝置,由於iOS 的裝置是沒有回上頁的實體按鈕,因此,您需要在這個頁面中,特別設計可以回上頁的按鈕或者手勢操作控制項,否則,使用者是無法回到上頁頁面中。
導航到下一頁(有強制回應)
現在,讓我們點選第二個按鈕,也就是導航到下兩頁(有強制回應) 這個按鈕,將會執行上述第二個按鈕命令的程式碼,此時,行動裝置上將會出現如下圖的畫面,這表示了Xamarin.Forms 成功的幫我們導航到了Page2 這個頁面,此時,同樣的,我們將無法看到頁面上的頁面的名稱Title,而且,在螢幕左上方也出現了回上頁的系統按鈕。
導航到下兩頁(有強制回應)
若我們使用裝置的實體回上頁按鈕,會讓我們回到了Page1Page 這個頁面上,這樣的結果符合我們的預期並且也是我們通常在Xamarin.Forms 專案中所做的頁面導航技巧。
警告 警告
不過,在使用這樣的應用情境下,您需要別的注意,在這個頁面上,您沒有左上角的回上頁按鈕可以使用,若當時您的行動裝置平台是iOS 的iPhone 或者iPad 這樣的裝置,由於iOS 的裝置是沒有回上頁的實體按鈕,因此,您需要在這個頁面中,特別設計可以回上頁的按鈕或者手勢操作控制項,否則,使用者是無法回到上頁頁面中。
導航到下一頁(有強制回應)
最後,我們在Page1 這個頁面中,使用裝置的實體回上頁按鈕,我們將又回到了首頁頁面。
Xamarin.Forms 強制對話窗的起始畫面
  • 使用有強制回應且有導航頁面NavigationPage 的頁面導航方法
    最後﹐我們先要來進行有強制回應且有導航頁面NavigationPage 的頁面導航的測試,我們將會使用首頁頁面上的最後兩個按鈕,這兩個按鈕的相對應檢視模型ViewModel 中的C# 程式碼,分別如下:
    在這樣的導航方式中,我們將會在要導航頁面路徑中,加入了導航頁面NavigationPage 這個頁面,讓我們來看看,會產生甚麼令人驚奇的效果。
c# code
GoPage1NaviCommand = new DelegateCommand(async () =>
{
    await _navigationService.NavigateAsync("NavigationPage/Page1Page", null, true);
});
GoPage2NaviCommand = new DelegateCommand(async () =>
{
    await _navigationService.NavigateAsync("NavigationPage/Page1Page/Page2Page", null, true);
});
當按下了導航到下一頁(有強制回應且有導航頁面) 這個按鈕,將會執行上述第一個按鈕命令的程式碼,此時,行動裝置上將會出現如下圖的畫面,這表示了Xamarin.Forms 成功的幫我們導航到了Page1 這個頁面,而且也看到了這個頁面的名稱Title,另外,不過,我們無法看到了螢幕左上方回上頁的系統按鈕的出現。
深度思考 想看看
當您在這個頁面的時候,請嘗試使用手勢操作,從螢幕的最左方往右滑動,看看能否將導航抽屜面板推出來。
我們可以看得出來,現在這個頁面是個具有導航工具列的起始頁面,原先最初我們設定的導航抽屜之漢堡按鈕也不見了,當然,在這個頁面中,您也無法使用手勢操作,有左往右滑動,推出抽屜面板,因此,我們可以把它當作全新的導航起始點。
若想要回到上頁頁面,我們需要裝置的實體回上頁按鈕,因為,此時螢幕左上方的回上頁按鈕已經不見了,這樣才能夠會讓我們回到了HomePage 這個頁面上。因此,我們需要在這個頁面上,設定一個UI,可以讓使用者來點選,讓這個應用程式可以回到最初的首頁頁面。
導航到下一頁(有強制回應且有導航頁面)
現在,讓我們點選第二個按鈕,也就是導航到下兩頁(有強制回應且有導航頁面) 這個按鈕,將會執行上述第二個按鈕命令的程式碼,此時,行動裝置上將會出現如下圖的畫面,這表示了Xamarin.Forms 成功的幫我們導航到了Page2 這個頁面,而且也看到了這個頁面的名稱Title,另外,我們也看到了螢幕左上方也出現了回上頁的系統按鈕。
導航到下兩頁(有強制回應且有導航頁面)
這樣的結果好像我們在使用非強制回應Modal 對話窗的頁面導航測試階段中看到的結果,可是,若我們使用裝置的實體回上頁按鈕或者螢幕左上方的回上頁按鈕,都會讓我們回到了Page1Page 這個頁面上;在這裡,我們看到的Page1 頁面,是沒有左上角的回上頁按鈕,僅有顯示頁面名稱的導航工具列出現。
導航到下一頁(有強制回應且有導航頁面)
最後,我們在Page1 這個頁面中,使用裝置的實體回上頁按鈕,我們將又回到了首頁頁面。
Xamarin.Forms 強制對話窗的起始畫面

延伸閱讀

結論

現在在Prism Template Pack 2.0.8 與Prism Library 7.0 的加持下,我們可以設計出更加多元與變化之頁面導航效果;在之前的頁面導航操作過程中,不論您導航到哪個頁面,我們都可以使用手勢操作把最前面的導航抽屜面板推出來,這樣的使用情境有時無法滿足我們的需求,因此,在以往,我們需要另外透過C# 程式碼來關閉這個功能;然而,現在我們可以不再需要這麼做了,就可以做到滿足我們的需求。
另外,有些時候,我們需要在同一個App 內,擁有多個導航抽屜內容,您可以試著嘗試看看,能否做出這樣的效果。
若您對於Xamarin / Xamarin.Forms的開發上有興趣或者有疑問的話,可以參考 Xamarin / Xamarin.Forms 行動跨平台Mobile Cross-Platform開發學習指引問答集FAQ 這篇文章內容。