Xamarin.Forms en proyectos nativos de Xamarin
Normalmente, una Xamarin.Forms aplicación incluye una o varias páginas que derivan de ContentPage
y todas las plataformas de un proyecto de biblioteca de .NET Standard o un proyecto compartido comparten estas páginas. Sin embargo, Native Forms permite ContentPage
agregar páginas derivadas directamente a aplicaciones nativas de Xamarin.iOS, Xamarin.Android y UWP. En comparación con que el proyecto nativo consume ContentPage
páginas derivadas de un proyecto de biblioteca de .NET Standard o un proyecto compartido, la ventaja de agregar páginas directamente a proyectos nativos es que las páginas se pueden ampliar con vistas nativas. A continuación, las vistas nativas se pueden denominar en XAML con x:Name
y hacer referencia desde el código subyacente. Para obtener más información sobre las vistas nativas, vea Vistas nativas.
El proceso para consumir una Xamarin.FormsContentPage
página derivada de en un proyecto nativo es el siguiente:
- Agregue el Xamarin.Forms paquete NuGet al proyecto nativo.
- Agregue la
ContentPage
página derivada de y las dependencias al proyecto nativo. - Llame al método
Forms.Init
. - Construya una instancia de la
ContentPage
página derivada de y conviértala en el tipo nativo adecuado mediante uno de los métodos de extensión siguientes:CreateViewController
para iOS,CreateSupportFragment
para Android oCreateFrameworkElement
para UWP. - Vaya a la representación de tipo nativo de la
ContentPage
página derivada de mediante la API de navegación nativa.
Xamarin.Forms debe inicializarse llamando al Forms.Init
método antes de que un proyecto nativo pueda construir una ContentPage
página derivada de . La elección de cuándo hacerlo depende principalmente de cuándo es más conveniente en el flujo de la aplicación: se puede realizar al iniciar la aplicación o justo antes de que se construya la ContentPage
página derivada de . En este artículo y las aplicaciones de ejemplo complementarias, se llama al método al inicio de la Forms.Init
aplicación.
Nota
La solución de aplicación de ejemplo NativeForms no contiene ningún Xamarin.Forms proyecto. En su lugar, consta de un proyecto de Xamarin.iOS, un proyecto de Xamarin.Android y un proyecto de UWP. Cada proyecto es un proyecto nativo que usa Native Forms para consumir ContentPage
páginas derivadas de . Sin embargo, no hay ninguna razón por la que los proyectos nativos no podían consumir ContentPage
páginas derivadas de un proyecto de biblioteca de .NET Standard o un proyecto compartido.
Cuando se usan formularios nativos, Xamarin.Forms las características como DependencyService
, MessagingCenter
y el motor de enlace de datos siguen funcionando. Sin embargo, la navegación por páginas debe realizarse mediante la API de navegación nativa.
iOS
En iOS, la FinishedLaunching
invalidación de la clase suele ser el lugar para realizar tareas relacionadas con el inicio de la AppDelegate
aplicación. Se llama después de que se haya iniciado la aplicación y normalmente se invalida para configurar la ventana principal y el controlador de vista. En el ejemplo de código siguiente se muestra la AppDelegate
clase en la aplicación de ejemplo:
[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;
}
// ...
}
El método FinishedLaunching
realiza las siguientes tareas:
- Xamarin.Forms se inicializa llamando al
Forms.Init
método . - Se crea un nuevo
Xamarin.Forms.Application
objeto y su diccionario de recursos de nivel de aplicación se establece en unResourceDictionary
que se define en XAML. - Una referencia a la
AppDelegate
clase se almacena en elstatic
Instance
campo . Esto es para proporcionar un mecanismo para que otras clases llamen a métodos definidos en laAppDelegate
clase . - Se
UIWindow
crea , que es el contenedor principal de vistas en aplicaciones nativas de iOS. - La
FolderPath
propiedad se inicializa en una ruta de acceso en el dispositivo donde se almacenarán los datos de nota. NotesPage
Se crea un objeto , que es una Xamarin.FormsContentPage
página derivada de XAML definida en XAML y su elemento primario se establece en el objeto creadoXamarin.Forms.Application
anteriormente.- El
NotesPage
objeto se convierte en medianteUIViewController
el método deCreateViewController
extensión . - La
Title
propiedad deUIViewController
se establece, que se mostrará en .UINavigationBar
AppNavigationController
Se crea un para administrar la navegación jerárquica. Se trata de una clase de controlador de navegación personalizada, que se deriva deUINavigationController
. ElAppNavigationController
objeto administra una pila de controladores de vista y elUIViewController
pasado al constructor se presentará inicialmente cuando se cargue .AppNavigationController
- El
AppNavigationController
objeto se establece como el nivelUIViewController
superior deUIWindow
yUIWindow
se establece como la ventana de clave de la aplicación y se hace visible. - La
Parent
propiedad delNotesPage
objeto se establece ennull
, para evitar una pérdida de memoria.
Una vez ejecutado el FinishedLaunching
método, se mostrará la interfaz de usuario definida en la Xamarin.FormsNotesPage
clase , como se muestra en la captura de pantalla siguiente:
Importante
Todas las ContentPage
páginas derivadas pueden consumir recursos definidos en el nivel ResourceDictionary
de aplicación , siempre que la Parent
propiedad de la página se establezca en el Application
objeto .
Al interactuar con la interfaz de usuario, por ejemplo, al pulsar en , +Button
se producirá el siguiente controlador de eventos en la ejecución del NotesPage
código subyacente:
void OnNoteAddedClicked(object sender, EventArgs e)
{
AppDelegate.Instance.NavigateToNoteEntryPage(new Note());
}
El static
AppDelegate.Instance
campo permite invocar el AppDelegate.NavigateToNoteEntryPage
método , que se muestra en el ejemplo de código siguiente:
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;
}
El NavigateToNoteEntryPage
método convierte la Xamarin.FormsContentPage
página derivada de en con UIViewController
el método de CreateViewController
extensión y establece la Title
propiedad de UIViewController
. A UIViewController
continuación, el método inserta AppNavigationController
en .PushViewController
Por lo tanto, se mostrará la interfaz de usuario definida en la Xamarin.FormsNoteEntryPage
clase , como se muestra en la captura de pantalla siguiente:
Cuando se muestra , NoteEntryPage
la navegación hacia atrás mostrará para UIViewController
la NoteEntryPage
clase desde AppNavigationController
, devolviendo el usuario a para UIViewController
la NotesPage
clase . Sin embargo, la obtención de un UIViewController
elemento de la pila de navegación nativa de iOS no elimina automáticamente el UIViewController
objeto adjunto y .Page
Por lo tanto, la AppNavigationController
clase invalida el PopViewController
método para eliminar controladores de vista en la navegación hacia atrás:
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);
}
}
La PopViewController
invalidación llama al Dispose
método en el UIViewController
objeto que se ha extraído de la pila de navegación nativa de iOS. Si no lo hace, se producirá que el UIViewController
objeto adjunto y esté Page
huérfano.
Importante
Los objetos huérfanos no se pueden recopilar como elementos no utilizados, por lo que se produce una pérdida de memoria.
Android
En Android, la OnCreate
invalidación de la clase suele ser el lugar para realizar tareas relacionadas con el inicio de la MainActivity
aplicación. En el ejemplo de código siguiente se muestra la MainActivity
clase en la aplicación de ejemplo:
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;
}
...
}
El método OnCreate
realiza las siguientes tareas:
- Xamarin.Forms se inicializa llamando al
Forms.Init
método . - Se crea un nuevo
Xamarin.Forms.Application
objeto y su diccionario de recursos de nivel de aplicación se establece en unResourceDictionary
que se define en XAML. - Una referencia a la
MainActivity
clase se almacena en elstatic
Instance
campo . Esto es para proporcionar un mecanismo para que otras clases llamen a métodos definidos en laMainActivity
clase . - El
Activity
contenido se establece a partir de un recurso de diseño. En la aplicación de ejemplo, el diseño consta de unLinearLayout
objeto que contiene yToolbar
un queFrameLayout
actúa como un contenedor de fragmentos. Toolbar
se recupera y se establece como la barra de acciones paraActivity
y se establece el título de la barra de acciones.- La
FolderPath
propiedad se inicializa en una ruta de acceso en el dispositivo donde se almacenarán los datos de nota. NotesPage
Se crea un objeto , que es una Xamarin.FormsContentPage
página derivada de XAML definida en XAML y su elemento primario se establece en el objeto creadoXamarin.Forms.Application
anteriormente.- El
NotesPage
objeto se convierte en medianteFragment
el método deCreateSupportFragment
extensión . - La
SupportFragmentManager
clase crea y confirma una transacción que reemplaza laFrameLayout
instancia por paraFragment
laNotesPage
clase . - La
Parent
propiedad delNotesPage
objeto se establece ennull
, para evitar una pérdida de memoria.
Para obtener más información sobre los fragmentos, vea Fragmentos.
Una vez ejecutado el OnCreate
método, se mostrará la interfaz de usuario definida en la Xamarin.FormsNotesPage
clase , como se muestra en la captura de pantalla siguiente:
Importante
Todas las ContentPage
páginas derivadas pueden consumir recursos definidos en el nivel ResourceDictionary
de aplicación , siempre que la Parent
propiedad de la página se establezca en el Application
objeto .
Al interactuar con la interfaz de usuario, por ejemplo, al pulsar en , +Button
se producirá el siguiente controlador de eventos en la ejecución del NotesPage
código subyacente:
void OnNoteAddedClicked(object sender, EventArgs e)
{
MainActivity.Instance.NavigateToNoteEntryPage(new Note());
}
El static
MainActivity.Instance
campo permite invocar el MainActivity.NavigateToNoteEntryPage
método , que se muestra en el ejemplo de código siguiente:
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;
}
El NavigateToNoteEntryPage
método convierte la Xamarin.FormsContentPage
página derivada de en un Fragment
objeto con el método de CreateSupportFragment
extensión y agrega al Fragment
fragmento de la pila de retroceso. Por lo tanto, se mostrará la interfaz de usuario definida en Xamarin.FormsNoteEntryPage
, como se muestra en la captura de pantalla siguiente:
NoteEntryPage
Cuando se muestre , al pulsar la flecha atrás se mostrará el Fragment
elemento de la NoteEntryPage
pila inversa del fragmento, devolviendo el usuario a para Fragment
la NotesPage
clase .
Habilitación de la compatibilidad con la navegación hacia atrás
La SupportFragmentManager
clase tiene un BackStackChanged
evento que se activa cada vez que cambia el contenido de la pila de retroceso del fragmento. El OnCreate
método de la MainActivity
clase contiene un controlador de eventos anónimo para este evento:
SupportFragmentManager.BackStackChanged += (sender, e) =>
{
bool hasBack = SupportFragmentManager.BackStackEntryCount > 0;
SupportActionBar.SetHomeButtonEnabled(hasBack);
SupportActionBar.SetDisplayHomeAsUpEnabled(hasBack);
SupportActionBar.Title = hasBack ? "Note Entry" : "Notes";
};
Este controlador de eventos muestra un botón Atrás en la barra de acciones siempre que haya una o varias Fragment
instancias en la pila de retroceso del fragmento. La invalidación controla OnOptionsItemSelected
la respuesta para pulsar el botón Atrás:
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);
}
Se llama a la OnOptionsItemSelected
invalidación cada vez que se selecciona un elemento en el menú de opciones. Esta implementación extrae el fragmento actual de la pila de retroceso del fragmento, siempre que se haya seleccionado el botón Atrás y que haya una o varias Fragment
instancias en la pila de retroceso del fragmento.
Varias actividades
Cuando una aplicación se compone de varias actividades, ContentPage
las páginas derivadas de se pueden insertar en cada una de las actividades. En este escenario, solo se debe llamar al Forms.Init
método en la OnCreate
invalidación de la primera Activity
que inserta .Xamarin.FormsContentPage
Sin embargo, esto tiene el siguiente impacto:
- El valor de
Xamarin.Forms.Color.Accent
se tomará delActivity
objeto que llamó alForms.Init
método . - El valor de
Xamarin.Forms.Application.Current
se asociará con elActivity
que llamó alForms.Init
método .
Elija un archivo
Al insertar una ContentPage
página derivada de que usa un WebView
que necesita admitir un botón Activity
HTML "Elegir archivo", deberá invalidar el OnActivityResult
método :
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
ActivityResultCallbackRegistry.InvokeCallback(requestCode, resultCode, data);
}
UWP
En UWP, la clase nativa App
suele ser el lugar para realizar tareas relacionadas con el inicio de la aplicación. Xamarin.Forms normalmente se inicializa, en Xamarin.Forms aplicaciones para UWP, en la OnLaunched
invalidación de la clase nativa App
, para pasar el LaunchActivatedEventArgs
argumento al Forms.Init
método . Por este motivo, las aplicaciones nativas para UWP que consumen una Xamarin.FormsContentPage
página derivada de pueden llamar más fácilmente al Forms.Init
método desde el App.OnLaunched
método :
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();
// ...
}
Además, el OnLaunched
método también puede crear cualquier diccionario de recursos de nivel de aplicación que requiera la aplicación.
De forma predeterminada, la clase nativa App
inicia la MainPage
clase como la primera página de la aplicación. En el ejemplo de código siguiente se muestra la MainPage
clase en la aplicación de ejemplo:
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;
}
// ...
}
El MainPage
constructor realiza las siguientes tareas:
- El almacenamiento en caché está habilitado para la página, por lo que un nuevo
MainPage
no se construye cuando un usuario vuelve a la página. - Una referencia a la
MainPage
clase se almacena en elstatic
Instance
campo . Esto es para proporcionar un mecanismo para que otras clases llamen a métodos definidos en laMainPage
clase . - La
FolderPath
propiedad se inicializa en una ruta de acceso en el dispositivo donde se almacenarán los datos de nota. NotesPage
Se crea un objeto , que es una Xamarin.FormsContentPage
página derivada de XAML definida en XAML y su elemento primario se establece en el objeto creadoXamarin.Forms.Application
anteriormente.- El
NotesPage
objeto se convierte en unFrameworkElement
mediante el método deCreateFrameworkElement
extensión y, a continuación, se establece como el contenido de laMainPage
clase . - La
Parent
propiedad delNotesPage
objeto se establece ennull
, para evitar una pérdida de memoria.
Una vez ejecutado el MainPage
constructor, se mostrará la interfaz de usuario definida en la Xamarin.FormsNotesPage
clase , como se muestra en la captura de pantalla siguiente:
Importante
Todas las ContentPage
páginas derivadas pueden consumir recursos definidos en el nivel ResourceDictionary
de aplicación , siempre que la Parent
propiedad de la página se establezca en el Application
objeto .
Al interactuar con la interfaz de usuario, por ejemplo, al pulsar en , +Button
se producirá el siguiente controlador de eventos en la ejecución del NotesPage
código subyacente:
void OnNoteAddedClicked(object sender, EventArgs e)
{
MainPage.Instance.NavigateToNoteEntryPage(new Note());
}
El static
MainPage.Instance
campo permite invocar el MainPage.NavigateToNoteEntryPage
método , que se muestra en el ejemplo de código siguiente:
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;
}
La navegación en UWP se realiza normalmente con el Frame.Navigate
método , que toma un Page
argumento. Xamarin.Forms define un método de Frame.Navigate
extensión que toma una ContentPage
instancia de página derivada de . Por lo tanto, cuando se ejecuta el NavigateToNoteEntryPage
método, se mostrará la interfaz de usuario definida en , Xamarin.FormsNoteEntryPage
como se muestra en la captura de pantalla siguiente:
NoteEntryPage
Cuando se muestre , al pulsar la flecha atrás se mostrará el FrameworkElement
elemento de la NoteEntryPage
pila atrás desde la pila posterior de la aplicación, devolviendo el usuario a para FrameworkElement
la NotesPage
clase .
Habilitación de la compatibilidad con el cambio de tamaño de página
Cuando se cambia el tamaño de la ventana de la aplicación para UWP, también se debe cambiar el tamaño del Xamarin.Forms contenido. Esto se logra mediante el registro de un controlador de eventos para el Loaded
evento, en el MainPage
constructor :
public MainPage()
{
// ...
this.Loaded += OnMainPageLoaded;
// ...
}
El Loaded
evento se desencadena cuando la página se diseña, representa y está lista para la interacción, y ejecuta el OnMainPageLoaded
método en respuesta:
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));
};
}
El OnMainPageLoaded
método registra un controlador de eventos anónimo para el Frame.SizeChanged
evento , que se genera cuando las ActualHeight
propiedades o ActualWidth
cambian en Frame
. En respuesta, se cambia el tamaño del Xamarin.Forms contenido de la página activa mediante una llamada al Layout
método .
Habilitación de la compatibilidad con la navegación hacia atrás
En UWP, las aplicaciones deben habilitar la navegación hacia atrás para todos los botones atrás de hardware y software, en diferentes factores de forma del dispositivo. Esto se puede lograr mediante el registro de un controlador de eventos para el BackRequested
evento, que se puede realizar en el MainPage
constructor:
public MainPage()
{
// ...
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
}
Cuando se inicia la aplicación, el GetForCurrentView
método recupera el SystemNavigationManager
objeto asociado a la vista actual y, a continuación, registra un controlador de eventos para el BackRequested
evento. La aplicación solo recibe este evento si es la aplicación en primer plano y, en respuesta, llama al OnBackRequested
controlador de eventos:
void OnBackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
noteEntryPage = null;
}
}
El OnBackRequested
controlador de eventos llama al GoBack
método en el marco raíz de la aplicación y establece la BackRequestedEventArgs.Handled
propiedad true
en para marcar el evento como controlado. Si no se marca el evento como controlado, se podría omitir el evento.
La aplicación elige si se va a mostrar un botón Atrás en la barra de título. Esto se logra estableciendo la AppViewBackButtonVisibility
propiedad en uno de los AppViewBackButtonVisibility
valores de enumeración, en la App
clase :
void OnNavigated(object sender, NavigationEventArgs e)
{
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
((Frame)sender).CanGoBack ? AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed;
}
El OnNavigated
controlador de eventos, que se ejecuta en respuesta a la Navigated
activación del evento, actualiza la visibilidad del botón Atrás de la barra de título cuando se produce la navegación por páginas. Esto garantiza que el botón Atrás de la barra de título esté visible si la pila atrás de la aplicación no está vacía o se quita de la barra de título si la pila atrás en la aplicación está vacía.
Para obtener más información sobre la compatibilidad con la navegación hacia atrás en UWP, consulta Historial de navegación y navegación hacia atrás para aplicaciones para UWP.