原文地址:https://github.com/rid00z/FreshMvvm
FreshMvvm for Xamarin.Forms
FreshMvvm是專門為Xamarin.Forms設(shè)計(jì)的輕量Mvvm框架埃疫。 它是簡單和靈活的绕娘。
與其他的選擇相比怎么樣?
- 輕量,超簡單
- 它專為Xamarin.Forms設(shè)計(jì)
- 設(shè)計(jì)易于學(xué)習(xí)和開發(fā)(當(dāng)你還沒有準(zhǔn)備好RxUI時(shí)它是很好的)
- 使用比配置更好的設(shè)定。
特點(diǎn)
- PageModel到PageModel導(dǎo)航
- BindingContext的自動(dòng)構(gòu)造
- 頁面事件的自動(dòng)構(gòu)造(例如頁面出現(xiàn))
- PageModel上的基本方法(允許傳值)(init,reverseinit)
- 內(nèi)置IOC容器(依賴注入)
- PageModel構(gòu)造函數(shù)注入
- Basic中提供的基本方法,如彈消息框
- 內(nèi)置導(dǎo)航類型為SimpleNavigation,Tabbed和MasterDetail
它的故事
當(dāng)Xamarin.Forms發(fā)布時(shí)蜕窿,我(Michael Ridland)是Xamarin傳統(tǒng)應(yīng)用程序的一部分谋逻。 我想將項(xiàng)目移到Xamarin.Forms上,但是在該項(xiàng)目中我使用的是MvvMCross桐经。 當(dāng)時(shí)MvvmCross不支持Xamarin.Forms毁兆,所以我有幾個(gè)選擇
- (1)適應(yīng)MvvmCross
- (2)找到一個(gè)替代的框架
- (3)實(shí)現(xiàn)我自己的MvvM。
關(guān)于MvvmCross的最好的部分是它是雙向數(shù)據(jù)綁定到原生的iOS / Android控件阴挣,但由于Xamarin.Forms已經(jīng)擁有Databinding內(nèi)置气堕,這是沒有用的,MvvMCross太大了畔咧,我不需要這么大茎芭。
我也無法找到一個(gè)可以輕松移動(dòng)的替代方案。所以我做了屬于我自己簡單并且靈活MvvM框架誓沸。
它是從這個(gè)帖子開始的——翻譯在這梅桩,為Xamarin.Forms實(shí)現(xiàn)自己的Mvvm。 我盡量為自己的MvvM框架做得簡單拜隧。
從來沒有想過來寫一個(gè)框架宿百,但在幾次發(fā)布Mvvm解決方案之后,我發(fā)現(xiàn)很多人都想要它洪添,并且似乎對此感興趣垦页。 另外考慮到我從Xamarin.Forms開始就在我所有的項(xiàng)目中使用了這個(gè)框架,我知道它的工作原理干奢,所以我創(chuàng)建了FreshMvvm痊焊,于是它誕生了。
共同遵守的設(shè)定
- 一個(gè)頁面必須有一個(gè)相應(yīng)的PageModel律胀,類的命名十分重要宋光,所以QuotePageModel必須有一個(gè)QuotePage頁面貌矿。QuotePage上的BindingContext將被自動(dòng)設(shè)置為Model
- 一個(gè)PageModel可以擁有一個(gè)接收一個(gè)對象的Init方法
- 一個(gè)PageModel可以有一個(gè)ReverseInit方法炭菌,它也可以使用一個(gè)對象,當(dāng)一個(gè)模型被一個(gè)對象引用時(shí)被調(diào)用
- PageModel可以將依賴關(guān)系自動(dòng)注入到構(gòu)造函數(shù)中
導(dǎo)航
FreshMvvm中的主要導(dǎo)航形式是PageModel到PageModel逛漫,這實(shí)際上意味著我們的觀點(diǎn)不了解導(dǎo)航黑低。
- 所以要在PageModels之間導(dǎo)航使用:
await CoreMethods.PushPageModel<QuotePageModel>(); // 推送導(dǎo)航堆棧
await CoreMethods.PushPageModel<QuotePageModel>(null, true); // 推送模態(tài)
- FreshMvvm中的導(dǎo)航引擎是通過一個(gè)簡單的界面完成的,其中包含了Push和Pop的方法酌毡。 基本上這些方法可以以任何他們喜歡的方式控制應(yīng)用程序的導(dǎo)航克握。
public interface IFreshNavigationService
{
Task PushPage(Page page, FreshBasePageModel model, bool modal = false);
Task PopPage(bool modal = false);
}
- 在PushPage和PopPage中,您可以執(zhí)行任何您喜歡的導(dǎo)航枷踏,這可以從簡單的導(dǎo)航到高級嵌套導(dǎo)航菩暗。
該框架包含一些內(nèi)置的導(dǎo)航容器,用于不同類型的導(dǎo)航旭蠕。
-
基本導(dǎo)航 - 內(nèi)置
var page = FreshPageModelResolver.ResolvePageModel<MainMenuPageModel> ();
var basicNavContainer = new FreshNavigationContainer (page);
MainPage = basicNavContainer;
-
主要細(xì)節(jié) - 內(nèi)置
var masterDetailNav = new FreshMasterDetailNavigationContainer ();
masterDetailNav.Init ("Menu");
masterDetailNav.AddPage<ContactListPageModel> ("Contacts", null);
masterDetailNav.AddPage<QuoteListPageModel> ("Pages", null);
MainPage = masterDetailNav;
-
標(biāo)簽導(dǎo)航 - 內(nèi)置
var tabbedNavigation = new FreshTabbedNavigationContainer ();
tabbedNavigation.AddTab<ContactListPageModel> ("Contacts", null);
tabbedNavigation.AddTab<QuoteListPageModel> ("Pages", null);
MainPage = tabbedNavigation;
-
實(shí)施自定義導(dǎo)航
可以通過實(shí)現(xiàn)IFreshNavigationService.來設(shè)置任何類型的導(dǎo)航停团。在示例應(yīng)用程序中有一個(gè)示例旷坦,名為CustomImplementedNav.cs。
示例應(yīng)用程序
- 基本導(dǎo)航例子
- 標(biāo)簽式導(dǎo)航例子
- MasterDetail導(dǎo)航例子
- 使用MasterDetail Popover示例的Tabbed導(dǎo)航(這在Sample App中被稱為CustomImplementedNav)
控制反轉(zhuǎn)(IOC)
所以你不需要使用你自己的IOC容器佑稠,F(xiàn)reshMvvm自帶了一個(gè)內(nèi)置的IOC容器秒梅,它使用的是TinyIOC,但使用不同的命名來避免沖突舌胶。要在容器中注冊服務(wù)注冊:
FreshIOC.Container.Register<IDatabaseService, DatabaseService>();
注入時(shí)使用:
FreshIOC.Container.Resolve<IDatabaseService>();
這也是驅(qū)動(dòng)構(gòu)建器注入的方式捆蜀。
我們現(xiàn)在流暢支持API來設(shè)置對象在IOC容器內(nèi)的生命周期。
// 默認(rèn)情況下幔嫂,我們將具體類型注冊為多例辆它,并將接口注冊為單例
FreshIOC.Container.Register<MyConcreteType>(); // 多例
FreshIOC.Container.Register<IMyInterface, MyConcreteType>(); // 單例
// Fluent API允許我們改變這種行為
FreshIOC.Container.Register<MyConcreteType>().AsSingleton(); // 單例
FreshIOC.Container.Register<IMyInterface, MyConcreteType>().AsMultiInstance(); // 多例
如下所示,IFreshIOC接口方法返回IRegisterOptions接口婉烟。
public interface IFreshIOC
{
object Resolve(Type resolveType);
IRegisterOptions Register<RegisterType>(RegisterType instance) where RegisterType : class;
IRegisterOptions Register<RegisterType>(RegisterType instance, string name) where RegisterType : class;
ResolveType Resolve<ResolveType>() where ResolveType : class;
ResolveType Resolve<ResolveType>(string name) where ResolveType : class;
IRegisterOptions Register<RegisterType, RegisterImplementation> () where RegisterType : class where RegisterImplementation : class, RegisterType;
}
從register方法返回的接口是IRegisterOptions娩井。
public interface IRegisterOptions
{
IRegisterOptions AsSingleton();
IRegisterOptions AsMultiInstance();
IRegisterOptions WithWeakReference();
IRegisterOptions WithStrongReference();
IRegisterOptions UsingConstructor<RegisterType>(Expression<Func<RegisterType>> constructor);
}
PageModel - 構(gòu)造函數(shù)注入
當(dāng)PageModels被推送到IOC容器中的services可以被推入構(gòu)造函數(shù)。
FreshIOC.Container.Register<IDatabaseService, DatabaseService>();
PageModel重要方法
/// <summary>
/// 以前的頁面模型似袁,這是自動(dòng)推送填充
/// </summary>
public FreshBasePageModel PreviousPageModel { get; set; }
/// <summary>
/// 對當(dāng)前頁面的引用洞辣,即自動(dòng)推送填充
/// </summary>
public Page CurrentPage { get; set; }
/// <summary>
/// 核心方法是應(yīng)用程序的基本內(nèi)置方法,包括推送昙衅,彈出和彈消息框
/// </summary>
public IPageModelCoreMethods CoreMethods { get; set; }
/// <summary>
/// 當(dāng)一個(gè)頁面調(diào)用Pop'd這個(gè)方法時(shí)扬霜,它也允許返回?cái)?shù)據(jù)。
/// </summary>
/// <param name="returndData">從...返回的數(shù)據(jù) </param>
public virtual void ReverseInit(object returndData) { }
/// <summary>
/// 在加載PageModel時(shí)調(diào)用此方法而涉,initData是之前從pagemodel發(fā)送來的數(shù)據(jù)
/// </summary>
/// <param name="initData">從推送器發(fā)送到此PageModel的數(shù)據(jù)</param>
public virtual void Init(object initData) { }
/// <summary>
/// View消失時(shí)調(diào)用此方法著瓶。
/// </summary>
protected virtual void ViewIsDisappearing (object sender, EventArgs e)
{
}
/// <summary>
/// View出現(xiàn)時(shí)調(diào)用此方法
/// </summary>
protected virtual void ViewIsAppearing (object sender, EventArgs e)
{
}
核心方法
每個(gè)PageModel都有一個(gè)名為“CoreMethods”的屬性,當(dāng)一個(gè)PageModel被推送時(shí)啼县,它被自動(dòng)填充材原,它是大多數(shù)應(yīng)用程序需要的基本功能,如彈消息框季眷,推送余蟹,彈出等。
public interface IPageModelCoreMethods
{
Task DisplayAlert (string title, string message, string cancel);
Task<string> DisplayActionSheet (string title, string cancel, string destruction, params string[] buttons);
Task<bool> DisplayAlert (string title, string message, string accept, string cancel);
Task PushPageModel<T>(object data, bool modal = false) where T : FreshBasePageModel;
Task PopPageModel(bool modal = false);
Task PopPageModel(object data, bool modal = false);
Task PushPageModel<T>() where T : FreshBasePageModel;
}
Page的重要方法
PageModel Init PropertyChanged
示例PageModel
[ImplementPropertyChanged] // 使用Fody for Property更改通知
public class QuoteListPageModel : FreshBasePageModel
{
IDatabaseService _databaseService;
//這些通過構(gòu)造函數(shù)注入IOC
public QuoteListPageModel (IDatabaseService databaseService)
{
_databaseService = databaseService;
}
public ObservableCollection<Quote> Quotes { get; set; }
public override void Init (object initData)
{
Quotes = new ObservableCollection<Quote> (_databaseService.GetQuotes ());
}
//框架支持標(biāo)準(zhǔn)View出現(xiàn)和消失事件
protected override void ViewIsAppearing (object sender, System.EventArgs e)
{
CoreMethods.DisplayAlert ("Page is appearing", "", "Ok");
base.ViewIsAppearing (sender, e);
}
protected override void ViewIsDisappearing (object sender, System.EventArgs e)
{
base.ViewIsDisappearing (sender, e);
}
//跳轉(zhuǎn)到另一個(gè)頁面后子刮,如果返回此頁面調(diào)用
public override void ReverseInit (object value)
{
var newContact = value as Quote;
if (!Quotes.Contains (newContact))
{
Quotes.Add (newContact);
}
}
public Command AddQuote
{
get
{
return new Command (async () =>
{
//Push A Page Model
await CoreMethods.PushPageModel<QuotePageModel> ();
});
}
}
Quote _selectedQuote;
public Quote SelectedQuote
{
get
{
return _selectedQuote;
}
set
{
_selectedQuote = value;
if (value != null)
QuoteSelected.Execute (value);
}
}
public Command<Quote> QuoteSelected
{
get
{
return new Command<Quote> (async (quote) =>
{
await CoreMethods.PushPageModel<QuotePageModel> (quote);
});
}
}
}
多個(gè)導(dǎo)航服務(wù)
在FreshMvvm中可以進(jìn)行任何類型的導(dǎo)航威酒,通過實(shí)現(xiàn)自定義導(dǎo)航服務(wù)來完成自定義或高級場景。即使有這種能力挺峡,發(fā)現(xiàn)在FreshMvvm中做高級導(dǎo)航方案有點(diǎn)困難葵孤。在我回顧了FreshMvvm的所有支持問題之后,我發(fā)現(xiàn)人們的基本問題是他們希望能夠多次使用我們內(nèi)置的導(dǎo)航容器橱赠,其中兩個(gè)主要例子是
- (1)具有導(dǎo)航堆棧的主要細(xì)節(jié):在一個(gè)master和另一個(gè)master的細(xì)節(jié)
- (2)使用新的推動(dòng)導(dǎo)航容器模型的能力尤仍。
為了支持這兩種情況,我得出結(jié)論狭姨,F(xiàn)reshMvvm需要具有命名NavigationServices的能力宰啦,以便我們可以支持多個(gè)NavigationService鲤嫡。
使用多個(gè)導(dǎo)航容器
在下面我們運(yùn)行一個(gè)單一的主細(xì)節(jié)的兩個(gè)導(dǎo)航堆棧。
var masterDetailsMultiple = new MasterDetailPage (); //generic master detail page
//我們使用ContactList設(shè)置第一個(gè)導(dǎo)航容器
var contactListPage = FreshPageModelResolver.ResolvePageModel<ContactListPageModel> ();
contactListPage.Title = "Contact List";
//我們設(shè)置名為MasterPageArea的第一個(gè)導(dǎo)航容器
var masterPageArea = new FreshNavigationContainer (contactListPage, "MasterPageArea");
masterPageArea.Title = "Menu";
masterDetailsMultiple.Master = masterPageArea; //將第一個(gè)導(dǎo)航容器設(shè)置為Master
//我們使用QuoteList設(shè)置第二個(gè)導(dǎo)航容器
var quoteListPage = FreshPageModelResolver.ResolvePageModel<QuoteListPageModel> ();
quoteListPage.Title = "Quote List";
//我們設(shè)置名為DetailPageArea的第二個(gè)導(dǎo)航容器
var detailPageArea = new FreshNavigationContainer (quoteListPage, "DetailPageArea");
masterDetailsMultiple.Detail = detailPageArea; //將第二個(gè)導(dǎo)航容器設(shè)置為“Detail”
MainPage = masterDetailsMultiple;
在新的導(dǎo)航堆棧使用PushModally
//push a basic page Modally
var page = FreshPageModelResolver.ResolvePageModel<MainMenuPageModel> ();
var basicNavContainer = new FreshNavigationContainer (page, "secondNavPage");
await CoreMethods.PushNewNavigationServiceModal(basicNavContainer, new FreshBasePageModel[] { page.GetModel() });
//推送標(biāo)簽頁模型
var tabbedNavigation = new FreshTabbedNavigationContainer ("secondNavPage");
tabbedNavigation.AddTab<ContactListPageModel> ("Contacts", "contacts.png", null);
tabbedNavigation.AddTab<QuoteListPageModel> ("Quotes", "document.png", null);
await CoreMethods.PushNewNavigationServiceModal(tabbedNavigation);
//推送主細(xì)節(jié)頁面
var masterDetailNav = new FreshMasterDetailNavigationContainer ("secondNavPage");
masterDetailNav.Init ("Menu", "Menu.png");
masterDetailNav.AddPage<ContactListPageModel> ("Contacts", null);
masterDetailNav.AddPage<QuoteListPageModel> ("Quotes", null);
await CoreMethods.PushNewNavigationServiceModal(masterDetailNav);
在Xamarin.Forms MainPage上切換NavigationStacks
Xamarin.Forms中有些情況可能需要運(yùn)行多個(gè)導(dǎo)航堆棧绑莺。 一個(gè)很好的例子是當(dāng)你有一個(gè)用于認(rèn)證的導(dǎo)航堆棧和一個(gè)應(yīng)用程序主區(qū)域的堆棧暖眼。
首先我們可以為導(dǎo)航容器設(shè)置一些名稱。
public class NavigationContainerNames
{
public const string AuthenticationContainer = "AuthenticationContainer";
public const string MainContainer = "MainContainer";
}
然后我們可以創(chuàng)建我們的兩個(gè)導(dǎo)航容器并分配到主頁面纺裁。
var loginPage = FreshMvvm.FreshPageModelResolver.ResolvePageModel<LoginViewModel>();
var loginContainer = new FreshNavigationContainer(loginPage, NavigationContainerNames.AuthenticationContainer);
var myPitchListViewContainer = new MainTabbedPage(NavigationContainerNames.MainContainer);
MainPage = loginContainer;
一旦我們設(shè)置好了诫肠,我們現(xiàn)在可以切換我們的導(dǎo)航容器。
CoreMethods.SwitchOutRootNavigation(NavigationContainerNames.MainContainer);
自定義IOC容器
FreshMvvm 1.0的第二個(gè)主要要求是允許自定義IOC容器欺缘。 在您的應(yīng)用程序已經(jīng)具有要使用的容器的情況下栋豫。
使用自定義IOC容器非常簡單,因?yàn)槟恍枰獙?shí)現(xiàn)單個(gè)接口谚殊。
public interface IFreshIOC
{
object Resolve(Type resolveType);
void Register<RegisterType>(RegisterType instance) where RegisterType : class;
void Register<RegisterType>(RegisterType instance, string name) where RegisterType : class;
ResolveType Resolve<ResolveType>() where ResolveType : class;
ResolveType Resolve<ResolveType>(string name) where ResolveType : class;
void Register<RegisterType, RegisterImplementation> () where RegisterType : class where RegisterImplementation : class, RegisterType;
然后在系統(tǒng)中設(shè)置IOC容器丧鸯。
FreshIOC.OverrideContainer(myContainer);
相關(guān)視頻/快速入門指南
- FreshMvvm n = 0 - Mvvm在Xamarin.Forms和為什么需要FreshMvvm
- FreshMvvm n = 1:你的第一個(gè)FreshMvvm應(yīng)用程序
- FreshMvvm n = 2 - IOC和構(gòu)造器注入
- FreshMvvm n = 3:在FreshMvvm中導(dǎo)航
- 在FreshMvvm中為Xamarin.Forms實(shí)現(xiàn)自定義導(dǎo)航
- TDD在Xamarin Studio - Live Coding FreshMvvm