自從6月份我們預覽了Native Forms以來,我們一直在努力平衡這些不足之處,并通過修複錯誤并整合來自社群的優秀回報來為Xamarin.Forms 2.5.0打磨它。
上個月在Microsoft Connect(); 在紐約,在主題演講中使用了幾個原生形式的例子。 最值得注意的是,James Montemagno示範了向開源Kickstarter iOS應用程式添加一個Xamarin.Forms頁面。 您可以在Channel9上觀看整個主題演講。
<a href="https://channel9.msdn.com/Events/Connect/2017/K100" target="_blank"></a>
另一個例子是SmartHotel360應用程式套件,它包括一個維護應用程式,它使用Native Forms将Xamarin.Forms頁面複合到本機Xamarin.iOS和Xamarin.Android應用程式中。 這些應用程式的完整代碼将很快在GitHub上提供。
本文将介紹這些更改,并為您開始将Native Forms嵌入到您自己的應用程式中作為指導。
本機形式允許您在本機Xamarin.iOS,Xamarin.Android或UWP應用程式中使用Xamarin.Forms ContentPage。
但是,為什麼你可能會問,我想這樣做? 讓我們考慮一些情況:
Existing Xamarin.Forms Reuse
也許你在之前的Xamarin.Forms項目中寫了一個服務的登入頁面。 現在,您正在開發一個使用相同服務的本地Xamarin項目。 而不是再次編寫該頁面,您可以直接從Forms項目中直接重用它。
Migrate Xamarin.Forms to Xamarin Native
假設您使用Xamarin.Forms來快速建立原型或概念驗證應用程式。 對于第二版,你已經決定在iOS上,你需要本土化。 不過,對于從原生應用程式的原型中重新編寫每一頁的前景,你并不感到興奮。 使用Native Forms,您可以重用原型中的頁面,隻重寫您絕對必須的頁面。
Mix Xamarin.Forms into Xamarin Native Projects
您正在處理多個本地項目,并面臨三次編寫相同的“設定”和“關于”頁面的前景。 停止! 隻需将它們寫入Xamarin.Forms項目并将其嵌入到您的本機應用程式中即可!
為了顯示原生形式,讓我們通過一些代碼來示範一個場景。 這個例子的完整代碼可以在GitHub上找到。
假設我正在為UWP,Android和iOS建立全新的本機應用程式。 在每一個,我想添加一個小費電腦,但我真的不想從頭開始寫三個小費電腦。 幸運的是,Charles Petzold已經在Xamarin.Forms中寫了一個,是以我所要做的就是将其嵌入到每個本地應用程式中。
在示範解決方案中,我建立了三個本地項目,另外還有一個TipCalc代碼項目:
Solution
在我的TipCalc項目中,我簡單地複制了原始的TipCalc解決方案的PCL項目中的代碼。 代碼的工作原理沒有任何改變,但在我的項目中,我已經更新了一些過時的XAML,并添加了一些MessagingCenter代碼,稍後我将會讨論這些代碼。
要将代碼嵌入到每個本機應用程式中,我需要添加對TipCalc項目的引用并安裝Xamarin.Forms NuGet包。 一旦我這樣做了,我在每個本地項目中的引用将如下所示:
iOS project references
在每個本地項目可以嵌入任何Xamarin.Forms頁面之前,他們需要通過調用Forms.Init()來初始化窗體。當一個本地應用程式,這是由你自己決定,隻要它發生在建構表單ContentPage的任何執行個體之前。您可以選擇在啟動時(例如,在UIApplicationDelegate.FinishedLaunching()或Activity.OnCreate())期間執行此操作,或者您可能要等到建構第一個ContentPage之前。您的正确選擇主要取決于您的應用程式流程中最友善的時間。
在Xamarin.Forms被初始化之後,隻需要設定導航到TipCalcPage。本機表單向Xamarin.Forms中添加了一些擴充方法,該方法将ContentPage轉換為适當的本機類型。
例如,在本地iOS項目中,通常使用帶有UIViewController的UINavigationController.PushViewController()。 Native Forms為ContentPage提供了一個擴充,它将頁面轉換為一個UIViewContoller。導航到小費電腦的方法可能很簡單:
點選(此處)折疊或打開
public void NavigateToTipCalc()
{
_navigation.PushViewController(new TipCalcPage().CreateViewController(), true);
}
其中_navigation是一個UINavigationController。 而已; 從“提示電腦”按鈕調用該方法,應用程式導航到Xamarin.Forms頁面。
Navigating to the embedded Forms page on iOS
在Android上,Native Forms提供了CreateFragment()和CreateSupportFragment()方法。 這些會将ContentPage轉換為适合導航的片段。 例如,像這樣的方法添加到FragmentActivity:
1
2
3
4
5
6
7
8
9
10
var ft = SupportFragmentManager.BeginTransaction();
ft.AddToBackStack(null);
ft.Replace(Resource.Id.fragment_frame_layout,
new TipCalcPage().CreateSupportFragment(this), "TipCalc");
ft.Commit();
Navigating to the embedded Forms page on Android
對于本地UWP應用程式,導航通常是使用Frame類來完成的; Frame.Navigate()方法接受一個Page類型并導航到它的一個執行個體。 本機窗體提供了一個類似的擴充架構,它需要一個ContentPage執行個體。 導航可以像下面這樣簡單:
private void NavigateToTipCalc()
Frame.Navigate(new TipCalcPage());
Navigating to the embedded Forms page on UWP
正如你所看到的,使用Native Forms不需要太多的代碼。
機會很好,你還需要在本地上下文和應用程式的Xamarin.Forms部分之間進行通信。 如果您正在重用另一個項目中的Xamarin.Forms代碼,那麼您可能已經使用任何事件聚合器,消息總線或其他方法對堆棧進行了設定。 但是,如果您還沒有到位,Xamarin.Forms附帶了一個内置的消息服務(稱為MessagingCenter),可用于連接配接本機應用程式和它們托管的Xamarin.Forms頁面之間的通信。
在這個示範解決方案中,我已經修改了TipCalc項目,允許它使用MessagingCenter向其主機應用程式發送和接收資料。 我已經定義了消息和他們的論點:
public static class Messages {
public static object Sender = new object();
public const string InitialAmount = "InitialAmount";
public const string Tip = "Tip";
public class InitialAmountArgs {
public InitialAmountArgs(double initialAmount) {
InitialAmount = initialAmount;
}
public double InitialAmount { get; }
public class TipArgs {
public TipArgs(double tip) {
Tip = tip;
public double Tip { get; }
有了這個基礎架構,原生應用程式很容易為小費電腦指定初始金額。 每個本機應用程式都有一個輸入初始金額的字段; 當使用者更新該字段時,将發送一條消息:
MessagingCenter.Send(TipCalc.Messages.Sender, TipCalc.Messages.InitialAmount,
new InitialAmountArgs(InitialAmount));
TipCalc項目(TipCalcModel)中的視圖模型會監聽該消息并作出相應的反應:
MessagingCenter.Subscribeobject, InitialAmountArgs>(this, Messages.InitialAmount, (s, e) =>
SubTotal = e.InitialAmount;
});
原生應用程式對計算的提示值的變化做出響應也很簡單。 每當TipCalcModel中的TipAmount發生更改時,都會發送一條消息:
MessagingCenter.Send(Messages.Sender, Messages.Tip, new TipArgs(value));
本地應用程式可以偵聽該消息并作出反應; 例如,iOS應用程式設定UILabel的文本:
MessagingCenter.Subscribeobject, TipArgs>(TipCalc.Messages.Sender, TipCalc.Messages.Tip,
(obj, args) => TipAmount.Text = args.Tip.ToString("C"));
這裡是在UWP上的應用程式。 使用者輸入傳遞給小費電腦的初始金額,并将小費電腦的結果中繼回到本地頁面:
Communication on UWP
精明的讀者會注意到,擴充方法都提供了不一定需要完整頁面導航的傳回類型。 也可以使用UIViewController,FrameworkElement和Fragment作為頁面的一部分而不是全屏。 我們之前展示了如何在Flyout内部的UWP上顯示ContentPage,而不是整頁。
從理論上講,可以使用這些方法在每個平台上嵌入一個ContentPage作為更大頁面的一部分。 截至目前,這是一個官方不受支援的用例。 這就是說,如果你嘗試了這個用例,我很想聽聽它是如何工作的。
您現在可以使用我們最新的穩定版本Xamarin.Forms 2.5.0,在NuGet上使用Native Forms。