Xamarin Forms的Prism概述:第一部分
盡管我知道它不可能是每個(gè)項(xiàng)目都適用的技術(shù),但我還是Xamarin Forms的超級(jí)粉絲。如果你看完我的博客,你可能現(xiàn)在也是一名Xamarin粉,我是一個(gè)Windows開(kāi)發(fā)者,Xamarin Forms允許我使用基本上都是我現(xiàn)有的技能(XAML、綁定、MVVM,等等)來(lái)創(chuàng)建應(yīng)用程序,也包括其他流行的像iOS和Android平臺(tái)。此外,它給了我利用平臺(tái)的具體功能的機(jī)會(huì)(如本地Xamarin),同時(shí)保持與操作系統(tǒng)的外觀和感覺(jué)相一致的用戶(hù)體驗(yàn)。
我最近用Xamarin Forms為我簡(jiǎn)單的Qwertee Shirts app創(chuàng)造一個(gè)Android移植,優(yōu)勢(shì)很明顯:我能夠重用我已經(jīng)寫(xiě)好的UWP版本的大多數(shù)后端代碼和我的XAML知識(shí),最后,我得到了一個(gè)從UI角度完全接受的應(yīng)用程序,由谷歌創(chuàng)建的新Material Design,所以它不像基于Web技術(shù)的跨平臺(tái)應(yīng)用經(jīng)常發(fā)生的看起來(lái)“外星人”一樣。
但是,我不只是一個(gè)Windows開(kāi)發(fā)者也是一個(gè)MVVM愛(ài)好者,我在多次寫(xiě)這個(gè)話(huà)題,涵蓋多個(gè)平臺(tái)和框架。如果MVVM模式對(duì)你來(lái)說(shuō)還是新事物,我建議你從這篇帖子開(kāi)始看,然后繼續(xù)看完本系列的其余部分。當(dāng)我決定恢復(fù)使用Xamarin Forms時(shí)我做的第一件事是轉(zhuǎn)向我的應(yīng)用程序端口。我正在尋找我的MVVM知識(shí)重用以開(kāi)發(fā)項(xiàng)目的最佳方式,像往常一樣,選擇是艱難的。在這種情況下,更為復(fù)雜的是Xamarin Forms,相比其他像WPF或UWP的XAML技術(shù),是相當(dāng)新的,所以很難找到一個(gè)完全滿(mǎn)足我的選擇。
別誤會(huì),如果你還記得我寫(xiě)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)提供了我們需要的概念來(lái)使用MVVM模式,如綁定、數(shù)據(jù)背景、依賴(lài)屬性、行為等。在這種情況下,MVVM Light仍然是一個(gè)極好的選擇但你仍然要推倒重來(lái)解決許多你必須處理的常見(jiàn)的場(chǎng)景,當(dāng)你開(kāi)發(fā)一個(gè)XAML應(yīng)用程序,如處理導(dǎo)航,進(jìn)入一個(gè)ViewModel導(dǎo)航事件,或通過(guò)一頁(yè)與另一頁(yè)之間的參數(shù)。
就在我開(kāi)始移植之前,我看到了Brian Lagunas的推特, Prism項(xiàng)目背后的MVP之一,宣布專(zhuān)為Xamarin Forms創(chuàng)建的Prism的新版本。來(lái)理清你的頭腦,Prism是一個(gè)MVVM框架,最初是由微軟的模式與實(shí)踐部門(mén)創(chuàng)造的,后來(lái)變成了社區(qū)運(yùn)營(yíng)的一個(gè)開(kāi)源項(xiàng)目。Prism一直是基于XAML的應(yīng)用實(shí)現(xiàn)MVVM模式的一個(gè)很好的選擇,但有時(shí)你可能會(huì)面臨使項(xiàng)目只是遵循命名約定和規(guī)則而過(guò)于復(fù)雜的風(fēng)險(xiǎn)(像是有一個(gè)引導(dǎo)程序要求對(duì)它進(jìn)行初始化,盡管基于XAML應(yīng)用程序已經(jīng)啟動(dòng)稱(chēng)為App的類(lèi))。
完成移植后,我發(fā)現(xiàn)自己對(duì)Xamarin Forms的Prism方法感到很滿(mǎn)意,所以我決定與你分享我的經(jīng)驗(yàn),希望這會(huì)讓你在開(kāi)始一個(gè)新的Xamarin Forms項(xiàng)目時(shí)更快地啟動(dòng)和運(yùn)行。
創(chuàng)建第一個(gè)項(xiàng)目
創(chuàng)建一個(gè)基于Prism的Xamarin Forms項(xiàng)目最簡(jiǎn)單的方法是使用自己的Visual Studio擴(kuò)展,你可以從Visual Studio Gallery下載。安裝完畢后,你將在Visual Studio中找到一個(gè)新的稱(chēng)為“Prism”的部分,每個(gè)支持的技術(shù)都有不同的模板。我們感興趣的模板被稱(chēng)為“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è)都沒(méi)有興趣。
當(dāng)你點(diǎn)擊Create 項(xiàng)目,你將得到一個(gè)標(biāo)準(zhǔn)的Xamarin Forms解決方案:一個(gè)便攜式類(lèi)庫(kù)和一個(gè)你選擇的每個(gè)平臺(tái)的特定項(xiàng)目。此外,便攜式類(lèi)庫(kù)已經(jīng)包含:
- Views文件夾,建立你的頁(yè)面。包括一個(gè)稱(chēng)為MainPage.xaml的默認(rèn)的模板。
- ViewModels文件夾,存放你的ViewModels。包括一個(gè)稱(chēng)為MainPageViewModel.cs的默認(rèn)的模板。
- 一個(gè)App類(lèi)已經(jīng)配置初始化Prism基礎(chǔ)構(gòu)造。
你的默認(rèn)項(xiàng)目看起來(lái)將是這樣:
為了演示Xamarin Forms的Prism,我要?jiǎng)?chuàng)造TrackSeries簡(jiǎn)單的客戶(hù)端,我的好朋友和同事Adrian Fernandez Garcia和Carlos Jimenez Aliaga創(chuàng)造的電視節(jié)目網(wǎng)站。
讓我們從頭開(kāi)始,看看哪些引用已被模板自動(dòng)添加到項(xiàng)目中去了:
你可以看到,除了標(biāo)準(zhǔn)Xamarin Forms NuGet包,Prism還需要兩個(gè)套包:Core(這在每個(gè)平臺(tái)都是常見(jiàn)的)和Forms(包含Xamarin Forms的特定的助手和服務(wù))。默認(rèn)情況下,標(biāo)準(zhǔn)模板利用Unity為依賴(lài)注入容器,所以你會(huì)發(fā)現(xiàn)一堆其他套包像Unity、Prism.Unity.Forms和CommonServiceLocator。然而,如果你不喜歡Unity,Xamarin Forms的Prism會(huì)提供了一些額外的套包,整合了其他流行的依賴(lài)注入容器,如Ninject或Autofac。
應(yīng)用程序類(lèi)
相比老的Prism版本,其中一個(gè)最大的變化是引導(dǎo)程序概念的去除,這是一個(gè)專(zhuān)門(mén)的項(xiàng)目類(lèi),負(fù)責(zé)初始化所有Prism基礎(chǔ)構(gòu)造。Xamarin Forms(同其他XAML技術(shù)一樣)已經(jīng)有一個(gè)初始化類(lèi):App,包含在便攜式類(lèi)庫(kù)里,所以團(tuán)隊(duì)決定利用它而不是要求開(kāi)發(fā)人員創(chuàng)建一個(gè)新的。默認(rèn)情況下,這個(gè)類(lèi)是繼承自應(yīng)用程序類(lèi)。為了正確地支持Prism,我們需要改變它并讓App類(lèi)從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="http://xamarin.com/schemas/2014/forms" xmlns:x="http://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類(lèi)有三個(gè)鮮明的特點(diǎn):
- 具有基本構(gòu)造函數(shù),以一個(gè)IPlatformInitializer對(duì)象作為參數(shù)。
- 有一個(gè)稱(chēng)為OnInitialized()的方法,我們初始化Forms基礎(chǔ)構(gòu)造(通過(guò)調(diào)用InitializeComponent()方法),并且我們觸發(fā)導(dǎo)航到該應(yīng)用程序的主頁(yè)(我們后面將看到導(dǎo)航如何工作的詳細(xì)內(nèi)容)。
- 有一個(gè)稱(chēng)為RegisterTypes()的方法,就是我們登記的依賴(lài)注入容器(在這種情況下,Unity框架)的每一頁(yè)和我們的應(yīng)用程序所需的所有服務(wù)。
默認(rèn)情況下,IPlatformInitializer參數(shù)為null,它可以在你需要注冊(cè)依賴(lài)容器一些只在特定平臺(tái)的項(xiàng)目存在的特定類(lèi)別時(shí)利用。你會(huì)發(fā)現(xiàn),事實(shí)上,每個(gè)平臺(tái)的特定項(xiàng)目都有自己的自定義初始化類(lèi)(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)用程序唯一的不同就是定義背景的屬性稱(chēng)為BindingContext而不是DataContext。Prism使用一種簡(jiǎn)單的命名約定來(lái)自動(dòng)分配ViewModel到它的View:
- XAML頁(yè)面應(yīng)該被存儲(chǔ)在一個(gè)被稱(chēng)為Views的項(xiàng)目文件夾中
- ViewModel應(yīng)存放在一個(gè)被稱(chēng)為ViewModels的項(xiàng)目文件夾中,它需要與頁(yè)面相同的名稱(chēng)加上后綴ViewModel(例如,ViewModel連接到MainPage.xaml將被稱(chēng)為MainPageViewModel)。
正如你所看到的,這是Prism模板為我們創(chuàng)建的確切的基礎(chǔ)構(gòu)造。我們添加到我們的應(yīng)用程序的每一個(gè)頁(yè)面都需要在容器中注冊(cè),以便我們能夠正確地處理導(dǎo)航。為了注冊(cè),我們可以利用App類(lèi)的RegisterTypes()方法和使用一種由Container提供的稱(chēng)為RegisterTypeForNavigation< T >的方法,其中T是網(wǎng)頁(yè)的類(lèi)型。在起始模板,我們只有一個(gè)稱(chēng)為MainPage的網(wǎng)頁(yè),所以這是唯一一個(gè)在應(yīng)用程序啟動(dòng)時(shí)自動(dòng)注冊(cè)的頁(yè)面。Prism和其他MVVM框架之間有一個(gè)最大的差異。使用其他的工具,你只能在容器中注冊(cè)ViewModels和最終與他們有關(guān)聯(lián)的所有服務(wù)。相反地,使用Prism你只需注冊(cè)頁(yè)面的類(lèi)型:根據(jù)Prism自動(dòng)在容器注冊(cè),ViewModel也連接到View。你可以看到在示例代碼中,我們已經(jīng)注冊(cè)了MainPage類(lèi)而不是MainPageViewModel。
如果你不是命名方法的粉絲,你不必使用它:事實(shí)上,RegisterTypeForNavigation()方法有另一個(gè)變種,其簽名是RegisterTypeForNavigation< T, Y >(),其中T是頁(yè)面的類(lèi)型,Y是我們要設(shè)置為BindingContext的ViewModel的類(lèi)型。所以,例如,如果你想把你的MainPage連接到一個(gè)稱(chēng)為MyCustomViewModel的ViewModel,使用下面的代碼就足以注冊(cè):
protected override void RegisterTypes() { Container.RegisterTypeForNavigation<MainPage, MyCustomViewModel>(); }
在OnInitialized()方法中你可以預(yù)覽默認(rèn)的導(dǎo)航是如何工作的:每次你調(diào)用RegisterTypeForNavigation< T >方法,Prism注冊(cè)NavigationService參考頁(yè)面作為關(guān)鍵使用,具有相同類(lèi)型名稱(chēng)的字符串。由于我們的頁(yè)面的類(lèi)型是MainPage,我們需要通過(guò)字符串“MainPage”作為NavigateAsync()方法參數(shù)觸發(fā)導(dǎo)航到該頁(yè)面。如果我們要重寫(xiě)此行為,我們可以通過(guò)作為RegisterTypeForNavigation< T >()參數(shù)自定義字符串,用于后續(xù)的導(dǎo)航,如下面的示例中,我們已經(jīng)用“MyCustomPage”頁(yè)面取代了關(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í)的方式來(lái)處理導(dǎo)航的詳細(xì)信息。
ViewModel
在Xamarin Forms的Prism中我最欣賞的特點(diǎn)是,它不需要我們?cè)赬AML頁(yè)面做任何變化就可以支持它(例如,其他一些MVVM框架需要你用定制的一個(gè)去改變ContentPage類(lèi)型)。你只會(huì)發(fā)現(xiàn),在MainPage.xaml文件中有一個(gè)Prism特性,就像ContentPage項(xiàng)目屬性,稱(chēng)為ViewModelLocator.AutowireViewModel:
<?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="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è)屬性是不再需要的,除非你想通過(guò)設(shè)置它為false明確禁用命名約定。標(biāo)準(zhǔn)的模板將它添加到一個(gè)更完整的示例中,但你仍然可以安全地刪除它。
每個(gè)MVVM框架所提供的一個(gè)關(guān)鍵的功能是一個(gè)類(lèi),給我們的ViewModels提供快速訪問(wèn)到最常用的功能,就像INotifyPropertyChanged接口的實(shí)現(xiàn)。Prism也不例外,它提供了一個(gè)稱(chēng)為BindableBase的類(lèi),我們的ViewModels可以繼承:
public class MainPageViewModel : BindableBase { private string _title; public string Title { get { return _title; } set { SetProperty(ref _title, value); } } public MainPageViewModel() { } }
多虧這個(gè)類(lèi),每當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)屬性來(lái)實(shí)現(xiàn)INotifyPropertyChanged接口(這樣可以通過(guò)結(jié)合渠道傳播改變),我們可以簡(jiǎn)單的使用屬性調(diào)節(jié)器中的SetProperty()方法。此方法將用于存儲(chǔ)值,同時(shí),發(fā)送一個(gè)通知到所有與此屬性綁定的控件,告知它的值已更改,因此需要更新它們的布局。
通過(guò)模板創(chuàng)建的示例應(yīng)用程序正是這樣:它創(chuàng)建一個(gè)名為Title的屬性,通過(guò)結(jié)合到XAML頁(yè)面的Label控件連接。當(dāng)我們改變屬性的值時(shí),我們會(huì)看到用戶(hù)界面的實(shí)時(shí)更新。說(shuō)實(shí)話(huà),這個(gè)示例應(yīng)用程序也展示了一些別的東西:它用一種稱(chēng)為OnNavigatedTo()的方法設(shè)置了Title屬性的值,并且解析了一些參數(shù)。我們將在下一篇文章中看到更多這種方法如何運(yùn)作的細(xì)節(jié)。
在下一篇文章中
在這篇文章中,我們只是觸及表面,展現(xiàn)了Prism創(chuàng)建的Xamarin Forms應(yīng)用程序的基本概念。在接下來(lái)的文章中,我們會(huì)看到一些更先進(jìn)的概念,像在ViewModel處理導(dǎo)航或在依賴(lài)容器注冊(cè)附加服務(wù)。
本文翻譯自:Prism for Xamarin Forms - An Overview: Part I
最新活動(dòng)推薦:年中大促|(zhì)在線(xiàn)訂購(gòu)全場(chǎng)7折起!點(diǎn)擊了解詳情>>