Xamarin.Forms 客製化導航服務 Navigation Service 與檢視模型定位器 ViewModel Locator
該文件的專案原始碼可以透過 GitHub 來取得
設定 MyGet 的可用套件來源
現在,已經把 檢視模型定位器 ViewModel Locator 與 客製化的導航服務推送到 MyGet 站台上,因此,可以透過底下方式是,來安裝這個 NuGet 套件
- 點選 Visual Studio 2019 功能表 [工具] > [選項]
- 當 [選項] 對話出現的時候,從左邊清單選項找到 [NuGet 套件管理員]
- 在左方 [名稱] 欄位中輸入適當的名字,這裡將會輸入
上課練習或者體驗專案會使用到的常用功能
- 在左方 [來源] 欄位中輸入
[上課練習或者體驗專案會使用到的常用功能](https://www.myget.org/F/course-lab/api/v3/index.json)
這個 URL - 點選 [更新] 按鈕)
- 記得要確認 [可用套件來源] 選項中,剛剛建立的套件來源項目有被勾選
現在可以建立一個練習測試用的專案
建立測試用的專案
- 請啟動 Visual Studio 2019
- 在啟動後的對話窗內,選擇右下方的 [建立新的專案] 選項
- 當顯示出 [建立新專案] 對話窗,請在中間上方的文字輸入盒內,輸入 Xamarin 這個關鍵字
- 此時將會在中間清單區域,找到 [行動應用程式 (Xamarin.Forms)] 這個選項,請點選這個項目
- 在 [設定新的專案] 對話窗內,請在專案名稱欄位內,輸入 CustNaviService
- 請點選右下方的 [建立] 按鈕
- 現在將會跳出一個 [New Cross Platform App - CustNaviService] 對話窗
- 請選擇 選取範本 區域內的 [空白] 項目
- 在下方的 [平台] 區域,請記得勾選 Android , iOS , Windows (UWP) 這三個檢查盒 Checkbox
- 點選右下方的 [OK] 按鈕
- 開始建立 Xamarin.Forms 開發方案
加入 檢視模型定位器 ViewModel Locator 與 客製化的導航服務 套件
- 滑鼠右擊 CustNaviService 核心專案節點
- 選擇管理 NuGet 套件選項
- 當 [NuGet: CustNaviService] 視窗 出現後,請在右上方的 [套件來源] 下拉選單中,選擇 [上課練習或者體驗專案會使用到的常用功能] 這個項目
- 在 [NuGet: CustNaviService] 視窗中,點選瀏覽頁次)
- 現在將會看到這裡擁有兩個 NuGet 套件,請選擇 [Vulcan.Courses.XamarinForms] 這個套件)
- 點選 [安裝] 按鈕,安裝這個套件到核心專案內
使用 MVVM 命名慣例來建立所需要用到的方案資料夾
- 滑鼠右擊 CustNaviService 核心專案節點
- 選擇 [加入] > [新增資料夾] 選項
- 請輸入 ViewModels 這個文字做為該資料夾的名稱
- 滑鼠右擊 CustNaviService 核心專案節點
- 選擇 [加入] > [新增資料夾] 選項
- 請輸入 Views 這個文字做為該資料夾的名稱
建立導航頁面 NavigationPage 與導航抽屜頁面 MasterDetailPage
這裡需要先建立起這兩個頁面,因此,請在 [Views] 資料夾內,建立這兩種類型的頁面
在這裡將會建立一個名為
NaviPage
的導航頁面
在這裡將會建立一個名為
MDPage
的導航頁面建立會用到的其他內容頁面 ContentPage 與 相對應的 檢視模型 ViewModel 類別
請在 [Views] 資料夾內,建立
HomePage
AboutPage
LoginPage
這三個頁面
請在 [ViewModels] 資料夾內,建立
HomePageViewModel
AboutPageViewModel
LoginPageViewModel
MDPageViewModel
MDPageMasterViewModel
這五個檢視模型類別
最後完成的結果如底下螢幕截圖
)
使用 檢視模型定位器 ViewModel Locator
想要使用這項服務,對於頁面 ( 檢視 ) 檔案 與 檢視模型 檔案,需要遵守底下的命名慣例
- 對於 頁面 ( 檢視 View ) 的 XAML 文件檔案,需要建立在 Views 這個方案資料夾內
- 每個頁面檔案名稱,最後需要使用 Page 作為結為,例如,這裡有建立一個 HomePage.xaml 這樣的 XAML 頁面文件檔案
接著,需要在每個頁面中,建立一個命名空間,這個命名空間要指向 服務定位器所在的 .NET 命名空間內 Namespace
在底下的 XAML 文件中,將會使用 xmlns 來宣告一個新的 viewmodelLocator 命名空間,其中, viewmodelLocator 稱之為 前置詞 prefix
有了這個前置詞,就可以使用 AutoWireViewModel 這個附加屬性,依據所規範的命名慣例,自動找出該頁面所匹配的檢視模型,當然,該檢視模型將會位於該專案的 ViewModel 方案資料夾內。
xmlns:viewmodelLocator="clr-namespace:Vulcan.Courses.XamarinForms.NaviServices;assembly=Vulcan.Courses.XamarinForms"
viewmodelLocator:ViewModelLocator.AutoWireViewModel="True"
使用 客製化導航服務
為了要能夠使用這裡所設計的 客製化導航服務,每個檢視模型 ViewModel 類別,需要繼承
ViewModelBase
這個基底類別,該類別是在之前所安裝的 NuGet 內所定義的。
另外,還需要在每個檢視模型類別的建構函式內,呼叫基底類別的建構函式,這裡需要傳入一個導航服務物件。不過,這裡的導航服務物件將會位於此 Xamarin.Forms 專案內的程式進入點,也就是在
App
這個類別內有宣告,稍後,將會交代如何做這一的設計。
從底下的範例程式碼可以看出,透過 Application.Current 這個靜態變數,取得了一個
App
型別的物件,就可以取出 NavigationService 這個變數物件值。public class AboutPageViewModel : ViewModelBase
{
public AboutPageViewModel() : base((Application.Current as App).NavigationService)
{
}
}
現在,請打開 App.xaml.cs 這個檔案節點,在 App 這個類別內,加入一個公開屬性
public INaviService NavigationService { get; set; }
,接著在該 App 類別的建構函式內,建立 NavigationService 這個物件,這裡使用了 NavigationService = new NaviService( () => { return Application.Current.MainPage as NaviPage; }, page => { return new NaviPage(page); }, () => { return (Application.Current.MainPage as MDPage)?.Detail as NaviPage; }, () => { return Application.Current.MainPage as MDPage; }, () => { return new MDPage(); });
對於
NaviService
的建構函式,將會需要傳遞五個委派方法,分別是要取得導航頁面的委派方法、產生一個新的導航頁面的委派方法、取得一個導航抽屜頁面+導航頁面的委派方法、取得一個導航抽屜的委派方法、產生一個導航抽屜的委派方法;所以,請將上面的所列的表示式,放到這個 App 類別內的建構函式內即可。
在這個 App 類別內的建構函式,將會看到如何使用客製化導航服務的頁面導航的程式設計程式碼,
NavigationService.NavigateToAsync<LoginPageViewModel>(NavigateMode.Absolute);
;這裡將會需要使用 NavigateToAsync 這個泛型方法,在泛型型別參數內,指定要導航過去的檢視模型型別名稱即可,而在這個方法內,可以指定一個導航模式,也就是 NavigateMode 這裡列舉型別。這裡使用到了 NavigateMode.Absolute 這個列舉值,表示將會使用沒有導航頁面的模式,切換到指定的內容頁面上。public partial class App : Application
{
public INaviService NavigationService { get; set; }
public App()
{
InitializeComponent();
NavigationService = new NaviService(
() => { return Application.Current.MainPage as NaviPage; },
page => { return new NaviPage(page); },
() => { return (Application.Current.MainPage as MDPage)?.Detail as NaviPage; },
() => { return Application.Current.MainPage as MDPage; },
() => { return new MDPage(); });
//MainPage = new MainPage();
NavigationService.NavigateToAsync<LoginPageViewModel>(NavigateMode.Absolute);
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
其他的頁面導航操作
若想要使用具有導航頁面的模式的頁面,可以使用
NavigationService.NavigateToAsync<LoginPageViewModel>(NavigateMode.Absolute);
這樣的用法
而要使用具有導航抽屜的頁面模式(一定會使用到導航頁面),可以使用
NavigationService.NavigateToAsync<HomePageViewModel>(NavigateMode.Master);
若在導航抽屜頁面下,想要切換到僅有導航頁面(沒有抽屜)的模式下,可以使用
NavigationService.NavigateToAsync<LoginPageViewModel>(NavigateMode.RestartRelative);
實際執行結果
底下是應用程式一起動要顯示的頁面,沒有任何導航頁面與導航抽屜頁面,這裡是使用
NavigationService.NavigateToAsync<LoginPageViewModel>(NavigateMode.Absolute);
)
當在上面畫面點選了 [登入] 按鈕,出現底下畫面(有導航抽屜),這裡使用的是
NavigationService.NavigateToAsync<HomePageViewModel>(NavigateMode.Master);
)
在 [首頁] 頁面下方,點選 [關於] 按鈕,將會使用導航頁面功能,切換到關於頁面,這裡使用的是
NavigationService.NavigateToAsync<AboutPageViewModel>();
)
在關於頁面左上方,將會看到一個箭頭,這表示現在這個頁面在導航堆疊結構中,前面至少還有一個頁面,此時,點選左上方箭頭(或者點選下方實體回上頁按鈕),便可以回到上一頁頁面。
)
在 [關於] 頁面內,從裝置外部左方往右滑動,將會看到導航抽屜出現,這裡的 Page 1 ~ Page 4 都會有建置功能,可以分批測試看看。
首先點選 Page 1,將會直接回到 [首頁] 頁面, 並且清空導航堆疊內的所有頁面,這裡使用的是
NavigationService.NavigateToAsync<HomePageViewModel>(NavigateMode.Master);
)
再度滑出導航抽屜,點選 Page 2,將會直接回到 [關於] 頁面, 並且清空導航堆疊內的所有頁面,這裡使用的是
NavigationService.NavigateToAsync<AboutPageViewModel>(NavigateMode.Master);
)
滑出導航抽屜,點選 Page 3,將會直接回到 [登入] 頁面,此時沒有導航抽屜頁面,也沒有導航頁面的存在,這裡使用的是
NavigationService.NavigateToAsync<LoginPageViewModel>(NavigateMode.Absolute);
)
請點選 [登入] 按鈕,進入到有導航抽屜頁面的首頁內,滑出導航抽屜,點選 Page 4,將進入到 [登入] 頁面,不過,此時沒有導航抽屜頁面,但卻有導航頁面的存在,這裡使用的是
NavigationService.NavigateToAsync<LoginPageViewModel>(NavigateMode.RestartRelative);
)