Xamarin Forms的Prism概述:第一部分

盡管我知道它不可能是每個項目都適用的技術(shù),但我還是Xamarin Forms的超級粉絲。如果你看完我的博客,你可能現(xiàn)在也是一名Xamarin粉,我是一個Windows開發(fā)者,Xamarin Forms允許我使用基本上都是我現(xiàn)有的技能(XAML、綁定、MVVM,等等)來創(chuàng)建應用程序,也包括其他流行的像iOS和Android平臺。此外,它給了我利用平臺的具體功能的機會(如本地Xamarin),同時保持與操作系統(tǒng)的外觀和感覺相一致的用戶體驗。
我最近用Xamarin Forms為我簡單的Qwertee Shirts app創(chuàng)造一個Android移植,優(yōu)勢很明顯:我能夠重用我已經(jīng)寫好的UWP版本的大多數(shù)后端代碼和我的XAML知識,最后,我得到了一個從UI角度完全接受的應用程序,由谷歌創(chuàng)建的新Material Design,所以它不像基于Web技術(shù)的跨平臺應用經(jīng)常發(fā)生的看起來“外星人”一樣。
但是,我不只是一個Windows開發(fā)者也是一個MVVM愛好者,我在多次寫這個話題,涵蓋多個平臺和框架。如果MVVM模式對你來說還是新事物,我建議你從這篇帖子開始看,然后繼續(xù)看完本系列的其余部分。當我決定恢復使用Xamarin Forms時我做的第一件事是轉(zhuǎn)向我的應用程序端口。我正在尋找我的MVVM知識重用以開發(fā)項目的最佳方式,像往常一樣,選擇是艱難的。在這種情況下,更為復雜的是Xamarin Forms,相比其他像WPF或UWP的XAML技術(shù),是相當新的,所以很難找到一個完全滿足我的選擇。
別誤會,如果你還記得我寫UWP應用關(guān)于Template10的帖子,你會知道我是MVVM Light所提供的靈活性的一個超級粉絲,Laurent Bugnion很好的介紹了在本身不支持的平臺的典型MVVM概念(如綁定和命令),像Android和iOS。然而,Xamarin Forms與標準Xamarin相比有一點不同:它已經(jīng)提供了我們需要的概念來使用MVVM模式,如綁定、數(shù)據(jù)背景、依賴屬性、行為等。在這種情況下,MVVM Light仍然是一個極好的選擇但你仍然要推倒重來解決許多你必須處理的常見的場景,當你開發(fā)一個XAML應用程序,如處理導航,進入一個ViewModel導航事件,或通過一頁與另一頁之間的參數(shù)。
就在我開始移植之前,我看到了Brian Lagunas的推特, Prism項目背后的MVP之一,宣布專為Xamarin Forms創(chuàng)建的Prism的新版本。來理清你的頭腦,Prism是一個MVVM框架,最初是由微軟的模式與實踐部門創(chuàng)造的,后來變成了社區(qū)運營的一個開源項目。Prism一直是基于XAML的應用實現(xiàn)MVVM模式的一個很好的選擇,但有時你可能會面臨使項目只是遵循命名約定和規(guī)則而過于復雜的風險(像是有一個引導程序要求對它進行初始化,盡管基于XAML應用程序已經(jīng)啟動稱為App的類)。
完成移植后,我發(fā)現(xiàn)自己對Xamarin Forms的Prism方法感到很滿意,所以我決定與你分享我的經(jīng)驗,希望這會讓你在開始一個新的Xamarin Forms項目時更快地啟動和運行。
創(chuàng)建第一個項目
創(chuàng)建一個基于Prism的Xamarin Forms項目最簡單的方法是使用自己的Visual Studio擴展,你可以從Visual Studio Gallery下載。安裝完畢后,你將在Visual Studio中找到一個新的稱為“Prism”的部分,每個支持的技術(shù)都有不同的模板。我們感興趣的模板被稱為“Prism Unity App (Forms)”:

其實,這個模板有一個優(yōu)于標準Xamarin Forms模板的優(yōu)點。正如你可以從下面的圖片看到的,它允許你當你創(chuàng)建你的項目時選擇你想要作為目標的平臺,而默認的Xamarin Forms模板為每個支持的平臺自動創(chuàng)建一個項目(Android、iOS、Windows Phone 8.1、Windows 8.1、UWP),即使你對它們?nèi)魏我粋€都沒有興趣。

當你點擊Create 項目,你將得到一個標準的Xamarin Forms解決方案:一個便攜式類庫和一個你選擇的每個平臺的特定項目。此外,便攜式類庫已經(jīng)包含:
- Views文件夾,建立你的頁面。包括一個稱為MainPage.xaml的默認的模板。
- ViewModels文件夾,存放你的ViewModels。包括一個稱為MainPageViewModel.cs的默認的模板。
- 一個App類已經(jīng)配置初始化Prism基礎(chǔ)構(gòu)造。
你的默認項目看起來將是這樣:

為了演示Xamarin Forms的Prism,我要創(chuàng)造TrackSeries簡單的客戶端,我的好朋友和同事Adrian Fernandez Garcia和Carlos Jimenez Aliaga創(chuàng)造的電視節(jié)目網(wǎng)站。
讓我們從頭開始,看看哪些引用已被模板自動添加到項目中去了:

你可以看到,除了標準Xamarin Forms NuGet包,Prism還需要兩個套包:Core(這在每個平臺都是常見的)和Forms(包含Xamarin Forms的特定的助手和服務)。默認情況下,標準模板利用Unity為依賴注入容器,所以你會發(fā)現(xiàn)一堆其他套包像Unity、Prism.Unity.Forms和CommonServiceLocator。然而,如果你不喜歡Unity,Xamarin Forms的Prism會提供了一些額外的套包,整合了其他流行的依賴注入容器,如Ninject或Autofac。
應用程序類
相比老的Prism版本,其中一個最大的變化是引導程序概念的去除,這是一個專門的項目類,負責初始化所有Prism基礎(chǔ)構(gòu)造。Xamarin Forms(同其他XAML技術(shù)一樣)已經(jīng)有一個初始化類:App,包含在便攜式類庫里,所以團隊決定利用它而不是要求開發(fā)人員創(chuàng)建一個新的。默認情況下,這個類是繼承自應用程序類。為了正確地支持Prism,我們需要改變它并讓App類從PrismApplication繼承:
在 App.xaml 文件中,添加新的命名空間標識符 Prism.Unity并用PrismApplication 節(jié)點替換Application 節(jié)點。
<?xml version="1.0" encoding="utf-8" ?> <prism:PrismApplication xmlns="//xamarin.com/schemas/2014/forms" xmlns:x="//schemas.microsoft.com/winfx/2009/xaml" xmlns:prism="clr-namespace:Prism.Unity;assembly=Prism.Unity.Forms" x:Class="InfoSeries.App"> </prism:PrismApplication>
在App.xaml.cs文件中,我們需要改變默認從Application到PrismApplication繼承。
public partial class App : PrismApplication { public App(IPlatformInitializer initializer = null) : base(initializer) { } protected override void OnInitialized() { InitializeComponent(); NavigationService.NavigateAsync("MainPage"); } protected override void RegisterTypes() { Container.RegisterTypeForNavigation<MainPage>(); } }
此外,App類有三個鮮明的特點:
- 具有基本構(gòu)造函數(shù),以一個IPlatformInitializer對象作為參數(shù)。
- 有一個稱為OnInitialized()的方法,我們初始化Forms基礎(chǔ)構(gòu)造(通過調(diào)用InitializeComponent()方法),并且我們觸發(fā)導航到該應用程序的主頁(我們后面將看到導航如何工作的詳細內(nèi)容)。
- 有一個稱為RegisterTypes()的方法,就是我們登記的依賴注入容器(在這種情況下,Unity框架)的每一頁和我們的應用程序所需的所有服務。
默認情況下,IPlatformInitializer參數(shù)為null,它可以在你需要注冊依賴容器一些只在特定平臺的項目存在的特定類別時利用。你會發(fā)現(xiàn),事實上,每個平臺的特定項目都有自己的自定義初始化類(AndroidInitializer 、UWP的UwpInitializer,等等),但是,默認的有RegisterTypes()方法空的實現(xiàn)。下面是UWP項目的MainPage.xaml.cs:
public sealed partial class MainPage { public MainPage() { this.InitializeComponent(); LoadApplication(new DeepNavigation.App(new UwpInitializer())); } } public class UwpInitializer : IPlatformInitializer { public void RegisterTypes(IUnityContainer container) { } }
連接Views和ViewModels
你應該已經(jīng)知道,如果你有一些MVVM的經(jīng)驗,使該模式運行的關(guān)鍵是將ViewModel與它自己的View連接。Xamarin Forms應用程序與Windows應用程序唯一的不同就是定義背景的屬性稱為BindingContext而不是DataContext。Prism使用一種簡單的命名約定來自動分配ViewModel到它的View:
- XAML頁面應該被存儲在一個被稱為Views的項目文件夾中
- ViewModel應存放在一個被稱為ViewModels的項目文件夾中,它需要與頁面相同的名稱加上后綴ViewModel(例如,ViewModel連接到MainPage.xaml將被稱為MainPageViewModel)。
正如你所看到的,這是Prism模板為我們創(chuàng)建的確切的基礎(chǔ)構(gòu)造。我們添加到我們的應用程序的每一個頁面都需要在容器中注冊,以便我們能夠正確地處理導航。為了注冊,我們可以利用App類的RegisterTypes()方法和使用一種由Container提供的稱為RegisterTypeForNavigation< T >的方法,其中T是網(wǎng)頁的類型。在起始模板,我們只有一個稱為MainPage的網(wǎng)頁,所以這是唯一一個在應用程序啟動時自動注冊的頁面。Prism和其他MVVM框架之間有一個最大的差異。使用其他的工具,你只能在容器中注冊ViewModels和最終與他們有關(guān)聯(lián)的所有服務。相反地,使用Prism你只需注冊頁面的類型:根據(jù)Prism自動在容器注冊,ViewModel也連接到View。你可以看到在示例代碼中,我們已經(jīng)注冊了MainPage類而不是MainPageViewModel。
如果你不是命名方法的粉絲,你不必使用它:事實上,RegisterTypeForNavigation()方法有另一個變種,其簽名是RegisterTypeForNavigation< T, Y >(),其中T是頁面的類型,Y是我們要設置為BindingContext的ViewModel的類型。所以,例如,如果你想把你的MainPage連接到一個稱為MyCustomViewModel的ViewModel,使用下面的代碼就足以注冊:
protected override void RegisterTypes() { Container.RegisterTypeForNavigation<MainPage, MyCustomViewModel>(); }
在OnInitialized()方法中你可以預覽默認的導航是如何工作的:每次你調(diào)用RegisterTypeForNavigation< T >方法,Prism注冊NavigationService參考頁面作為關(guān)鍵使用,具有相同類型名稱的字符串。由于我們的頁面的類型是MainPage,我們需要通過字符串“MainPage”作為NavigateAsync()方法參數(shù)觸發(fā)導航到該頁面。如果我們要重寫此行為,我們可以通過作為RegisterTypeForNavigation< T >()參數(shù)自定義字符串,用于后續(xù)的導航,如下面的示例中,我們已經(jīng)用“MyCustomPage”頁面取代了關(guān)鍵的“MainPage”。
public partial class App : PrismApplication { public App(IPlatformInitializer initializer = null) : base(initializer) { } protected override void OnInitialized() { InitializeComponent(); NavigationService.NavigateAsync("MyCustomPage"); } protected override void RegisterTypes() { Container.RegisterTypeForNavigation<MainPage>("MyCustomPage"); } }
然而,在下一篇文章中,我們將看到更多關(guān)于如何以更高級的方式來處理導航的詳細信息。
ViewModel
在Xamarin Forms的Prism中我最欣賞的特點是,它不需要我們在XAML頁面做任何變化就可以支持它(例如,其他一些MVVM框架需要你用定制的一個去改變ContentPage類型)。你只會發(fā)現(xiàn),在MainPage.xaml文件中有一個Prism特性,就像ContentPage項目屬性,稱為ViewModelLocator.AutowireViewModel:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="//xamarin.com/schemas/2014/forms" xmlns:x="//schemas.microsoft.com/winfx/2009/xaml" xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" prism:ViewModelLocator.AutowireViewModel="True" x:Class="InfoSeries.Views.MainPage" Title="MainPage"> <StackLayout HorizontalOptions="Center" VerticalOptions="Center"> <Label Text="{Binding Title}" /> </StackLayout> </ContentPage>
該屬性負責連接View與ViewModel:當它設置為true,ViewModel將自動設置為View 的BindingContext,如果我們遵循先前描述的命名慣例。然而,在“Prism 6.2”中介紹的一個變化是,這個屬性是不再需要的,除非你想通過設置它為false明確禁用命名約定。標準的模板將它添加到一個更完整的示例中,但你仍然可以安全地刪除它。
每個MVVM框架所提供的一個關(guān)鍵的功能是一個類,給我們的ViewModels提供快速訪問到最常用的功能,就像INotifyPropertyChanged接口的實現(xiàn)。Prism也不例外,它提供了一個稱為BindableBase的類,我們的ViewModels可以繼承:
public class MainPageViewModel : BindableBase { private string _title; public string Title { get { return _title; } set { SetProperty(ref _title, value); } } public MainPageViewModel() { } }
多虧這個類,每當我們需要創(chuàng)建一個屬性來實現(xiàn)INotifyPropertyChanged接口(這樣可以通過結(jié)合渠道傳播改變),我們可以簡單的使用屬性調(diào)節(jié)器中的SetProperty()方法。此方法將用于存儲值,同時,發(fā)送一個通知到所有與此屬性綁定的控件,告知它的值已更改,因此需要更新它們的布局。
通過模板創(chuàng)建的示例應用程序正是這樣:它創(chuàng)建一個名為Title的屬性,通過結(jié)合到XAML頁面的Label控件連接。當我們改變屬性的值時,我們會看到用戶界面的實時更新。說實話,這個示例應用程序也展示了一些別的東西:它用一種稱為OnNavigatedTo()的方法設置了Title屬性的值,并且解析了一些參數(shù)。我們將在下一篇文章中看到更多這種方法如何運作的細節(jié)。
在下一篇文章中
在這篇文章中,我們只是觸及表面,展現(xiàn)了Prism創(chuàng)建的Xamarin Forms應用程序的基本概念。在接下來的文章中,我們會看到一些更先進的概念,像在ViewModel處理導航或在依賴容器注冊附加服務。
本文翻譯自:
最新活動推薦:年中大促|(zhì)在線訂購全場7折起!點擊了解詳情>>