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 agregar páginas derivadas de ContentPage
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 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 se hace referencia a las vistas nativas desde el código subyacente. Para obtener más información sobre las vistas nativas, vea Vistas nativas.
El proceso para consumir una página derivada de un Xamarin.FormsContentPage
proyecto nativo es el siguiente:
- Agregue el paquete NuGet Xamarin.Forms al proyecto nativo.
- Agregue la página derivada de
ContentPage
, 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 página derivada de
ContentPage
mediante la API de navegación nativa.
Xamarin.Forms debe inicializarse llamando al método Forms.Init
antes de que un proyecto nativo pueda construir una página derivada de ContentPage
. Elegir cuándo hacerlo depende principalmente de cuándo es más conveniente en el flujo de la aplicación, que se puede realizar al iniciar la aplicación o justo antes de que se construya la página derivada ContentPage
. En este artículo, y las aplicaciones de ejemplo complementarias, se llama al método Forms.Init
en el inicio de la 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 Formularios nativos para consumir ContentPage
páginas derivadas. 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.
Al usar formularios nativos, Xamarin.Forms las características como DependencyService
, MessagingCenter
, y el motor de enlace de datos siguen funcionando. Sin embargo, la navegación de páginas debe realizarse mediante la API de navegación nativa.
iOS
En iOS, el FinishedLaunching
invalidación en la clase AppDelegate
suele ser el lugar para realizar tareas relacionadas con el inicio de la aplicación. Se llama después de iniciar la aplicación y normalmente se reemplaza para configurar la ventana principal y el controlador de vista. En el ejemplo de código siguiente se muestra la clase AppDelegate
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 método
Forms.Init
. - Se crea un nuevo
Xamarin.Forms.Application
objeto y su diccionario de recursos de nivel de aplicación se establece en unResourceDictionary
objeto definido en XAML. - Una referencia a la
AppDelegate
clase se almacena en elstatic
Instance
campo . Se trata de proporcionar un mecanismo para que otras clases llamen a métodos definidos en laAppDelegate
clase. - Se
UIWindow
crea el, que es el contenedor principal para las vistas en aplicaciones nativas de iOS. - La propiedad
FolderPath
se inicializa en una ruta de acceso en el dispositivo donde se almacenarán los datos de nota. - Se crea un objeto
NotesPage
, que es una página derivada de Xamarin.FormsContentPage
definida en XAML y su elemento primario se establece en el objetoXamarin.Forms.Application
creado anteriormente. - El
NotesPage
objeto se convierte en medianteUIViewController
el método deCreateViewController
extensión. - La
Title
propiedad deUIViewController
se establece, que se mostrará enUINavigationBar
. - Se crea un
AppNavigationController
para administrar la navegación jerárquica. Se trata de una clase de controlador de navegación personalizada, que deriva deUINavigationController
. ElAppNavigationController
objeto administra una pila de controladores de vista y elUIViewController
pasado al constructor se presentará inicialmente cuandoAppNavigationController
se cargue. - El
AppNavigationController
objeto se establece como el nivelUIViewController
superior deUIWindow
, y seUIWindow
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 propiedadParent
de la página esté establecida en el objeto Application
.
La interacción con la interfaz de usuario, por ejemplo, al pulsar en , + Button
dará como resultado 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 UIViewController
con el CreateViewController
método de extensión y establece la Title
propiedad de UIViewController
. A UIViewController
continuación, el método inserta AppNavigationController
el PushViewController
control. 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 la NoteEntryPage
, navegación hacia atrás mostrará el UIViewController
de la clase NoteEntryPage
de la AppNavigationController
, devolviendo el usuario al UIViewController
de la clase NotesPage
. Sin embargo, la salida 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 clase AppNavigationController
invalida el método PopViewController
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 se hace esto, se producirá que el UIViewController
objeto adjunto y el objeto adjunto Page
estén huérfanos.
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 en la clase MainActivity
suele ser el lugar para realizar tareas relacionadas con el inicio de la aplicación. En el ejemplo de código siguiente se muestra la clase MainActivity
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 método
Forms.Init
. - Se crea un nuevo
Xamarin.Forms.Application
objeto y su diccionario de recursos de nivel de aplicación se establece en unResourceDictionary
objeto definido en XAML. - Una referencia a la
MainActivity
clase se almacena en elstatic
Instance
campo . Se trata de proporcionar un mecanismo para que otras clases llamen a métodos definidos en laMainActivity
clase. - El
Activity
contenido se establece desde un recurso de diseño. En la aplicación de ejemplo, el diseño consta de unLinearLayout
que contiene unToolbar
, y unFrameLayout
para actuar como un contenedor de fragmentos. - El
Toolbar
se recupera y se establece como la barra de acciones de laActivity
, y se establece el título de la barra de acciones. - La propiedad
FolderPath
se inicializa en una ruta de acceso en el dispositivo donde se almacenarán los datos de nota. - Se crea un objeto
NotesPage
, que es una página derivada de Xamarin.FormsContentPage
definida en XAML y su elemento primario se establece en el objetoXamarin.Forms.Application
creado anteriormente. - El
NotesPage
objeto se convierte en medianteFragment
el método deCreateSupportFragment
extensión. - La clase
SupportFragmentManager
crea y confirma una transacción que reemplaza la instancia deFrameLayout
por elFragment
de la claseNotesPage
. - 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 método OnCreate
, se mostrará la interfaz de usuario definida en la clase Xamarin.FormsNotesPage
, 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 propiedadParent
de la página esté establecida en el objeto Application
.
La interacción con la interfaz de usuario, por ejemplo, al pulsar en , + Button
dará como resultado 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 método NavigateToNoteEntryPage
convierte la página derivada de Xamarin.FormsContentPage
en un Fragment
con el método de extensión CreateSupportFragment
y agrega el Fragment
a la pila de retroceso del fragmento. Por lo tanto, se mostrará la interfaz de usuario definida en el Xamarin.FormsNoteEntryPage
, como se muestra en la captura de pantalla siguiente:
Cuando se muestra NoteEntryPage
, al pulsar la flecha atrás se abrirá el Fragment
para el NoteEntryPage
de la pila de retroceso del fragmento, devolviendo el usuario al Fragment
de la clase NotesPage
.
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 posterior del fragmento. La respuesta para pulsar el botón Atrás se controla mediante la OnOptionsItemSelected
invalidación:
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 invalidación OnOptionsItemSelected
cada vez que se selecciona un elemento en el menú de opciones. Esta implementación muestra 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 posterior del fragmento.
Varias actividades
Cuando una aplicación se compone de varias actividades, ContentPage
las páginas derivadas se pueden insertar en cada una de las actividades. En este escenario, Forms.Init
solo se debe llamar al método en la OnCreate
invalidación del primero Activity
que inserta un Xamarin.FormsContentPage
. Sin embargo, esto tiene el siguiente impacto:
- El valor de
Xamarin.Forms.Color.Accent
se tomará delActivity
que llamó alForms.Init
método. - El valor de
Xamarin.Forms.Application.Current
se asociará alActivity
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 App
nativa suele ser el lugar para realizar tareas relacionadas con el inicio de la aplicación. Xamarin.Forms normalmente se inicializa, en Xamarin.Forms las 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 método OnLaunched
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 clase MainPage
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 constructor MainPage
realiza las siguientes tareas:
- El almacenamiento en caché está habilitado para la página, de modo que un nuevo
MainPage
no se construya cuando un usuario vuelva a la página. - Una referencia a la
MainPage
clase se almacena en elstatic
Instance
campo . Se trata de proporcionar un mecanismo para que otras clases llamen a métodos definidos en laMainPage
clase. - La propiedad
FolderPath
se inicializa en una ruta de acceso en el dispositivo donde se almacenarán los datos de nota. - Se crea un objeto
NotesPage
, que es una página derivada de Xamarin.FormsContentPage
definida en XAML y su elemento primario se establece en el objetoXamarin.Forms.Application
creado anteriormente. - El
NotesPage
objeto se convierte en unFrameworkElement
mediante elCreateFrameworkElement
método de 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 constructor MainPage
, se mostrará la interfaz de usuario definida en la clase Xamarin.FormsNotesPage
, 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 propiedadParent
de la página esté establecida en el objeto Application
.
La interacción con la interfaz de usuario, por ejemplo, al pulsar en , + Button
dará como resultado 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 método Frame.Navigate
, que toma un argumento Page
. Xamarin.Forms define un método de extensión Frame.Navigate
que toma una instancia de página derivada ContentPage
. Por lo tanto, cuando se ejecuta el método NavigateToNoteEntryPage
, se mostrará la interfaz de usuario definida en el Xamarin.FormsNoteEntryPage
, como se muestra en la captura de pantalla siguiente:
Cuando se muestre el NoteEntryPage
, al pulsar la flecha atrás se abrirá el FrameworkElement
de la NoteEntryPage
de la pila de retroceso de la aplicación, devolviendo el usuario al FrameworkElement
de la clase NotesPage
.
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 se diseña, representa y prepara la página 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 llamando 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 de dispositivo. Esto se puede lograr mediante el registro de un controlador de eventos para el evento BackRequested
, que se puede realizar en el constructor MainPage
:
public MainPage()
{
// ...
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
}
Cuando se inicia la aplicación, el método GetForCurrentView
recupera el objeto SystemNavigationManager
asociado a la vista actual y, a continuación, registra un controlador de eventos para el evento BackRequested
. La aplicación solo recibe este evento si es la aplicación en primer plano y, en respuesta, llama al controlador de eventos OnBackRequested
:
void OnBackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
noteEntryPage = null;
}
}
El controlador de eventos OnBackRequested
llama al método GoBack
en el marco raíz de la aplicación y establece la propiedad BackRequestedEventArgs.Handled
en true
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 propiedad AppViewBackButtonVisibility
en uno de los valores de enumeración AppViewBackButtonVisibility
, en la clase App
:
void OnNavigated(object sender, NavigationEventArgs e)
{
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
((Frame)sender).CanGoBack ? AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed;
}
El controlador de eventos OnNavigated
, que se ejecuta en respuesta a la activación del evento Navigated
, 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 de retroceso de la aplicación no está vacía o se quita de la barra de título si la pila de retroceso en la aplicación está vacía.
Para obtener más información sobre la compatibilidad con la navegación inversa en UWP, vea Historial de navegación y navegación hacia atrás para aplicaciones para UWP.