Xamarin Forms的Prism概述:第一部分

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

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

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

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

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