Xamarin.Forms 在 Xamarin 原生專案中
一般而言, Xamarin.Forms 應用程式包含衍生自 ContentPage
的一或多個頁面,而且這些頁面是由 .NET Standard 連結庫專案或共享專案中的所有平台共用。 不過,Native Forms 可讓 ContentPage
衍生頁面直接新增至原生 Xamarin.iOS、Xamarin.Android 和 UWP 應用程式。 相較於原生專案 ContentPage
取用 .NET Standard 連結庫專案或共用專案的衍生頁面,直接將頁面新增至原生專案的優點是頁面可以使用原生檢視來擴充。 然後,原生檢視可以在 XAML 中命名,並從 x:Name
程式代碼後置參考。 如需原生檢視的詳細資訊,請參閱 原生檢視。
在原生專案中取 Xamarin.FormsContentPage
用衍生頁面的程式如下:
- 將 Xamarin.Forms NuGet 套件新增至原生專案。
- 將
ContentPage
衍生頁面和任何相依性新增至原生專案。 - 呼叫
Forms.Init
方法。 - 使用下列其中一個擴充方法,建構衍生頁面的
ContentPage
實例,並將它轉換成適當的原生類型:CreateViewController
適用於 iOS、CreateSupportFragment
適用於 Android 或CreateFrameworkElement
UWP。 - 使用原生導覽 API 瀏覽至衍生頁面的
ContentPage
原生類型表示法。
Xamarin.Forms 必須先呼叫 Forms.Init
方法,才能建構 ContentPage
衍生頁面的原生專案。 選擇執行此動作的時機主要取決於應用程式流程中最方便的時機– 它可以在應用程式啟動時執行,或是在建構衍生頁面之前 ContentPage
執行。 在本文中,以及隨附的範例應用程式,會在 Forms.Init
應用程式啟動時呼叫 方法。
注意
NativeForms 範例應用程式解決方案不包含任何Xamarin.Forms專案。 而是由 Xamarin.iOS 專案、Xamarin.Android 專案和 UWP 專案所組成。 每個專案都是使用原生窗體來取 ContentPage
用衍生頁面的原生專案。 不過,原生項目無法從 .NET Standard 連結庫專案或共用專案取用 ContentPage
衍生頁面的原因。
使用原生表單時,Xamarin.Forms、、 MessagingCenter
和數據系結引擎等DependencyService
功能仍可正常運作。 不過,頁面導覽必須使用原生導覽 API 來執行。
iOS
在 iOS 上, FinishedLaunching
類別中的 AppDelegate
覆寫通常是執行應用程式啟動相關工作的位置。 它會在應用程式啟動之後呼叫,而且通常會覆寫以設定主視窗和檢視控制器。 下列程式代碼範例顯示 AppDelegate
範例應用程式中的 類別:
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public static AppDelegate Instance;
UIWindow _window;
AppNavigationController _navigation;
public static string FolderPath { get; private set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
Forms.Init();
// Create app-level resource dictionary.
Xamarin.Forms.Application.Current = new Xamarin.Forms.Application();
Xamarin.Forms.Application.Current.Resources = new MyDictionary();
Instance = this;
_window = new UIWindow(UIScreen.MainScreen.Bounds);
UINavigationBar.Appearance.SetTitleTextAttributes(new UITextAttributes
{
TextColor = UIColor.Black
});
FolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
NotesPage notesPage = new NotesPage()
{
// Set the parent so that the app-level resource dictionary can be located.
Parent = Xamarin.Forms.Application.Current
};
UIViewController notesPageController = notesPage.CreateViewController();
notesPageController.Title = "Notes";
_navigation = new AppNavigationController(notesPageController);
_window.RootViewController = _navigation;
_window.MakeKeyAndVisible();
notesPage.Parent = null;
return true;
}
// ...
}
FinishedLaunching
方法會執行下列工作:
- Xamarin.Forms 呼叫 方法會
Forms.Init
初始化。 - 系統會建立新的
Xamarin.Forms.Application
物件,並將其應用層級資源字典設定為ResourceDictionary
XAML 中定義的 。 - 類別的
AppDelegate
參考會儲存在static
Instance
欄位中。 這是為其他類別提供機制,以呼叫 類別中AppDelegate
定義的方法。 - 會
UIWindow
建立 ,這是原生 iOS 應用程式中檢視的主要容器。 - 屬性
FolderPath
會初始化為將儲存記事數據之裝置上的路徑。 - 系統會
NotesPage
建立 物件,這是 Xamarin.FormsContentPage
XAML 中定義的衍生頁面,且其父系設定為先前建立Xamarin.Forms.Application
的物件。 - 物件
NotesPage
會使用CreateViewController
擴充方法轉換成UIViewController
。 - 的
Title
UIViewController
屬性已設定,其會顯示在UINavigationBar
上。 AppNavigationController
會建立 來管理階層式導覽。 這是衍生自UINavigationController
的自定義導覽控制器類別。 物件AppNavigationController
會管理檢視控制器的堆疊,而UIViewController
傳入建構函式的 一開始會在載入時AppNavigationController
呈現。- 物件
AppNavigationController
會設定為的最上層UIViewController
UIWindow
,而UIWindow
會設定為應用程式的索引鍵視窗,並顯示。 - 對象的
Parent
屬性NotesPage
設定為null
,以防止記憶體流失。
FinishedLaunching
執行 方法之後,類別中Xamarin.FormsNotesPage
定義的UI將會顯示,如下列螢幕快照所示:
重要
所有 ContentPage
衍生的頁面都可以取用應用層級 ResourceDictionary
中定義的資源,前提是 Parent
頁面的 屬性設定為 Application
物件。
例如,藉由點+Button
選 來與UI互動,會導致程式代碼後置執行中的NotesPage
下列事件處理程式:
void OnNoteAddedClicked(object sender, EventArgs e)
{
AppDelegate.Instance.NavigateToNoteEntryPage(new Note());
}
欄位static
AppDelegate.Instance
可叫AppDelegate.NavigateToNoteEntryPage
用 方法,如下列程式代碼範例所示:
public void NavigateToNoteEntryPage(Note note)
{
NoteEntryPage noteEntryPage = new NoteEntryPage
{
BindingContext = note,
// Set the parent so that the app-level resource dictionary can be located.
Parent = Xamarin.Forms.Application.Current
};
var noteEntryViewController = noteEntryPage.CreateViewController();
noteEntryViewController.Title = "Note Entry";
_navigation.PushViewController(noteEntryViewController, true);
noteEntryPage.Parent = null;
}
方法NavigateToNoteEntryPage
會Xamarin.FormsContentPage
使用CreateViewController
擴充方法將 衍生頁面UIViewController
轉換成 ,並設定 Title
的UIViewController
屬性。 UIViewController
然後,PushViewController
方法會推送至 AppNavigationController
。 因此,將會顯示 類別中 Xamarin.FormsNoteEntryPage
定義的UI,如下列螢幕快照所示:
NoteEntryPage
顯示 時,傳回瀏覽會從 AppNavigationController
中彈出 NoteEntryPage
UIViewController
類別的 ,並將使用者傳回 給 UIViewController
類別的 NotesPage
。 不過,從iOS原生流覽堆疊快 UIViewController
顯 不會自動處置 UIViewController
和附加 Page
物件。 因此,類別 AppNavigationController
會 PopViewController
覆寫 方法,以在向後瀏覽時處置檢視控制器:
public class AppNavigationController : UINavigationController
{
//...
public override UIViewController PopViewController(bool animated)
{
UIViewController topView = TopViewController;
if (topView != null)
{
// Dispose of ViewController on back navigation.
topView.Dispose();
}
return base.PopViewController(animated);
}
}
覆寫會在PopViewController
從 iOS 原生瀏覽堆疊彈出的物件上UIViewController
呼叫 Dispose
方法。 若無法這麼做,會導致 UIViewController
和附加 Page
對象被遺棄。
重要
孤立的對象無法進行垃圾收集,因此會導致記憶體流失。
Android
在Android上, OnCreate
類別中的 MainActivity
覆寫通常是執行應用程式啟動相關工作的位置。 下列程式代碼範例顯示 MainActivity
範例應用程式中的 類別:
public class MainActivity : AppCompatActivity
{
public static string FolderPath { get; private set; }
public static MainActivity Instance;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Forms.Init(this, bundle);
// Create app-level resource dictionary.
Xamarin.Forms.Application.Current = new Xamarin.Forms.Application();
Xamarin.Forms.Application.Current.Resources = new MyDictionary();
Instance = this;
SetContentView(Resource.Layout.Main);
var toolbar = FindViewById<Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
SupportActionBar.Title = "Notes";
FolderPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData));
NotesPage notesPage = new NotesPage()
{
// Set the parent so that the app-level resource dictionary can be located.
Parent = Xamarin.Forms.Application.Current
};
AndroidX.Fragment.App.Fragment notesPageFragment = notesPage.CreateSupportFragment(this);
SupportFragmentManager
.BeginTransaction()
.Replace(Resource.Id.fragment_frame_layout, mainPage)
.Commit();
//...
notesPage.Parent = null;
}
...
}
OnCreate
方法會執行下列工作:
- Xamarin.Forms 呼叫 方法會
Forms.Init
初始化。 - 系統會建立新的
Xamarin.Forms.Application
物件,並將其應用層級資源字典設定為ResourceDictionary
XAML 中定義的 。 - 類別的
MainActivity
參考會儲存在static
Instance
欄位中。 這是為其他類別提供機制,以呼叫 類別中MainActivity
定義的方法。 - 內容
Activity
是從版面配置資源設定。 在範例應用程式中,版面配置包含LinearLayout
Toolbar
的 ,以及FrameLayout
做為片段容器的 。 Toolbar
會擷取 並設定為的動作列Activity
,並設定動作列標題。- 屬性
FolderPath
會初始化為將儲存記事數據之裝置上的路徑。 - 系統會
NotesPage
建立 物件,這是 Xamarin.FormsContentPage
XAML 中定義的衍生頁面,且其父系設定為先前建立Xamarin.Forms.Application
的物件。 - 物件
NotesPage
會使用CreateSupportFragment
擴充方法轉換成Fragment
。 - 類別
SupportFragmentManager
會建立並認可交易,該交易會將FrameLayout
實例Fragment
取代為 類別的NotesPage
。 - 對象的
Parent
屬性NotesPage
設定為null
,以防止記憶體流失。
如需片段的詳細資訊,請參閱 片段。
OnCreate
執行 方法之後,類別中Xamarin.FormsNotesPage
定義的UI將會顯示,如下列螢幕快照所示:
重要
所有 ContentPage
衍生的頁面都可以取用應用層級 ResourceDictionary
中定義的資源,前提是 Parent
頁面的 屬性設定為 Application
物件。
例如,藉由點+Button
選 來與UI互動,會導致程式代碼後置執行中的NotesPage
下列事件處理程式:
void OnNoteAddedClicked(object sender, EventArgs e)
{
MainActivity.Instance.NavigateToNoteEntryPage(new Note());
}
欄位static
MainActivity.Instance
可叫MainActivity.NavigateToNoteEntryPage
用 方法,如下列程式代碼範例所示:
public void NavigateToNoteEntryPage(Note note)
{
NoteEntryPage noteEntryPage = new NoteEntryPage
{
BindingContext = note,
// Set the parent so that the app-level resource dictionary can be located.
Parent = Xamarin.Forms.Application.Current
};
AndroidX.Fragment.App.Fragment noteEntryFragment = noteEntryPage.CreateSupportFragment(this);
SupportFragmentManager
.BeginTransaction()
.AddToBackStack(null)
.Replace(Resource.Id.fragment_frame_layout, noteEntryFragment)
.Commit();
noteEntryPage.Parent = null;
}
方法NavigateToNoteEntryPage
會Xamarin.FormsContentPage
使用CreateSupportFragment
擴充方法將 衍生頁面Fragment
轉換成 ,並將 新增Fragment
至片段返回堆疊。 因此,將會顯示 中 Xamarin.FormsNoteEntryPage
定義的UI,如下列螢幕快照所示:
NoteEntryPage
顯示時,點選返回箭號會從片段傳回的堆疊中彈出 NoteEntryPage
Fragment
的 ,並將使用者傳回 給 Fragment
類別的 NotesPage
。
啟用返回瀏覽支援
類別 SupportFragmentManager
具有 BackStackChanged
每當片段返回堆疊的內容變更時引發的事件。 類別 OnCreate
中的 MainActivity
方法包含此事件的匿名事件處理程式:
SupportFragmentManager.BackStackChanged += (sender, e) =>
{
bool hasBack = SupportFragmentManager.BackStackEntryCount > 0;
SupportActionBar.SetHomeButtonEnabled(hasBack);
SupportActionBar.SetDisplayHomeAsUpEnabled(hasBack);
SupportActionBar.Title = hasBack ? "Note Entry" : "Notes";
};
此事件處理程式會在動作列上顯示返回按鈕,前提是片段返回堆疊上有一或多個 Fragment
實例。 點選返回按鈕的 OnOptionsItemSelected
回應是由覆寫處理:
public override bool OnOptionsItemSelected(Android.Views.IMenuItem item)
{
if (item.ItemId == global::Android.Resource.Id.Home && SupportFragmentManager.BackStackEntryCount > 0)
{
SupportFragmentManager.PopBackStack();
return true;
}
return base.OnOptionsItemSelected(item);
}
OnOptionsItemSelected
每當選取選項功能表中的專案時,就會呼叫 覆寫。 此實作會從片段返回堆疊快顯目前的片段,前提是已選取 [上一頁] 按鈕,且片段返回堆棧上有一或多個 Fragment
實例。
多個活動
當應用程式由多個活動組成時, ContentPage
衍生的頁面可以內嵌至每個活動。 在此案例中,Forms.Init
只有在第一個Activity
內嵌Xamarin.FormsContentPage
的 覆寫中OnCreate
,才需要呼叫 方法。 不過,這有下列影響:
- 的值
Xamarin.Forms.Color.Accent
會取自Activity
呼叫 方法的Forms.Init
。 - 的值
Xamarin.Forms.Application.Current
將會與Activity
呼叫 方法的Forms.Init
相關聯。
選擇檔案
在內嵌 ContentPage
使用需要支援 HTML [選擇檔案] 按鈕的 衍生頁面 WebView
時, Activity
必須覆寫 OnActivityResult
方法:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
ActivityResultCallbackRegistry.InvokeCallback(requestCode, resultCode, data);
}
UWP
在UWP上,原生 App
類別通常是執行應用程式啟動相關工作的位置。 Xamarin.Forms通常會在 UWP 應用程式中OnLaunched
,在Xamarin.Forms原生App
類別的覆寫中初始化 ,以將 LaunchActivatedEventArgs
自變數傳遞至 Forms.Init
方法。 基於這個理由,取用Xamarin.FormsContentPage
衍生頁面的原生 UWP 應用程式最容易從 App.OnLaunched
方法呼叫 Forms.Init
方法:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
// ...
Xamarin.Forms.Forms.Init(e);
// Create app-level resource dictionary.
Xamarin.Forms.Application.Current = new Xamarin.Forms.Application();
Xamarin.Forms.Application.Current.Resources = new MyDictionary();
// ...
}
此外, OnLaunched
方法也可以建立應用程式所需的任何應用層級資源字典。
根據預設,原生 App
類別會啟動 類別 MainPage
作為應用程式的第一頁。 下列程式代碼範例顯示 MainPage
範例應用程式中的 類別:
public sealed partial class MainPage : Page
{
NotesPage notesPage;
NoteEntryPage noteEntryPage;
public static MainPage Instance;
public static string FolderPath { get; private set; }
public MainPage()
{
this.NavigationCacheMode = NavigationCacheMode.Enabled;
Instance = this;
FolderPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData));
notesPage = new Notes.UWP.Views.NotesPage
{
// Set the parent so that the app-level resource dictionary can be located.
Parent = Xamarin.Forms.Application.Current
};
this.Content = notesPage.CreateFrameworkElement();
// ...
notesPage.Parent = null;
}
// ...
}
建 MainPage
構函式會執行下列工作:
- 頁面已啟用快取功能,因此當使用者巡覽回頁面時,不會建構新的
MainPage
。 - 類別的
MainPage
參考會儲存在static
Instance
欄位中。 這是為其他類別提供機制,以呼叫 類別中MainPage
定義的方法。 - 屬性
FolderPath
會初始化為將儲存記事數據之裝置上的路徑。 - 系統會
NotesPage
建立 物件,這是 Xamarin.FormsContentPage
XAML 中定義的衍生頁面,且其父系設定為先前建立Xamarin.Forms.Application
的物件。 - 物件
NotesPage
會使用CreateFrameworkElement
擴充方法轉換成FrameworkElement
,然後設定為 類別的內容MainPage
。 - 對象的
Parent
屬性NotesPage
設定為null
,以防止記憶體流失。
執行建 MainPage
構函式之後,類別中 Xamarin.FormsNotesPage
定義的UI將會顯示,如下列螢幕快照所示:
重要
所有 ContentPage
衍生的頁面都可以取用應用層級 ResourceDictionary
中定義的資源,前提是 Parent
頁面的 屬性設定為 Application
物件。
例如,藉由點+Button
選 來與UI互動,會導致程式代碼後置執行中的NotesPage
下列事件處理程式:
void OnNoteAddedClicked(object sender, EventArgs e)
{
MainPage.Instance.NavigateToNoteEntryPage(new Note());
}
欄位static
MainPage.Instance
可叫MainPage.NavigateToNoteEntryPage
用 方法,如下列程式代碼範例所示:
public void NavigateToNoteEntryPage(Note note)
{
noteEntryPage = new Notes.UWP.Views.NoteEntryPage
{
BindingContext = note,
// Set the parent so that the app-level resource dictionary can be located.
Parent = Xamarin.Forms.Application.Current
};
this.Frame.Navigate(noteEntryPage);
noteEntryPage.Parent = null;
}
UWP 中的流覽通常會使用 Frame.Navigate
採用 自變數的 方法 Page
來執行。 Xamarin.Forms 定義 Frame.Navigate
採用衍生頁面實例的 ContentPage
擴充方法。 因此,當方法執行時 NavigateToNoteEntryPage
,將會顯示 中 Xamarin.FormsNoteEntryPage
定義的UI,如下列螢幕快照所示:
NoteEntryPage
顯示 時,點選 [上一頁] 箭號會從應用程式內傳回堆疊彈出 FrameworkElement
NoteEntryPage
的 ,並將使用者傳回 給 FrameworkElement
類別的 NotesPage
。
啟用頁面重設大小支援
當UWP應用程式視窗重設大小時, Xamarin.Forms 也應該調整內容的大小。 這可藉由在建構函式中MainPage
註冊 事件的事件處理程式Loaded
來完成:
public MainPage()
{
// ...
this.Loaded += OnMainPageLoaded;
// ...
}
當 Loaded
頁面配置、轉譯並準備好進行互動時,就會引發事件,並在響應中執行 OnMainPageLoaded
方法:
void OnMainPageLoaded(object sender, RoutedEventArgs e)
{
this.Frame.SizeChanged += (o, args) =>
{
if (noteEntryPage != null)
noteEntryPage.Layout(new Xamarin.Forms.Rectangle(0, 0, args.NewSize.Width, args.NewSize.Height));
else
notesPage.Layout(new Xamarin.Forms.Rectangle(0, 0, args.NewSize.Width, args.NewSize.Height));
};
}
方法OnMainPageLoaded
會註冊 事件的Frame.SizeChanged
匿名事件處理程式,當 或 ActualWidth
屬性在 上Frame
變更時ActualHeight
,就會引發此事件處理程式。 回應中, Xamarin.Forms 使用中頁面的內容會藉由呼叫 Layout
方法來重設大小。
啟用返回瀏覽支援
在 UWP 上,應用程式必須針對不同裝置尺寸的所有硬體和軟體返回按鈕啟用返回流覽。 這可以藉由註冊 事件的事件處理程式 BackRequested
來完成,這可以在建構函式中 MainPage
執行:
public MainPage()
{
// ...
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
}
啟動應用程式時, GetForCurrentView
方法會擷取 SystemNavigationManager
與目前檢視相關聯的物件,然後註冊事件的事件處理程式 BackRequested
。 如果應用程式是前景應用程式,而且在回應中呼叫事件處理程式, OnBackRequested
則應用程式只會接收此事件:
void OnBackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
noteEntryPage = null;
}
}
OnBackRequested
事件處理程式會在應用程式的根框架上呼叫 GoBack
方法,並將屬性設定BackRequestedEventArgs.Handled
為 true
,將 事件標示為已處理。 無法將事件標示為已處理,可能會導致事件遭到忽略。
應用程式會選擇是否要在標題列上顯示返回按鈕。 這可藉由將 AppViewBackButtonVisibility
屬性設定為 類別中的App
其中AppViewBackButtonVisibility
一個列舉值來達成:
void OnNavigated(object sender, NavigationEventArgs e)
{
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
((Frame)sender).CanGoBack ? AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed;
}
OnNavigated
事件處理程式會在發生頁面導覽時更新標題列返回按鈕的可見度,而事件處理程式是針對事件引發而執行的Navigated
。 這可確保如果應用程式內返回堆疊不是空白,或如果應用程式內返回堆疊是空的,則會從標題列移除標題列返回按鈕。
如需 UWP 上傳回瀏覽支援的詳細資訊,請參閱 UWP 應用程式的瀏覽歷程記錄和向後流覽。