XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

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

2018/05/31

Xamarin.Android 執行時期的權限處理說明 1 最低的 Android 版本 / 目標 Android 版本 / 目標 Framework

這篇文章的說明範例專案,可以從 這裡 取得

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式
當我們在進行 Xamarin.Android 專案建置的時候,我們可以從 Android 專案內的 Properties 中,也就是這個專案的屬性視窗內,看到關於這三個名詞:最低的 Android 版本 / 目標 Android 版本 / 目標 Framework。若您想要更進一步的瞭解這三者的差異,可以參考 了解 Android API 層級 ,底下根據這篇文章內容,列出這三者的說明
  • 目標 Framework (也稱為 compileSdkVersion)
    是在建置階段編譯您的應用程式特定的 Android 架構版本 (API 層級)
  • 最低的 Android 版本 (也稱為 minSdkVersion)
    是 Android OS (亦即,最低 API 層級),可以安裝和執行應用程式的舊版本
  • Android 的版本為目標 (也稱為 targetSdkVersion)
    是應用程式開發介面層級的 Android 裝置,應用程式預期執行。
由於 從 2018年 8 月之後 ,Google Play Console 將會需要新的應用程式目標應用程式開發介面層級 26 (Android 8.0) 或更高版本,這裡所指的是 目標 Android 版本。 現有的應用程式必須以 API 層級 26 或更高版本。如需詳細資訊,請參閱 改善應用程式安全性和效能
因此,在這裡,我們要來做些小測試,了解如何進行執行時期的權限檢查與請求使用者授予使用這些權限的程式設計方法。

建立測試專案

  • 首先,使用 Xamarin.Forms for Prism 建立一個專案
  • 滑鼠雙擊 Android 專案內的 Properties 節點,此時,這個專案的屬性視窗會顯示出來
  • 請切換到 應用程式 標籤頁次,點選 使用下列 Android 版本編譯: (目標 Framework) 下拉選單,確定選擇的是 使用最新平台,這裡是會設定 compileSdkVersion
  • Android Target Version compileSdkVersion
    這裡是英文版的 Visual Studio 2017 看到的內容,我們可以看到這個欄位名稱為 Compile using Android version: (Target Framework)
    Android Target Version compileSdkVersion
  • 請切換到 Android 資訊清單 (Manifest) 標籤頁次,點選 目標 Android 版本 下拉選單,根據文件建議,請在這裡選擇最新的 SDK 版本,這裡是會設定 targetSdkVersion
  • Android Target Version compileSdkVersion
    這裡是英文版的 Visual Studio 2017 看到的內容,我們可以看到這個欄位名稱為 Target Android version
    Android Target Version compileSdkVersion
  • 請打開 Android 專案內的 MainActivity.cs 檔案,在組件層級,也就是命名空間之外,輸入底下程式碼,我們在這宣告了這個 Android 應用程式,需要使用這四種權限 Permission
[assembly: UsesPermission(Name = Android.Manifest.Permission.ReadExternalStorage)]
[assembly: UsesPermission(Name = Android.Manifest.Permission.WriteExternalStorage)]
[assembly: UsesPermission(Name = Android.Manifest.Permission.Camera)]
[assembly: UsesPermission(Name = Android.Manifest.Permission.CallPhone)]
  • 現在,請開始執行這個專案,您可以使用 Android SDK 6.0 以上的模擬器或者是實體手機來進行測試。
  • 當這個應用程式執行完成後,請中止執行,並且在 Android 裝置或模擬器上,打開設定中的應用程式頁面,請在清單中找到您剛剛執行成功的應用程式名稱,並且點選它
  • 當這個應用程式打開之後,您會看到這個應用程式明細頁面中,有個 Permissions 選項,請點選它,接著您就會發現到,這裡有三個 Permissions,不過,他們都是沒有被啟用的。
    Visual Studio for Android Emulator Visual Studio for Android Emulator
    Visual Studio for Android Emulator Visual Studio for Android Emulator
  • 此時,若您的應用程式中,剛好有執行與使用到這三個權限相關的 API,不幸的是,您的應用程式,就會造成閃退的問題。

加入執行時期的所需權限檢查

  • 建立一個 RuntimePermissionEvent 類別,用來定義 事件聚合器需要訂閱的事件類別
public class RuntimePermissionEvent : PubSubEvent<RuntimePermissionPayload>
{

}

public class RuntimePermissionPayload
{

}
  • 請將測試頁面的 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"
             x:Class="XFRunPermission.Views.MainPage"
             Title="{Binding Title}">

    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
        <Label Text="Welcome to Xamarin Forms and Prism!" />
        <Button Text="Permission 檢查與授權" Command="{Binding CheckPermissionCommand}" />
    </StackLayout>

</ContentPage>
  • 請將該頁面的 ViewModel,修改成為底下 C# 程式碼
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XFRunPermission.Events;

namespace XFRunPermission.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {
        private readonly IEventAggregator _eventAggregator;
        public DelegateCommand CheckPermissionCommand { get; set; }
        public MainPageViewModel(INavigationService navigationService,
            IEventAggregator eventAggregator) 
            : base (navigationService)
        {
            _eventAggregator = eventAggregator;
            CheckPermissionCommand = new DelegateCommand(() =>
            {
                _eventAggregator.GetEvent<RuntimePermissionEvent>().Publish(
                    new RuntimePermissionPayload());
            });
            Title = "Main Page";
        }
    }
}
  • 在 Android 專案內找到 MainActivity.cs 節點,將這個檔案內的內容,使用底下程式碼來替換
using Android.App;
using Android.Content.PM;
using Android.OS;
using Prism;
using Prism.Events;
using Prism.Ioc;
using XFRunPermission.Events;

[assembly: UsesPermission(Name = Android.Manifest.Permission.ReadExternalStorage)]
[assembly: UsesPermission(Name = Android.Manifest.Permission.WriteExternalStorage)]
[assembly: UsesPermission(Name = Android.Manifest.Permission.Camera)]
[assembly: UsesPermission(Name = Android.Manifest.Permission.CallPhone)]
namespace XFRunPermission.Droid
{
    [Activity(Label = "XFRunPermission", Icon = "@mipmap/ic_launcher", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        IEventAggregator fooIEventAggregator;
        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

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

            var fooContainer = ((App.Current) as Prism.Unity.PrismApplication).Container;
            fooIEventAggregator = fooContainer.Resolve<IEventAggregator>();
            fooIEventAggregator.GetEvent<RuntimePermissionEvent>().Subscribe(x =>
            {
                CheckPermissions();
            });
        }

        void CheckPermissions()
        {
            if ((int)Build.VERSION.SdkInt < 23)
            {
                return;
            }

            var fooRead = CheckSelfPermission(Android.Manifest.Permission.ReadExternalStorage);
            var fooWrite = CheckSelfPermission(Android.Manifest.Permission.WriteExternalStorage);
            if ((fooRead == (int)Permission.Granted) && (fooWrite == (int)Permission.Granted))
            {
                var foo = 0;
            }
            else
            {
                Android.Support.V4.App.ActivityCompat.RequestPermissions(this,
                    new string[] { Android.Manifest.Permission.ReadExternalStorage, Android.Manifest.Permission.WriteExternalStorage,
                    Android.Manifest.Permission.CallPhone, Android.Manifest.Permission.Camera}, 4);
            }
        }
        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
        {
            var foo = 1;
        }
    }

    public class AndroidInitializer : IPlatformInitializer
    {
        public void RegisterTypes(IContainerRegistry container)
        {
            // Register any platform specific implementations
        }
    }
}

開始進行測試

  • 請再度執行這個專案,這個時候,首頁會看到這個畫面,請點選螢幕上的按鈕
    Visual Studio for Android Emulator
  • 當按鈕按下去之後,您將會看到了下圖畫面,系統將會要求您同意授權給予三種應用程式使用權限,在這裡,請在第一個權限使用授權點選 DENY,另外兩個,可以點選 ALLOW
  •  Visual Studio for Android Emulator Visual Studio for Android EmulatorVisual Studio for Android Emulator
  • 完成後,您就可以關閉這個應用程式,回到 設定 > 應用程式,來查看這個應用程式可以使用權限的說明,現在,我們可以看到這個應用程式,經過使用者授權,已經可以使用 Camera & Phone 兩種 Permission 了。
  •  Visual Studio for Android Emulator




2018/05/29

關於 Xamarin.Android 提交到 Google Play 時候,得到 變更應用程式的目標 API 等級 的警告訊息

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式

今天收到學員提問的問題,發現到若您沒有做些調整,當您要把 Xamarin.Forms 的 APK 佈署檔案上傳到 Google Play 時候,會得到底下錯誤訊息
警告
你的應用程式目前的目標 API 等級是 17,請將最低目標 API 等級調整為 26,以確保應用程式採用最新的 API,讓安全性與執行效能達到最佳狀態。
自 2018 年 8 月起,新應用程式的指定目標至少必須是 Android 8.0 (API 等級 26)。
自 2018 年 11 月起,應用程式更新的指定目標必須是 Android 8.0 (API 等級 26)。
Your app currently targets API level 17 and must target at least API level 26 to ensure it is built on the latest APIs optimised for security and performance.
From August 2018, new apps must target at least Android 8.0 (API level 26).
From November 2018, app updates must target Android 8.0 (API level 26).
Meet Google Play's target API level requirement
此時,我連上我的 Google Play Console 來查看之前上架的 App 資訊,從下圖中,您將會看到,在 區別性 APK 詳細資料 欄位中,底下的 目標 SDK 版本並沒有定義支援哪個版本。
Android Target SDK Version
由於有這項新的政策推出,我們在進行 Android 發佈檔案 .apk 的時候,請依照底下步驟進行設定,這樣所產生出來的 .apk 檔案提交到 Goolge Play 上的時候,就不會出現這樣的警告訊息了。
  • 請展開 Android 專案內的 Properties 節點,滑鼠雙擊 AndroidManifest.xml
    AndroidManifest.xml
  • 在 AndroidManifest.xml 加入這行
  • 底下是我這裡測試的檔案內容
    在這裡,我的開發環境的最新 Android SDK 有安裝 SDK 8.1,因此,我將會設定我的目標 Framework 為 API Level 27
    您可以依據您當時的開發環境來進行設定,若您的開發環境沒有安裝 Android SDK 8.1 (API Level 27),您可以選擇 Android SDK 8.0 (API Level 26)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="11" android:versionName="1.3.8" package="com.vulcanlab.LOBForm" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="17" />
    <uses-sdk android:targetSdkVersion="27" />
    <uses-permission android:name="android.permission.INTERNET" />
    <application android:label="企業表單" android:icon="@drawable/icon"></application>
</manifest>
  • 滑鼠雙擊 Android 專案中的 Properties 節點
  • 切換到 應用程式 Application 標籤頁次視窗
  • 確認 使用下列 Android 版本編譯: (目標 Framework) 下拉選單,有選取 使用最新平台 (Android 8.1 (Oreo)) 或者 Android 8.1 (Oreo) Meet Google Play's target API level requirement
  • 現在,您可以進行這個 Android 專案的封存動作,在完成封存與程式碼簽名動作之後,將會得到一個 .apk 檔案,此時,您可以將這個 .apk 檔案上傳到 Google Play 上
  • 如此,您將不會再看到警告訊息,如同下圖所示
Meet Google Play's target API level requirement
當這個 App 上架成功之後,我再度到 Goolge Play Console 中查看這個 APK 詳細資訊,此時, 目標 SDK 版本 就會出現我所指定的 27 版本資訊
Android Target SDK Version

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式


2018/05/23

Xamarn.Forms TabbedPage 標籤式頁面的不同情境之使用

在從事 Xamarin.Forms 教學經驗中,有許多人都在詢問要如何設計出具有 TabbedPage 標籤式頁面 功能,不過,因為我個人比較少使用這個頁面,通常遇到這樣的視覺需求,我都是自行設計使用者控制向來完成這樣的需求,因此,在這篇文章中,我們將要來體驗 TabbedPage 標籤式頁面在 Xamarin.Forms 中要如何使用?不過,我們在這裡所練習的架構,是採用 Prism 框架下的 MVVM 設計模式,並且需要使用到 Prism7 的版本才能夠時做出來。
這篇文章所提到的所有專案原始碼,可以從 這裡 取得
在我們的練習情境之中,我們需要設計一個應用程式的首頁為 TabbedPage 標籤式頁面,在這個 TabbedPage 標籤式頁面中,共會存在四個標籤 (在 Prism7 中,我們可以使用這個查詢字串 createTab 來完成宣告,這個標籤頁面總共需要有多少個頁面存在),不過,我們需要指定第三個標籤頁面為我們預設顯示的頁面,底下為我們實際執行後的螢幕截圖,預設顯示第三頁面內容 (在 Prism7 中,我們可以使用這個查詢字串 selectedTab 來指定預設顯示的頁次是哪個)。
TabbedPage 標籤式頁面
另外,我們希望切換到第二個標籤頁面的時候,他是具有導航工具列的效果,也就是說,我們切換到第二頁的時候,在第二頁內會有一個按鈕,按下這個按鈕之後,將會導航到另外一個新的頁面,在此同時,螢幕上會有導航工具列出現,如同下圖:
TabbedPage 標籤式頁面 TabbedPage 標籤式頁面
不過,若我們沒有使用 Prism7 所提供的標籤頁面之特定查詢字串,此時,我們將會變成這樣的情境,這樣的結果將不是我們所期望需要的。
TabbedPage 標籤式頁面 TabbedPage 標籤式頁面
接下來,我們來實際練習開發出這樣的應用。

建立練習專案

  • 首先,我們使用 Prism Template Pack (現在使用的 2.0.9 版本)建立起一個 Xamarin.Forms 開發專案
  • 我們需要建立一個 TabbedPage 頁面,我們把它命名為 MainTabbedPage,在這個頁面 View 與頁面檢視 ViewModel 中,我們不需要做任何特別處理。
  • 另外,因為我們需要有許多標籤頁次需要顯示,因此,我們需要建立出五個 ContentPage,所以,我們產生出五個 ContentPage,分別名稱為 Page1, Page2, Page3, Page4, Page21。 Visual Studio 2017 Solution Explorer
  • 接下來,我們需要分別將這五個 ContentPage 的 View 和 ViewModel 進行設計
  • 最後,我們需要修正 Xamarin.Forms 專案的進入點 ( Entry Point ),也就是 App.xaml.cs 檔案,我們將其打開,在 OnInitialized 方法內,將 await NavigationService.NavigateAsync 方法內需要的引數,修改成為 MainTabbedPage?createTab=Page1&createTab=NavigationPage|Page2&createTab=Page3&createTab=Page4&selectedTab=Page3
    在這裡,我們透過了查詢字串中 createTab 這個參數,指定這個標籤式頁面需要那些 ContentPage,由於我們在這個練習中,總共需要用到四個標籤頁面,所以,我們在查詢字串中,總共需要使用到四次的 createTab 。其中,您將會看到在第二個 createTab 中,其設定值似乎與其他的不太一樣,在這裡,我們需要在頁面2中,提供導航到其他頁面的機制,因此,我們需要在 createTab 參數名稱之後,先使用 NavigationPage 緊接著使用管道 | 字元,最後再加上 Page2,這表示了,第二個標籤頁面中,將會顯示出具有導航工具列的效果。
    TabbedPage 標籤式頁面
    而在頁面2中,我們放置了一個按鈕,當您按下這個按鈕之後,將會導航到 Page21 這個頁面,底下是我們所期望呈現的結果。我們可以看到,最上方會出現導航工具列,並且會有回上頁的按鈕出現,不過,新的頁面還是出現在 TabbedPage 標籤式頁面的第二個頁次中。
    TabbedPage 標籤式頁面
    若您的專案需要不想要做到這樣的結果,您可以參考頁面1的設計,在這個頁面中,我們放置了一個按鈕,按下這個按鈕,就一樣會導航到 Page21,不過,當您切換到 頁面1 頁次的時候,看到的是如下螢幕截圖,他與頁面2 的內容完全不一樣,您知道為什麼會有這樣的結果嗎?
    TabbedPage 標籤式頁面
    當我們在頁面1點選了這個按鈕,此時,將會出現如下圖內容。
    TabbedPage 標籤式頁面

MainTabbedPage 的 View 內容

<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage 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="XFTabbed2.Views.MainTabbedPage">

</TabbedPage>

Page1 的 View 內容

<?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="XFTabbed2.Views.Page1"
             Title="{Binding Title}"
             BackgroundColor="LightBlue">

    <StackLayout
        >
        <Button
            Text="Go Page21"
            Command="{Binding GoNextCommand}"/>
    </StackLayout>
</ContentPage>

Page1ViewModel 的 ViewModel 程式碼

using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;

namespace XFTabbed2.ViewModels
{
    using System.ComponentModel;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    public class Page1ViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public string Title { get; set; }
        private readonly INavigationService _navigationService;
        public DelegateCommand GoNextCommand { get; set; }
        public Page1ViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            Title = "頁面 1";
            GoNextCommand = new DelegateCommand(() =>
            {
                _navigationService.NavigateAsync("Page21");
            });
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {

        }

    }
}

Page2 的 View 內容

<?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="XFTabbed2.Views.Page2"
             Title="{Binding Title}"
             BackgroundColor="LightGoldenrodYellow">

    <StackLayout
        >
        <Button
            Text="Go Page21"
            Command="{Binding GoNextCommand}"/>
    </StackLayout>

</ContentPage>

Page2ViewModel 的 ViewModel 程式碼

using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;

namespace XFTabbed2.ViewModels
{
    using System.ComponentModel;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    public class Page2ViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public string Title { get; set; }
        private readonly INavigationService _navigationService;
        public DelegateCommand GoNextCommand { get; set; }
        public Page2ViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            Title = "頁面 2";
            GoNextCommand = new DelegateCommand(() =>
            {
                _navigationService.NavigateAsync("Page21");
            });
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {

        }

    }
}

Page21 的 View 內容

<?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="XFTabbed2.Views.Page21"
             Title="{Binding Title}"
             BackgroundColor="LightPink">

</ContentPage>

Page21ViewModel 的 ViewModel 程式碼

using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;

namespace XFTabbed2.ViewModels
{
    using System.ComponentModel;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    public class Page21ViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public string Title { get; set; }
        private readonly INavigationService _navigationService;

        public Page21ViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            Title = "頁面 21";
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {

        }

    }
}

Page3 的 View 內容

<?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="XFTabbed2.Views.Page3"
             Title="{Binding Title}"
             BackgroundColor="LightSalmon">

</ContentPage>

Page3ViewModel 的 ViewModel 程式碼

using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;

namespace XFTabbed2.ViewModels
{
    using System.ComponentModel;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    public class Page3ViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public string Title { get; set; }
        private readonly INavigationService _navigationService;

        public Page3ViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            Title = "頁面 3";
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {

        }

    }
}

Page4 的 View 內容

<?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="XFTabbed2.Views.Page4"
             Title="{Binding Title}"
             BackgroundColor="LightSteelBlue">

</ContentPage>

Page4ViewModel 的 ViewModel 程式碼

using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;

namespace XFTabbed2.ViewModels
{
    using System.ComponentModel;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    public class Page4ViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public string Title { get; set; }
        private readonly INavigationService _navigationService;

        public Page4ViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            Title = "頁面 4";
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {

        }

    }
}