Sei sulla pagina 1di 1691

Contents

Xamarin.Forms
Primeros pasos
Requisitos
Instalación
Compilación de la primera aplicación
Inicio rápido de una sola página
Inicio rápido de varias páginas
Inicio rápido para bases de datos locales
Inicio rápido para estilos
Profundización
XAML
Controles de XAML
XAML Basics
Parte 1. Introducción a XAML
Parte 2. Sintaxis XAML esencial
Parte 3. Extensiones de marcado XAML
Parte 4. Conceptos básicos del enlace de datos
Parte 5. De enlaces de datos a MVVM
Compilación de XAML
Cuadro de herramientas de XAML
Controlador de vista previa de XAML
Datos en tiempo de diseño
Controles personalizados
Espacios de nombres XAML
Esquemas de espacio de nombres personalizado XAML
Prefijos recomendados de espacio de nombres de XAML
Extensiones de marcado XAML
Consumo de extensiones de marcado XAML
Creación de extensiones de marcado XAML
Modificadores de campo
Paso de argumentos
Propiedades enlazables
Propiedades asociadas
Diccionarios de recursos
XAML estándar (versión preliminar)
Controles
Carga de XAML en tiempo de ejecución
Principios de la aplicación
Accesibilidad
Propiedades de automatización
Accesibilidad de teclado
Clase de aplicación
Ciclo de vida de la aplicación
Indexación de la aplicación y vinculación en profundidad
Comportamientos
Introducción
Comportamientos asociados
Comportamientos de Xamarin.Forms
Comportamientos reutilizables
EffectBehavior
EventToCommandBehavior
Representadores personalizados
Introducción
Clases base y controles nativos del representador
Personalización de una entrada
Personalización de una página de contenido
Personalización de un mapa
Personalización de un anclado de mapa
Resaltado de un área circular en un mapa
Resaltado de una región en un mapa
Resaltado de una ruta en un mapa
Personalización de la clase ListView
Personalización de la clase ViewCell
Implementación de una vista
Implementación de una clase HybridWebView
Implementación de un reproductor de vídeo
Creación de reproductores de vídeo de la plataforma
Reproducción de un vídeo de web
Enlace de orígenes de vídeo con el reproductor
Carga de vídeos de recursos de aplicación
Acceso a la biblioteca de vídeos del dispositivo
Controles de transporte de vídeo personalizados
Posicionamiento de vídeo personalizado
Enlace de datos
Enlaces básicos
Modo de enlace
Formato de cadena
Enlace de ruta de acceso
Enlace de convertidores de valores
Conmutación por recuperación de enlaces
Interfaz de comandos
Enlaces compilados
DependencyService
Introducción
Registro y resolución
Selección de biblioteca de fotos
Efectos
Introducción
Creación de efectos
Pasar parámetros
Parámetros como propiedades CLR
Parámetros como propiedades asociadas
Invocación de eventos
Gestos
Pulsar
Reducir
Movimiento panorámico
Deslizar rápidamente
Localización
Localización de cadenas e imágenes
Localización de derecha a izquierda
MessagingCenter
Navegación
Navegación jerárquica
TabbedPage
CarouselPage
MasterDetailPage
Páginas modales
Shell
Introducción
Creación de una aplicación Shell
Control flotante
Pestañas
Configuración de la página
Navegación
Búsqueda
Representadores personalizados
Templates
Plantillas de control
Introducción
Creación de plantilla de control
Enlaces a plantilla
Plantillas de datos
Introducción
Creación de plantilla de datos
Selección de plantilla de datos
Desencadenadores
Interfaz de usuario
Indicador de actividad
Animación
Animaciones simples
Funciones de aceleración
Animaciones personalizadas
BoxView
Button
CheckBox
CollectionView
Introducción
Data
Diseño
Selección
EmptyView
Desplazarse
Colores
Referencia de controles
Páginas
Diseños
Vistas
Celdas
DataPages
Primeros pasos
Controls Reference
DatePicker
Mostrar elementos emergentes
Gráficos con SkiaSharp
Imágenes
ImageButton
Diseños
StackLayout
AbsoluteLayout
RelativeLayout
Grid
FlexLayout
ScrollView
LayoutOptions
Margen y relleno
Orientación del dispositivo
Tableta y escritorio
Diseños enlazables
Creación de un diseño personalizado
Compresión de diseño
ListView
Orígenes de datos
Apariencia de etiqueta
Apariencia de línea
Interactividad
Rendimiento
Mapas
Selector
Configuración de la propiedad ItemsSource de un selector
Adición de datos a la colección de elementos de un selector
Barra de progreso
Slider
Control de incremento
Estilos
Aplicación de estilos para aplicaciones Xamarin.Forms con estilos XAML
Introducción
Estilos explícitos
Estilos implícitos
Estilos globales
Herencia de estilo
Estilos dinámicos
Estilos de dispositivo
Clases de estilo
Aplicación de estilos para aplicaciones Xamarin.Forms con hojas de estilo (CSS)
Switch
TableView
Texto
Label
Entrada
Editor
Fuentes
Estilos
Temas
Tema claro
Tema oscuro
Creación de un tema personalizado
TimePicker
Visual
Objeto visual de material
Creación de un representador visual
Administrador de estado visual
WebView
Características de la plataforma
Android
AppCompat y Material Design
Relleno y sombras de los botones
Opciones de entrada del Editor de métodos de entrada
Sombras paralelas ImageButton
Desplazamiento rápido ListView
Altura de la barra NavigationPage
Eventos del ciclo de vida de la página
Modo de entrada de teclado en pantalla
Deslizamiento de página TabbedPage
Animaciones de transición de página TabbedPage
Color y colocación de la barra de herramientas TabbedPage
Elevación VisualElement
Modo de color heredado VisualElement
Contenido mixto WebView
Zoom de WebView
Clase de dispositivo
iOS
Accessibility Scaling for Named Font Sizes
Color de fondo de celda
Color del cursor de entrada
Tamaño de la fuente de entrada
Formato
Estilo de presentación de la página modal de iPad
Títulos de página grandes
Estilo de encabezado de grupo de ListView
Animaciones de la fila de ListView
Estilo del separador ListView
Actualizaciones de control del subproceso principal
Separador de barra NavigationPage
Modo de color del texto de barra NavigationPage
Translucidez de barra NavigationPage
Visibilidad del indicador de página principal
Visibilidad de barra de estado de página
Selección de elementos de selector
Guía de diseño de área segura
Toques de contenido ScrollView
Reconocimiento de gestos simultáneos de desplazamiento lateral
Control deslizante con el uso de toques
Desenfoque VisualElement
Sombras paralelas VisualElement
Modo de color heredado VisualElement
Formularios nativos
Vistas nativas
Vistas nativas en XAML
Vistas nativas en C#
Otras plataformas
GTK#
Mac
Tizen
WPF
Funcionalidad específica de plataforma
Windows
Orden de lectura InputView
Modo de selección ListView
Barra de navegación MasterDetailPage
Colocación de la barra de herramientas de página
Configuración de la plataforma
Spell Check SearchBar
Iconos TabbedPage
Claves de acceso VisualElement
Modo de color heredado VisualElement
Alertas de JavaScript en WebView
Xamarin.Essentials
Primeros pasos
Compatibilidad de plataforma y características
Acelerómetro
Información de la aplicación
Barómetro
Batería
Portapapeles
Convertidores de colores
Brújula
Conectividad
Detección de vibraciones
Información de pantalla del dispositivo
Información del dispositivo
Correo electrónico
Asistentes del sistema de archivos
Linterna
Codificación geográfica
Geolocalización
Giroscopio
Selector
Magnetómetro
Subproceso principal
Mapas
Apertura del explorador
Sensor de orientación
Marcador telefónico
Extensiones de plataforma (Size, Rect, Point)
Preferencias
Almacenamiento seguro
Compartir
SMS
Texto a voz
Convertidores de unidades
Seguimiento de versiones
Vibración
Solución de problemas
Datos y Azure Cloud Services
Información general
Almacenamiento de datos local
Información general
Control de archivos
Bases de datos locales
Servicios de Azure
Introducción a servicios de Azure
Base de datos de documentos de Azure Cosmos DB
Centros de notificaciones de Azure
Almacenamiento de Azure
Azure Search
Funciones de Azure
Servicio Azure SignalR
Azure Cognitive Services
Información general de Cognitive Services
Introducción
Reconocimiento de voz
Corrector ortográfico
Traducción de texto
Reconocimiento de emociones
Servicios Web
Información general de servicios web
Introducción
ASMX
WCF
REST
Autenticación
Información general sobre la autenticación
REST
OAuth
Azure Active Directory B2C
Autenticación en Azure Cosmos DB
Implementación y pruebas
Publicación de aplicaciones de iOS
Publicación de aplicaciones de Android
Publicación de aplicaciones de UWP
Publicación de aplicaciones de Mac
Rendimiento
Pruebas automatizadas con Visual Studio App Center
Conceptos avanzados y funcionamiento interno
Resolución de dependencias
Representadores rápidos
.NET Standard
Solución de problemas
Preguntas más frecuentes
¿Se puede actualizar la plantilla predeterminada de Xamarin.Forms en un paquete
NuGet más reciente?
¿Por qué no funciona el diseñador XAML de Visual Studio para los archivos XAML
de Xamarin.Forms?
Error de compilación de Android: error inesperado en la tarea "LinkAssemblies"
¿Por qué se realiza mi proyecto de Xamarin.Forms.Maps para Android produce el
siguiente error: COMPILETODALVIK : UNEXPECTED TOP-LEVEL ERROR?
Notas de la versión
Muestras
Libro Creating Mobile Apps with Xamarin.Forms
Libro electrónico sobre patrones de aplicación empresarial
Gráficos de SkiaSharp en Xamarin.Forms
Requisitos de Xamarin.Forms
11/07/2019 • 3 minutes to read • Edit Online

Requisitos de sistema de desarrollo y plataformas para Xamarin.Forms.


Consulte el artículo Instalación para obtener información general de las prácticas de instalación y configuración
que se aplican entre plataformas.

Plataformas de destino
Las aplicaciones de Xamarin.Forms pueden escribirse para los siguientes sistemas operativos:
iOS 8 o versiones posteriores
Android 5.0 (API 21) o superior (más detalles)
Plataforma universal de Windows de Windows 10 (más detalles)
Se supone que los desarrolladores están familiarizados con .NET Standard.
Compatibilidad con plataforma adicional
El estado de estas plataformas está disponible en el GitHub de Xamarin.Forms:
Samsung Tizen
macOS
GTK#
WPF
Android
Debe tener instalada la plataforma más reciente de Android SDK Tools y de la API de Android. Puede actualizar a
las versiones más recientes con Android SDK Manager.
Además, la versión de compilación o de destino de los proyectos de Android debe establecerse en Usar la última
plataforma instalada, aunque la versión mínima se puede establecer en API 19 para que pueda seguir admitiendo
dispositivos que usen Android 4.4 y versiones más recientes. Estos valores se establecen en Opciones del
proyecto:
Visual Studio
Visual Studio para Mac
Opciones de proyecto > Aplicación > Propiedades de la aplicación

Requisitos del sistema de desarrollo


Las aplicaciones de Xamarin.Forms se pueden desarrollar en equipos macOS y Windows. Sin embargo, Windows
y Visual Studio son necesarios para producir versiones de Windows de la aplicación.
Requisitos del sistema de equipos Mac
Puede usar Visual Studio para Mac para desarrollar aplicaciones de Xamarin.Forms en macOS High Sierra (10.13)
o una versión posterior. Para desarrollar aplicaciones de iOS, se recomienda tener al menos el SDK de iOS 10 y
Xcode 9 instalado.

NOTE
Las aplicaciones de Windows no se pueden desarrollar en macOS.

Requisitos del sistema de equipos Windows


Las aplicaciones de Xamarin.Forms para iOS y Android se pueden compilar en cualquier instalación de Windows
que admita el desarrollo de Xamarin. Para ello se necesita Visual Studio 2017 o una versión más reciente que se
ejecute en Windows 7 o posterior. Se necesita un equipo Mac en red para el desarrollo de iOS.
Plataforma universal de Windows (UWP)
El desarrollo de aplicaciones de Xamarin.Forms para UWP requiere lo siguiente:
Windows 10 (versión más reciente recomendada, mínimo Fall Creators Update)
Visual Studio de 2019 recomendado (Visual Studio 2017 versión 15,8 mínimo)
SDK de Windows 10
Puede agregar una aplicación de la Plataforma universal de Windows (UWP ) a una solución existente de
Xamarin.Forms en cualquier momento.

En plataformas obsoletas
No se admiten estas plataformas cuando se usa Xamarin.Forms 3.0 o posterior:
Windows 8.1 / Windows Phone 8.1 WinRT
Windows Phone 8 Silverlight
Instalación de Xamarin
11/07/2019 • 4 minutes to read • Edit Online

Cómo configurar Visual Studio y Xamarin para comenzar a crear aplicaciones móviles con .NET.

Instalación de Xamarin en Windows

Instrucciones paso a paso


Xamarin puede instalarse como parte de un nuevo instalación 2019 de Visual Studio, siga estos pasos:
1. Descargar Visual Studio 2019 Community, Visual Studio Professional o Visual Studio Enterprise desde la
Visual Studio página (descarga vínculos se proporcionan en la parte inferior).
2. Haga doble clic en el paquete descargado para iniciar la instalación.
3. Seleccione la carga de trabajo Desarrollo para dispositivos móviles con .NET en la pantalla de
instalación:

4. Cuando esté listo para comenzar la instalación de Visual Studio de 2019, haga clic en el instalar botón en
la esquina inferior derecha:

Utilice las barras de progreso para supervisar la instalación:


5. Cuando haya finalizado la instalación de Visual Studio de 2019, haga clic en el iniciar botón para iniciar
Visual Studio:

Agregar Xamarin a Visual Studio de 2019


Si ya está instalado Visual Studio de 2019, agregar Xamarin volviendo a ejecutar el instalador de Visual Studio de
2019 para modificar las cargas de trabajo (consulte modificar Visual Studio para obtener más información).
Después, siga los pasos indicados anteriormente para instalar Xamarin.
Para obtener más información sobre cómo descargar e instalar Visual Studio de 2019, consulte instalar 2019 de
Visual Studio.

Instalación de Xamarin en Windows

Instrucciones paso a paso


Xamarin se puede instalar como parte de una nueva instalación de Visual Studio 2017, con los pasos siguientes:
1. Descargue Visual Studio 2017 Community, Visual Studio Professional o Visual Studio Enterprise desde la
página de Visual Studio (en la parte inferior se encuentran los vínculos de descarga).
2. Haga doble clic en el paquete descargado para iniciar la instalación.
3. Seleccione la carga de trabajo Desarrollo para dispositivos móviles con .NET en la pantalla de
instalación:
4. Con la opción Desarrollo para dispositivos móviles con .NET seleccionada, eche un vistazo al panel
Detalles de la instalación de la derecha. Aquí puede anular la selección de opciones de desarrollo para
dispositivos móviles que no quiera instalar.

5. Cuando esté listo para comenzar la instalación de Visual Studio 2017, haga clic en el botón Instalar
situado en la esquina inferior derecha:

En función de la edición de Visual Studio 2017 que vaya a instalar, el proceso de instalación puede tardar
bastante tiempo. Puede usar las barras de progreso para supervisar la instalación:
6. Cuando haya finalizado la instalación de Visual Studio 2017, haga clic en el botón Iniciar para iniciar
Visual Studio:

Agregar Xamarin a Visual Studio 2017


Si Visual Studio 2017 ya está instalado, agregue Xamarin al volver a ejecutar el instalador de Visual Studio 2017
para modificar las cargas de trabajo (consulte Modificar Visual Studio para más información). Después, siga los
pasos indicados anteriormente para instalar Xamarin.
Para obtener más información sobre la descarga e instalación de Visual Studio 2017, consulte Instalación de
Visual Studio 2017.

Instalación de Xamarin en macOS

Instrucciones paso a paso


Además de este vídeo, hay una guía de instalación paso a paso que cubre Visual Studio para Mac y Xamarin.

Vínculos relacionados
Desinstalación de Xamarin
Instrucciones de configuración del firewall de Xamarin
Creación de una primera aplicación de
Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

Vea este vídeo y siga el tutorial para crear una primera aplicación móvil con Xamarin.Forms.

Instrucciones paso a paso para Windows


descargar el ejemplo
Siga estos pasos, junto con el vídeo anterior:
1. Elija archivo > Nuevo > proyecto... o presione la crear nuevo proyecto... botón:

2. Busque "Xamarin" o elija Mobile desde el tipo de proyecto menú. Seleccione el aplicación móvil
(Xamarin.Forms) tipo de proyecto:
3. Elija un nombre de proyecto – en el ejemplo se usa "AwesomeApp":

4. Haga clic en el en blanco tipo de proyecto y asegúrese de Android y iOS seleccionados:

5. Espere hasta que se restauren los paquetes de NuGet (aparecerá un mensaje de "Restauración completada"
en la barra de estado).
6. Inicie Android Emulator presionando el botón de depuración (o el elemento de menú Depurar > Iniciar
depuración).
7. Edite MainPage.xaml, agregando este XAML antes del final de </StackLayout> :
<Button Text="Click Me" Clicked="Button_Clicked" />

8. Edite MainPage.xaml, agregando este código al final de la clase:

int count = 0;
void Button_Clicked(object sender, System.EventArgs e)
{
count++;
((Button)sender).Text = $"You clicked {count} times.";
}

9. Depurar la aplicación en Android:

TIP
Es posible crear y depurar la aplicación de iOS desde Visual Studio con un equipo Mac en red. Consulte las
instrucciones de configuración para obtener más información.

Compilar una aplicación de iOS en Visual Studio de 2019


Este vídeo trata el proceso de crear y probar una aplicación iOS mediante Visual Studio de 2019 en Windows:

Instrucciones paso a paso para Windows


descargar el ejemplo
Siga estos pasos, junto con el vídeo anterior:
1. Elija Archivo > Nuevo > Proyecto... o presione el botón Crear proyecto nuevo... y, a continuación,
seleccione Visual C# > Multiplataforma > Aplicación móvil (Xamarin.Forms) :

2. Asegúrese de que Android y iOS están seleccionados, con uso compartido del código .NET Standard:

3. Espere hasta que se restauren los paquetes de NuGet (aparecerá un mensaje de "Restauración completada"
en la barra de estado).
4. Inicie Android Emulator presionando el botón de depuración (o el elemento de menú Depurar > Iniciar
depuración).
5. Edite MainPage.xaml, agregando este XAML antes del final de </StackLayout> :

<Button Text="Click Me" Clicked="Button_Clicked" />

6. Edite MainPage.xaml, agregando este código al final de la clase:

int count = 0;
void Button_Clicked(object sender, System.EventArgs e)
{
count++;
((Button)sender).Text = $"You clicked {count} times.";
}

7. Depurar la aplicación en Android:


TIP
Es posible crear y depurar la aplicación de iOS desde Visual Studio con un equipo Mac en red. Consulte las
instrucciones de configuración para obtener más información.

Instrucciones paso a paso para Mac


descargar el ejemplo
Siga estos pasos, junto con el vídeo anterior:
1. Elija Archivo > Nueva solución... o presione el botón Nuevo proyecto... y, posteriormente, seleccione
Multiplataforma > Aplicación > Aplicación de Forms en blanco:

2. Asegúrese de que Android y iOS están seleccionados, con uso compartido del código .NET Standard:
3. Restaurar paquetes de NuGet, haciendo clic en el botón derecho en la solución:

4. Inicie Android Emulator presionando el botón de depuración (o Ejecutar > Iniciar depuración).
5. Edite MainPage.xaml, agregando este XAML antes del final de </StackLayout> :

<Button Text="Click Me" Clicked="Handle_Clicked" />

6. Edite MainPage.xaml, agregando este código al final de la clase:

int count = 0;
void Handle_Clicked(object sender, System.EventArgs e)
{
count++;
((Button)sender).Text = $"You clicked {count} times.";
}

7. Depurar la aplicación en Android:


8. Haga clic con el botón derecho para establecer iOS en el Proyecto de inicio:

9. Depurar la aplicación en iOS:


Puede descargar el código completo desde la galería de ejemplos o puede verlo en GitHub.

Pasos siguientes
Único Page Quickstart – compilar una aplicación más funcional.
Ejemplos de Xamarin.Forms: Descarga y ejecución de ejemplos de código y aplicaciones de muestra.
Libro electrónico Creating Mobile Apps (Creación de Mobile Apps): Capítulos detallados que enseñan el
desarrollo de Xamarin.Forms, disponible como archivo PDF y que incluye cientos de ejemplos adicionales.
Crear una aplicación de Xamarin.Forms de página
única
12/07/2019 • 24 minutes to read • Edit Online

Descargar el ejemplo
En este tutorial, obtendrá información sobre cómo:
Crear una aplicación de Xamarin.Forms multiplataforma.
Definir la interfaz de usuario para una página con el lenguaje de marcado de aplicaciones eXtensible (XAML ).
Interactuar con elementos de interfaz de usuario XAML desde el código.
El tutorial le guía a través de la creación de una aplicación de Xamarin.Forms multiplataforma, lo que permite
escribir una nota y enviarlo al almacenamiento del dispositivo. A continuación se muestra la aplicación final:

Requisitos previos
Visual Studio 2019 (versión más reciente), con el desarrollo móvil con .NET carga de trabajo instalada.
Conocimiento de C#.
(opcional) Un equipo Mac emparejado para compilar la aplicación en iOS.

Para obtener más información sobre estos requisitos previos, consulte instalar Xamarin. Para obtener información
sobre cómo conectar Visual Studio 2019 a un host de compilación de Mac, consulte Emparejar con Mac para el
desarrollo de Xamarin.iOS.

Introducción a Visual Studio de 2019


1. Inicie Visual Studio de 2019 y haga clic en la ventana de inicio crear un nuevo proyecto para crear un
nuevo proyecto:
2. En el crear un nuevo proyecto ventana, seleccione Mobile en el tipo de proyecto lista desplegable,
seleccione el Mobile App (Xamarin.Forms plantilla) y haga clic en el siguiente botón:

3. En el configurar el nuevo proyecto ventana, establezca el nombre del proyecto a notas, elija una
ubicación adecuada para el proyecto y haga clic en el crear botón:
IMPORTANT
Los fragmentos de XAML y C# en este inicio rápido requieren que la solución se denomine Notes. El uso de otro
nombre dará como resultado errores de compilación al copiar el código de este inicio rápido en la solución.

4. En el nueva aplicación multiplataforma cuadro de diálogo, haga clic en aplicación vacíay haga clic en
el Aceptar botón:

Para obtener más información sobre la biblioteca de .NET Standard creada, vea Anatomía de una aplicación
de Xamarin.Forms en Análisis detallado de inicio rápido de Xamarin.Forms.
5. En el Explorador de soluciones, en el proyecto Notes, haga doble clic en MainPage.xaml para abrirlo:
6. En MainPage.xaml, quite todo el código de plantilla y sustitúyalo por el siguiente código:

<?xml version="1.0" encoding="utf-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.MainPage">
<StackLayout Margin="10,35,10,10">
<Label Text="Notes"
HorizontalOptions="Center"
FontAttributes="Bold" />
<Editor x:Name="editor"
Placeholder="Enter your note"
HeightRequest="100" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save"
Clicked="OnSaveButtonClicked" />
<Button Grid.Column="1"
Text="Delete"
Clicked="OnDeleteButtonClicked"/>
</Grid>
</StackLayout>
</ContentPage>

Este código define mediante declaración la interfaz de usuario para la página, que consta de un Label para
mostrar texto, un Editor para entrada de texto y dos Button instancias que dirigir la aplicación para
guardar o eliminar un archivo. Las dos instancias de Button se disponen horizontalmente en Grid ,
mientras que Label , Editor y Grid se disponen verticalmente en StackLayout . Para obtener más
información sobre la creación de la interfaz de usuario, consulte interfaz de usuario en el análisis detallado
de inicio rápido de Xamarin.Forms.
Guarde los cambios en MainPage.xaml presionando CTRL+S y cierre el archivo.
7. En el Explorador de soluciones, en el proyecto Notes, expanda MainPage.xaml y haga doble clic en
MainPage.xaml.cs para abrirlo:
8. En MainPage.xaml.cs, quite todo el código de plantilla y sustitúyalo por el siguiente código:

using System;
using System.IO;
using Xamarin.Forms;

namespace Notes
{
public partial class MainPage : ContentPage
{
string _fileName =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "notes.txt");

public MainPage()
{
InitializeComponent();

if (File.Exists(_fileName))
{
editor.Text = File.ReadAllText(_fileName);
}
}

void OnSaveButtonClicked(object sender, EventArgs e)


{
File.WriteAllText(_fileName, editor.Text);
}

void OnDeleteButtonClicked(object sender, EventArgs e)


{
if (File.Exists(_fileName))
{
File.Delete(_fileName);
}
editor.Text = string.Empty;
}
}
}
Este código define un campo _fileName , que hace referencia a un archivo denominado notes.txt que
almacenará los datos de la nota en la carpeta de datos de la aplicación local para la aplicación. Cuando se
ejecuta el constructor de páginas, el archivo se lee, si existe, y se muestra en el Editor . Al pulsar el botón
Guardar Button , se ejecuta el controlador de eventos OnSaveButtonClicked , que guarda el contenido de
Editor en el archivo. Al pulsar el botón Eliminar Button , se ejecuta el controlador de eventos
OnDeleteButtonClicked , que elimina el archivo, siempre que exista, y quita cualquier texto de Editor . Para
obtener más información acerca de la interacción del usuario, consulte responde a la interacción del usuario
en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en MainPage.xaml.cs presionando CTRL+S y cierre el archivo.
Compilar el tutorial rápido
1. En Visual Studio, seleccione el elemento de menú Compilación > Compilar solución (o presione F6). La
solución se compilará y aparecerá un mensaje de operación correcta en la barra de estado de Visual Studio:

Si hay errores, repita los pasos anteriores y corrija los errores hasta que la solución se compile
correctamente.
2. En la barra de herramientas de Visual Studio, pulse el botón Iniciar (el botón triangular que parece un
botón de reproducción) para iniciar la aplicación en la instancia elegida de Android Emulator:

Escriba una nota y pulse el botón Guardar.


Para obtener más información acerca de cómo se inicia la aplicación en cada plataforma, consulte iniciando
la aplicación en cada plataforma en el análisis detallado de inicio rápido de Xamarin.Forms.

NOTE
Los pasos siguientes solo deben realizarse si se tiene un equipo Mac emparejado que cumpla los requisitos del
sistema para el desarrollo de Xamarin.Forms.

3. En la barra de herramientas de Visual Studio, haga clic con el botón derecho en el proyecto Notes.iOS y
seleccione Establecer como proyecto de inicio.

4. En la barra de herramientas de Visual Studio, pulse el botón Iniciar (el botón triangular que parece un
botón de reproducción) para iniciar la aplicación en la instancia elegida de iOS Simulator:

Escriba una nota y pulse el botón Guardar.


Para obtener más información acerca de cómo se inicia la aplicación en cada plataforma, consulte iniciando
la aplicación en cada plataforma en el análisis detallado de inicio rápido de Xamarin.Forms.
Requisitos previos
Visual Studio 2017, con el desarrollo móvil con .NET carga de trabajo instalada.
Conocimiento de C#.
(opcional) Un equipo Mac emparejado para compilar la aplicación en iOS.

Para obtener más información sobre estos requisitos previos, consulte instalar Xamarin. Para obtener información
sobre cómo conectar Visual Studio 2019 a un host de compilación de Mac, consulte Emparejar con Mac para el
desarrollo de Xamarin.iOS.

Introducción a Visual Studio 2017


1. Inicie Visual Studio 2017 y en la página de inicio, haga clic en crear nuevo proyecto... para crear un nuevo
proyecto:

2. En el cuadro de diálogo Nuevo proyecto, haga clic en Multiplataforma, seleccione la plantilla


Aplicación móvil (Xamarin.Forms) , establezca el nombre en Notes, elija una ubicación adecuada para
el proyecto y haga clic en el botón Aceptar:

IMPORTANT
Los fragmentos de XAML y C# en este inicio rápido requieren que la solución se denomine Notes. El uso de otro
nombre dará como resultado errores de compilación al copiar el código de este inicio rápido en la solución.

3. En el cuadro de diálogo New Cross Platform App (Nueva aplicación multiplataforma), haga clic en
Aplicación en blanco, seleccione .NET Standard como estrategia de código de uso compartido y haga
clic en el botón Aceptar:

Para obtener más información sobre la biblioteca de .NET Standard creada, vea Anatomía de una aplicación
de Xamarin.Forms en Análisis detallado de inicio rápido de Xamarin.Forms.
4. En el Explorador de soluciones, en el proyecto Notes, haga doble clic en MainPage.xaml para abrirlo:

5. En MainPage.xaml, quite todo el código de plantilla y sustitúyalo por el siguiente código:


<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.MainPage">
<StackLayout Margin="10,35,10,10">
<Label Text="Notes"
HorizontalOptions="Center"
FontAttributes="Bold" />
<Editor x:Name="editor"
Placeholder="Enter your note"
HeightRequest="100" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save"
Clicked="OnSaveButtonClicked" />
<Button Grid.Column="1"
Text="Delete"
Clicked="OnDeleteButtonClicked"/>
</Grid>
</StackLayout>
</ContentPage>

Este código define mediante declaración la interfaz de usuario para la página, que consta de un Label para
mostrar texto, un Editor para entrada de texto y dos Button instancias que dirigir la aplicación para
guardar o eliminar un archivo. Las dos instancias de Button se disponen horizontalmente en Grid ,
mientras que Label , Editor y Grid se disponen verticalmente en StackLayout . Para obtener más
información sobre la creación de la interfaz de usuario, consulte interfaz de usuario en el análisis detallado
de inicio rápido de Xamarin.Forms.
Guarde los cambios en MainPage.xaml presionando CTRL+S y cierre el archivo.
6. En el Explorador de soluciones, en el proyecto Notes, expanda MainPage.xaml y haga doble clic en
MainPage.xaml.cs para abrirlo:
7. En MainPage.xaml.cs, quite todo el código de plantilla y sustitúyalo por el siguiente código:
using System;
using System.IO;
using Xamarin.Forms;

namespace Notes
{
public partial class MainPage : ContentPage
{
string _fileName =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "notes.txt");

public MainPage()
{
InitializeComponent();

if (File.Exists(_fileName))
{
editor.Text = File.ReadAllText(_fileName);
}
}

void OnSaveButtonClicked(object sender, EventArgs e)


{
File.WriteAllText(_fileName, editor.Text);
}

void OnDeleteButtonClicked(object sender, EventArgs e)


{
if (File.Exists(_fileName))
{
File.Delete(_fileName);
}
editor.Text = string.Empty;
}
}
}

Este código define un campo _fileName , que hace referencia a un archivo denominado notes.txt que
almacenará los datos de la nota en la carpeta de datos de la aplicación local para la aplicación. Cuando se
ejecuta el constructor de páginas, el archivo se lee, si existe, y se muestra en el Editor . Al pulsar el botón
Guardar Button , se ejecuta el controlador de eventos OnSaveButtonClicked , que guarda el contenido de
Editor en el archivo. Al pulsar el botón Eliminar Button , se ejecuta el controlador de eventos
OnDeleteButtonClicked , que elimina el archivo, siempre que exista, y quita cualquier texto de Editor . Para
obtener más información acerca de la interacción del usuario, consulte responde a la interacción del usuario
en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en MainPage.xaml.cs presionando CTRL+S y cierre el archivo.
Compilar el tutorial rápido
1. En Visual Studio, seleccione el elemento de menú Compilación > Compilar solución (o presione F6). La
solución se compilará y aparecerá un mensaje de operación correcta en la barra de estado de Visual Studio:
Si hay errores, repita los pasos anteriores y corrija los errores hasta que la solución se compile
correctamente.
2. En la barra de herramientas de Visual Studio, pulse el botón Iniciar (el botón triangular que parece un
botón de reproducción) para iniciar la aplicación en la instancia elegida de Android Emulator:

Escriba una nota y pulse el botón Guardar.


Para obtener más información acerca de cómo se inicia la aplicación en cada plataforma, consulte iniciando
la aplicación en cada plataforma en el análisis detallado de inicio rápido de Xamarin.Forms.

NOTE
Los pasos siguientes solo deben realizarse si se tiene un equipo Mac emparejado que cumpla los requisitos del
sistema para el desarrollo de Xamarin.Forms.

3. En la barra de herramientas de Visual Studio, haga clic con el botón derecho en el proyecto Notes.iOS y
seleccione Establecer como proyecto de inicio.
4. En la barra de herramientas de Visual Studio, pulse el botón Iniciar (el botón triangular que parece un
botón de reproducción) para iniciar la aplicación en la instancia elegida de iOS Simulator:

Escriba una nota y pulse el botón Guardar.


Para obtener más información acerca de cómo se inicia la aplicación en cada plataforma, consulte iniciando
la aplicación en cada plataforma en el análisis detallado de inicio rápido de Xamarin.Forms.
Requisitos previos
Visual Studio para Mac (versión más reciente), con iOS y compatibilidad con la plataforma de Android
instalado.
Xcode (versión más reciente).
Conocimiento de C#.
Para obtener más información sobre estos requisitos previos, consulte instalar Xamarin.

Introducción a Visual Studio para Mac


1. Inicie Visual Studio para Mac y en la ventana de inicio, haga clic en New para crear un nuevo proyecto:
2. En el cuadro de diálogo Elija una plantilla para el nuevo proyecto, haga clic en Multiplataforma >
Aplicación, seleccione la plantilla Aplicación de Forms en blanco y haga clic en el botón Siguiente:

3. En el cuadro de diálogo Configurar su nueva aplicación de Forms en blanco, asigne a la nueva


aplicación el nombre Notes, asegúrese de que el botón de radio Usar .NET Standard esté seleccionado y
haga clic en el botón Siguiente:
4. En el cuadro de diálogo Configurar su nueva aplicación de Forms en blanco, deje los nombres de
solución y de proyecto establecidos en Notes, elija una ubicación adecuada para el proyecto y haga clic en
el botón Crear para crearlo:
IMPORTANT
Los fragmentos de XAML y C# en este inicio rápido requieren que tanto la solución como el proyecto se denominen
Notes. El uso de otro nombre dará como resultado errores de compilación al copiar el código de este inicio rápido en
la solución.

Para obtener más información sobre la biblioteca de .NET Standard creada, vea Anatomía de una aplicación
de Xamarin.Forms en Análisis detallado de inicio rápido de Xamarin.Forms.
5. En el Panel de solución, en el proyecto Notes, haga doble clic en MainPage.xaml para abrirlo:

6. En MainPage.xaml, quite todo el código de plantilla y sustitúyalo por el siguiente código:

<?xml version="1.0" encoding="utf-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.MainPage">
<StackLayout Margin="10,35,10,10">
<Label Text="Notes"
HorizontalOptions="Center"
FontAttributes="Bold" />
<Editor x:Name="editor"
Placeholder="Enter your note"
HeightRequest="100" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save"
Clicked="OnSaveButtonClicked" />
<Button Grid.Column="1"
Text="Delete"
Clicked="OnDeleteButtonClicked"/>
</Grid>
</StackLayout>
</ContentPage>

Este código define mediante declaración la interfaz de usuario para la página, que consta de un Label para
mostrar texto, un Editor para entrada de texto y dos Button instancias que dirigir la aplicación para
guardar o eliminar un archivo. Las dos instancias de Button se disponen horizontalmente en Grid ,
mientras que Label , Editor y Grid se disponen verticalmente en StackLayout . Para obtener más
información sobre la creación de la interfaz de usuario, consulte interfaz de usuario en el análisis detallado
de inicio rápido de Xamarin.Forms.
Guarde los cambios en MainPage.xaml eligiendo Archivo > Guardar (o presionando ⌘ + S ) y cierre el
archivo.
7. En el Panel de solución, en el proyecto Notes, expanda MainPage.xaml y haga doble clic en
MainPage.xaml.cs para abrirlo:
8. En MainPage.xaml.cs, quite todo el código de plantilla y sustitúyalo por el siguiente código:

using System;
using System.IO;
using Xamarin.Forms;

namespace Notes
{
public partial class MainPage : ContentPage
{
string _fileName =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "notes.txt");

public MainPage()
{
InitializeComponent();

if (File.Exists(_fileName))
{
editor.Text = File.ReadAllText(_fileName);
}
}

void OnSaveButtonClicked(object sender, EventArgs e)


{
File.WriteAllText(_fileName, editor.Text);
}

void OnDeleteButtonClicked(object sender, EventArgs e)


{
if (File.Exists(_fileName))
{
File.Delete(_fileName);
}
editor.Text = string.Empty;
}
}
}

Este código define un campo _fileName , que hace referencia a un archivo denominado notes.txt que
almacenará los datos de la nota en la carpeta de datos de la aplicación local para la aplicación. Cuando se
ejecuta el constructor de páginas, el archivo se lee, si existe, y se muestra en el Editor . Al pulsar el botón
Guardar Button , se ejecuta el controlador de eventos OnSaveButtonClicked , que guarda el contenido de
Editor en el archivo. Al pulsar el botón Eliminar Button , se ejecuta el controlador de eventos
OnDeleteButtonClicked , que elimina el archivo, siempre que exista, y quita cualquier texto de Editor . Para
obtener más información acerca de la interacción del usuario, consulte responde a la interacción del usuario
en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en MainPage.xaml.cs eligiendo Archivo > Guardar (o presionando ⌘ + S ) y cierre
el archivo.
Compilación del inicio rápido
1. En Visual Studio para Mac, seleccione el elemento de menú Compilación > Compilar todo (o pulse ⌘ +
B ). El proyecto se compilará y aparecerá un mensaje de operación correcta en la barra de herramientas de
Visual Studio para Mac.

Si hay errores, repita los pasos anteriores y corrija los errores hasta que la solución se compile
correctamente.
2. En el panel de solución, seleccione el Notes.iOS proyecto, secundario y seleccione establecer como
proyecto de inicio:

3. En la barra de herramientas de Visual Studio para Mac, pulse el botón Iniciar (el botón triangular similar a
un botón de reproducción) para iniciar la aplicación en la instancia elegida de iOS Simulator:

Escriba una nota y pulse el botón Guardar.


Para obtener más información acerca de cómo se inicia la aplicación en cada plataforma, consulte iniciando
la aplicación en cada plataforma en el análisis detallado de inicio rápido de Xamarin.Forms.
4. En el panel de solución, seleccione el Notes.Droid proyecto, secundario y seleccione establecer como
proyecto de inicio:

5. En la barra de herramientas de Visual Studio para Mac, pulse el botón Iniciar (el botón triangular similar a
un botón de reproducción) para iniciar la aplicación en la instancia elegida de Android Emulator:

Escriba una nota y pulse el botón Guardar.


Para obtener más información acerca de cómo se inicia la aplicación en cada plataforma, consulte iniciando
la aplicación en cada plataforma en el análisis detallado de inicio rápido de Xamarin.Forms.

Pasos siguientes
En este tutorial, ha aprendido cómo:
Crear una aplicación de Xamarin.Forms multiplataforma.
Definir la interfaz de usuario para una página con el lenguaje de marcado de aplicaciones eXtensible (XAML ).
Interactuar con elementos de interfaz de usuario XAML desde el código.
Para activar esta aplicación de página única en una aplicación de varias página, continúe con el siguiente inicio
rápido.
Siguiente
Vínculos relacionados
Notes (ejemplo)
Análisis detallado de inicio rápido de Xamarin.Forms
Realizar la navegación en una aplicación de
Xamarin.Forms de varias páginas
11/07/2019 • 26 minutes to read • Edit Online

descargar el ejemplo
En este tutorial, obtendrá información sobre cómo:
Agregar páginas adicionales a una solución de Xamarin.Forms.
Realizar la navegación entre páginas.
Utilice el enlace de datos para sincronizar datos entre los elementos de la interfaz de usuario y su origen de
datos.
El tutorial le guía a través de cómo convertir una aplicación de Xamarin.Forms multiplataforma de página única,
que puede almacenar una sola nota, en una aplicación de varias página, capaz de almacenar varias notas. A
continuación se muestra la aplicación final:

Requisitos previos
Debe completar correctamente el inicio rápido anterior antes de intentar este inicio rápido. Como alternativa,
descargue el anterior ejemplo de tutorial rápido y usarlo como punto de partida para este inicio rápido.

Actualizar la aplicación con Visual Studio


1. Inicie Visual Studio. En la ventana de inicio, haga clic en el notas solución en la lista de proyectos y
soluciones recientes, o haga clic en abrir un proyecto o solucióny en el Abrir proyecto o solución
cuadro de diálogo Seleccione el archivo de solución para el proyecto de notas:
2. En el Explorador de soluciones, haga doble clic en el notas del proyecto y seleccione Agregar > nueva
carpeta:

3. En el Explorador de soluciones, asigne el nombre de la nueva carpeta modelos:


4. En el Explorador de soluciones, seleccione el modelos carpeta, con el botón secundario y seleccione
Agregar > nuevo elemento... :

5. En el Agregar nuevo elemento cuadro de diálogo, seleccione Visual C# elementos > clase, asigne al
nuevo archivo Notay haga clic en el agregar botón:

Esto agregará una clase denominada Nota a la modelos carpeta de la notas proyecto.
6. En Note.cs, quite todo el código de plantilla y sustitúyalo por el código siguiente:
using System;

namespace Notes.Models
{
public class Note
{
public string Filename { get; set; }
public string Text { get; set; }
public DateTime Date { get; set; }
}
}

Esta clase define un Note modelo donde se almacenará los datos sobre todas las notas de la aplicación.
Guarde los cambios en Note.cs presionando CTRL+Sy cierre el archivo.
7. En el Explorador de soluciones, haga doble clic en el notas del proyecto y seleccione Agregar > nuevo
elemento... . En el Agregar nuevo elemento cuadro de diálogo, seleccione Visual C# elementos >
Xamarin.Forms > página de contenido, asigne al nuevo archivo NoteEntryPagey haga clic en el
Agregar botón:

Esto agregará una nueva página denominada NoteEntryPage a la carpeta raíz del proyecto. Esta página
será la segunda página de la aplicación.
8. En NoteEntryPage.xaml, quite todo el código de plantilla y sustitúyalo por el código siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.NoteEntryPage"
Title="Note Entry">
<StackLayout Margin="20">
<Editor Placeholder="Enter your note"
Text="{Binding Text}"
HeightRequest="100" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save"
Clicked="OnSaveButtonClicked" />
<Button Grid.Column="1"
Text="Delete"
Clicked="OnDeleteButtonClicked"/>
</Grid>
</StackLayout>
</ContentPage>

Este código define mediante declaración la interfaz de usuario para la página, que consta de un Editor
para entrada de texto y dos Button instancias que dirigir la aplicación para guardar o eliminar un archivo.
Los dos Button instancias están dispuestas horizontalmente en un Grid , con el Editor y Grid se
dispuestos verticalmente en una StackLayout . Además, el Editor utiliza el enlace de datos para enlazar el
Text propiedad de la Note modelo. Para obtener más información sobre el enlace de datos, vea enlace de
datos en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en NoteEntryPage.xaml presionando CTRL+Sy cierre el archivo.
9. En NoteEntryPage.xaml.cs, quite todo el código de plantilla y sustitúyalo por el código siguiente:
using System;
using System.IO;
using Xamarin.Forms;
using Notes.Models;

namespace Notes
{
public partial class NoteEntryPage : ContentPage
{
public NoteEntryPage()
{
InitializeComponent();
}

async void OnSaveButtonClicked(object sender, EventArgs e)


{
var note = (Note)BindingContext;

if (string.IsNullOrWhiteSpace(note.Filename))
{
// Save
var filename = Path.Combine(App.FolderPath, $"{Path.GetRandomFileName()}.notes.txt");
File.WriteAllText(filename, note.Text);
}
else
{
// Update
File.WriteAllText(note.Filename, note.Text);
}

await Navigation.PopAsync();
}

async void OnDeleteButtonClicked(object sender, EventArgs e)


{
var note = (Note)BindingContext;

if (File.Exists(note.Filename))
{
File.Delete(note.Filename);
}

await Navigation.PopAsync();
}
}
}

Este código se almacena un Note instancia, que representa una sola nota, en el BindingContext de la
página. Cuando el guardar Button se presiona el OnSaveButtonClicked se ejecuta el controlador de
eventos, ya sea guarda el contenido de la Editor en un archivo nuevo con un nombre de archivo generado
aleatoriamente, o a un archivo existente si se está actualizando una nota. En ambos casos, el archivo se
almacena en la carpeta de datos de aplicación local para la aplicación. A continuación, el método se desplaza
a la página anterior. Cuando el eliminar Button se presiona el OnDeleteButtonClicked se ejecuta el
controlador de eventos, lo que elimina el archivo, siempre que exista y se desplaza a la página anterior. Para
obtener más información sobre la navegación, consulte navegación en el análisis detallado de inicio rápido
de Xamarin.Forms.
Guarde los cambios en NoteEntryPage.xaml.cs presionando CTRL+Sy cierre el archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.
10. En el Explorador de soluciones, haga doble clic en el notas del proyecto y seleccione Agregar > nuevo
elemento... . En el Agregar nuevo elemento cuadro de diálogo, seleccione Visual C# elementos >
Xamarin.Forms > página de contenido, asigne al nuevo archivo NotesPagey haga clic en el agregar
botón.
Esto agregará una página denominada NotesPage a la carpeta raíz del proyecto. Esta página será la página
raíz de la aplicación.
11. En NotesPage.xaml, quite todo el código de plantilla y sustitúyalo por el código siguiente:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.NotesPage"
Title="Notes">
<ContentPage.ToolbarItems>
<ToolbarItem Text="+"
Clicked="OnNoteAddedClicked" />
</ContentPage.ToolbarItems>
<ListView x:Name="listView"
Margin="20"
ItemSelected="OnListViewItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Text}"
Detail="{Binding Date}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>

Este código define mediante declaración la interfaz de usuario para la página, que consta de un ListView y
un ToolbarItem . El ListView utiliza enlace de datos para mostrar las notas que se recuperan por la
aplicación y seleccionar una nota, irá a la NoteEntryPage donde se puede modificar la nota. Como
alternativa, puede crearse una nueva nota presionando el ToolbarItem . Para obtener más información
sobre el enlace de datos, vea enlace de datos en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en NotesPage.xaml presionando CTRL+Sy cierre el archivo.
12. En NotesPage.xaml.cs, quite todo el código de plantilla y sustitúyalo por el código siguiente:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Xamarin.Forms;
using Notes.Models;

namespace Notes
{
public partial class NotesPage : ContentPage
{
public NotesPage()
{
InitializeComponent();
}

protected override void OnAppearing()


{
base.OnAppearing();

var notes = new List<Note>();

var files = Directory.EnumerateFiles(App.FolderPath, "*.notes.txt");


foreach (var filename in files)
{
notes.Add(new Note
{
Filename = filename,
Text = File.ReadAllText(filename),
Date = File.GetCreationTime(filename)
});
}

listView.ItemsSource = notes
.OrderBy(d => d.Date)
.ToList();
}

async void OnNoteAddedClicked(object sender, EventArgs e)


{
await Navigation.PushAsync(new NoteEntryPage
{
BindingContext = new Note()
});
}

async void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)


{
if (e.SelectedItem != null)
{
await Navigation.PushAsync(new NoteEntryPage
{
BindingContext = e.SelectedItem as Note
});
}
}
}
}

Este código define la funcionalidad para el NotesPage . Cuando aparezca la página, el OnAppearing se
ejecuta el método, que rellena la ListView con las notas que se han recuperado desde la carpeta de datos
de aplicación local. Cuando el ToolbarItem se presiona el OnNoteAddedClicked se ejecuta el controlador de
eventos. Este método se desplaza a la NoteEntryPage , estableciendo el BindingContext de la NoteEntryPage
a un nuevo Note instancia. Cuando un elemento de la ListView está seleccionado el
OnListViewItemSelected se ejecuta el controlador de eventos. Este método se desplaza a la NoteEntryPage ,
estableciendo el BindingContext de la NoteEntryPage seleccionada Note instancia. Para obtener más
información sobre la navegación, consulte navegación en el análisis detallado de inicio rápido de
Xamarin.Forms.
Guarde los cambios en NotesPage.xaml.cs presionando CTRL+Sy cierre el archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

13. En el Explorador de soluciones, haga doble clic en App.xaml.cs para abrirlo. A continuación, reemplace
el código existente por el código siguiente:

using System;
using System.IO;
using Xamarin.Forms;

namespace Notes
{
public partial class App : Application
{
public static string FolderPath { get; private set; }

public App()
{
InitializeComponent();
FolderPath =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
MainPage = new NavigationPage(new NotesPage());
}
// ...
}
}

Este código agrega una declaración de espacio de nombres para el System.IO espacio de nombres y agrega
una declaración para una variable static FolderPath propiedad de tipo string . El FolderPath propiedad se
utiliza para almacenar la ruta de acceso en el dispositivo donde se almacenarán los datos de la nota.
Además, el código inicializa el FolderPath propiedad en el App constructor e inicializa el MainPage
propiedad sea un NavigationPage que hospeda una instancia de NotesPage . Para obtener más información
sobre la navegación, consulte navegación en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en App.xaml.cs presionando CTRL+S y cierre el archivo.
14. En el Explorador de soluciones, en el notas del proyecto, haga clic en MainPage.xamly seleccione
eliminar. En el cuadro de diálogo que aparece presione el Aceptar botón para quitar el archivo de disco
duro.
Esto quita una página que ya no se utiliza.
15. Compile y ejecute el proyecto en cada plataforma. Para obtener más información, consulte crear la Guía de
inicio rápido.
En el NotesPage presione el + botón para navegar a la NoteEntryPage y escriba una nota. Después de
guardar la nota de la aplicación navegará a la NotesPage.
Escriba un número de notas, de longitud variable, observar el comportamiento de la aplicación.

Actualizar la aplicación con Visual Studio para Mac


Actualizar la aplicación con Visual Studio para Mac
1. Inicie Visual Studio para Mac. En la ventana de inicio, haga clic en abiertoy en el cuadro de diálogo,
seleccione el archivo de solución para el proyecto de notas:

2. En el panel de solución, seleccione el notas proyecto, secundario y seleccione Agregar > nueva carpeta:

3. En el panel de solución, asigne el nombre de la nueva carpeta modelos:


4. En el panel de solución, seleccione el modelos carpeta, con el botón secundario y seleccione Agregar >
nuevo archivo... :

5. En el nuevo archivo cuadro de diálogo, seleccione General > clase vacía, asigne al nuevo archivo Notay
haga clic en el New botón:

Esto agregará una clase denominada Nota a la modelos carpeta de la notas proyecto.
6. En Note.cs, quite todo el código de plantilla y sustitúyalo por el código siguiente:

using System;

namespace Notes.Models
{
public class Note
{
public string Filename { get; set; }
public string Text { get; set; }
public DateTime Date { get; set; }
}
}

Esta clase define un Note modelo donde se almacenará los datos sobre todas las notas de la aplicación.
Guarde los cambios en Note.cs eligiendo archivo > Guardar (o presionando ⌘ + S ) y cierre el archivo.
7. En el panel de solución, seleccione el notas proyecto, con el botón secundario y seleccione Agregar >
nuevo archivo... . En el nuevo archivo cuadro de diálogo, seleccione formularios > formularios
ContentPage XAML, asigne al nuevo archivo NoteEntryPagey haga clic en el New botón:

Esto agregará una nueva página denominada NoteEntryPage a la carpeta raíz del proyecto. Esta página
será la segunda página de la aplicación.
8. En NoteEntryPage.xaml, quite todo el código de plantilla y sustitúyalo por el código siguiente:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.NoteEntryPage"
Title="Note Entry">
<StackLayout Margin="20">
<Editor Placeholder="Enter your note"
Text="{Binding Text}"
HeightRequest="100" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save"
Clicked="OnSaveButtonClicked" />
<Button Grid.Column="1"
Text="Delete"
Clicked="OnDeleteButtonClicked"/>
</Grid>
</StackLayout>
</ContentPage>

Este código define mediante declaración la interfaz de usuario para la página, que consta de un Editor
para entrada de texto y dos Button instancias que dirigir la aplicación para guardar o eliminar un archivo.
Los dos Button instancias están dispuestas horizontalmente en un Grid , con el Editor y Grid se
dispuestos verticalmente en una StackLayout . Además, el Editor utiliza el enlace de datos para enlazar el
Text propiedad de la Note modelo. Para obtener más información sobre el enlace de datos, vea enlace de
datos en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en NoteEntryPage.xaml eligiendo archivo > Guardar (o presionando ⌘ + S ) y
cierre el archivo.
9. En NoteEntryPage.xaml.cs, quite todo el código de plantilla y sustitúyalo por el código siguiente:
using System;
using System.IO;
using Xamarin.Forms;
using Notes.Models;

namespace Notes
{
public partial class NoteEntryPage : ContentPage
{
public NoteEntryPage()
{
InitializeComponent();
}

async void OnSaveButtonClicked(object sender, EventArgs e)


{
var note = (Note)BindingContext;

if (string.IsNullOrWhiteSpace(note.Filename))
{
// Save
var filename = Path.Combine(App.FolderPath, $"{Path.GetRandomFileName()}.notes.txt");
File.WriteAllText(filename, note.Text);
}
else
{
// Update
File.WriteAllText(note.Filename, note.Text);
}

await Navigation.PopAsync();
}

async void OnDeleteButtonClicked(object sender, EventArgs e)


{
var note = (Note)BindingContext;

if (File.Exists(note.Filename))
{
File.Delete(note.Filename);
}

await Navigation.PopAsync();
}
}
}

Este código se almacena un Note instancia, que representa una sola nota, en el BindingContext de la
página. Cuando el guardar Button se presiona el OnSaveButtonClicked se ejecuta el controlador de
eventos, ya sea guarda el contenido de la Editor en un archivo nuevo con un nombre de archivo generado
aleatoriamente, o a un archivo existente si se está actualizando una nota. En ambos casos, el archivo se
almacena en la carpeta de datos de aplicación local para la aplicación. A continuación, el método se desplaza
a la página anterior. Cuando el eliminar Button se presiona el OnDeleteButtonClicked se ejecuta el
controlador de eventos, lo que elimina el archivo, siempre que exista y se desplaza a la página anterior. Para
obtener más información sobre la navegación, consulte navegación en el análisis detallado de inicio rápido
de Xamarin.Forms.
Guarde los cambios en NoteEntryPage.xaml.cs eligiendo archivo > Guardar (o presionando ⌘ + S ) y
cierre el archivo.
WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

10. En el panel de solución, seleccione el notas proyecto, con el botón secundario y seleccione Agregar >
nuevo archivo... . En el nuevo archivo cuadro de diálogo, seleccione formularios > formularios
ContentPage XAML, asigne al nuevo archivo NotesPagey haga clic en el New botón.
Esto agregará una página denominada NotesPage a la carpeta raíz del proyecto. Esta página será la página
raíz de la aplicación.
11. En NotesPage.xaml, quite todo el código de plantilla y sustitúyalo por el código siguiente:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.NotesPage"
Title="Notes">
<ContentPage.ToolbarItems>
<ToolbarItem Text="+"
Clicked="OnNoteAddedClicked" />
</ContentPage.ToolbarItems>
<ListView x:Name="listView"
Margin="20"
ItemSelected="OnListViewItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Text}"
Detail="{Binding Date}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>

Este código define mediante declaración la interfaz de usuario para la página, que consta de un ListView y
un ToolbarItem . El ListView utiliza enlace de datos para mostrar las notas que se recuperan por la
aplicación y seleccionar una nota, irá a la NoteEntryPage donde se puede modificar la nota. Como
alternativa, puede crearse una nueva nota presionando el ToolbarItem . Para obtener más información
sobre el enlace de datos, vea enlace de datos en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en NotesPage.xaml eligiendo archivo > Guardar (o presionando ⌘ + S ) y cierre el
archivo.
12. En NotesPage.xaml.cs, quite todo el código de plantilla y sustitúyalo por el código siguiente:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Xamarin.Forms;
using Notes.Models;

namespace Notes
{
public partial class NotesPage : ContentPage
{
public NotesPage()
{
InitializeComponent();
}

protected override void OnAppearing()


{
base.OnAppearing();

var notes = new List<Note>();

var files = Directory.EnumerateFiles(App.FolderPath, "*.notes.txt");


foreach (var filename in files)
{
notes.Add(new Note
{
Filename = filename,
Text = File.ReadAllText(filename),
Date = File.GetCreationTime(filename)
});
}

listView.ItemsSource = notes
.OrderBy(d => d.Date)
.ToList();
}

async void OnNoteAddedClicked(object sender, EventArgs e)


{
await Navigation.PushAsync(new NoteEntryPage
{
BindingContext = new Note()
});
}

async void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)


{
if (e.SelectedItem != null)
{
await Navigation.PushAsync(new NoteEntryPage
{
BindingContext = e.SelectedItem as Note
});
}
}
}
}

Este código define la funcionalidad para el NotesPage . Cuando aparezca la página, el OnAppearing se
ejecuta el método, que rellena la ListView con las notas que se han recuperado desde la carpeta de datos
de aplicación local. Cuando el ToolbarItem se presiona el OnNoteAddedClicked se ejecuta el controlador de
eventos. Este método se desplaza a la NoteEntryPage , estableciendo el BindingContext de la NoteEntryPage
a un nuevo Note instancia. Cuando un elemento de la ListView está seleccionado el
OnListViewItemSelected se ejecuta el controlador de eventos. Este método se desplaza a la NoteEntryPage ,
estableciendo el BindingContext de la NoteEntryPage seleccionada Note instancia. Para obtener más
información sobre la navegación, consulte navegación en el análisis detallado de inicio rápido de
Xamarin.Forms.
Guarde los cambios en NotesPage.xaml.cs eligiendo archivo > Guardar (o presionando ⌘ + S ) y cierre
el archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

13. En el panel de solución, haga doble clic en App.xaml.cs para abrirlo. A continuación, reemplace el código
existente por el código siguiente:

using System;
using System.IO;
using Xamarin.Forms;

namespace Notes
{
public partial class App : Application
{
public static string FolderPath { get; private set; }

public App()
{
InitializeComponent();
FolderPath =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
MainPage = new NavigationPage(new NotesPage());
}
// ...
}
}

Este código agrega una declaración de espacio de nombres para el System.IO espacio de nombres y agrega
una declaración para una variable static FolderPath propiedad de tipo string . El FolderPath propiedad se
utiliza para almacenar la ruta de acceso en el dispositivo donde se almacenarán los datos de la nota.
Además, el código inicializa el FolderPath propiedad en el App constructor e inicializa el MainPage
propiedad sea un NavigationPage que hospeda una instancia de NotesPage . Para obtener más información
sobre la navegación, consulte navegación en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en App.xaml.cs eligiendo Archivo > Guardar (o presionando ⌘ + S ) y cierre el
archivo.
14. En el panel de solución, en el notas del proyecto, haga clic en MainPage.xamly seleccione quitar. En el
cuadro de diálogo que aparece presione el eliminar botón para quitar el archivo de disco duro.
Esto quita una página que ya no se utiliza.
15. Compile y ejecute el proyecto en cada plataforma. Para obtener más información, consulte crear la Guía de
inicio rápido.
En el NotesPage presione el + botón para navegar a la NoteEntryPage y escriba una nota. Después de
guardar la nota de la aplicación navegará a la NotesPage.
Escriba un número de notas, de longitud variable, observar el comportamiento de la aplicación.
Pasos siguientes
En este tutorial, ha aprendido cómo:
Agregar páginas adicionales a una solución de Xamarin.Forms.
Realizar la navegación entre páginas.
Utilice el enlace de datos para sincronizar datos entre los elementos de la interfaz de usuario y su origen de
datos.
Para modificar la aplicación para que almacena sus datos en una base de datos local de SQLite.NET, continúe con
el siguiente inicio rápido.
Siguiente

Vínculos relacionados
Notes (ejemplo)
Análisis detallado de inicio rápido de Xamarin.Forms
Store los datos en una base de datos Local de
SQLite.NET
12/07/2019 • 18 minutes to read • Edit Online

Descargar el ejemplo
En este tutorial, obtendrá información sobre cómo:
Use el Administrador de paquetes de NuGet para agregar un paquete de NuGet a un proyecto.
Store datos localmente en una base de datos de SQLite.NET.
La Guía de inicio rápido le guía a través almacenar datos en una base de datos local de SQLite.NET. A
continuación se muestra la aplicación final:

Requisitos previos
Debe completar correctamente el inicio rápido anterior antes de intentar este inicio rápido. Como alternativa,
descargue el anterior ejemplo de tutorial rápido y usarlo como punto de partida para este inicio rápido.

Actualizar la aplicación con Visual Studio


1. Inicie Visual Studio y abra la solución de notas.
2. En el Explorador de soluciones, seleccione el notas del proyecto, haga clic en y seleccione administrar
paquetes NuGet... :
3. En el Administrador de paquetes NuGet, seleccione la pestaña Examinar, busque el paquete NuGet
sqlite-net-pcl, selecciónelo y haga clic en el botón Instalar para agregarlo al proyecto:

NOTE
Hay varios paquetes NuGet con nombres similares. El paquete correcto tiene estos atributos:
Autores: Frank A. Krueger
Id.: sqlite-net-pcl
Vínculo de NuGet: sqlite-net-pcl
A pesar del nombre del paquete, este paquete NuGet puede usarse en proyectos de .NET Standard.

Este paquete se usará para incorporar operaciones de bases de datos en la aplicación.


4. En el Explorador de soluciones, en el notas proyecto, abra Note.cs en el modelos reemplazar el
existente y carpeta de código con el código siguiente:
using System;
using SQLite;

namespace Notes.Models
{
public class Note
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Text { get; set; }
public DateTime Date { get; set; }
}
}

Esta clase define un Note modelo donde se almacenará los datos sobre todas las notas de la aplicación. El
ID propiedad está marcada con PrimaryKey y AutoIncrement atributos para asegurarse de que cada Note
instancia en la base de datos de SQLite.NET tendrá un identificador único proporcionado por SQLite.NET.
Guarde los cambios en Note.cs presionando CTRL+Sy cierre el archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

5. En el Explorador de soluciones, agregue una nueva carpeta denominada datos a la notas proyecto.
6. En el Explorador de soluciones, en el notas proyecto, agregar una nueva clase denominada
NoteDatabase a la datos carpeta.
7. En NoteDatabase.cs, reemplace el código existente por el código siguiente:
using System.Collections.Generic;
using System.Threading.Tasks;
using SQLite;
using Notes.Models;

namespace Notes.Data
{
public class NoteDatabase
{
readonly SQLiteAsyncConnection _database;

public NoteDatabase(string dbPath)


{
_database = new SQLiteAsyncConnection(dbPath);
_database.CreateTableAsync<Note>().Wait();
}

public Task<List<Note>> GetNotesAsync()


{
return _database.Table<Note>().ToListAsync();
}

public Task<Note> GetNoteAsync(int id)


{
return _database.Table<Note>()
.Where(i => i.ID == id)
.FirstOrDefaultAsync();
}

public Task<int> SaveNoteAsync(Note note)


{
if (note.ID != 0)
{
return _database.UpdateAsync(note);
}
else
{
return _database.InsertAsync(note);
}
}

public Task<int> DeleteNoteAsync(Note note)


{
return _database.DeleteAsync(note);
}
}
}

Esta clase contiene código para crear la base de datos, leen datos, escribir datos en él y eliminar datos. El
código usa API asincrónicas de SQLite.NET que mueven las operaciones de base de datos a subprocesos
en segundo plano. Además, el constructor NoteDatabase toma la ruta de acceso del archivo de base de
datos como un argumento. Esta ruta de acceso será proporcionado por el App clase en el paso siguiente.
Guarde los cambios en NoteDatabase.cs presionando CTRL+Sy cierre el archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

8. En el Explorador de soluciones, en el notas del proyecto, haga doble clic en App.xaml.cs para abrirlo. A
continuación, reemplace el código existente por el código siguiente:
using System;
using System.IO;
using Xamarin.Forms;
using Notes.Data;

namespace Notes
{
public partial class App : Application
{
static NoteDatabase database;

public static NoteDatabase Database


{
get
{
if (database == null)
{
database = new
NoteDatabase(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"Notes.db3"));
}
return database;
}
}

public App()
{
InitializeComponent();
MainPage = new NavigationPage(new NotesPage());
}

protected override void OnStart()


{
// Handle when your app starts
}

protected override void OnSleep()


{
// Handle when your app sleeps
}

protected override void OnResume()


{
// Handle when your app resumes
}
}
}

Este código define un Database propiedad que se crea un nuevo NoteDatabase instancia como un
singleton, pasando el nombre de archivo de la base de datos como el argumento para el NoteDatabase
constructor. La ventaja de exponer la base de datos como un singleton es que se crea una conexión de base
de datos única que se mantiene abierta mientras la aplicación se ejecuta, lo que evita el gasto de abrir y
cerrar el archivo de base de datos cada vez que se realiza una operación de base de datos.
Guarde los cambios en App.xaml.cs presionando CTRL+S y cierre el archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

9. En el Explorador de soluciones, en el notas del proyecto, haga doble clic en NotesPage.xaml.cs para
abrirlo. A continuación, reemplace el OnAppearing método con el código siguiente:
protected override async void OnAppearing()
{
base.OnAppearing();

listView.ItemsSource = await App.Database.GetNotesAsync();


}

Este código rellena la ListView con las notas que se almacenan en la base de datos.
Guarde los cambios en NotesPage.xaml.cs presionando CTRL+Sy cierre el archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

10. En el Explorador de soluciones, haga doble clic en NoteEntryPage.xaml.cs para abrirlo. A


continuación, reemplace el OnSaveButtonClicked y OnDeleteButtonClicked métodos con el código siguiente:

async void OnSaveButtonClicked(object sender, EventArgs e)


{
var note = (Note)BindingContext;
note.Date = DateTime.UtcNow;
await App.Database.SaveNoteAsync(note);
await Navigation.PopAsync();
}

async void OnDeleteButtonClicked(object sender, EventArgs e)


{
var note = (Note)BindingContext;
await App.Database.DeleteNoteAsync(note);
await Navigation.PopAsync();
}

El NoteEntryPage almacena un Note instancia, que representa una sola nota, en el BindingContext de la
página. Cuando el OnSaveButtonClicked se ejecuta el controlador de eventos, el Note instancia se guarda
en la base de datos y la aplicación regresa a la página anterior. Cuando el OnDeleteButtonClicked se ejecuta
el controlador de eventos, el Note instancia se elimina de la base de datos y la aplicación regresa a la
página anterior.
Guarde los cambios en NoteEntryPage.xaml.cs presionando CTRL+Sy cierre el archivo.
11. Compile y ejecute el proyecto en cada plataforma. Para obtener más información, consulte crear la Guía de
inicio rápido.
En el NotesPage presione el + botón para navegar a la NoteEntryPage y escriba una nota. Después de
guardar la nota de la aplicación navegará a la NotesPage.
Escriba un número de notas, de longitud variable, observar el comportamiento de la aplicación.

Actualizar la aplicación con Visual Studio para Mac


1. Inicie Visual Studio para Mac y abra el proyecto de notas.
2. En el panel de solución, seleccione el notas del proyecto, haga clic en y seleccione Agregar > Agregar
paquetes NuGet... :
3. En la ventana Agregar paquetes, busque el paquete NuGet sqlite-net-pcl, selecciónelo y haga clic en el
botón Agregar paquete para agregarlo al proyecto:

NOTE
Hay varios paquetes NuGet con nombres similares. El paquete correcto tiene estos atributos:
Autor: Frank A. Krueger
Id.: sqlite-net-pcl
Vínculo de NuGet: sqlite-net-pcl
A pesar del nombre del paquete, este paquete NuGet puede usarse en proyectos de .NET Standard.

Este paquete se usará para incorporar operaciones de bases de datos en la aplicación.


4. En el panel de solución, en el notas proyecto, abra Note.cs en el modelos carpeta y reemplace el código
existente por lo siguiente código:
using System;
using SQLite;

namespace Notes.Models
{
public class Note
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Text { get; set; }
public DateTime Date { get; set; }
}
}

Esta clase define un Note modelo donde se almacenará los datos sobre todas las notas de la aplicación. El
ID propiedad está marcada con PrimaryKey y AutoIncrement atributos para asegurarse de que cada Note
instancia en la base de datos de SQLite.NET tendrá un identificador único proporcionado por SQLite.NET.
Guarde los cambios en Note.cs eligiendo archivo > Guardar (o presionando ⌘ + S ) y cierre el archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

5. En el panel de solución, agregue una nueva carpeta denominada datos a la notas proyecto.
6. En el panel de solución, en el notas proyecto, agregar una nueva clase denominada NoteDatabase a la
datos carpeta.
7. En NoteDatabase.cs, reemplace el código existente por el código siguiente:
using System.Collections.Generic;
using System.Threading.Tasks;
using SQLite;
using Notes.Models;

namespace Notes.Data
{
public class NoteDatabase
{
readonly SQLiteAsyncConnection _database;

public NoteDatabase(string dbPath)


{
_database = new SQLiteAsyncConnection(dbPath);
_database.CreateTableAsync<Note>().Wait();
}

public Task<List<Note>> GetNotesAsync()


{
return _database.Table<Note>().ToListAsync();
}

public Task<Note> GetNoteAsync(int id)


{
return _database.Table<Note>()
.Where(i => i.ID == id)
.FirstOrDefaultAsync();
}

public Task<int> SaveNoteAsync(Note note)


{
if (note.ID != 0)
{
return _database.UpdateAsync(note);
}
else
{
return _database.InsertAsync(note);
}
}

public Task<int> DeleteNoteAsync(Note note)


{
return _database.DeleteAsync(note);
}
}
}

Esta clase contiene código para crear la base de datos, leen datos, escribir datos en él y eliminar datos. El
código usa API asincrónicas de SQLite.NET que mueven las operaciones de base de datos a subprocesos
en segundo plano. Además, el constructor NoteDatabase toma la ruta de acceso del archivo de base de
datos como un argumento. Esta ruta de acceso será proporcionado por el App clase en el paso siguiente.
Guarde los cambios en NoteDatabase.cs eligiendo archivo > Guardar (o presionando ⌘ + S ) y cierre el
archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

8. En el panel de solución, en el notas del proyecto, haga doble clic en App.xaml.cs para abrirlo. A
continuación, reemplace el código existente por el código siguiente:
using System;
using System.IO;
using Xamarin.Forms;
using Notes.Data;

namespace Notes
{
public partial class App : Application
{
static NoteDatabase database;

public static NoteDatabase Database


{
get
{
if (database == null)
{
database = new
NoteDatabase(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"Notes.db3"));
}
return database;
}
}

public App()
{
InitializeComponent();
MainPage = new NavigationPage(new NotesPage());
}

protected override void OnStart()


{
// Handle when your app starts
}

protected override void OnSleep()


{
// Handle when your app sleeps
}

protected override void OnResume()


{
// Handle when your app resumes
}
}
}

Este código define un Database propiedad que se crea un nuevo NoteDatabase instancia como un
singleton, pasando el nombre de archivo de la base de datos como el argumento para el NoteDatabase
constructor. La ventaja de exponer la base de datos como un singleton es que se crea una conexión de base
de datos única que se mantiene abierta mientras la aplicación se ejecuta, lo que evita el gasto de abrir y
cerrar el archivo de base de datos cada vez que se realiza una operación de base de datos.
Guarde los cambios en App.xaml.cs eligiendo Archivo > Guardar (o presionando ⌘ + S ) y cierre el
archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

9. En el panel de solución, en el notas del proyecto, haga doble clic en NotesPage.xaml.cs para abrirlo. A
continuación, reemplace el OnAppearing método con el código siguiente:

protected override async void OnAppearing()


{
base.OnAppearing();

listView.ItemsSource = await App.Database.GetNotesAsync();


}

Este código rellena la ListView con las notas que se almacenan en la base de datos.
Guarde los cambios en NotesPage.xaml.cs eligiendo archivo > Guardar (o presionando ⌘ + S ) y cierre
el archivo.

WARNING
Al intentar compilar la aplicación en este momento se producirá errores que se corregirán en pasos posteriores.

10. En el panel de solución, haga doble clic en NoteEntryPage.xaml.cs para abrirlo. A continuación,
reemplace el OnSaveButtonClicked y OnDeleteButtonClicked métodos con el código siguiente:

async void OnSaveButtonClicked(object sender, EventArgs e)


{
var note = (Note)BindingContext;
note.Date = DateTime.UtcNow;
await App.Database.SaveNoteAsync(note);
await Navigation.PopAsync();
}

async void OnDeleteButtonClicked(object sender, EventArgs e)


{
var note = (Note)BindingContext;
await App.Database.DeleteNoteAsync(note);
await Navigation.PopAsync();
}

El NoteEntryPage almacena un Note instancia, que representa una sola nota, en el BindingContext de la
página. Cuando el OnSaveButtonClicked se ejecuta el controlador de eventos, el Note instancia se guarda
en la base de datos y la aplicación regresa a la página anterior. Cuando el OnDeleteButtonClicked se ejecuta
el controlador de eventos, el Note instancia se elimina de la base de datos y la aplicación regresa a la
página anterior.
Guarde los cambios en NoteEntryPage.xaml.cs eligiendo archivo > Guardar (o presionando ⌘ + S ) y
cierre el archivo.
11. Compile y ejecute el proyecto en cada plataforma. Para obtener más información, consulte crear la Guía de
inicio rápido.
En el NotesPage presione el + botón para navegar a la NoteEntryPage y escriba una nota. Después de
guardar la nota de la aplicación navegará a la NotesPage.
Escriba un número de notas, de longitud variable, observar el comportamiento de la aplicación.

Pasos siguientes
En este tutorial, ha aprendido cómo:
Use el Administrador de paquetes de NuGet para agregar un paquete de NuGet a un proyecto.
Store datos localmente en una base de datos de SQLite.NET.
Para aplicar estilo a la aplicación con los estilos XAML, continúe con el siguiente inicio rápido.
Siguiente

Vínculos relacionados
Notes (ejemplo)
Análisis detallado de inicio rápido de Xamarin.Forms
Aplicar estilo a una aplicación de Xamarin.Forms
multiplataforma
13/07/2019 • 10 minutes to read • Edit Online

Descargar el ejemplo
En este tutorial, obtendrá información sobre cómo:
Aplicar estilo a una aplicación de Xamarin.Forms con estilos XAML.
La Guía de inicio rápido le guía en una aplicación de Xamarin.Forms multiplataforma de estilo con estilos XAML. A
continuación se muestra la aplicación final:

Requisitos previos
Debe completar correctamente el inicio rápido anterior antes de intentar este inicio rápido. Como alternativa,
descargue el anterior ejemplo de tutorial rápido y usarlo como punto de partida para este inicio rápido.

Actualizar la aplicación con Visual Studio


1. Inicie Visual Studio y abra la solución de notas.
2. En el Explorador de soluciones, en el notas del proyecto, haga doble clic en App.xaml para abrirlo. A
continuación, reemplace el código existente por el código siguiente:
<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.App">
<Application.Resources>

<Thickness x:Key="PageMargin">20</Thickness>

<!-- Colors -->


<Color x:Key="AppBackgroundColor">WhiteSmoke</Color>
<Color x:Key="iOSNavigationBarColor">WhiteSmoke</Color>
<Color x:Key="AndroidNavigationBarColor">#2196F3</Color>
<Color x:Key="iOSNavigationBarTextColor">Black</Color>
<Color x:Key="AndroidNavigationBarTextColor">White</Color>

<!-- Implicit styles -->


<Style TargetType="{x:Type NavigationPage}">
<Setter Property="BarBackgroundColor"
Value="{OnPlatform iOS={StaticResource iOSNavigationBarColor},
Android={StaticResource AndroidNavigationBarColor}}" />
<Setter Property="BarTextColor"
Value="{OnPlatform iOS={StaticResource iOSNavigationBarTextColor},
Android={StaticResource AndroidNavigationBarTextColor}}" />
</Style>

<Style TargetType="{x:Type ContentPage}"


ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor"
Value="{StaticResource AppBackgroundColor}" />
</Style>

</Application.Resources>
</Application>

Este código define un Thickness valor, una serie de Color valores y los estilos implícitos para el
NavigationPage y ContentPage . Observe que estos estilos, que se encuentran en el nivel de aplicación
ResourceDictionary , pueden utilizarse en toda la aplicación. Para obtener más información acerca de la
aplicación de estilos de XAML, vea estilo en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en App.xaml presionando CTRL+Sy cierre el archivo.
3. En el Explorador de soluciones, en el notas del proyecto, haga doble clic en NotesPage.xaml para
abrirlo. A continuación, reemplace el código existente por el código siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.NotesPage"
Title="Notes">
<ContentPage.Resources>
<!-- Implicit styles -->
<Style TargetType="{x:Type ListView}">
<Setter Property="BackgroundColor"
Value="{StaticResource AppBackgroundColor}" />
</Style>
</ContentPage.Resources>

<ContentPage.ToolbarItems>
<ToolbarItem Text="+"
Clicked="OnNoteAddedClicked" />
</ContentPage.ToolbarItems>

<ListView x:Name="listView"
Margin="{StaticResource PageMargin}"
ItemSelected="OnListViewItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Text}"
Detail="{Binding Date}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

</ContentPage>

Este código agrega un estilo implícito para la ListView para el nivel de página ResourceDictionary y
establece el ListView.Margin propiedad a un valor definido en el nivel de aplicación ResourceDictionary .
Tenga en cuenta que el ListView estilo implícito se agregó en el nivel de página ResourceDictionary , ya que
sólo es usado por el NotesPage . Para obtener más información acerca de la aplicación de estilos de XAML,
vea estilo en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en NotesPage.xaml presionando CTRL+Sy cierre el archivo.
4. En el Explorador de soluciones, en el notas del proyecto, haga doble clic en NoteEntryPage.xaml para
abrirlo. A continuación, reemplace el código existente por el código siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.NoteEntryPage"
Title="Note Entry">
<ContentPage.Resources>
<!-- Implicit styles -->
<Style TargetType="{x:Type Editor}">
<Setter Property="BackgroundColor"
Value="{StaticResource AppBackgroundColor}" />
</Style>

<Style TargetType="Button"
ApplyToDerivedTypes="True"
CanCascade="True">
<Setter Property="FontSize" Value="Medium" />
<Setter Property="BackgroundColor" Value="LightGray" />
<Setter Property="TextColor" Value="Black" />
<Setter Property="BorderRadius" Value="5" />
</Style>
</ContentPage.Resources>

<StackLayout Margin="{StaticResource PageMargin}">


<Editor Placeholder="Enter your note"
Text="{Binding Text}"
HeightRequest="100" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save"
Clicked="OnSaveButtonClicked" />
<Button Grid.Column="1"
Text="Delete"
Clicked="OnDeleteButtonClicked" />
</Grid>
</StackLayout>

</ContentPage>

Este código agrega los estilos implícitos para el Editor y Button vistas en el nivel de página
ResourceDictionary y establece el StackLayout.Margin propiedad a un valor definido en el nivel de
aplicación ResourceDictionary . Tenga en cuenta que el Editor y Button se agregaron los estilos implícitos
en el nivel de página ResourceDictionary , ya que solo consumen el NoteEntryPage . Para obtener más
información acerca de la aplicación de estilos de XAML, vea estilo en el análisis detallado de inicio rápido de
Xamarin.Forms.
Guarde los cambios en NoteEntryPage.xaml presionando CTRL+Sy cierre el archivo.
5. Compile y ejecute el proyecto en cada plataforma. Para obtener más información, consulte crear la Guía de
inicio rápido.
En el NotesPage presione el + botón para navegar a la NoteEntryPage y escriba una nota. En cada
página, observe cómo ha cambiado el estilo desde el inicio rápido anterior.

Actualizar la aplicación con Visual Studio para Mac


1. Inicie Visual Studio para Mac y abra el proyecto de notas.
2. En el panel de solución, en el notas del proyecto, haga doble clic en App.xaml para abrirlo. A
continuación, reemplace el código existente por el código siguiente:
<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.App">
<Application.Resources>

<Thickness x:Key="PageMargin">20</Thickness>

<!-- Colors -->


<Color x:Key="AppBackgroundColor">WhiteSmoke</Color>
<Color x:Key="iOSNavigationBarColor">WhiteSmoke</Color>
<Color x:Key="AndroidNavigationBarColor">#2196F3</Color>
<Color x:Key="iOSNavigationBarTextColor">Black</Color>
<Color x:Key="AndroidNavigationBarTextColor">White</Color>

<!-- Implicit styles -->


<Style TargetType="{x:Type NavigationPage}">
<Setter Property="BarBackgroundColor"
Value="{OnPlatform iOS={StaticResource iOSNavigationBarColor},
Android={StaticResource AndroidNavigationBarColor}}" />
<Setter Property="BarTextColor"
Value="{OnPlatform iOS={StaticResource iOSNavigationBarTextColor},
Android={StaticResource AndroidNavigationBarTextColor}}" />
</Style>

<Style TargetType="{x:Type ContentPage}"


ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor"
Value="{StaticResource AppBackgroundColor}" />
</Style>

</Application.Resources>
</Application>

Este código define un Thickness valor, una serie de Color valores y los estilos implícitos para el
NavigationPage y ContentPage . Observe que estos estilos, que se encuentran en el nivel de aplicación
ResourceDictionary , pueden utilizarse en toda la aplicación. Para obtener más información acerca de la
aplicación de estilos de XAML, vea estilo en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en App.xaml eligiendo archivo > Guardar (o presionando ⌘ + S ) y cierre el archivo.
3. En el panel de solución, en el notas del proyecto, haga doble clic en NotesPage.xaml para abrirlo. A
continuación, reemplace el código existente por el código siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.NotesPage"
Title="Notes">
<ContentPage.Resources>
<!-- Implicit styles -->
<Style TargetType="{x:Type ListView}">
<Setter Property="BackgroundColor"
Value="{StaticResource AppBackgroundColor}" />
</Style>
</ContentPage.Resources>

<ContentPage.ToolbarItems>
<ToolbarItem Text="+"
Clicked="OnNoteAddedClicked" />
</ContentPage.ToolbarItems>

<ListView x:Name="listView"
Margin="{StaticResource PageMargin}"
ItemSelected="OnListViewItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Text}"
Detail="{Binding Date}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

</ContentPage>

Este código agrega un estilo implícito para la ListView para el nivel de página ResourceDictionary y
establece el ListView.Margin propiedad a un valor definido en el nivel de aplicación ResourceDictionary .
Tenga en cuenta que el ListView estilo implícito se agregó en el nivel de página ResourceDictionary , ya que
sólo es usado por el NotesPage . Para obtener más información acerca de la aplicación de estilos de XAML,
vea estilo en el análisis detallado de inicio rápido de Xamarin.Forms.
Guarde los cambios en NotesPage.xaml eligiendo archivo > Guardar (o presionando ⌘ + S ) y cierre el
archivo.
4. En el panel de solución, en el notas del proyecto, haga doble clic en NoteEntryPage.xaml para abrirlo. A
continuación, reemplace el código existente por el código siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.NoteEntryPage"
Title="Note Entry">
<ContentPage.Resources>
<!-- Implicit styles -->
<Style TargetType="{x:Type Editor}">
<Setter Property="BackgroundColor"
Value="{StaticResource AppBackgroundColor}" />
</Style>

<Style TargetType="Button"
ApplyToDerivedTypes="True"
CanCascade="True">
<Setter Property="FontSize" Value="Medium" />
<Setter Property="BackgroundColor" Value="LightGray" />
<Setter Property="TextColor" Value="Black" />
<Setter Property="BorderRadius" Value="5" />
</Style>
</ContentPage.Resources>

<StackLayout Margin="{StaticResource PageMargin}">


<Editor Placeholder="Enter your note"
Text="{Binding Text}"
HeightRequest="100" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save"
Clicked="OnSaveButtonClicked" />
<Button Grid.Column="1"
Text="Delete"
Clicked="OnDeleteButtonClicked" />
</Grid>
</StackLayout>

</ContentPage>

Este código agrega los estilos implícitos para el Editor y Button vistas en el nivel de página
ResourceDictionary y establece el StackLayout.Margin propiedad a un valor definido en el nivel de
aplicación ResourceDictionary . Tenga en cuenta que el Editor y Button se agregaron los estilos implícitos
en el nivel de página ResourceDictionary , ya que solo consumen el NoteEntryPage . Para obtener más
información acerca de la aplicación de estilos de XAML, vea estilo en el análisis detallado de inicio rápido de
Xamarin.Forms.
Guarde los cambios en NoteEntryPage.xaml eligiendo archivo > Guardar (o presionando ⌘ + S ) y
cierre el archivo.
5. Compile y ejecute el proyecto en cada plataforma. Para obtener más información, consulte crear la Guía de
inicio rápido.
En el NotesPage presione el + botón para navegar a la NoteEntryPage y escriba una nota. En cada
página, observe cómo ha cambiado el estilo desde el inicio rápido anterior.

Pasos siguientes
En este tutorial, ha aprendido cómo:
Aplicar estilo a una aplicación de Xamarin.Forms con estilos XAML.
Para obtener más información sobre los aspectos básicos del desarrollo de aplicaciones con Xamarin.Forms,
continúe con el análisis detallado de inicio rápido.
Siguiente

Vínculos relacionados
Notes (ejemplo)
Análisis detallado de inicio rápido de Xamarin.Forms
Análisis detallado de inicio rápido de
Xamarin.Forms
11/07/2019 • 36 minutes to read • Edit Online

En el inicio rápido de Xamarin.Forms, se creó la aplicación de notas. En este artículo se revisa lo


que se ha compilado para comprender los aspectos fundamentales del funcionamiento de las
aplicaciones de Xamarin.Forms.

Introducción a Visual Studio


Visual Studio organiza el código en soluciones y proyectos. Una solución es un contenedor que
puede incluir uno o varios proyectos. Un proyecto puede ser una aplicación, una biblioteca
auxiliar o una aplicación de prueba, entre otros. La aplicación de Notes consta de una solución
que contiene cuatro proyectos, como se muestra en la captura de pantalla siguiente:

Los proyectos son:


Notas: este proyecto es el proyecto de biblioteca .NET Standard que incluye todo el código
compartido y la interfaz de usuario compartida.
Notes.Android: este proyecto contiene código específico de Android y es el punto de entrada
para la aplicación Android.
Notes.iOS: este proyecto contiene código específico de iOS y es el punto de entrada para la
aplicación de iOS.
Notes.UWP: este proyecto incluye el código específico de plataforma Universal de Windows
(UWP ) y es el punto de entrada para la aplicación de UWP.

Anatomía de una aplicación de Xamarin.Forms


Captura de pantalla siguiente muestra el contenido del proyecto de biblioteca estándar de .NET
de notas en Visual Studio:
El proyecto tiene un nodo Dependencias que contiene los nodos NuGet y SDK:
NuGet – el Xamarin.Forms y paquetes de NuGet de sqlite-net-pcl que se han agregado al
proyecto.
SDK: el metapaquete NETStandard.Library que hace referencia al conjunto completo de
paquetes NuGet que definen .NET Standard.

Introducción a Visual Studio para Mac


Visual Studio para Mac sigue la práctica de Visual Studio consistente en organizar el código en
soluciones y proyectos. Una solución es un contenedor que puede incluir uno o varios proyectos.
Un proyecto puede ser una aplicación, una biblioteca auxiliar o una aplicación de prueba, entre
otros. La aplicación de Notes consta de una solución que contiene tres proyectos, como se
muestra en la captura de pantalla siguiente:

Los proyectos son:


Notas: este proyecto es el proyecto de biblioteca .NET Standard que incluye todo el código
compartido y la interfaz de usuario compartida.
Notes.Android: este proyecto contiene código específico de Android y es el punto de entrada
para aplicaciones de Android.
Notes.iOS: este proyecto contiene código específico de iOS y es el punto de entrada para las
aplicaciones de iOS.

Anatomía de una aplicación de Xamarin.Forms


Captura de pantalla siguiente muestra el contenido del proyecto de biblioteca estándar de .NET
de notas en Visual Studio para Mac:
El proyecto tiene un nodo Dependencias que contiene los nodos NuGet y SDK:
NuGet – el Xamarin.Forms y paquetes de NuGet de sqlite-net-pcl que se han agregado al
proyecto.
SDK: el metapaquete NETStandard.Library que hace referencia al conjunto completo de
paquetes NuGet que definen .NET Standard.
El proyecto también consta de varios archivos:
Data\NoteDatabase.cs : esta clase contiene código para crear la base de datos, leen datos,
escribir datos en él y eliminar datos.
Models\Note.cs : esta clase define un Note modelo cuyas instancias almacenan datos sobre
todas las notas de la aplicación.
App.xaml: el marcado XAML para la clase App , que define un diccionario de recursos para la
aplicación.
App.xaml.cs: el código subyacente para la clase App , que es el responsable de crear
instancias de la primera página que se mostrarán mediante la aplicación en cada plataforma, y
para controlar los eventos del ciclo de vida de la aplicación.
AssemblyInfo.cs : este archivo contiene un atributo de aplicación sobre el proyecto, que se
aplica en el nivel de ensamblado.
NotesPage.xaml – marcado el XAML para el NotesPage (clase), que define la interfaz de
usuario para la página que aparece cuando se inicia la aplicación.
NotesPage.xaml.cs : el código subyacente para el NotesPage (clase), que contiene la lógica
de negocios que se ejecuta cuando el usuario interactúa con la página.
NoteEntryPage.xaml – marcado el XAML para el NoteEntryPage (clase), que define la
interfaz de usuario para la página que aparece cuando el usuario escribe una nota.
NoteEntryPage.xaml.cs : el código subyacente para el NoteEntryPage (clase), que contiene
la lógica de negocios que se ejecuta cuando el usuario interactúa con la página.
Para obtener más información sobre la anatomía de una aplicación de Xamarin.iOS, consulte
Anatomía de una aplicación de Xamarin.iOS. Para obtener más información sobre la anatomía de
una aplicación de Xamarin.Android, consulte Anatomía de una aplicación de Xamarin.Android.

Aspectos básicos de arquitectura y la aplicación


Una aplicación de Xamarin.Forms tiene la misma arquitectura que una aplicación
multiplataforma tradicional. El código compartido normalmente se coloca en una biblioteca de
.NET Standard, y las aplicaciones específicas de la plataforma consumen el código compartido. El
siguiente diagrama muestra una visión general de esta relación para la aplicación de notas:

Para maximizar la reutilización del código de inicio, las aplicaciones de Xamarin.Forms tienen una
clase única denominada App que es responsable de crear instancias de la primera página que
mostrará la aplicación en cada plataforma, como se muestra en el siguiente ejemplo de código:
using Xamarin.Forms;

namespace Notes
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new NotesPage());
}
...
}
}

Este código establece la MainPage propiedad de la App clase a un NavigationPage instancia cuyo
contenido es un NotesPage instancia.
Además, el AssemblyInfo.cs archivo contiene un atributo de aplicación único, que se aplica en el
nivel de ensamblado:

using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]

El XamlCompilation atributo activa en el compilador XAML, por lo que se compila XAML


directamente en lenguaje intermedio. Para obtener más información, consulte Compilación
XAML.

Iniciando la aplicación en cada plataforma


iOS
Para iniciar la página de Xamarin.Forms inicial en iOS, el proyecto Notes.iOS define la
AppDelegate clase que hereda de la FormsApplicationDelegate clase:

namespace Notes.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate :
global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
}
}

El reemplazo FinishedLaunching inicializa el marco de Xamarin.Forms mediante una llamada al


método Init . Esto provoca que la implementación específica de iOS de Xamarin.Forms se
cargue en la aplicación antes de que se establezca el controlador de vista raíz mediante la llamada
al método LoadApplication .
Android
Para iniciar la página inicial de Xamarin.Forms en Android, el proyecto Notes.Android incluye
código que crea un Activity con el MainLauncher atributo con la actividad se hereda de la
FormsAppCompatActivity clase:

namespace Notes.Droid
{
[Activity(Label = "Notes",
Icon = "@mipmap/icon",
Theme = "@style/MainTheme",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;

base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
}
}

El reemplazo OnCreate inicializa el marco de Xamarin.Forms mediante una llamada al método


Init . Esto provoca que la implementación específica de Android de Xamarin.Forms se cargue
en la aplicación antes de que lo haga la aplicación de Xamarin.Forms.
Plataforma universal de Windows
En las aplicaciones de plataforma universal de Windows (UWP ), el método Init que inicializa el
marco de Xamarin.Forms se invoca desde la clase App :

Xamarin.Forms.Forms.Init (e);

if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
...
}

Esto provoca que la implementación específica de UWP de Xamarin.Forms se cargue en la


aplicación. La página de Xamarin.Forms inicial se inicia por la MainPage clase:

namespace Notes.UWP
{
public sealed partial class MainPage
{
public MainPage()
{
this.InitializeComponent();
this.LoadApplication(new Notes.App());
}
}
}

La aplicación de Xamarin.Forms se carga con el método LoadApplication .


NOTE
Aplicaciones de la plataforma de Windows universales pueden ser compilada con Xamarin.Forms, pero
solo mediante Visual Studio en Windows.

Interfaz de usuario
Hay cuatro grupos principales de control usados para crear la interfaz de usuario de una
aplicación de Xamarin.Forms:
1. Páginas: las páginas de Xamarin.Forms representan pantallas de aplicaciones móviles
multiplataforma. Usa la aplicación de notas de la ContentPage clase para mostrar pantallas
únicas. Para obtener más información sobre las páginas, consulte Xamarin.Forms Pages
(Páginas de Xamarin.Forms).
2. Vistas: las vistas de Xamarin.Forms son los controles que se muestran en la interfaz de
usuario, como etiquetas, botones y cuadros de entrada de texto. Usa la aplicación finalizada de
notas el ListView , Editor , y Button vistas. Para obtener más información sobre las vistas,
consulte Xamarin.Forms Views (Vistas de Xamarin.Forms).
3. Diseños: los diseños de Xamarin.Forms son contenedores que se usan para crear vistas en
estructuras lógicas. Usa la aplicación de notas de la StackLayout clase para organizar las
vistas en una pila vertical y el Grid clase para organizar botones horizontalmente. Para
obtener más información sobre los diseños, consulte Xamarin.Forms Layouts (Diseños de
Xamarin.Forms).
4. Celdas: las celdas de Xamarin.Forms son elementos especializados que se usan para los
elementos de una lista, y describen cómo debe dibujarse cada elemento de una lista. Usa la
aplicación de notas de la TextCell para mostrar los dos elementos para cada fila en la lista.
Para obtener más información sobre las celdas, consulte Xamarin.Forms Cells (Celdas de
Xamarin.Forms).
En tiempo de ejecución, cada control se asignará a su equivalente nativo, que es lo que se
representará.
Diseño
Usa la aplicación de notas de la StackLayout para simplificar el desarrollo de aplicaciones
multiplataforma al organizar automáticamente las vistas en la pantalla, independientemente del
tamaño de pantalla. Cada elemento secundario se coloca uno detrás del otro, ya sea horizontal o
verticalmente, en el orden en el que se ha agregado. La cantidad de espacio que usará la clase
StackLayout depende de cómo se establezcan las propiedades HorizontalOptions y
VerticalOptions , pero StackLayout intentará usar toda la pantalla de forma predeterminada.

El código XAML siguiente muestra un ejemplo del uso de un StackLayout al diseño del
NoteEntryPage :
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.NoteEntryPage"
Title="Note Entry">
...
<StackLayout Margin="{StaticResource PageMargin}">
<Editor Placeholder="Enter your note"
Text="{Binding Text}"
HeightRequest="100" />
<Grid>
...
</Grid>
</StackLayout>
</ContentPage>

De forma predeterminada el StackLayout asume una orientación vertical. Sin embargo, se puede
cambiar a una orientación horizontal estableciendo el StackLayout.Orientation propiedad a la
StackOrientation.Horizontal miembro de enumeración.

NOTE
Se puede establecer el tamaño de las vistas a través de la HeightRequest y WidthRequest
propiedades.

Para obtener más información sobre la clase StackLayout , consulte StackLayout.


Responder a la interacción del usuario
Un objeto que se ha definido en XAML puede desencadenar un evento que se controla en el
archivo de código subyacente. El siguiente ejemplo de código muestra la OnSaveButtonClicked
método en el código subyacente para el NoteEntryPage (clase), que se ejecuta en respuesta a la
Clicked evento y activar el guardar botón .

async void OnSaveButtonClicked(object sender, EventArgs e)


{
var note = (Note)BindingContext;
note.Date = DateTime.UtcNow;
await App.Database.SaveNoteAsync(note);
await Navigation.PopAsync();
}

El OnSaveButtonClicked método guarda la nota en la base de datos y vuelve a la página anterior.

NOTE
El archivo de código subyacente de una clase XAML puede tener acceso a un objeto que se ha definido
en XAML con el nombre asignado a él con el atributo x:Name . El valor que se ha asignado a este
atributo tiene las mismas reglas que las variables de C#, ya que debe comenzar con una letra o guion
bajo y no contener espacios incrustados.

Botón el cableado de la operación de guardar el OnSaveButtonClicked método se produce en el


marcado XAML para el NoteEntryPage clase:
<Button Text="Save"
Clicked="OnSaveButtonClicked" />

Listas
El ListView es responsable de mostrar una colección de elementos verticalmente en una lista.
Cada elemento en el ListView se incluirá en una sola celda.
El siguiente ejemplo de código muestra la ListView desde el NotesPage :

<ListView x:Name="listView"
Margin="{StaticResource PageMargin}"
ItemSelected="OnListViewItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Text}"
Detail="{Binding Date}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

El diseño de cada fila de la ListView se define dentro el ListView.ItemTemplate elemento y la usa


para mostrar las notas que se recuperan mediante la aplicación de enlace de datos. El
ListView.ItemsSource propiedad está establecida en el origen de datos en NotesPage.xaml.cs :

protected override async void OnAppearing()


{
base.OnAppearing();

listView.ItemsSource = await App.Database.GetNotesAsync();


}

Este código rellena la ListView con las notas que se almacenan en la base de datos.
Cuando se selecciona una fila en la ListView , ItemSelected desencadena el evento. Un
controlador de eventos, llamado OnListViewItemSelected , se ejecuta cuando se activa el evento:

async void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)


{
if (e.SelectedItem != null)
{
...
}
}

El ItemSelected evento puede tener acceso al objeto que se asoció a la celda a través de la
e.SelectedItem propiedad.
Para obtener más información sobre la ListView de clases, vea ListView.

Navegación
Xamarin.Forms proporciona una serie de experiencias de navegación de páginas diferente, en
función del tipo de Page que se use. Para ContentPage navegación instancias puede ser
jerárquica, o no modales. Para obtener información sobre la navegación modal, consulte páginas
modales de Xamarin.Forms.
NOTE
Las clases CarouselPage , MasterDetailPage y TabbedPage proporcionan experiencias de navegación
alternativas. Para obtener más información, consulte Navigation (Navegación).

En la navegación jerárquica, el NavigationPage clase se utiliza para navegar a través de una pila
de ContentPage objetos hacia delante y hacia atrás, según sea necesario. La clase implementa la
navegación como una pila de objetos Page en la que el último en entrar es el primero en salir
(LIFO ). Para pasar de una página a otra, una aplicación insertará una nueva página en la pila de
navegación, donde se convertirá en la página activa. Para volver a la página anterior, la aplicación
mostrará la página actual de la pila de navegación y la nueva página de nivel superior se
convertirá en la página activa.
La clase NavigationPage también agregará una barra de navegación en la parte superior de la
página que muestra un título y un botón Atrás adecuado para la plataforma para volver a la
página anterior.
La primera página que se agrega a una pila de navegación se conoce como el raíz página de la
aplicación y en el ejemplo de código siguiente muestra cómo se consigue en la aplicación de
notas:

public App ()
{
...
MainPage = new NavigationPage (new NotesPage ());
}

Todas las instancias ContentPage tienen una propiedad Navigation que expone métodos para
modificar la pila de la página. Solo se deberían invocar estos métodos si la aplicación incluye una
NavigationPage . Para navegar a la NoteEntryPage , es necesario invocar el método PushAsync
como se muestra en el ejemplo de código siguiente:

await Navigation.PushAsync(new NoteEntryPage());

Esto hace que el nuevo NoteEntryPage objeto se inserte en la pila de navegación, donde se
convertirá en la página activa.
La página activa se puede extraer de la pila de navegación. Para ello, pulse el botón Atrás del
dispositivo, independientemente de si se trata de un botón físico en el dispositivo o de un botón
en la pantalla. Para volver mediante programación a la página original, el objeto NoteEntryPage
debe invocar el método PopAsync , como se muestra en el ejemplo de código siguiente:

await Navigation.PopAsync();

Para obtener más información sobre la navegación jerárquica, consulte Hierarchical Navigation
(Navegación jerárquica).

Enlace de datos
El enlace de datos se usa para simplificar la forma en que una aplicación de Xamarin.Forms
muestra e interactúa con sus datos. Establece una conexión entre la interfaz de usuario y la
aplicación subyacente. La clase BindableObject contiene gran parte de la infraestructura para
admitir el enlace de datos.
El enlace de datos conecta dos objetos, denominados origen y destino. El objeto de origen
proporciona los datos. El objeto de destino usa (y, a menudo, muestra) los datos del objeto de
origen. Por ejemplo, un Editor (destino objeto) normalmente enlazará su Text propiedad para
un público string propiedad en un origen objeto. En el diagrama siguiente se muestra la
relación de enlace:

El principal beneficio del enlace de datos es que ya no tiene que preocuparse de sincronizar los
datos entre las vistas y el origen de datos. Los cambios en el objeto de origen se insertan
automáticamente en el objeto de destino en segundo plano por medio del marco de enlace,
mientras que los cambios en el objeto de destino pueden insertarse de manera opcional en el
objeto de origen.
Establecer datos de enlace es un proceso de dos pasos:
La propiedad BindingContext del objeto de destino se debe establecer en el de origen.
Es necesario establecer un enlace entre el destino y el origen. En XAML, esto se consigue
mediante la extensión de marcado Binding .
En la aplicación de notas, el destino de enlace es el Editor que muestra una nota, mientras que
el Note instancia establecida como el BindingContext de NoteEntryPage es el enlace código
fuente.
El BindingContext de la NoteEntryPage se establece durante la navegación en páginas, como se
muestra en el ejemplo de código siguiente:

async void OnNoteAddedClicked(object sender, EventArgs e)


{
await Navigation.PushAsync(new NoteEntryPage
{
BindingContext = new Note()
});
}

async void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)


{
if (e.SelectedItem != null)
{
await Navigation.PushAsync(new NoteEntryPage
{
BindingContext = e.SelectedItem as Note
});
}
}

En el OnNoteAddedClicked método, que se ejecuta cuando se agrega una nota nueva a la


aplicación, el BindingContext de NoteEntryPage se establece en un nuevo Note instancia. En el
OnListViewItemSelected método, que se ejecuta cuando se selecciona una nota existente en el
ListView , el BindingContext de la NoteEntryPage está establecido en seleccionado Note
instancia, que se accede mediante el e.SelectedItem propiedad.
IMPORTANT
Aunque la propiedad BindingContext de cada objeto de destino se puede establecer de manera
individual, no es necesario hacerlo. BindingContext es una propiedad especial que heredan todos sus
elementos secundarios. Por lo tanto, cuando el BindingContext en el ContentPage está establecido
en un Note instancia, todos los elementos secundarios de la ContentPage tienen el mismo
BindingContext y se puede enlazar a propiedades públicas de la Note objeto.

El Editor en NoteEntryPage , a continuación, se enlaza a la Text propiedad de la Note objeto:

<Editor Placeholder="Enter your note"


Text="{Binding Text}"
... />

Se establece un enlace entre la propiedad Editor.Text y la propiedad Text del objeto de origen.
Los cambios realizados en el Editor se propagarán automáticamente a la Note objeto. De
forma similar, si se realizan cambios en el Note.Text propiedad, el motor de enlace de
Xamarin.Forms también actualizará el contenido de la Editor . Esto se conoce como enlace
bidireccional.
Para obtener más información sobre el enlace de datos, vea Enlace de datos de Xamarin.Forms.

Aplicación de estilos
A menudo, las aplicaciones de Xamarin.Forms contienen varios elementos visuales que tienen
una apariencia idéntica. Establecer la apariencia de cada elemento visual puede ser repetitiva y
propensas a errores. En su lugar, pueden crearse estilos que definen la apariencia y, a
continuación, se aplica a los elementos visuales necesarios.
El Style clase agrupa una colección de valores de propiedad en un objeto que, a continuación,
se puede aplicar a varias instancias del elemento visual. Los estilos se almacenan en un
ResourceDictionary , ya sea en el nivel de aplicación, el nivel de página o el nivel de vista. Elegir
dónde se puede definir un Style impactos en el que se puede usar:
Style las instancias definidas en el nivel de aplicación se pueden aplicar a lo largo de la
aplicación.
Style las instancias definidas en el nivel de página se pueden aplicar a la página y a sus
elementos secundarios.
Style las instancias definidas en el nivel de vista se pueden aplicar a la vista y a sus
elementos secundarios.

IMPORTANT
Los estilos que se usan en toda la aplicación se almacenan en el diccionario de recursos de la aplicación
para evitar la duplicación. Sin embargo, XAML que es específico de una página no debería incluirse en el
diccionario de recursos de la aplicación, como los recursos, a continuación, se analizarán al iniciarse la
aplicación en lugar de cuando se solicite una página.

Cada Style instancia contiene una colección de uno o varios Setter objetos, con cada Setter
tener un Property y un Value . El Property es el nombre de la propiedad enlazable del
elemento que se aplica el estilo, y el Value es el valor que se aplica a la propiedad. En el ejemplo
de código siguiente se muestra un estilo de NoteEntryPage :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.NoteEntryPage"
Title="Note Entry">
<ContentPage.Resources>
<!-- Implicit styles -->
<Style TargetType="{x:Type Editor}">
<Setter Property="BackgroundColor"
Value="{StaticResource AppBackgroundColor}" />
</Style>
...
</ContentPage.Resources>
...
</ContentPage>

Este estilo se aplica a cualquier Editor instancias de la página.


Al crear un Style , TargetType propiedad siempre es necesaria.

NOTE
Aplicar un estilo a una aplicación de Xamarin.Forms tradicionalmente se logra mediante el uso de estilos
XAML. Sin embargo, Xamarin.Forms también admite los elementos de estilo visual con hojas de estilos en
cascada (CSS). Para obtener más información, consulte aplicaciones estilo Xamarin.Forms con hojas de
estilos en cascada (CSS).

Para obtener más información sobre los estilos XAML, vea aplicar estilos a las aplicaciones de
Xamarin.Forms con estilos XAML.
Proporcionar estilos específicos de la plataforma
El OnPlatform las extensiones de marcado le permiten personalizar la apariencia de la interfaz de
usuario en forma de acuerdo con la plataforma:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Notes.App">
<Application.Resources>
...
<Color x:Key="iOSNavigationBarColor">WhiteSmoke</Color>
<Color x:Key="AndroidNavigationBarColor">#2196F3</Color>
<Color x:Key="iOSNavigationBarTextColor">Black</Color>
<Color x:Key="AndroidNavigationBarTextColor">White</Color>

<Style TargetType="{x:Type NavigationPage}">


<Setter Property="BarBackgroundColor"
Value="{OnPlatform iOS={StaticResource iOSNavigationBarColor},
Android={StaticResource AndroidNavigationBarColor}}"
/>
<Setter Property="BarTextColor"
Value="{OnPlatform iOS={StaticResource iOSNavigationBarTextColor},
Android={StaticResource
AndroidNavigationBarTextColor}}" />
</Style>
...
</Application.Resources>
</Application>

Esto Style establece diferentes Color valores para el BarBackgroundColor y BarTextColor


propiedades de NavigationPage , según la plataforma que se va a usar.
Para más información sobre las extensiones de marcado, vea Extensiones de marcado para el
lenguaje XAML. Para obtener información sobre la OnPlatform extensión de marcado, consulte
OnPlatform Markup Extension.

Prueba e implementación
Tanto Visual Studio para Mac como Visual Studio ofrecen numerosas opciones para probar e
implementar una aplicación. Depurar aplicaciones es una parte común del ciclo de vida del
desarrollo de la aplicación y ayuda a diagnosticar problemas de código. Para obtener más
información, consulte Set a Breakpoint (Establecer un punto de interrupción), Step Through Code
(Recorrer el código paso a paso) y Output Information to the Log Window (Información de salida
para la ventana Registro).
Los simuladores son un buen lugar para comenzar a implementar y probar una aplicación, y
cuentan con una funcionalidad que resulta útil a la hora de probar las aplicaciones. Sin embargo,
los usuarios no usarán la aplicación final en un simulador, por lo que las aplicaciones deben
probarse en dispositivos reales desde el primer momento y con frecuencia. Para obtener más
información sobre el aprovisionamiento de dispositivos de iOS, consulte Aprovisionamiento de
dispositivos. Para obtener más información sobre el aprovisionamiento de dispositivos de
Android, consulte Configurar el dispositivo para el desarrollo.

Pasos siguientes
Este análisis detallado de ha examinado los aspectos básicos del desarrollo de aplicaciones con
Xamarin.Forms. Se recomienda que, como paso siguiente, lea sobre las funcionalidades que se
indican a continuación:
Existen cuatro grupos de control principales que se usan para crear la interfaz de usuario de
una aplicación de Xamarin.Forms. Para obtener más información, vea Referencia de controles.
El enlace de datos es la técnica que consiste en vincular las propiedades de dos objetos para
que los cambios en una propiedad se reflejen automáticamente en la otra propiedad. Para
obtener más información, vea Enlace de datos.
Xamarin.Forms proporciona una serie de experiencias de navegación de páginas diferente, en
función del tipo de página que se use. Para obtener más información, consulte Navigation
(Navegación).
Los estilos permiten reducir el uso de marcado repetitivo y conseguir que la apariencia de las
aplicaciones pueda cambiarse con mayor facilidad. Para obtener más información, vea
Aplicación de estilos a aplicaciones de Xamarin.Forms.
Las extensiones de marcado XAML extienden las funciones avanzadas y la flexibilidad de
XAML al permitir establecer atributos de elementos desde orígenes distintos de cadenas de
texto literales. Para obtener más información, vea Extensiones de marcado XAML.
Las plantillas de datos permiten definir la presentación de los datos en las vistas admitidas.
Para obtener más información, consulte Data Templates (Plantillas de datos).
Cada página, diseño y control se representan de forma diferente en cada plataforma mediante
una clase Renderer que, a su vez, crea un control nativo, lo organiza en la pantalla y agrega el
comportamiento especificado al código compartido. Los desarrolladores pueden implementar
sus propias clases Renderer personalizadas para personalizar la apariencia o el
comportamiento de un control. Para obtener más información, consulte Custom Renderers
(Representadores personalizados).
Los efectos también permiten personalizar los controles nativos de cada plataforma. Los
efectos se crean en proyectos específicos de la plataforma mediante la creación de subclases
de la clase PlatformEffect . Para usarlos, se adjuntan a un control adecuado de
Xamarin.Forms. Para obtener más información, consulte Effects (Efectos).
El código compartido puede tener acceso a la funcionalidad nativa mediante la clase
DependencyService . Para obtener más información, consulte Accessing Native Features with
DependencyService (Acceso a características nativas con DependencyService).
También es recomendable el libro de Charles Petzold titulado Creating Mobile Apps with
Xamarin.Forms (Creación de aplicaciones móviles con Xamarin.Forms) para obtener más
información sobre Xamarin.Forms. El libro está disponible como un archivo PDF o en una
variedad de formatos de libro electrónico.

Vínculos relacionados
Lenguaje de marcado de aplicaciones eXtensible (XAML )
Enlace de datos
Controls Reference (Referencia de controles)
Extensiones de marcado XAML
Xamarin.Forms Samples (Ejemplos de Xamarin.Forms)
Getting Started Samples (Ejemplos de introducción)
Referencia de la API de Xamarin.Forms
Aprendizaje autoguiado gratuito (vídeo)
Lenguaje de marcado de aplicaciones eXtensible
(XAML)
11/07/2019 • 6 minutes to read • Edit Online

XAML es un lenguaje de marcado declarativo que puede utilizarse para definir las interfaces de usuario. La
interfaz de usuario se define en un archivo XML utilizando la sintaxis XAML, mientras que el comportamiento de
tiempo de ejecución se define en un archivo de código subyacente independiente.

Evolve 2016: Convertirse en un patrón de XAML

Controles de XAML
Todas las vistas que se definen en Xamarin.Forms pueden hacer referencia a partir de los archivos XAML.

XAML Basics (Conceptos básicos de XAML)


XAML permite a los desarrolladores definir interfaces de usuario en aplicaciones de Xamarin.Forms mediante
marcado en lugar de código. XAML nunca es necesario en un programa de Xamarin.Forms, pero dispone de
herramientas y, a menudo es más coherente visualmente y más concisa que el código equivalente. XAML es
especialmente adecuada para su uso con la arquitectura de aplicaciones populares de Model-View -ViewModel
(MVVM ): XAML define la vista que esté vinculada a ViewModel código a través de enlaces de datos basados en
XAML.

Compilación de XAML
XAML se puede compilar de forma opcional directamente en lenguaje intermedio (IL ) con el compilador XAML
(XAMLC ). Este artículo describe cómo usar XAMLC y sus ventajas.

Controlador de vista previa de XAML


El controlador de vista previa de XAML presenta una vista previa dinámica de una página side-by-side con el
marcado XAML, lo que permite ver la interfaz de usuario representada a medida que escribe.

Espacios de nombres XAML


XAML usa la xmlns atributo XML para las declaraciones de espacio de nombres. Este artículo presenta la sintaxis
del espacio de nombres XAML y muestra cómo declarar un espacio de nombres XAML para tener acceso a un
tipo.

Esquemas de espacio de nombres personalizado XAML


Se puede definir un esquema del espacio de nombres personalizado XAML con la clase XmlnsDefinitionAttribute ,
que especifica una asignación entre una dirección URL personalizada y uno o varios espacios de nombres CLR. El
esquema del espacio de nombres personalizado se puede usar en las declaraciones del espacio de nombres XAML.

Prefijos recomendados de espacio de nombres de XAML


La XmlnsPrefixAttribute clase puede ser usada por los autores de controles para especificar un prefijo
recomendado para asociar a un espacio de nombres XAML para el uso XAML.

Extensiones de marcado XAML


XAML incluye las extensiones de marcado para establecer atributos en los valores u objetos más allá de lo que se
puede expresar con cadenas simples. Estos incluyen que hacen referencia a constantes, campos y propiedades
estáticas, los diccionarios de recursos y enlaces de datos.

Modificadores de campo
El x:FieldModifier namespace (atributo) especifica el nivel de acceso para los campos generados para los
elementos XAML con nombre.

Paso de argumentos
XAML puede utilizarse para pasar argumentos a métodos de fábrica o constructores no predeterminados. En este
artículo muestra cómo utilizar los atributos XAML que se pueden usar para pasar argumentos a los constructores
para llamar a métodos de fábrica y para especificar el tipo de argumento genérico.

Propiedades enlazables
En Xamarin.Forms, la funcionalidad de las propiedades de common language runtime (CLR ) se extiende por las
propiedades enlazables. Una propiedad enlazable es un tipo especial de propiedad, donde el valor de propiedad se
realiza el seguimiento por el sistema de propiedades de Xamarin.Forms. Este artículo proporciona una
introducción a las propiedades enlazables y muestra cómo crear y consumirlos.

Propiedades asociadas
Una propiedad adjunta es un tipo especial de propiedad enlazable, definido en una clase pero conectado a otros
objetos y reconocible en XAML como un atributo que contiene una clase y un nombre de propiedad separados por
un punto. Este artículo proporciona una introducción a las propiedades adjuntas y muestra cómo crear y
consumirlos.

Diccionarios de recursos
Recursos XAML son definiciones de objetos que se pueden usar más de una vez. Un ResourceDictionary permite
que los recursos definidos en una sola ubicación y volver a utilizarse en toda una aplicación de Xamarin.Forms. En
este artículo se muestra cómo crear y consumir un ResourceDictionary y cómo combinar una ResourceDictionary
a otro.

Carga de XAML en tiempo de ejecución


XAML se puede cargar y analizar en tiempo de ejecución con el LoadFromXaml métodos de extensión.
Controles de XAML
12/07/2019 • 4 minutes to read • Edit Online

Descargar el ejemplo
Las vistas son objetos de interfaz de usuario, como etiquetas, botones y los controles deslizantes que se conocen
normalmente como controles o widgets en otros entornos de programación de gráficos. Las vistas compatibles con
Xamarin.Forms todos se derivan los View clase.
Todas las vistas que se definen en Xamarin.Forms pueden hacer referencia a partir de los archivos XAML.

Vistas de presentación

BoxView
<BoxView Color="Accent"
Muestra un rectángulo de un color determinado. WidthRequest="150"
HeightRequest="150"
HorizontalOptions="Center">

API / guía

Image
<Image Source="https://aka.ms/campus.jpg"
muestra un mapa de bits. Aspect="AspectFit"
HorizontalOptions="Center" />

API / guía

Etiqueta
<Label Text="Hello, Xamarin.Forms!"
Muestra una o varias líneas de texto. FontSize="Large"
FontAttributes="Italic"
HorizontalTextAlignment="Center" />

API / guía
Map
<maps:Map ItemsSource="{Binding Locations}" />
muestra un mapa.

API / guía

WebView
<WebView
Muestra las páginas Web o contenido HTML. Source="https://docs.microsoft.com/xamarin/"
VerticalOptions="FillAndExpand" />

API / guía

Vistas que inician comandos

Botón
<Button Text="Click Me!"
Muestra el texto en un objeto rectangular. Font="Large"
BorderWidth="1"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Clicked="OnButtonClicked" />

API / guía

ImageButton
<ImageButton Source="XamarinLogo.png"
Muestra una imagen de un objeto rectangular. HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Clicked="OnImageButtonClicked" />

API / guía

Barra de búsqueda
<SearchBar Placeholder="Xamarin.Forms Property"
Muestra una barra de búsqueda, para realizar una búsqueda.
SearchButtonPressed="OnSearchBarButtonPressed" />

API
Para establecer valores de las vistas

CheckBox
<CheckBox IsChecked="true"
Permite la selección de un boolean valor. HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

Guía

Slider
<Slider Minimum="0"
Permite la selección de un double valor desde un intervalo Maximum="100"
continuo. VerticalOptions="CenterAndExpand" />

API / guía

Control de incremento
<Stepper Minimum="0"
Permite la selección de un double valor a partir de un Maximum="10"
intervalo incremental. Increment="0.1"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

API / guía

Modificador
<Switch IsToggled="false"
Permite la selección de un boolean valor. HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

API / guía
DatePicker
<DatePicker Format="D"
Permite la selección de una fecha. VerticalOptions="CenterAndExpand" />

API / guía

TimePicker
<TimePicker Format="T"
Permite la selección de una hora. VerticalOptions="CenterAndExpand" />

API / guía

Vistas para editar texto

Entrada
<Entry Keyboard="Email"
Permite que una sola línea de texto que se especifique y se Placeholder="Enter email address"
puede editar. VerticalOptions="CenterAndExpand" />

API / guía
Editor
<Editor VerticalOptions="FillAndExpand" />
Permite varias líneas de texto que se especifique y se puede
editar.

API / guía

Vistas para indicar actividad

ActivityIndicator
<ActivityIndicator IsRunning="True"
Muestra una animación para mostrar que la aplicación esté
implicada en una actividad larga, sin dar ninguna indicación VerticalOptions="CenterAndExpand" />
del progreso.

API

ProgressBar
<ProgressBar Progress=".5"
Muestra una animación para mostrar que la aplicación está VerticalOptions="CenterAndExpand" />
progresando a través de una actividad larga.

API

Vistas que muestran colecciones

CollectionView
<CollectionView ItemsSource="{Binding Monkeys}">
muestra una lista desplazable de elementos de datos ItemTemplate="{StaticResource
seleccionable, utilizando las especificaciones de diseño MonkeyTemplate}"
diferente. <CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
</CollectionView/>

Guía
ListView
<ListView ItemsSource="{Binding Monkeys}">
Muestra una lista desplazable de elementos de datos ItemTemplate="{StaticResource
seleccionable. MonkeyTemplate}" />

API / guía

Selector
<Picker Title="Select a monkey"
Muestra un elemento select de una lista de cadenas de texto. TitleColor="Red">
<Picker.ItemsSource<
<x:Array Type="{x:Type x:String}">
<x:String>Baboon</x:String>
<x:String>Capuchin Monkey</x:String>
<x:String>Blue Monkey</x:String>
<x:String>Squirrel Monkey</x:String>
<x:String>Golden Lion Tamarin</x:String>
<x:String>Howler Monkey</x:String>
<x:String>Japanese Macaque</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>

API / guía

TableView
<TableView Intent="Settings">
Muestra una lista de filas interactivas. <TableRoot>
<TableSection Title="Ring">
<SwitchCell Text="New Voice Mail" />
<SwitchCell Text="New Mail" On="true"
/>
</TableSection>
</TableRoot>
API / guía </TableView>

Vínculos relacionados
Ejemplo de Xamarin.Forms FormsGallery
Xamarin.Forms Samples (Ejemplos de Xamarin.Forms)
Documentación de la API de Xamarin.Forms
Parte 1. Introducción a XAML
11/07/2019 • 31 minutes to read • Edit Online

descargar el ejemplo
En una aplicación de Xamarin.Forms, se usa principalmente para definir el contenido visual de una página XAML
y funciona junto con un C# archivo de código subyacente.
El archivo de código subyacente proporciona compatibilidad de código para el marcado. Juntos, estos dos archivos
contribuyen a una nueva definición de clase que incluye vistas secundarias e inicialización de la propiedad. En el
archivo XAML, se hace referencia a clases y propiedades con los elementos y atributos XML y se establecen
vínculos entre el código y marcado.

Crear la solución
Para comenzar a editar el primer archivo XAML, utilice Visual Studio o Visual Studio para Mac para crear una
nueva solución de Xamarin.Forms. (Seleccione la pestaña siguiente correspondiente a su entorno).
Visual Studio
Visual Studio para Mac
En Windows, use Visual Studio para seleccionar archivo > Nuevo > proyecto en el menú. En el nuevo
proyecto cuadro de diálogo, seleccione Visual C# > multiplataforma a la izquierda y, a continuación,
aplicación móvil (Xamarin.Forms) en la lista en el centro.

Seleccione una ubicación para la solución, asígnele un nombre de XamlSamples (o lo que prefiere) y presione
Aceptar.
En la siguiente pantalla, seleccione el aplicación vacía plantilla y la .NET Standard estrategia de uso compartido
de código:
Presione Aceptar.
Se crean cuatro proyectos en la solución: el XamlSamples biblioteca .NET Standard, XamlSamples.Android,
XamlSamples.iOSy la plataforma Universal de Windows solución de XamlSamples.UWP.
Después de crear el XamlSamples solución, desea probar el entorno de desarrollo seleccionando los diversos
proyectos de plataforma como proyecto de inicio de la solución y crear e implementar la aplicación simple crean
por la plantilla de proyecto en dispositivos reales o emuladores de teléfono.
A menos que necesite escribir código específico de plataforma, el recurso compartido XamlSamples proyecto de
biblioteca de .NET Standard es donde va a pasar prácticamente todo su tiempo de programación. Estos artículos
no se atreviesen fuera de ese proyecto.
Anatomía de un archivo XAML
Dentro de la XamlSamples biblioteca .NET Standard son un par de archivos con los nombres siguientes:
App.XAML, el archivo XAML; y
App.Xaml.cs, un C# código archivo asociado con el archivo XAML.
Necesitará hacer clic en la flecha situada junto a App.xaml para ver el archivo de código subyacente.
Ambos App.xaml y App.xaml.cs contribuyen a una clase denominada App que se deriva de Application . La
mayoría de otras clases con archivos XAML contribuyen a una clase que derive de ContentPage ; esos archivos
usan XAML para definir el contenido visual de una página completa. Es el caso de los dos archivos en el
XamlSamples proyecto:
MainPage.xaml, el archivo XAML; y
MainPage.xaml.cs, el C# archivo de código subyacente.
El MainPage.xaml archivo tiene este aspecto (aunque el formato puede ser un poco diferente):
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
x:Class="XamlSamples.MainPage">

<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin Forms!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>

</ContentPage>

El espacio de nombres XML dos ( xmlns ) declaraciones hagan referencia a los URI, la primera aparentemente en
el sitio web de Xamarin y la segunda en Microsoft. No se moleste en comprobar qué esos URI que quiera. No hay
nada. Son simplemente los URI que pertenecen a Xamarin y Microsoft, y básicamente funcionan como
identificadores de versión.
La primera declaración de espacio de nombres XML significa que las etiquetas definidas en el archivo XAML con
ningún prefijo hacen referencia a las clases de Xamarin.Forms, por ejemplo ContentPage . La segunda declaración
de espacio de nombres define un prefijo de x . Esto sirve para varios elementos y atributos que son intrínsecos de
XAML propio y que son compatibles con otras implementaciones de XAML. Sin embargo, estos elementos y
atributos son ligeramente diferentes según el año incrustado en el URI. Xamarin.Forms es compatible con la
especificación de XAML 2009, pero no todos del mismo.
El local declaración de espacio de nombres permite obtener acceso a otras clases desde el proyecto de biblioteca
.NET Standard.
Al final de la primera etiqueta, el x prefijo se utiliza para un atributo denominado Class . Dado que el uso de este
x prefijo es prácticamente universal para el espacio de nombres XAML, los atributos XAML, como Class casi
siempre se conocen como x:Class .
El x:Class atributo especifica un nombre de clase de .NET completo: el MainPage clase en el XamlSamples espacio
de nombres. Esto significa que este archivo XAML define una clase nueva denominada MainPage en el
XamlSamples espacio de nombres que se deriva de ContentPage : la etiqueta en el que el x:Class atributo aparece.

El x:Class atributo solo puede aparecer en el elemento raíz de un archivo XAML para definir una derivada C#
clase. Se trata de la clase solo nueva definida en el archivo XAML. Todo el contenido que aparece en el archivo
XAML está en su lugar simplemente crear instancias de clases existentes e inicializado.
El MainPage.xaml.cs archivo tiene este aspecto (aparte de sin usar using directivas):

using Xamarin.Forms;

namespace XamlSamples
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
}

El MainPage clase se deriva de ContentPage , pero tenga en cuenta el partial definición de clase. Esto sugiere que
debe haber otra definición de clase parcial para MainPage , pero ¿dónde está? Y ¿qué es eso InitializeComponent
método?
Cuando Visual Studio compila el proyecto, analiza el archivo XAML para generar un C# archivo de código. Si
observa la XamlSamples\XamlSamples\obj\Debug directory, encontrará un archivo denominado
XamlSamples.MainPage.xaml.g.cs. La "g" son las siglas de generadas. Se trata de la otra definición de clase
parcial de MainPage que contiene la definición de la InitializeComponent método se llama desde el MainPage
constructor. Estos dos parcial MainPage las definiciones de clase, a continuación, se pueden compilar juntas.
Dependiendo de si el XAML se compila o no, el archivo XAML o en un formato binario del archivo XAML se
incrusta en el archivo ejecutable.
En tiempo de ejecución de código en el proyecto de plataforma concreta llama a un LoadApplication método y
pasa a él una nueva instancia de la App class en la biblioteca .NET Standard. El App crea una instancia de
constructor de clase MainPage . El constructor de esa clase llama a InitializeComponent , que llama a la
LoadFromXaml método que se extrae el archivo XAML (o el binario compilado) de la biblioteca .NET Standard.
LoadFromXaml Inicializa todos los objetos definidos en el archivo XAML, se conectan todos juntos en las relaciones
de elementos primarios y secundarios, adjunta los controladores de eventos definidos en el código para los
eventos que se establece en el archivo XAML y establece el árbol resultante de objetos como el contenido de la
página.
Aunque normalmente no es necesario pasar mucho tiempo con archivos de código generado, en ocasiones, en
tiempo de ejecución se producen excepciones en código en los archivos generados, por lo que debe estar
familiarizado con ellas.
Al compilar y ejecutar este programa, el Label elemento aparece en el centro de la página, tal como sugiere el
XAML:

Para los objetos visuales más interesantes, todo lo que necesita es más interesante de XAML.

Agregar nuevas páginas XAML


Visual Studio
Visual Studio para Mac
Para agregar otros basada en XAML ContentPage clases al proyecto, seleccione el XamlSamples biblioteca
estándar de .NET del proyecto e invocar el proyecto > Agregar nuevo elemento elemento de menú. En la
izquierda de la Agregar nuevo elemento cuadro de diálogo, seleccione Visual C# y Xamarin.Forms. En la lista
seleccione página de contenido (no página de contenido (C#) , que crea una página de sólo código, o vista
contenido, que no es una página). Asigne un nombre, por ejemplo, de la página HelloXamlPage.xaml:
Se agregan dos archivos al proyecto, HelloXamlPage.xaml y el archivo de código subyacente
HelloXamlPage.xaml.cs.

Contenido de la página de configuración


Editar el HelloXamlPage.xaml archivo para que las etiquetas sola son aquellas de ContentPage y
ContentPage.Content :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.HelloXamlPage">
<ContentPage.Content>

</ContentPage.Content>
</ContentPage>

El ContentPage.Content etiquetas forman parte de la sintaxis de XAML única. En primer lugar, es posible que
parecen ser XML no válido, pero son válidas. El período no es un carácter especial en XML.
El ContentPage.Contentse denominan etiquetas property (elemento ) etiquetas. Content es una propiedad de
ContentPage y generalmente se establece en una vista única o un diseño con vistas secundarias. Normalmente las
propiedades se convierten en atributos en XAML, pero sería complicado establecer un Content atributo a un
objeto complejo. Por ese motivo, la propiedad se expresa como un elemento XML formada por el nombre de clase
y el nombre de propiedad separados por un punto. Ahora el Content propiedad puede establecerse entre el
ContentPage.Content etiquetas, similar al siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.HelloXamlPage"
Title="Hello XAML Page">
<ContentPage.Content>

<Label Text="Hello, XAML!"


VerticalOptions="Center"
HorizontalTextAlignment="Center"
Rotation="-15"
IsVisible="true"
FontSize="Large"
FontAttributes="Bold"
TextColor="Blue" />

</ContentPage.Content>
</ContentPage>

Tenga en cuenta también que un Title se ha establecido el atributo en la etiqueta raíz.


En este momento, la relación entre las clases, propiedades y XML debe ser evidente: Una clase de Xamarin.Forms
(como ContentPage o Label ) aparece en el archivo XAML como un elemento XML. Propiedades de esa clase,
incluidas Title en ContentPage y siete propiedades de Label , suelen aparecer como atributos XML.
Existen muchos métodos abreviados para establecer los valores de estas propiedades. Algunas propiedades son
tipos de datos básicos: Por ejemplo, el Title y Text propiedades son de tipo String , Rotation es de tipo
Double , y IsVisible (que es true de forma predeterminada y se establece aquí sólo con fines demostrativos) es
de tipo Boolean .
El HorizontalTextAlignment propiedad es de tipo TextAlignment , que es una enumeración. Para una propiedad de
cualquier tipo de enumeración, todo lo que necesita la fuente de alimentación es un nombre de miembro.
Sin embargo, para las propiedades de tipos más complejos, los convertidores de tipos se utilizan para analizar el
XAML. Estas son las clases de Xamarin.Forms que se derivan de TypeConverter . Muchos son las clases públicas,
pero otras no. Para este archivo XAML en particular, varias de estas clases desempeñan un papel en segundo
plano:
LayoutOptionsConverter para el VerticalOptions propiedad
FontSizeConverter para el FontSize propiedad
ColorTypeConverter para el TextColor propiedad

Estos convertidores rigen la sintaxis permitida de los valores de propiedad.


El ThicknessTypeConverter puede controlar uno, dos o cuatro números separados por comas. Si se proporciona un
número, se aplica a los cuatro lados. Con dos números, el primero es left y right relleno y el segundo es la parte
superior e inferior. Son cuatro números en el orden izquierda, superior, derecha e inferior.
El LayoutOptionsConverter puede convertir los nombres de campos estáticos públicos de la LayoutOptions a
valores de tipo LayoutOptions .
El FontSizeConverter puede controlar un NamedSize miembro o un tamaño de fuente numérico.
El ColorTypeConverter acepta los nombres de campos estáticos públicos de la Color estructura o los valores RGB
hexadecimales, con o sin un canal alfa, precedido por un signo de número (#). Esta es la sintaxis sin un canal alfa:
TextColor="#rrggbb"

Cada una de las letras poco es un dígito hexadecimal. Aquí es cómo se incluye un canal alfa:
TextColor="#aarrggbb">

Para el canal alfa, tenga en cuenta que es completamente opaco FF y 00 es completamente transparente.
Otros dos formatos le permiten especificar un único dígito hexadecimal para cada canal:
TextColor="#rgb" TextColor="#argb"

En estos casos, el dígito se repite para formar el valor. Por ejemplo, #CF3 es el color RGB CC -FF -33.

Navegación de páginas
Al ejecutar el XamlSamples programa, el MainPage se muestra. Para ver el nuevo HelloXamlPage puede
establecer que, como el inicio de la nueva página en el App.xaml.cs de archivos o vaya a la nueva página desde
MainPage .

Para implementar la navegación, cambie primero el código de la App.xaml.cs constructor para que un
NavigationPage se crea el objeto:
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}

En el MainPage.xaml.cs constructor, puede crear una sencilla Button y usar el controlador de eventos para
navegar a HelloXamlPage :

public MainPage()
{
InitializeComponent();

Button button = new Button


{
Text = "Navigate!",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
};

button.Clicked += async (sender, args) =>


{
await Navigation.PushAsync(new HelloXamlPage());
};

Content = button;
}

Establecer el Content propiedad de la página reemplaza la configuración de la Content propiedad en el archivo


XAML. Al compilar e implementar la nueva versión de este programa, aparece un botón en la pantalla. Al
presionar navega a HelloXamlPage . Esta es la página resultante en el iPhone, Android y UWP:

Puede navegar hasta MainPage utilizando el < Atrás botón en iOS, mediante la flecha izquierda en la parte
superior de la página o en la parte inferior del teléfono en Android, o con la flecha izquierda en la parte superior
de la página en Windows 10.
No dude en experimentar con el XAML para las distintas formas representar el Label . Si tiene que insertar
caracteres Unicode en el texto, puede usar la sintaxis XML estándar. Por ejemplo, para poner el saludo en las
comillas tipográficas, use:
<Label Text="&#x201C;Hello, XAML!&#x201D;" … />
Este es su aspecto:

Interacciones de código y XAML


El HelloXamlPage ejemplo contiene una sola Label en la página, pero esto es muy poco común. La mayoría
ContentPage conjunto derivados del Content ordenar propiedad a un diseño de algunos, como un StackLayout .
El Children propiedad de la StackLayout se define como de tipo IList<View> pero es realmente un objeto de
tipo ElementCollection<View> , y que se puede rellenar la colección con varias vistas u otros esquemas. En XAML,
estas relaciones de elementos primarios y secundarios se establecen con la jerarquía XML normal. Este es un
archivo XAML para una nueva página denominada XamlPlusCodePage:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.XamlPlusCodePage"
Title="XAML + Code Page">
<StackLayout>
<Slider VerticalOptions="CenterAndExpand" />

<Label Text="A simple Label"


Font="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

<Button Text="Click Me!"


HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

Este archivo XAML es sintácticamente completa, y este es su aspecto:


Sin embargo, es probable que observe este programa sea funcionalmente deficientes. Quizás el Slider se supone
que debe hacer que el Label para mostrar el valor actual y el Button probablemente está pensado para hacer
algo dentro del programa.
Como verá en parte 4. Conceptos básicos del enlace de datos, el trabajo de mostrar un Slider valor mediante un
Label puede controlarse completamente en XAML con un enlace de datos. Pero resulta útil ver primero la
solución de código. Aun así, controlar la Button clic definitivamente requiere código. Esto significa que el archivo
de código subyacente para XamlPlusCodePage debe contener controladores para la ValueChanged eventos de la
Slider y Clicked eventos de la Button . Vamos a agregarlos:

namespace XamlSamples
{
public partial class XamlPlusCodePage
{
public XamlPlusCodePage()
{
InitializeComponent();
}

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)


{

void OnButtonClicked(object sender, EventArgs args)


{

}
}
}

Estos controladores de eventos no es necesario ser público.


En el archivo XAML, el Slider y Button etiquetas deben incluir los atributos para el ValueChanged y Clicked
eventos que hacen referencia a estos controladores:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.XamlPlusCodePage"
Title="XAML + Code Page">
<StackLayout>
<Slider VerticalOptions="CenterAndExpand"
ValueChanged="OnSliderValueChanged" />

<Label Text="A simple Label"


Font="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

<Button Text="Click Me!"


HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Clicked="OnButtonClicked" />
</StackLayout>
</ContentPage>

Tenga en cuenta que la asignación de un controlador a un evento tiene la misma sintaxis que asignar un valor a
una propiedad.
Si el controlador para el ValueChanged eventos de la Slider va a utilizar el Label para mostrar el valor actual, el
controlador necesita hacer referencia a ese objeto desde el código. El Label necesita un nombre, que se especifica
con el x:Name atributo.

<Label x:Name="valueLabel"
Text="A simple Label"
Font="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

El x prefijo de la x:Name atributo indica que este atributo es intrínseco a XAML.


El nombre que asigne a la x:Name atributo tiene las mismas reglas que C# los nombres de variable. Por ejemplo,
debe comenzar con una letra o un carácter de subrayado y no contener espacios incrustados.
Ahora el ValueChanged controlador de eventos puede establecer el Label para mostrar el nuevo Slider valor. El
nuevo valor está disponible en los argumentos de evento:

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)


{
valueLabel.Text = args.NewValue.ToString("F3");
}

O bien, podría obtener el controlador de la Slider objeto que genera este evento desde el sender argumento y
obtener el Value propiedad desde que:

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)


{
valueLabel.Text = ((Slider)sender).Value.ToString("F3");
}

Al ejecutar el programa, en primer lugar el Label no muestra el Slider valor porque el ValueChanged aún no ha
desencadenado el evento. Pero cualquier manipulación de los Slider hace que el valor que se mostrará:
Ahora el Button . Vamos a simular una respuesta a un Clicked eventos mediante una alerta con el Text del
botón. El controlador de eventos puede difundir con seguridad el sender argumento para un Button y, a
continuación, obtener acceso a sus propiedades:

async void OnButtonClicked(object sender, EventArgs args)


{
Button button = (Button)sender;
await DisplayAlert("Clicked!",
"The button labeled '" + button.Text + "' has been clicked",
"OK");
}

El método se define como async porque el DisplayAlert método es asincrónico y debe ir precedido por el await
operador, que se devuelve cuando se completa el método. Dado que este método obtiene la Button
desencadenando el evento desde el sender argumento, el mismo controlador podría usarse para varios botones.
Ha visto que un objeto definido en XAML puede desencadenar un evento que se controla en el archivo de código
subyacente y que el archivo de código subyacente puede tener acceso a un objeto definido en XAML con el
nombre asignado a él con el x:Name atributo. Estas son las dos maneras fundamentales que interactúan de código
y XAML.
Algunas características adicionales sobre cómo funciona el XAML se puede deducir examinando recién generado
XamlPlusCode.xaml.g.cs archivo, que ahora incluye cualquier nombre asignado a cualquier x:Name atributo
como un campo privado. Esta es una versión simplificada de ese archivo:

public partial class XamlPlusCodePage : ContentPage {

private Label valueLabel;

private void InitializeComponent() {


this.LoadFromXaml(typeof(XamlPlusCodePage));
valueLabel = this.FindByName<Label>("valueLabel");
}
}

La declaración de este campo permite que la variable se usan libremente en cualquier lugar dentro de la
XamlPlusCodePage archivo de clase parcial en su jurisdicción. En tiempo de ejecución, el campo se asigna después
de que se ha analizado el XAML. Esto significa que el valueLabel campo es null cuando el XamlPlusCodePage
constructor comienza válido, pero después InitializeComponent se llama.
Después de InitializeComponent devuelve el control vuelve al constructor, se han construido los objetos visuales
de la página como si se tenía inicializados en el código y crea una instancia. El archivo XAML ya no desempeña
ningún rol en la clase. Puede manipular estos objetos en la página de cualquier manera que desee, por ejemplo,
mediante la adición de vistas para la StackLayout , o la configuración de la Content propiedad de la página a otra
cosa completamente. Se puede "recorrer el árbol" examinando el Content propiedad de la página y los elementos
de la Children colecciones de diseños. Puede establecer las propiedades de las vistas que se tiene acceso de esta
forma, o controladores de eventos se les asignan dinámicamente.
No dude. Es la página y XAML es sólo una herramienta para crear su contenido.

Resumen
Con esta introducción, ha visto cómo contribuir con un archivo XAML y el archivo de código para una definición
de clase, y cómo interactúan los archivos XAML y código. Pero XAML también tiene sus propias características
sintácticas únicas que le permiten que se usará de forma muy flexible. Puede empezar a explorar estos en parte 2.
Sintaxis XAML esencial.

Vínculos relacionados
XamlSamples
Parte 2. Sintaxis XAML esencial
Parte 3. Extensiones de marcado XAML
Parte 4. Conceptos básicos del enlace de datos
Parte 5. Desde el enlace de datos a MVVM
Parte 2. Sintaxis XAML esencial
11/07/2019 • 18 minutes to read • Edit Online

descargar el ejemplo
XAML está diseñado principalmente para crear instancias e inicializar objetos. Pero a menudo, se deben
establecer propiedades en objetos complejos que no pueden representarse fácilmente como cadenas XML y a
veces se deben establecer las propiedades definidas por una clase en una clase secundaria. Estas dos necesidades
requieren las características de sintaxis XAML esencial de los elementos de propiedad y las propiedades adjuntas.

Elementos de propiedad
En XAML, las propiedades de las clases normalmente se establecen como atributos XML:

<Label Text="Hello, XAML!"


VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large"
TextColor="Aqua" />

Sin embargo, hay una manera alternativa de establecer una propiedad en XAML. Para probar esta alternativa con
TextColor , primero elimine existente TextColor configuración:

<Label Text="Hello, XAML!"


VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large" />

Abra el elemento vacío Label etiqueta separando en etiquetas de inicio y finalización:

<Label Text="Hello, XAML!"


VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">

</Label>

Dentro de estas etiquetas, agregue las etiquetas inicial y final que se componen de nombre de clase y un nombre
de propiedad separados por un período de:

<Label Text="Hello, XAML!"


VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
<Label.TextColor>

</Label.TextColor>
</Label>

Establezca el valor de propiedad como contenido de estas nuevas etiquetas, similar al siguiente:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
<Label.TextColor>
Aqua
</Label.TextColor>
</Label>

Estas dos maneras de especificar el TextColor propiedad son funcionalmente equivalentes, pero no use las dos
maneras para la misma propiedad ya que podría ser establecer de forma efectiva la propiedad dos veces y podría
ser ambiguo.
Con esta nueva sintaxis, se puede introducir cierta terminología útil:
Label es un elemento object. Es un objeto Xamarin.Forms expresado como un elemento XML.
Text , VerticalOptions , FontAttributes y FontSize son atributos de la propiedad. Son propiedades de
Xamarin.Forms expresadas como atributos XML.
En ese fragmento final, TextColor se ha convertido en un elemento property. Es una propiedad de
Xamarin.Forms, pero ahora es un elemento XML.
En primer lugar parecer la definición de propiedad, es posible que los elementos en sean una infracción de la
sintaxis XML, pero no lo es. El período no tiene ningún significado especial en XML. Para un descodificador de
XML, Label.TextColor es simplemente un elemento secundario normal.
En XAML, sin embargo, esta sintaxis es muy especial. Una de las reglas de los elementos de propiedad es que
nada puede aparecer en la Label.TextColor etiqueta. El valor de la propiedad siempre se define como contenido
entre el elemento de propiedad etiquetas inicial y final.
Puede usar la sintaxis de elemento de propiedad en más de una propiedad:

<Label Text="Hello, XAML!"


VerticalOptions="Center">
<Label.FontAttributes>
Bold
</Label.FontAttributes>
<Label.FontSize>
Large
</Label.FontSize>
<Label.TextColor>
Aqua
</Label.TextColor>
</Label>

O bien, puede usar sintaxis de elemento de propiedad para todas las propiedades:
<Label>
<Label.Text>
Hello, XAML!
</Label.Text>
<Label.FontAttributes>
Bold
</Label.FontAttributes>
<Label.FontSize>
Large
</Label.FontSize>
<Label.TextColor>
Aqua
</Label.TextColor>
<Label.VerticalOptions>
Center
</Label.VerticalOptions>
</Label>

En primer lugar, sintaxis de elemento de propiedad pueden parecer un reemplazo extiende innecesario para algo
comparativamente bastante simple y, en estos ejemplos que es ciertamente el caso.
Sin embargo, la sintaxis de elemento de propiedad es de importancia fundamental cuando el valor de una
propiedad es demasiado complejo para expresarse como una cadena simple. Dentro de las etiquetas de elemento
de propiedad puede crear una instancia de otro objeto y establecer sus propiedades. Por ejemplo, se puede
establecer explícitamente una propiedad como VerticalOptions a un LayoutOptions valor con valores de
propiedad:

<Label>
...
<Label.VerticalOptions>
<LayoutOptions Alignment="Center" />
</Label.VerticalOptions>
</Label>

Otro ejemplo: El Grid tiene dos propiedades denominadas RowDefinitions y ColumnDefinitions . Estas dos
propiedades son de tipo RowDefinitionCollection y ColumnDefinitionCollection , que son colecciones de
RowDefinition y ColumnDefinition objetos. Deberá utilizar la sintaxis de elemento de propiedad para establecer
estas colecciones.
Este es el comienzo del archivo XAML para un GridDemoPage (clase), que muestra las etiquetas de elemento de
propiedad para el RowDefinitions y ColumnDefinitions colecciones:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.GridDemoPage"
Title="Grid Demo Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
...
</Grid>
</ContentPage>

Tenga en cuenta la sintaxis abreviada para la definición de celdas de tamaño automático, las de ancho de píxel y el
alto y configuración de estrella.

Propiedades asociadas
Ya ha visto que la Grid requiere que los elementos de propiedad para el RowDefinitions y ColumnDefinitions
recopilaciones para definir las filas y columnas. Sin embargo, también debe haber alguna manera para el
programador indicar la fila y columna donde cada miembro secundario de la Grid reside.
Dentro de la etiqueta para cada miembro secundario de la Grid especificar la fila y columna de ese elemento
secundario con los siguientes atributos:
Grid.Row
Grid.Column

Los valores predeterminados de estos atributos son 0. También puede indicar si un elemento secundario abarca
más de una fila o columna con estos atributos:
Grid.RowSpan
Grid.ColumnSpan

Estos dos atributos tienen valores predeterminados de 1.


Este es el archivo GridDemoPage.xaml completo:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.GridDemoPage"
Title="Grid Demo Page">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>

<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>

<Label Text="Autosized cell"


Grid.Row="0" Grid.Column="0"
TextColor="White"
BackgroundColor="Blue" />

<BoxView Color="Silver"
HeightRequest="0"
Grid.Row="0" Grid.Column="1" />

<BoxView Color="Teal"
Grid.Row="1" Grid.Column="0" />

<Label Text="Leftover space"


Grid.Row="1" Grid.Column="1"
TextColor="Purple"
BackgroundColor="Aqua"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />

<Label Text="Span two rows (or more if you want)"


Grid.Row="0" Grid.Column="2" Grid.RowSpan="2"
TextColor="Yellow"
BackgroundColor="Blue"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />

<Label Text="Span two columns"


Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
TextColor="Blue"
BackgroundColor="Yellow"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />

<Label Text="Fixed 100x100"


Grid.Row="2" Grid.Column="2"
TextColor="Aqua"
BackgroundColor="Red"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />

</Grid>
</ContentPage>

El Grid.Row y Grid.Column configuración 0 no son necesarias, pero se incluyen con carácter general para una
mayor claridad.
Este es su aspecto:
A juzgar únicamente por la sintaxis, estos Grid.Row , Grid.Column , Grid.RowSpan , y Grid.ColumnSpan atributos
aparecen como campos estáticos ni propiedades de Grid , pero, curiosamente, Grid no define nada con el
nombre Row , Column , RowSpan , o ColumnSpan .
En su lugar, Griddefine cuatro propiedades enlazables denominadas RowProperty , ColumnProperty ,
RowSpanProperty , y ColumnSpanProperty . Estos son tipos especiales de propiedades enlazables conocidos como
propiedades adjuntas. Que define el Grid clase pero establece en elementos secundarios de la Grid .
Cuando se desea usarlas las propiedades adjuntas de código, el Grid clase proporciona métodos estáticos
denominados SetRow , GetColumn , y así sucesivamente. Pero en XAML, estas propiedades adjuntas se establecen
como atributos en los elementos secundarios de la Grid mediante nombres de propiedades simples.
Las propiedades adjuntas siempre son reconocibles en archivos XAML como atributos que contiene una clase y
un nombre de propiedad separados por un punto. Se denominan propiedades adjuntas porque ya están definidas
por una clase (en este caso, Grid ) pero conectado a otros objetos (en este caso, los elementos secundarios de la
Grid ). Durante el diseño, el Grid puede consultar los valores de estas propiedades adjuntas para saber dónde
colocar cada elemento secundario.
El AbsoluteLayout clase define dos propiedades adjuntas denominadas LayoutBounds y LayoutFlags . Este es un
patrón de tablero logrado usando el posicionamiento proporcional y las características de ajuste de tamaño de
AbsoluteLayout :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.AbsoluteDemoPage"
Title="Absolute Demo Page">

<AbsoluteLayout BackgroundColor="#FF8080">
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.33, 0, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />

<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="1, 0, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />

<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0, 0.33, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />

<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.67, 0.33, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />

<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.33, 0.67, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />

<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="1, 0.67, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />

<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0, 1, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />

<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.67, 1, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />

</AbsoluteLayout>
</ContentPage>

Y aquí es:
Para algo parecido a esto, podría preguntarse la conveniencia de usar de XAML. Sin duda, la repetición y la
regularidad del LayoutBounds rectángulo sugiere que se podría generarse mejor en código.
Sin duda es una preocupación legítima y no hay ningún problema con el uso de código y marcado de equilibrio al
definir las interfaces de usuario. Es fácil definir algunos de los objetos visuales en XAML y, a continuación, utilice
el constructor del archivo de código subyacente para agregar algunos elementos visuales más que podrían
generar mejor en bucles.

Propiedades de contenido
En los ejemplos anteriores, el StackLayout , Grid , y AbsoluteLayout objetos se establecen en el Content
propiedad de la ContentPage , y los elementos secundarios de estos diseños son realmente los elementos de la
Children colección. Aunque estos Content y Children propiedades son ningún destino en el archivo XAML.

Sin duda puede incluir el Content y Children propiedades como elementos de propiedad, como en el
XamlPlusCode ejemplo:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.XamlPlusCodePage"
Title="XAML + Code Page">
<ContentPage.Content>
<StackLayout>
<StackLayout.Children>
<Slider VerticalOptions="CenterAndExpand"
ValueChanged="OnSliderValueChanged" />

<Label x:Name="valueLabel"
Text="A simple Label"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

<Button Text="Click Me!"


HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Clicked="OnButtonClicked" />
</StackLayout.Children>
</StackLayout>
</ContentPage.Content>
</ContentPage>

La verdadera pregunta es: ¿Por qué estos elementos de propiedad no necesarios en el archivo XAML?
Los elementos definidos en Xamarin.Forms para su uso en XAML pueden tener una propiedad de marca en el
ContentProperty atributo de la clase. Si busca la ContentPage clase en la documentación de Xamarin.Forms en
línea, verá que este atributo:

[Xamarin.Forms.ContentProperty("Content")]
public class ContentPage : TemplatedPage

Esto significa que el Content no son necesarias las etiquetas de elemento de propiedad. Cualquier contenido
XML que aparece entre el inicio y finalización ContentPage etiquetas se supone que se asignará a la Content
propiedad.
StackLayout, Grid , AbsoluteLayout , y RelativeLayout todos se derivan Layout<View> , y si busca Layout<T> en
la documentación de Xamarin.Forms, verá otro ContentProperty atributo:

[Xamarin.Forms.ContentProperty("Children")]
public abstract class Layout<T> : Layout ...

Que permite que el contenido del diseño se agregan automáticamente a la Children colección sin explícita
Children etiquetas de elemento de propiedad.

También tienen otras clases ContentProperty definiciones de atributo. Por ejemplo, la propiedad content de
Label es Text . Consulte la documentación de API para que otros usuarios.

Diferencias entre las plataformas con OnPlatform


En las aplicaciones de página única, es común para establecer el Padding propiedad en la página para evitar
sobrescribir la barra de estado de iOS. En el código, puede usar el Device.RuntimePlatform propiedad para este
propósito:
if (Device.RuntimePlatform == Device.iOS)
{
Padding = new Thickness(0, 20, 0, 0);
}

También puede hacer algo similar en XAML usando el OnPlatform y On clases. En primer lugar, incluya los
elementos de propiedad para el Padding propiedad cerca de la parte superior de la página:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">

<ContentPage.Padding>

</ContentPage.Padding>
...
</ContentPage>

Dentro de estas etiquetas, incluyen una OnPlatform etiqueta. OnPlatform es una clase genérica. Debe especificar
el argumento de tipo genérico, en este caso, Thickness , que es el tipo de Padding propiedad. Afortunadamente,
hay un atributo XAML específicamente para definir argumentos genéricos llama x:TypeArguments . Debe coincidir
con el tipo de la propiedad que está configurando:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">

<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">

</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>

OnPlatform tiene una propiedad denominada Platforms que es un IList de On objetos. Use las etiquetas de
elemento de propiedad para la propiedad:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">

<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<OnPlatform.Platforms>

</OnPlatform.Platforms>
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>

Ahora agregue On elementos. Para cada uno de ellos, establezca el Platform propiedad y el Value propiedad al
código de marcado para el Thickness propiedad:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">

<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<OnPlatform.Platforms>
<On Platform="iOS" Value="0, 20, 0, 0" />
<On Platform="Android" Value="0, 0, 0, 0" />
<On Platform="UWP" Value="0, 0, 0, 0" />
</OnPlatform.Platforms>
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>

Se puede simplificar este marcado. La propiedad content de OnPlatform es Platforms , por lo que se pueden
quitar esas etiquetas de elemento de propiedad:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">

<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
<On Platform="Android" Value="0, 0, 0, 0" />
<On Platform="UWP" Value="0, 0, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>

El Platform propiedad de On es de tipo IList<string> , por lo que puede incluir varias plataformas, si los valores
son iguales:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">

<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
<On Platform="Android, UWP" Value="0, 0, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>

Dado que Android y UWP se establecen en el valor predeterminado de Padding , que se puede quitar la etiqueta:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">

<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>

Esta es la manera estándar para establecer un depende de la plataforma Padding propiedad en XAML. Si el
Value configuración no puede representarse mediante una sola cadena, puede definir los elementos de
propiedad para él:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">

<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS">
<On.Value>
0, 20, 0, 0
</On.Value>
</On>
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>

NOTE
El OnPlatform extensión de marcado también puede utilizarse en XAML para personalizar la apariencia de la interfaz de
usuario en forma de acuerdo con la plataforma. Proporciona la misma funcionalidad que el OnPlatform y On clases, pero
con una representación más concisa. Para obtener más información, consulte OnPlatform Markup Extension.

Resumen
Con los elementos de propiedad y las propiedades adjuntas, gran parte de la sintaxis XAML básica se ha
establecido. Sin embargo, a veces, deberá establecer propiedades a los objetos de una manera indirecta, por
ejemplo, en un diccionario de recursos. Este enfoque se trata en la siguiente parte, parte 3. Las extensiones de
marcado XAML.

Vínculos relacionados
XamlSamples
Parte 1. Introducción a XAML
Parte 3. Extensiones de marcado XAML
Parte 4. Conceptos básicos del enlace de datos
Parte 5. Desde el enlace de datos a MVVM
Parte 3. Extensiones de marcado XAML
11/07/2019 • 22 minutes to read • Edit Online

descargar el ejemplo
Las extensiones de marcado XAML constituyen una característica importante de XAML que permiten establecer
objetos o valores que se hace referencia indirectamente desde otros orígenes de propiedades. Las extensiones de
marcado XAML son especialmente importantes para compartir objetos y hacer referencia a las constantes que
se utilizan en toda una aplicación, pero encuentra su mayor utilidad los enlaces de datos.

Extensiones de marcado XAML


En general, usa XAML para establecer las propiedades de un objeto a los valores explícitos, como una cadena, un
número, un miembro de enumeración o una cadena que se convierte en un valor en segundo plano.
Sin embargo, en ocasiones, las propiedades en su lugar, deben hacer referencia a valores definidos en alguna
parte de lo contrario, o que pueden requerir un procesamiento poco por código en tiempo de ejecución. Para
estos fines, XAML las extensiones de marcado están disponibles.
Estas extensiones de marcado XAML no son extensiones de XML. XAML es XML completamente válido. Se
denominan "extensions" porque están respaldadas por el código en las clases que implementan
IMarkupExtension . Puede escribir sus propias extensiones de marcado personalizada.

En muchos casos, las extensiones de marcado XAML son reconocibles al instante en archivos XAML porque
aparecen como valores de atributo delimitados por llaves: {y}, pero a veces aparecen las extensiones de marcado
en el marcado como elementos convencionales.

Recursos compartidos
Algunas páginas XAML contienen varias vistas con propiedades establecidas en los mismos valores. Por
ejemplo, muchos de los valores de propiedad para estos Button objetos son iguales:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
Title="Shared Resources Page">

<StackLayout>
<Button Text="Do this!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />

<Button Text="Do that!"


HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />

<Button Text="Do the other thing!"


HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />

</StackLayout>
</ContentPage>

Si una de estas propiedades debe cambiarse, es preferible realizar el cambio de una sola vez en lugar de tres
veces. Si se tratara de código, que es probable que va a utilizar constantes y los objetos de solo lectura estáticos
para ayudar a mantener tales valores coherentes y fáciles de modificar.
En XAML, una conocida solución consiste en almacenar estos valores u objetos en un diccionario de recursos. El
VisualElement clase define una propiedad denominada Resources typu ResourceDictionary , que es un
diccionario con claves de tipo string y valores de tipo object . Puede colocar objetos en este diccionario y, a
continuación, hacer referencia a ellos desde el marcado, todo ello en XAML.
Para usar un diccionario de recursos en una página, incluya un par de Resources etiquetas de elemento de
propiedad. Resulta más conveniente colocar estos elementos en la parte superior de la página:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
Title="Shared Resources Page">

<ContentPage.Resources>

</ContentPage.Resources>
...
</ContentPage>

También es necesario incluir explícitamente ResourceDictionary etiquetas:


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
Title="Shared Resources Page">

<ContentPage.Resources>
<ResourceDictionary>

</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>

Ahora se pueden agregar objetos y valores de distintos tipos al diccionario de recursos. Estos tipos deben ser
instanciables. No pueden ser clases abstractas, por ejemplo. Estos tipos también deben tener un constructor
público sin parámetros. Cada elemento requiere una clave del diccionario especificada con el x:Key atributo. Por
ejemplo:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
Title="Shared Resources Page">

<ContentPage.Resources>
<ResourceDictionary>
<LayoutOptions x:Key="horzOptions"
Alignment="Center" />

<LayoutOptions x:Key="vertOptions"
Alignment="Center"
Expands="True" />
</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>

Estos dos elementos son valores de tipo de estructura LayoutOptions y cada una tiene una clave única y una o
dos propiedades establecidas. En el código y marcado, es mucho más habitual utilizar los campos estáticos de
LayoutOptions , pero aquí es más conveniente establecer las propiedades.

Ahora es necesario establecer la HorizontalOptions y VerticalOptions propiedades de estos botones para estos
recursos, y que se realiza con el StaticResource extensión de marcado XAML:

<Button Text="Do this!"


HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />

El StaticResource extensión de marcado siempre está delimitada por llaves e incluye la clave del diccionario.
El nombre lo distingue de DynamicResource , que también admite Xamarin.Forms.
StaticResource
DynamicResource es para las claves del diccionario asociadas con los valores que podrían cambiar en tiempo de
ejecución, mientras que StaticResource tiene acceso a los elementos del diccionario de solo una vez cuando se
construyen los elementos en la página.
Para el BorderWidth propiedad, es necesario almacenar un valor double en el diccionario. XAML define
convenientemente etiquetas para los tipos de datos comunes, como x:Double y x:Int32 :

<ContentPage.Resources>
<ResourceDictionary>
<LayoutOptions x:Key="horzOptions"
Alignment="Center" />

<LayoutOptions x:Key="vertOptions"
Alignment="Center"
Expands="True" />

<x:Double x:Key="borderWidth">
3
</x:Double>
</ResourceDictionary>
</ContentPage.Resources>

No es necesario ponerlo en tres líneas. Esta entrada del diccionario para este ángulo de giro solo toma una línea
hacia arriba:

<ContentPage.Resources>
<ResourceDictionary>
<LayoutOptions x:Key="horzOptions"
Alignment="Center" />

<LayoutOptions x:Key="vertOptions"
Alignment="Center"
Expands="True" />

<x:Double x:Key="borderWidth">
3
</x:Double>

<x:Double x:Key="rotationAngle">-15</x:Double>
</ResourceDictionary>
</ContentPage.Resources>

Se pueden hacer referencia a esos dos recursos en la misma manera que el LayoutOptions valores:

<Button Text="Do this!"


HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="Red"
FontSize="24" />

Para los recursos de tipo Color , puede usar las mismas representaciones de cadena que utilizar al asignar
directamente los atributos de estos tipos. Los convertidores de tipos se invocan cuando se crea el recurso. Este
es un recurso de tipo Color :

<Color x:Key="textColor">Red</Color>

Conjunto de programas a menudo, un FontSize propiedad a un miembro de la NamedSize enumeración como


Large . El FontSizeConverter clase funciona en segundo plano para convertirlo en un valor depende de la
plataforma mediante el Device.GetNamedSized método. Sin embargo, al definir un recurso de tamaño de fuente,
tiene más sentido usar un valor numérico, se muestra aquí como una x:Double tipo:
<x:Double x:Key="fontSize">24</x:Double>

Ahora todas las propiedades excepto Text definida según la configuración de recursos:

<Button Text="Do this!"


HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />

También es posible usar OnPlatform dentro del diccionario de recursos para definir valores diferentes para las
plataformas. Le mostramos cómo un OnPlatform objeto puede ser parte del diccionario de recursos para los
colores de texto diferente:

<OnPlatform x:Key="textColor"
x:TypeArguments="Color">
<On Platform="iOS" Value="Red" />
<On Platform="Android" Value="Aqua" />
<On Platform="UWP" Value="#80FF80" />
</OnPlatform>

Tenga en cuenta que OnPlatform obtiene tanto un x:Key porque es un objeto en el diccionario de atributos y un
x:TypeArguments atributo porque es una clase genérica. El iOS , Android , y UWP atributos se convierten en
Color valores cuando se inicializa el objeto.

Este es el archivo XAML completo final con tres botones de acceso a seis valores compartidos:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
Title="Shared Resources Page">

<ContentPage.Resources>
<ResourceDictionary>
<LayoutOptions x:Key="horzOptions"
Alignment="Center" />

<LayoutOptions x:Key="vertOptions"
Alignment="Center"
Expands="True" />

<x:Double x:Key="borderWidth">3</x:Double>

<x:Double x:Key="rotationAngle">-15</x:Double>

<OnPlatform x:Key="textColor"
x:TypeArguments="Color">
<On Platform="iOS" Value="Red" />
<On Platform="Android" Value="Aqua" />
<On Platform="UWP" Value="#80FF80" />
</OnPlatform>

<x:Double x:Key="fontSize">24</x:Double>
</ResourceDictionary>
</ContentPage.Resources>

<StackLayout>
<Button Text="Do this!"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />

<Button Text="Do that!"


HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />

<Button Text="Do the other thing!"


HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />

</StackLayout>
</ContentPage>

Las capturas de pantalla comprobación un estilo coherente y el estilo depende de la plataforma:


Aunque es más común para definir el Resources colección en la parte superior de la página, tenga en cuenta que
el Resources propiedad se define mediante VisualElement , y puede tener Resources colecciones en otros
elementos en la página. Por ejemplo, pruebe a agregar uno para el StackLayout en este ejemplo:

<StackLayout>
<StackLayout.Resources>
<ResourceDictionary>
<Color x:Key="textColor">Blue</Color>
</ResourceDictionary>
</StackLayout.Resources>
...
</StackLayout>

Descubrirá que el color del texto de los botones ahora es azul. Básicamente, cada vez que el analizador de XAML
encuentra un StaticResource extensión de marcado, se busca en el árbol visual y se usa la primera
ResourceDictionary encuentra que contiene esa clave.

Uno de los tipos más comunes de los objetos almacenados en diccionarios de recursos es Xamarin.Forms Style
, que define una colección de valores de propiedad. Los estilos se tratan en el artículo estilos.
Los programadores nuevos en XAML se preguntan si puede colocar un elemento visual como Label o Button
en un ResourceDictionary . Si bien es seguramente posible, no tiene mucho sentido. El propósito de la
ResourceDictionary es compartir objetos. No se puede compartir un elemento visual. La misma instancia no
puede aparecer dos veces en una sola página.

La extensión de marcado x: Static


A pesar de las similitudes de sus nombres, x:Static y StaticResource son muy diferentes. StaticResource
Devuelve un objeto de un diccionario de recursos mientras x:Static tiene acceso a uno de los siguientes:
un campo estático público
una propiedad estática pública
un campo constante público
un miembro de enumeración.
El StaticResource extensión de marcado es compatible con las implementaciones de XAML que definen un
diccionario de recursos, mientras que x:Static es una parte intrínseca de XAML, como el x recolectarse de
prefijo.
Estos son algunos ejemplos que muestran cómo x:Static puede hacer referencia explícitamente los campos
estáticos y los miembros de enumeración:

<Label Text="Hello, XAML!"


VerticalOptions="{x:Static LayoutOptions.Start}"
HorizontalTextAlignment="{x:Static TextAlignment.Center}"
TextColor="{x:Static Color.Aqua}" />

Hasta ahora, esto no es muy impresionante. Pero la x:Static extensión de marcado puede hacer referencia a
propiedades o campos estáticos desde su propio código. Por ejemplo, este es un AppConstants clase que
contiene algunos campos estáticos que desea usar en varias páginas en toda una aplicación:

using System;
using Xamarin.Forms;

namespace XamlSamples
{
static class AppConstants
{
public static readonly Thickness PagePadding;

public static readonly Font TitleFont;

public static readonly Color BackgroundColor = Color.Aqua;

public static readonly Color ForegroundColor = Color.Brown;

static AppConstants()
{
switch (Device.RuntimePlatform)
{
case Device.iOS:
PagePadding = new Thickness(5, 20, 5, 0);
TitleFont = Font.SystemFontOfSize(35, FontAttributes.Bold);
break;

case Device.Android:
PagePadding = new Thickness(5, 0, 5, 0);
TitleFont = Font.SystemFontOfSize(40, FontAttributes.Bold);
break;

case Device.UWP:
PagePadding = new Thickness(5, 0, 5, 0);
TitleFont = Font.SystemFontOfSize(50, FontAttributes.Bold);
break;
}
}
}
}

Para hacer referencia a los campos estáticos de esta clase en el archivo XAML, necesitará alguna manera de
indicar dentro del archivo XAML donde se encuentra este archivo. Hacer esto con una declaración de espacio de
nombres XML.
Recuerde que los archivos XAML que se creó como parte de la plantilla estándar de XAML de Xamarin.Forms
contienen dos declaraciones de espacio de nombres XML: uno para tener acceso a las clases de Xamarin.Forms y
otro para hacer referencia a las etiquetas y atributos intrínsecos de XAML:

xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Necesitará las declaraciones de espacio de nombres XML adicionales para tener acceso a otras clases. Cada
declaración de espacio de nombres XML adicional define un nuevo prefijo. Para obtener acceso a las clases de
locales a la biblioteca estándar de .NET de aplicación compartida, como AppConstants , a menudo, los
programadores XAML usan el prefijo local . La declaración de espacio de nombres debe indicar el nombre de
espacio de nombres CLR (Common Language Runtime), también conocido como el .NET espacio de nombres,
que es el nombre que aparece en C# namespace definición o en un using directiva:

xmlns:local="clr-namespace:XamlSamples"

También puede definir las declaraciones de espacio de nombres XML para los espacios de nombres de .NET en
cualquier ensamblado al que hace referencia la biblioteca .NET Standard. Por ejemplo, este es un sys prefijo
para .NET standard System espacio de nombres, que se encuentra en la mscorlib ensamblado, que una vez
significaba "Biblioteca común de Microsoft objeto en tiempo de ejecución", pero ahora significa "multilingüe
estándar Objeto en tiempo de ejecución biblioteca común." Se trata de otro ensamblado, también debe
especificar el nombre del ensamblado, en este caso mscorlib:

xmlns:sys="clr-namespace:System;assembly=mscorlib"

Tenga en cuenta que la palabra clave clr-namespace seguida de dos puntos y, a continuación, el nombre de
espacio de nombres. NET, seguido por un punto y coma, la palabra clave assembly , un signo igual y el nombre
del ensamblado.
Sí, sigue una coma clr-namespace pero sigue el signo igual assembly . La sintaxis se definió en este modo
deliberadamente: La mayoría de las declaraciones de espacio de nombres XML hacen referencia a un URI que
empieza como un nombre de esquema de URI http , que siempre está seguido de dos puntos. El clr-namespace
parte de esta cadena está pensado para imitar esta convención.
Ambas declaraciones de espacio de nombres de estos se incluyen en el StaticConstantsPage ejemplo. Tenga en
cuenta que el BoxView dimensiones se establecen en Math.PI y Math.E , pero ha escalado por un factor de 100:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
x:Class="XamlSamples.StaticConstantsPage"
Title="Static Constants Page"
Padding="{x:Static local:AppConstants.PagePadding}">

<StackLayout>
<Label Text="Hello, XAML!"
TextColor="{x:Static local:AppConstants.BackgroundColor}"
BackgroundColor="{x:Static local:AppConstants.ForegroundColor}"
Font="{x:Static local:AppConstants.TitleFont}"
HorizontalOptions="Center" />

<BoxView WidthRequest="{x:Static sys:Math.PI}"


HeightRequest="{x:Static sys:Math.E}"
Color="{x:Static local:AppConstants.ForegroundColor}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Scale="100" />
</StackLayout>
</ContentPage>

El tamaño de los resultantes BoxView respecto a la pantalla es dependiente de la plataforma:


Otras extensiones de marcado estándar
Varias extensiones de marcado son intrínsecas de XAML y se admiten en los archivos XAML de Xamarin.Forms.
Algunas de ellas no se utilizan muy a menudo, pero son esenciales cuando necesite:
Si tiene una propiedad que no es null valor de forma predeterminada, pero se desea establecerlo en null ,
establézcalo como el {x:Null} extensión de marcado.
Si una propiedad es de tipo Type , puede asignarla a un Type objeto mediante la extensión de marcado
{x:Type someClass} .
Puede definir matrices en XAML usando el x:Array extensión de marcado. Esta extensión de marcado tiene
un atributo obligatorio denominado Type que indica el tipo de los elementos de la matriz.
El Binding se describe la extensión de marcado en parte 4. Conceptos básicos del enlace de datos.

La extensión de marcado ConstraintExpression


Las extensiones de marcado pueden tener propiedades, pero no se establecen como atributos XML. En una
extensión de marcado, valores de las propiedades están separados por comas y comillas no aparece encerrado
entre llaves.
Esto se puede ilustrar con la extensión de marcado de Xamarin.Forms denominada ConstraintExpression , que se
usa con el RelativeLayout clase. Puede especificar la ubicación o el tamaño de una vista secundaria como una
constante o con respecto a un elemento primario o en otra vista con nombre. La sintaxis de la
ConstraintExpression permite establecer la posición o el tamaño de una vista mediante una Factor veces una
propiedad de otra vista, más una Constant . Algo más complejo que requiere el código.
A continuación se ofrece un ejemplo:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.RelativeLayoutPage"
Title="RelativeLayout Page">

<RelativeLayout>

<!-- Upper left -->


<BoxView Color="Red"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=Constant,
Constant=0}"
RelativeLayout.YConstraint=
RelativeLayout.YConstraint=
"{ConstraintExpression Type=Constant,
Constant=0}" />
<!-- Upper right -->
<BoxView Color="Green"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Width,
Factor=1,
Constant=-40}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=Constant,
Constant=0}" />
<!-- Lower left -->
<BoxView Color="Blue"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=Constant,
Constant=0}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Height,
Factor=1,
Constant=-40}" />
<!-- Lower right -->
<BoxView Color="Yellow"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Width,
Factor=1,
Constant=-40}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Height,
Factor=1,
Constant=-40}" />

<!-- Centered and 1/3 width and height of parent -->


<BoxView x:Name="oneThird"
Color="Red"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Width,
Factor=0.33}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Height,
Factor=0.33}"
RelativeLayout.WidthConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Width,
Factor=0.33}"
RelativeLayout.HeightConstraint=
"{ConstraintExpression Type=RelativeToParent,
Property=Height,
Factor=0.33}" />

<!-- 1/3 width and height of previous -->


<BoxView Color="Blue"
RelativeLayout.XConstraint=
"{ConstraintExpression Type=RelativeToView,
ElementName=oneThird,
Property=X}"
RelativeLayout.YConstraint=
"{ConstraintExpression Type=RelativeToView,
ElementName=oneThird,
Property=Y}"
RelativeLayout.WidthConstraint=
"{ConstraintExpression Type=RelativeToView,
ElementName=oneThird,
Property=Width,
Factor=0.33}"
RelativeLayout.HeightConstraint=
"{ConstraintExpression Type=RelativeToView,
ElementName=oneThird,
Property=Height,
Factor=0.33}" />
</RelativeLayout>
</ContentPage>

Quizás la lección más importante que se deben realizar desde este ejemplo es la sintaxis de la extensión de
marcado: Comillas no deben aparecer dentro de las llaves de una extensión de marcado. Al escribir la extensión
de marcado en un archivo XAML, es normal que desee incluir los valores de las propiedades de comillas.
¡ Resistir la tentación!

Este es el programa que se ejecuta:

Resumen
Las extensiones de marcado XAML que se muestra a continuación proporcionan compatibilidad importante para
los archivos XAML. Pero quizás es la extensión de marcado XAML más valiosa Binding , que se trata en la
siguiente parte de esta serie, parte 4. Conceptos básicos del enlace de datos.

Vínculos relacionados
XamlSamples
Parte 1. Introducción a XAML
Parte 2. Sintaxis XAML esencial
Parte 4. Conceptos básicos del enlace de datos
Parte 5. Desde el enlace de datos a MVVM
Parte 4. Conceptos básicos del enlace de datos
11/07/2019 • 21 minutes to read • Edit Online

descargar el ejemplo
Enlaces de datos permiten propiedades de dos objetos se vinculen para que un cambio en uno provoca un
cambio en el otro. Esto es una herramienta muy valiosa, y mientras los enlaces de datos se pueden definir
completamente en código, XAML proporciona accesos directos y comodidad. Por lo tanto, se enlaza una de las
extensiones de marcado más importantes en Xamarin.Forms.

Enlaces de datos
Enlaces de datos conectan las propiedades de dos objetos, denominados el origen y destino. En el código, se
requieren dos pasos: El BindingContext propiedad del objeto de destino debe establecerse en el objeto de
origen y el SetBinding método (a menudo se usa junto con el Binding clase) debe invocarse en el objeto de
destino para enlazar una propiedad de ese objeto a una propiedad del origen de objeto.
La propiedad de destino debe ser una propiedad enlazable, lo que significa que el objeto de destino debe
derivar de BindableObject . La documentación de Xamarin.Forms en línea indica qué propiedades son
propiedades enlazables. Una propiedad de Label como Text está asociado con la propiedad enlazable
TextProperty .

En el marcado, también debe realizar los mismos dos pasos que son necesarios en el código, salvo que el
Binding extensión de marcado ocupa el lugar de la SetBinding llamar y la Binding clase.

Sin embargo, al definir los enlaces de datos en XAML, hay varias maneras de establecer el BindingContext del
objeto de destino. A veces se establece desde el archivo de código subyacente, en ocasiones, con un
StaticResource o x:Static extensión de marcado y a veces como el contenido de BindingContext etiquetas
de elemento de propiedad.
Los enlaces se usan con mayor frecuencia para conectarse a los objetos visuales de un programa con un
modelo de datos subyacente, normalmente en una realización de la arquitectura de aplicación MVVM (Model-
View -ViewModel), como se describe en parte 5. Enlaces de datos a MVVM, pero también son posibles otros
escenarios.

Para ver los enlaces


Puede definir los enlaces de datos para vincular las propiedades de las dos vistas en la misma página. En este
caso, Establece el BindingContext del objeto de destino mediante el x:Reference extensión de marcado.
Este es un archivo XAML que contiene un Slider y dos Label vistas, uno de los cuales se gira el Slider
valor y otra que muestra la Slider valor:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderBindingsPage"
Title="Slider Bindings Page">

<StackLayout>
<Label Text="ROTATION"
BindingContext="{x:Reference Name=slider}"
Rotation="{Binding Path=Value}"
FontAttributes="Bold"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

<Slider x:Name="slider"
Maximum="360"
VerticalOptions="CenterAndExpand" />

<Label BindingContext="{x:Reference slider}"


Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
FontAttributes="Bold"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

El Slider contiene un x:Name atributo que se hace referencia a los dos Label vistas mediante el x:Reference
extensión de marcado.
El x:Reference extensión de enlace define una propiedad denominada Name para establecer en el nombre del
elemento que se hace referencia, en este caso slider . Sin embargo, el ReferenceExtension clase que define el
x:Reference extensión de marcado también define un ContentProperty atributo Name , lo que significa que no
requiere de forma explícita. Solo para diversas, la primera x:Reference incluye "nombre =" pero no así la
segunda:

BindingContext="{x:Reference Name=slider}"

BindingContext="{x:Reference slider}"

El Bindingpropia extensión de marcado puede tener varias propiedades, al igual que el BindingBase y
Binding clase. El ContentProperty para Binding es Path , pero la "ruta de acceso =" si la ruta de acceso es el
primer elemento de parte de la extensión de marcado que se puede omitir el Binding extensión de marcado.
El primer ejemplo tiene "ruta de acceso =", pero omite el segundo ejemplo:

Rotation="{Binding Path=Value}"

Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"

Las propiedades pueden estar en una línea o se dividen en varias líneas:

Text="{Binding Value,
StringFormat='The angle is {0:F0} degrees'}"

Hacer lo que sea conveniente.


Tenga en cuenta la StringFormat propiedad en el segundo Binding extensión de marcado. En Xamarin.Forms,
los enlaces no realizan ninguna conversión de tipos implícita, y si necesita mostrar un objeto que no son de
cadena como una cadena debe proporcionar un convertidor de tipos o utilizar StringFormat . En segundo
plano, estático String.Format método se utiliza para implementar StringFormat . Que es potencialmente un
problema, porque las especificaciones de formato .NET implican entre llaves, que también se utilizan para
delimitar las extensiones de marcado. Esto crea un riesgo de confundir el analizador XAML. Para evitar esto,
coloque toda la cadena de formato en las comillas simples:

Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"

Este es el programa en ejecución:

El modo de enlace
Una vista única puede tener enlaces de datos en varias de sus propiedades. Sin embargo, cada vista solo puede
tener un BindingContext , por lo que varios enlaces de datos en esa vista, deben hacer referencia a propiedades
del mismo objeto.
La solución a este y otros problemas implica la Mode propiedad, que se establece en un miembro de la
BindingMode enumeración:

Default
OneWay : los valores se transfieren desde el origen al destino
OneWayToSource : los valores se transfieren desde el destino al origen
TwoWay : los valores se transfieren ambos sentidos entre el origen y destino
OneTime : datos van desde el origen al destino, pero solo cuando el BindingContext cambios

El programa siguiente muestra un uso habitual de la OneWayToSource y TwoWay modos de enlace. Cuatro
Slider vistas están pensadas para controlar la Scale , Rotate , RotateX , y RotateY las propiedades de un
Label . En primer lugar, parece como si estas cuatro propiedades de la Label debe ser los destinos de enlace
de datos porque cada una se establece un Slider . Sin embargo, el BindingContext de Label puede ser un
solo objeto, y hay cuatro controles deslizantes diferentes.
Por ese motivo, todos los enlaces se establecen aparentemente hacia atrás formas: El BindingContext de cada
uno de los controles cuatro deslizantes se establece en el Label , y los enlaces se establecen en el Value las
propiedades de los controles deslizantes. Mediante el uso de la OneWayToSource y TwoWay modos, estos Value
propiedades pueden establecer las propiedades de origen, que son el Scale , Rotate , RotateX , y RotateY
propiedades de la Label :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderTransformsPage"
Padding="5"
Title="Slider Transforms Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<!-- Scaled and rotated Label -->


<Label x:Name="label"
Text="TEXT"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

<!-- Slider and identifying Label for Scale -->


<Slider x:Name="scaleSlider"
BindingContext="{x:Reference label}"
Grid.Row="1" Grid.Column="0"
Maximum="10"
Value="{Binding Scale, Mode=TwoWay}" />

<Label BindingContext="{x:Reference scaleSlider}"


Text="{Binding Value, StringFormat='Scale = {0:F1}'}"
Grid.Row="1" Grid.Column="1"
VerticalTextAlignment="Center" />

<!-- Slider and identifying Label for Rotation -->


<Slider x:Name="rotationSlider"
BindingContext="{x:Reference label}"
Grid.Row="2" Grid.Column="0"
Maximum="360"
Value="{Binding Rotation, Mode=OneWayToSource}" />

<Label BindingContext="{x:Reference rotationSlider}"


Text="{Binding Value, StringFormat='Rotation = {0:F0}'}"
Grid.Row="2" Grid.Column="1"
VerticalTextAlignment="Center" />

<!-- Slider and identifying Label for RotationX -->


<Slider x:Name="rotationXSlider"
BindingContext="{x:Reference label}"
Grid.Row="3" Grid.Column="0"
Maximum="360"
Value="{Binding RotationX, Mode=OneWayToSource}" />

<Label BindingContext="{x:Reference rotationXSlider}"


Text="{Binding Value, StringFormat='RotationX = {0:F0}'}"
Grid.Row="3" Grid.Column="1"
VerticalTextAlignment="Center" />

<!-- Slider and identifying Label for RotationY -->


<Slider x:Name="rotationYSlider"
BindingContext="{x:Reference label}"
Grid.Row="4" Grid.Column="0"
Maximum="360"
Value="{Binding RotationY, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationYSlider}"
Text="{Binding Value, StringFormat='RotationY = {0:F0}'}"
Grid.Row="4" Grid.Column="1"
VerticalTextAlignment="Center" />
</Grid>
</ContentPage>

Los enlaces en tres de los Slider las vistas son OneWayToSource , lo que significa que el Slider valor provoca
un cambio en la propiedad de su BindingContext , que es el Label denominado label . Estos tres Slider
vistas provocan cambios en el Rotate , RotateX , y RotateY propiedades de la Label .
Sin embargo, el enlace para el Scale propiedad es TwoWay . Esto es porque el Scale propiedad tiene un valor
predeterminado de 1 y usar un TwoWay enlace hace que el Slider inicial del valor que se establecerá en 1 en
lugar de 0. Si fuera ese enlace OneWayToSource , Scale inicialmente se establecería la propiedad en 0 de la
Slider valor predeterminado. El Label no estará visible y que podría provocar confusión en el usuario.

NOTE
El VisualElement clase también tiene ScaleX y ScaleY propiedades, que escalen el VisualElement en los ejes x y
y respectivamente.

Los enlaces y colecciones


Nada ilustra el poder de XAML y enlaces de datos mejores que una plantilla ListView .
ListView define un ItemsSource propiedad de tipo IEnumerable , y muestra los elementos de la colección.
Estos elementos pueden ser objetos de cualquier tipo. De forma predeterminada, ListView usa el ToString
método de cada elemento para mostrar ese elemento. A veces esto es lo que desea, pero en muchos casos,
ToString devuelve solo el nombre de clase completo del objeto.

Sin embargo, los elementos de la ListView colección puede mostrarse como desee mediante el uso de un
plantilla, lo que implica una clase que deriva de Cell . La plantilla se clona para todos los elementos de la
ListView , y los enlaces de datos que se han establecido en la plantilla se transfieren a los clones individuales.

Muy a menudo, deseará crear una celda personalizada para estos elementos mediante el ViewCell clase. Este
proceso es un tanto confuso en código, pero en XAML resulta muy sencillo.
Incluye en el XamlSamples proyecto es una clase llamada NamedColor . Cada NamedColor objeto tiene Name y
FriendlyName las propiedades de tipo string y un Color propiedad de tipo Color . Además, NamedColor tiene
141 campos estáticos de sólo lectura de tipo Color correspondientes a los colores definidos en
Xamarin.Forms Color clase. Crea un constructor estático una IEnumerable<NamedColor> colección que contiene
NamedColor objetos correspondientes a estos campos estáticos y se asigna a su público estático All
propiedad.
Establecimiento estático NamedColor.All propiedad a la ItemsSource de un ListView es fácil mediante el
x:Static extensión de marcado:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">

<ListView ItemsSource="{x:Static local:NamedColor.All}" />

</ContentPage>

La presentación resultante se establece que los elementos son realmente del tipo XamlSamples.NamedColor :

No es toda la información, pero la ListView es desplazable y seleccionable.


Para definir una plantilla para los elementos, desea dividir el ItemTemplate propiedad como un elemento de
propiedad y establézcalo en un DataTemplate , que hace referencia a continuación, un ViewCell . Para el View
propiedad de la ViewCell puede definir un diseño de una o más vistas para mostrar cada elemento. Este es un
ejemplo sencillo:

<ListView ItemsSource="{x:Static local:NamedColor.All}">


<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Label Text="{Binding FriendlyName}" />
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
NOTE
El origen de enlace de celdas y elementos secundarios de las celdas, es el ListView.ItemsSource colección.

El Label elemento está establecido en el View propiedad de la ViewCell . (El ViewCell.View etiquetas no son
necesarios porque el View propiedad es la propiedad content de ViewCell .) Este marcado se muestra el
FriendlyName propiedad de cada uno NamedColor objeto:

Mucho mejor. Ahora todo lo necesario es refinar la plantilla de elemento con el color real y obtener más
información. Para admitir esta plantilla, algunos valores y los objetos se han definido en el diccionario de
recursos de la página:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">

<ContentPage.Resources>
<ResourceDictionary>
<OnPlatform x:Key="boxSize"
x:TypeArguments="x:Double">
<On Platform="iOS, Android, UWP" Value="50" />
</OnPlatform>

<OnPlatform x:Key="rowHeight"
x:TypeArguments="x:Int32">
<On Platform="iOS, Android, UWP" Value="60" />
</OnPlatform>

<local:DoubleToIntConverter x:Key="intConverter" />

</ResourceDictionary>
</ContentPage.Resources>

<ListView ItemsSource="{x:Static local:NamedColor.All}"


RowHeight="{StaticResource rowHeight}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="5, 5, 0, 5"
Orientation="Horizontal"
Spacing="15">
<BoxView WidthRequest="{StaticResource boxSize}"
HeightRequest="{StaticResource boxSize}"
Color="{Binding Color}" />

<StackLayout Padding="5, 0, 0, 0"


VerticalOptions="Center">

<Label Text="{Binding FriendlyName}"


FontAttributes="Bold"
FontSize="Medium" />

<StackLayout Orientation="Horizontal"
Spacing="0">
<Label Text="{Binding Color.R,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />

<Label Text="{Binding Color.G,


Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', G={0:X2}'}" />

<Label Text="{Binding Color.B,


Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', B={0:X2}'}" />
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>

Tenga en cuenta el uso de OnPlatform para definir el tamaño de un BoxView y el alto de la ListView filas.
Aunque los valores para todas las plataformas son iguales, el marcado fácilmente podría ser adaptado para
otros valores ajustar la visualización.

Convertidores de valores de enlace


La anterior ListView demostración archivo XAML muestra el individuo R , G , y B propiedades de
Xamarin.Forms Color estructura. Estas propiedades son de tipo double y el intervalo comprendido entre 0 y
1. Si desea mostrar los valores hexadecimales, no basta con utilizar StringFormat con una especificación de
formato "X2". Esto sólo funciona para números enteros y además, el double valores deben ser multiplicado
por 255.
Este pequeño problema se resolvió con una convertidor, también denominado una conversor de enlaces. Se
trata de una clase que implementa el IValueConverter interfaz, lo que significa tiene dos métodos
denominados Convert y ConvertBack . El Convert método se llama cuando se transfiere un valor de origen al
destino; el ConvertBack se llama al método para las transferencias de destino al origen en OneWayToSource o
TwoWay enlaces:
using System;
using System.Globalization;
using Xamarin.Forms;

namespace XamlSamples
{
class DoubleToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
double multiplier;

if (!Double.TryParse(parameter as string, out multiplier))


multiplier = 1;

return (int)Math.Round(multiplier * (double)value);


}

public object ConvertBack(object value, Type targetType,


object parameter, CultureInfo culture)
{
double divider;

if (!Double.TryParse(parameter as string, out divider))


divider = 1;

return ((double)(int)value) / divider;


}
}
}

El ConvertBack método desempeñan un papel en este programa porque los enlaces son sólo una forma de
origen al destino.
Un enlace hace referencia a un conversor de enlaces con el Converter propiedad. Un conversor de enlaces
también puede aceptar un parámetro especificado con el ConverterParameter propiedad. Para algunos
versatilidad, es cómo se especifica el multiplicador. El conversor de enlaces comprueba el parámetro de
convertidor para válido double valor.
Por lo que se puede compartir entre varios enlaces, se crea una instancia de convertidor en el diccionario de
recursos:

<local:DoubleToIntConverter x:Key="intConverter" />

Enlaces de datos de tres hacen referencia a esta instancia. Tenga en cuenta que el Binding extensión de
marcado contiene incrustada StaticResource extensión de marcado:

<Label Text="{Binding Color.R,


Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />

Este es el resultado:
El ListView es bastante sofisticada en el control de cambios que se produzcan dinámicamente en los datos
subyacentes, pero solo si realizar ciertos pasos. Si la colección de elementos que se asigna a la ItemsSource
propiedad de la ListView cambios en tiempo de ejecución: es decir, si los elementos se pueden agregar a o
quitar de la colección, utilice un ObservableCollection clase para estos elementos. ObservableCollection
implementa el INotifyCollectionChanged interfaz, y ListView instalará un controlador para el
CollectionChanged eventos.

Si las propiedades de los propios elementos cambian en tiempo de ejecución, los elementos de la colección
deben implementar la INotifyPropertyChanged cambios de interfaz y la señal a los valores de propiedad
mediante la PropertyChanged eventos. Esto se muestra en la siguiente parte de esta serie, parte 5. Desde el
enlace de datos a MVVM.

Resumen
Los enlaces de datos proporcionan un mecanismo eficaz para vincular las propiedades entre dos objetos
dentro de una página, o entre objetos visuales y los datos subyacentes. Pero cuando la aplicación comienza a
funcionar con orígenes de datos, un patrón arquitectónico popular aplicación comienza a aparecer como un
paradigma útil. Este tema se trata en parte 5. Enlaces de datos a MVVM.

Vínculos relacionados
XamlSamples
Parte 1. Introducción a XAML (ejemplo)
Parte 2. Sintaxis de XAML esencial (ejemplo)
Parte 3. Extensiones de marcado de XAML (ejemplo)
Parte 5. De enlaces de datos a MVVM (ejemplo)
Parte 5. Enlaces de datos a MVVM
11/07/2019 • 22 minutes to read • Edit Online

descargar el ejemplo
El patrón de arquitectura Model-View -ViewModel (MVVM ) se inventó con XAML en mente. El patrón exige una
separación entre las tres capas de software, la interfaz de usuario XAML, llama a la vista; los datos subyacentes,
denominados modelo; y un intermediario entre la vista y el modelo, se denominan ViewModel. La vista y el
ViewModel a menudo están conectados a través de enlaces de datos definidos en el archivo XAML.
BindingContext para la vista normalmente es una instancia de ViewModel.

Un Simple ViewModel
Como una introducción a ViewModel, veamos primero un programa sin uno. Antes vimos cómo definir una
nueva declaración de espacio de nombres XML para permitir que un archivo XAML para las clases de
referencia de otros ensamblados. Este es un programa que define una declaración de espacio de nombres XML
para el System espacio de nombres:

xmlns:sys="clr-namespace:System;assembly=mscorlib"

Puede usar el programa x:Static para obtener la fecha y hora actuales de estático DateTime.Now propiedad y
establecer que DateTime valor para el BindingContext en un StackLayout :

<StackLayout BindingContext="{x:Static sys:DateTime.Now}" …>

BindingContext es una propiedad muy especial: Al establecer el BindingContext en un elemento, que es


heredada por todos los elementos secundarios de ese elemento. Esto significa que todos los elementos
secundarios de la StackLayout tienen este mismo BindingContext , y pueden contener enlaces simples a las
propiedades de ese objeto.
En el One-Shot DateTime programa, dos de los elementos secundarios contiene enlaces a las propiedades de
ese DateTime valor, pero otras dos hijos contienen los enlaces que parecen faltar una ruta de acceso de enlace.
Esto significa que el DateTime propio valor se usa para el StringFormat :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
x:Class="XamlSamples.OneShotDateTimePage"
Title="One-Shot DateTime Page">

<StackLayout BindingContext="{x:Static sys:DateTime.Now}"


HorizontalOptions="Center"
VerticalOptions="Center">

<Label Text="{Binding Year, StringFormat='The year is {0}'}" />


<Label Text="{Binding StringFormat='The month is {0:MMMM}'}" />
<Label Text="{Binding Day, StringFormat='The day is {0}'}" />
<Label Text="{Binding StringFormat='The time is {0:T}'}" />

</StackLayout>
</ContentPage>
Por supuesto, el gran problema es que la fecha y hora son una vez que se genera por primera vez cuando la
página de conjunto y nunca cambian:

Un archivo XAML puede mostrar un reloj que siempre se muestra la hora actual, pero necesita algo de código
para ayudar a. Al pensar en términos de MVVM, Model y ViewModel son clases escritas completamente en
código. La vista a menudo es un archivo XAML que hace referencia a las propiedades definidas en el modelo de
vista a través de enlaces de datos.
Un modelo adecuado es que ignoran la persistencia del ViewModel y un ViewModel adecuado es que ignoran
la persistencia de la vista. Sin embargo, muy a menudo, un programador adapta los tipos de datos expuestos
por el ViewModel a los tipos de datos asociados con las interfaces de usuario determinado. Por ejemplo, si un
modelo tiene acceso a una base de datos que contiene las cadenas de caracteres de 8 bits ASCII, necesitaría el
ViewModel convertir entre esas cadenas para cadenas Unicode para acomodar el uso exclusivo de Unicode en
la interfaz de usuario.
En ejemplos sencillos de MVVM (como los mostrados aquí), a menudo hay ningún modelo en absoluto y el
patrón implica simplemente una vista y ViewModel vincula a enlaces de datos.
Este es un modelo de vista de un reloj con una propiedad única denominada DateTime , pero que las
actualizaciones que se DateTime propiedad cada segundo:
using System;
using System.ComponentModel;
using Xamarin.Forms;

namespace XamlSamples
{
class ClockViewModel : INotifyPropertyChanged
{
DateTime dateTime;

public event PropertyChangedEventHandler PropertyChanged;

public ClockViewModel()
{
this.DateTime = DateTime.Now;

Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
this.DateTime = DateTime.Now;
return true;
});
}

public DateTime DateTime


{
set
{
if (dateTime != value)
{
dateTime = value;

if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("DateTime"));
}
}
}
get
{
return dateTime;
}
}
}
}

ViewModels implementan generalmente la INotifyPropertyChanged interfaz, lo que significa que la clase se


activa un PropertyChanged evento cada vez que cambia una de sus propiedades. El mecanismo de enlace de
datos en Xamarin.Forms adjunta un controlador a este PropertyChanged por lo que puede recibir una
notificación cuando cambia una propiedad de evento y mantener el destino que se actualiza con el nuevo valor.
Un reloj según este ViewModel puede ser tan simple como esto:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.ClockPage"
Title="Clock Page">

<Label Text="{Binding DateTime, StringFormat='{0:T}'}"


FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="Center">
<Label.BindingContext>
<local:ClockViewModel />
</Label.BindingContext>
</Label>
</ContentPage>

Observe cómo el ClockViewModel está establecido en el BindingContext de la Label mediante etiquetas de


elemento de propiedad. Como alternativa, puede crear una instancia el ClockViewModel en un Resources
colección y establézcalo como el BindingContext a través de un StaticResource extensión de marcado. O bien,
el archivo de código subyacente puede crear instancias de ViewModel.
El Binding extensión de marcado en el Text propiedad de la Label formatos el DateTime propiedad. Aquí es
la pantalla:

También es posible tener acceso a propiedades individuales de la DateTime propiedad del ViewModel mediante
la separación de las propiedades con los puntos:

<Label Text="{Binding DateTime.Second, StringFormat='{0}'}" … >

MVVM interactivo
MVVM con bastante frecuencia se utiliza con los enlaces de datos bidireccional para una vista interactiva
basada en un modelo de datos subyacente.
Esta es una clase denominada HslViewModel que convierte un Color valor en Hue , Saturation ,y Luminosity
valores y viceversa:

using System;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms;

namespace XamlSamples
{
public class HslViewModel : INotifyPropertyChanged
{
double hue, saturation, luminosity;
Color color;

public event PropertyChangedEventHandler PropertyChanged;

public double Hue


{
set
{
if (hue != value)
{
hue = value;
OnPropertyChanged("Hue");
SetNewColor();
}
}
get
{
return hue;
}
}

public double Saturation


{
set
{
if (saturation != value)
{
saturation = value;
OnPropertyChanged("Saturation");
SetNewColor();
}
}
get
{
return saturation;
}
}

public double Luminosity


{
set
{
if (luminosity != value)
{
luminosity = value;
OnPropertyChanged("Luminosity");
SetNewColor();
}
}
get
{
return luminosity;
}
}

public Color Color


{
set
{
if (color != value)
{
color = value;
OnPropertyChanged("Color");

Hue = value.Hue;
Saturation = value.Saturation;
Luminosity = value.Luminosity;
}
}
get
{
return color;
}
}

void SetNewColor()
{
Color = Color.FromHsla(Hue, Saturation, Luminosity);
}

protected virtual void OnPropertyChanged(string propertyName)


{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Cambia a la Hue , Saturation , y Luminosity propiedades causa el Color propiedad quiere cambiar y los
cambios realizados en Color hace que las tres propiedades cambiar. Esto puede parecer un bucle infinito, salvo
que la clase no invocar el PropertyChanged eventos a menos que realmente ha cambiado la propiedad. Esto
acabe con el bucle de comentarios en caso contrario, no controlado.
El siguiente archivo XAML contiene una BoxView cuyo Color propiedad está enlazada a la Color propiedad
del ViewModel y tres Slider y tres Label vistas enlazado a la Hue , Saturation y Luminosity propiedades:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.HslColorScrollPage"
Title="HSL Color Scroll Page">
<ContentPage.BindingContext>
<local:HslViewModel Color="Aqua" />
</ContentPage.BindingContext>

<StackLayout Padding="10, 0">


<BoxView Color="{Binding Color}"
VerticalOptions="FillAndExpand" />

<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}"


HorizontalOptions="Center" />

<Slider Value="{Binding Hue, Mode=TwoWay}" />

<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}"


HorizontalOptions="Center" />

<Slider Value="{Binding Saturation, Mode=TwoWay}" />

<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}"


HorizontalOptions="Center" />

<Slider Value="{Binding Luminosity, Mode=TwoWay}" />


</StackLayout>
</ContentPage>

El enlace en cada Label es el valor predeterminado OneWay . Solo se necesita para mostrar el valor. Pero el
enlace en cada Slider es TwoWay . Esto permite la Slider van a inicializar desde el modelo de vista. Tenga en
cuenta que el Color propiedad está establecida en Aqua cuando se crea una instancia de ViewModel. Pero un
cambio en el Slider también necesita establecer un nuevo valor para la propiedad en ViewModel, que, a
continuación, calcula un nuevo color.

Comandos con modelos de vista


En muchos casos, el patrón MVVM está restringido a la manipulación de los elementos de datos: Objetos de
interfaz de usuario en la vista de objetos de datos en el modelo de vista en paralelo.
Sin embargo, en ocasiones, la vista debe contener botones que desencadenan varias acciones en el modelo de
vista. Pero no debe contener el ViewModel Clicked controladores para los botones ya que podría enlazar el
ViewModel a un paradigma de interfaz de usuario concreta.
Para permitir que los ViewModel, para ser más independientes de los objetos de interfaz de usuario en
particular pero permitirá que los métodos se llamen en ViewModel, un comando interfaz existe. Esta interfaz de
comando es compatible con los siguientes elementos en Xamarin.Forms:
Button
MenuItem
ToolbarItem
SearchBar
TextCell (y por lo tanto, también ImageCell )
ListView
TapGestureRecognizer

Con la excepción de la SearchBar y ListView elemento, estos elementos definen dos propiedades:
Command de tipo System.Windows.Input.ICommand
CommandParameter de tipo Object

El define SearchCommand y SearchCommandParameter propiedades, mientras que el


SearchBar ListView define un
RefreshCommand propiedad de tipo ICommand .

El ICommand interfaz define dos métodos y un evento:


void Execute(object arg)
bool CanExecute(object arg)
event EventHandler CanExecuteChanged

ViewModel puede definir las propiedades de tipo ICommand . A continuación, puede enlazar estas propiedades
para el Command propiedad de cada uno Button u otro elemento, o quizás una vista personalizada que
implementa esta interfaz. Opcionalmente, puede establecer el CommandParameter propiedad para identificar
individuales Button objetos (u otros elementos) que están enlazados a esta propiedad ViewModel.
Internamente, el Button llamadas la Execute método cada vez que el usuario pulsa el Button , pasando a la
Execute método su CommandParameter .

El CanExecute método y CanExecuteChanged se usan para los casos donde un Button tap puede no ser
actualmente válido, en cuyo caso el Button debe deshabilitarse. El Button llamadas CanExecute cuando el
Command propiedad se establece en primer lugar y en cualquier momento el CanExecuteChanged desencadena el
evento. Si CanExecute devuelve false , Button deshabilita a sí mismo y no genera Execute llamadas.
Para obtener ayuda en Agregar comandos a sus ViewModels, Xamarin.Forms define dos clases que
implementan ICommand : Command y Command<T> donde T es el tipo de los argumentos Execute y CanExecute .
Estas dos clases definen varios constructores más una ChangeCanExecute método que ViewModel puede llamar
para forzar la Command objeto para desencadenar la CanExecuteChanged eventos.
Este es un modelo de vista para un teclado simple que sirve para escribir números de teléfono. Tenga en cuenta
que el Execute y CanExecute método se definen como funciones directamente en el constructor de lambda:

using System;
using System.ComponentModel;
using System.Windows.Input;
using Xamarin.Forms;

namespace XamlSamples
{
class KeypadViewModel : INotifyPropertyChanged
{
string inputString = "";
string displayText = "";
char[] specialChars = { '*', '#' };

public event PropertyChangedEventHandler PropertyChanged;

// Constructor
public KeypadViewModel()
{
AddCharCommand = new Command<string>((key) =>
{
// Add the key to the input string.
InputString += key;
});

DeleteCharCommand = new Command(() =>


{
// Strip a character from the input string.
InputString = InputString.Substring(0, InputString.Length - 1);
},
() =>
{
// Return true if there's something to delete.
return InputString.Length > 0;
});
}

// Public properties
public string InputString
{
protected set
{
{
if (inputString != value)
{
inputString = value;
OnPropertyChanged("InputString");
DisplayText = FormatText(inputString);

// Perhaps the delete button must be enabled/disabled.


((Command)DeleteCharCommand).ChangeCanExecute();
}
}

get { return inputString; }


}

public string DisplayText


{
protected set
{
if (displayText != value)
{
displayText = value;
OnPropertyChanged("DisplayText");
}
}
get { return displayText; }
}

// ICommand implementations
public ICommand AddCharCommand { protected set; get; }

public ICommand DeleteCharCommand { protected set; get; }

string FormatText(string str)


{
bool hasNonNumbers = str.IndexOfAny(specialChars) != -1;
string formatted = str;

if (hasNonNumbers || str.Length < 4 || str.Length > 10)


{
}
else if (str.Length < 8)
{
formatted = String.Format("{0}-{1}",
str.Substring(0, 3),
str.Substring(3));
}
else
{
formatted = String.Format("({0}) {1}-{2}",
str.Substring(0, 3),
str.Substring(3, 3),
str.Substring(6));
}
return formatted;
}

protected void OnPropertyChanged(string propertyName)


{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Este ViewModel se da por supuesto que el AddCharCommand propiedad está enlazada a la Command propiedad de
varios botones (o cualquier otra cosa que tiene una interfaz de comandos), cada uno de los cuales se identifica
mediante el CommandParameter . Estos botones agregan caracteres a un InputString propiedad, que, a
continuación, se da formato como un número de teléfono para el DisplayText propiedad.
También hay una segunda propiedad de tipo ICommand denominado DeleteCharCommand . Esto está enlazado a
un botón Atrás espaciado, pero el botón debe deshabilitarse si no hay ningún carácter que se va a eliminar.
El teclado siguiente no es como visualmente sofisticado como podría ser. En su lugar, el marcado se ha reducido
al mínimo para mostrar más claramente el uso de la interfaz de comandos:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.KeypadPage"
Title="Keypad Page">

<Grid HorizontalOptions="Center"
VerticalOptions="Center">
<Grid.BindingContext>
<local:KeypadViewModel />
</Grid.BindingContext>

<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>

<!-- Internal Grid for top row of items -->


<Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<Frame Grid.Column="0"
OutlineColor="Accent">
<Label Text="{Binding DisplayText}" />
</Frame>

<Button Text="&#x21E6;"
Command="{Binding DeleteCharCommand}"
Grid.Column="1"
BorderWidth="0" />
</Grid>

<Button Text="1"
Command="{Binding AddCharCommand}"
CommandParameter="1"
Grid.Row="1" Grid.Column="0" />

<Button Text="2"
Command="{Binding AddCharCommand}"
CommandParameter="2"
Grid.Row="1" Grid.Column="1" />

<Button Text="3"
Command="{Binding AddCharCommand}"
CommandParameter="3"
Grid.Row="1" Grid.Column="2" />
<Button Text="4"
Command="{Binding AddCharCommand}"
CommandParameter="4"
Grid.Row="2" Grid.Column="0" />

<Button Text="5"
Command="{Binding AddCharCommand}"
CommandParameter="5"
Grid.Row="2" Grid.Column="1" />

<Button Text="6"
Command="{Binding AddCharCommand}"
CommandParameter="6"
Grid.Row="2" Grid.Column="2" />

<Button Text="7"
Command="{Binding AddCharCommand}"
CommandParameter="7"
Grid.Row="3" Grid.Column="0" />

<Button Text="8"
Command="{Binding AddCharCommand}"
CommandParameter="8"
Grid.Row="3" Grid.Column="1" />

<Button Text="9"
Command="{Binding AddCharCommand}"
CommandParameter="9"
Grid.Row="3" Grid.Column="2" />

<Button Text="*"
Command="{Binding AddCharCommand}"
CommandParameter="*"
Grid.Row="4" Grid.Column="0" />

<Button Text="0"
Command="{Binding AddCharCommand}"
CommandParameter="0"
Grid.Row="4" Grid.Column="1" />

<Button Text="#"
Command="{Binding AddCharCommand}"
CommandParameter="#"
Grid.Row="4" Grid.Column="2" />
</Grid>
</ContentPage>

El Command propiedad de la primera Button que aparece en este marcado se enlaza a la DeleteCharCommand ; el
resto se enlazan a la AddCharCommand con un CommandParameter que es el mismo que el carácter que aparece en
el Button cara. Este es el programa en acción:
Invocar métodos asincrónicos
Los comandos también pueden invocar los métodos asincrónicos. Esto se logra mediante el uso de la async y
await palabras clave al especificar el Execute método:

DownloadCommand = new Command (async () => await DownloadAsync ());

Esto indica que el DownloadAsync método es un Task y se debe esperar:

async Task DownloadAsync ()


{
await Task.Run (() => Download ());
}

void Download ()
{
...
}

Implementación de un menú de navegación


El XamlSamples programa que contiene todo el código fuente en esta serie de artículos usa un modelo de vista
para su página principal. Este modelo de vista es una definición de una clase corto con tres propiedades
denominadas Type , Title , y Description que contiene el tipo de cada una de las páginas de ejemplo, un
título y una descripción breve. Además, el modelo de vista define una propiedad estática denominada All que
es una colección de todas las páginas en el programa:

public class PageDataViewModel


{
public PageDataViewModel(Type type, string title, string description)
{
Type = type;
Title = title;
Description = description;
}

public Type Type { private set; get; }

public string Title { private set; get; }

public string Description { private set; get; }


public string Description { private set; get; }

static PageDataViewModel()
{
All = new List<PageDataViewModel>
{
// Part 1. Getting Started with XAML
new PageDataViewModel(typeof(HelloXamlPage), "Hello, XAML",
"Display a Label with many properties set"),

new PageDataViewModel(typeof(XamlPlusCodePage), "XAML + Code",


"Interact with a Slider and Button"),

// Part 2. Essential XAML Syntax


new PageDataViewModel(typeof(GridDemoPage), "Grid Demo",
"Explore XAML syntax with the Grid"),

new PageDataViewModel(typeof(AbsoluteDemoPage), "Absolute Demo",


"Explore XAML syntax with AbsoluteLayout"),

// Part 3. XAML Markup Extensions


new PageDataViewModel(typeof(SharedResourcesPage), "Shared Resources",
"Using resource dictionaries to share resources"),

new PageDataViewModel(typeof(StaticConstantsPage), "Static Constants",


"Using the x:Static markup extensions"),

new PageDataViewModel(typeof(RelativeLayoutPage), "Relative Layout",


"Explore XAML markup extensions"),

// Part 4. Data Binding Basics


new PageDataViewModel(typeof(SliderBindingsPage), "Slider Bindings",
"Bind properties of two views on the page"),

new PageDataViewModel(typeof(SliderTransformsPage), "Slider Transforms",


"Use Sliders with reverse bindings"),

new PageDataViewModel(typeof(ListViewDemoPage), "ListView Demo",


"Use a ListView with data bindings"),

// Part 5. From Data Bindings to MVVM


new PageDataViewModel(typeof(OneShotDateTimePage), "One-Shot DateTime",
"Obtain the current DateTime and display it"),

new PageDataViewModel(typeof(ClockPage), "Clock",


"Dynamically display the current time"),

new PageDataViewModel(typeof(HslColorScrollPage), "HSL Color Scroll",


"Use a view model to select HSL colors"),

new PageDataViewModel(typeof(KeypadPage), "Keypad",


"Use a view model for numeric keypad logic")
};
}

public static IList<PageDataViewModel> All { private set; get; }


}

El archivo XAML para MainPage define un ListBox cuyo ItemsSource propiedad está establecida en el que
All propiedad y que contiene un TextCell para mostrar el Title y Description las propiedades de cada
página:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
x:Class="XamlSamples.MainPage"
Padding="5, 0"
Title="XAML Samples">

<ListView ItemsSource="{x:Static local:PageDataViewModel.All}"


ItemSelected="OnListViewItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Title}"
Detail="{Binding Description}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>

Las páginas se muestran en una lista desplazable:

El controlador en el archivo de código subyacente se desencadena cuando el usuario selecciona un elemento. El


controlador se establece la SelectedItem propiedad de la ListBox a null y, a continuación, crea una instancia
de la página seleccionada y navega a ella:

private async void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs args)


{
(sender as ListView).SelectedItem = null;

if (args.SelectedItem != null)
{
PageDataViewModel pageData = args.SelectedItem as PageDataViewModel;
Page page = (Page)Activator.CreateInstance(pageData.Type);
await Navigation.PushAsync(page);
}
}

Vídeo
Xamarin Evolve 2016: MVVM sencilla con Xamarin.Forms y Prism
Resumen
XAML es una herramienta eficaz para definir las interfaces de usuario en las aplicaciones de Xamarin.Forms,
especialmente cuando el enlace de datos y se usan MVVM. El resultado es una representación limpia, elegante
y potencialmente dispone de herramientas de una interfaz de usuario con toda la compatibilidad en segundo
plano en el código.

Vínculos relacionados
XamlSamples
Parte 1. Introducción a XAML
Parte 2. Sintaxis XAML esencial
Parte 3. Extensiones de marcado XAML
Parte 4. Conceptos básicos del enlace de datos
Compilación de XAML en Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

XAML se puede compilar opcionalmente directamente en lenguaje intermedio (IL ) con el compilador XAML
(XAMLC ).
Compilación de XAML ofrece una serie de ventajas:
Realiza la comprobación en tiempo de compilación de XAML, notificando al usuario de los errores.
Reduce parte del tiempo de carga y creación de instancias para los elementos XAML.
Facilita reducir el tamaño de archivo del ensamblado final al dejar de incluir archivos .xaml.
Compilación de XAML está deshabilitado de forma predeterminada para garantizar la compatibilidad con
versiones anteriores. Puede habilitarse en el nivel de clase y ensamblado agregando la XamlCompilation atributo.
En el ejemplo de código siguiente se muestra cómo habilitar la compilación de XAML en el nivel de ensamblado:

using Xamarin.Forms.Xaml;
...
[assembly: XamlCompilation (XamlCompilationOptions.Compile)]
namespace PhotoApp
{
...
}

En este ejemplo, se realizará la comprobación de todo el XAML contenido dentro del ensamblado de tiempo de
compilación, con errores XAML que se notifica en tiempo de compilación en lugar de tiempo de ejecución. Por lo
tanto, el assembly prefijo para el XamlCompilation atributo especifica que el atributo se aplica a todo el
ensamblado.

NOTE
El XamlCompilation atributo y el XamlCompilationOptions enumeración residen en el Xamarin.Forms.Xaml espacio de
nombres que debe importarse al usarlas.

En el ejemplo de código siguiente se muestra cómo habilitar la compilación de XAML en el nivel de clase:

using Xamarin.Forms.Xaml;
...
[XamlCompilation (XamlCompilationOptions.Compile)]
public class HomePage : ContentPage
{
...
}

En este ejemplo, la comprobación de que el XAML para el tiempo de compilación la HomePage se realizará la clase
y los errores se notifican como parte del proceso de compilación.
NOTE
Enlaces compilados se pueden habilitar para mejorar el rendimiento de enlace de datos en las aplicaciones de
Xamarin.Forms. Para obtener más información, consulte compilado enlaces.

Vínculos relacionados
XamlCompilation
XamlCompilationOptions
Cuadro de herramientas de XAML de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Visual Studio 2017 versión 15,8 y Visual Studio para Mac 7.6 ahora tienen un cuadro de herramientas disponible al
editar archivos XAML de Xamarin.Forms. El cuadro de herramientas contiene todos los controles integrados de
Xamarin.Forms y diseños, que se pueden arrastrar en el editor de XAML.
Visual Studio
Visual Studio para Mac
En Visual Studio 2017, abra un archivo XAML de Xamarin.Forms para su edición. Se puede mostrar el cuadro de
herramientas, presione Ctrl + W, X en el teclado, o eligiendo el Ver > cuadro de herramientas elemento de
menú.

El cuadro de herramientas puede ser oculto y acoplada como otros paneles en Visual Studio 2017, con los iconos
en la parte superior derecha o en el menú contextual. El cuadro de herramientas XAML de Xamarin.Forms tiene
opciones de vista personalizada que se pueden cambiar con el botón secundario en cada sección. Alternar el vista
de lista opción para cambiar entre la lista y vistas compactas:

Cuando se abre un archivo XAML de Xamarin.Forms para su edición, arrastre cualquier control o diseño del
cuadro de herramientas en el archivo, y aprovechar las ventajas de Intellisense para personalizar la interfaz de
usuario.
Controlador de vista previa XAML para
Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

Consulte los diseños de Xamarin.Forms representan a medida que escribe

Información general
El controlador de vista previa de XAML muestra el aspecto de la página XAML de Xamarin.Forms en iOS y
Android. Al realizar cambios en el XAML, verá una vista previa inmediatamente junto con el código. El controlador
de vista previa de XAML está disponible en Visual Studio y Visual Studio para Mac.

Introducción
Visual Studio 2019
Puede abrir a la vista previa de XAML, haga clic en las flechas en el panel de vista de división. Si desea cambiar el
valor predeterminado divide el comportamiento de la vista, use el Herramientas > Opciones > Xamarin >
controlador de vista previa de formularios cuadro de diálogo. En este cuadro de diálogo, puede seleccionar la
vista de documento predeterminada y la orientación dividida.

Al abrir un archivo XAML, se abrirá el editor en tamaño completo o en siguiente para el controlador de vista
previa, en función de la configuración seleccionada en el Herramientas > Opciones > Xamarin > controlador
de vista previa de formularios cuadro de diálogo. Sin embargo, se puede cambiar la división para cada archivo
en la ventana del editor.
Controles de vista previa XAML
Elija si desea ver el código, el controlador de vista previa de XAML, o ambos mediante la selección de estos
botones en la división ver panel. El botón central intercambia qué lado de la vista previa y el código están en:

Puede cambiar si la pantalla se divide verticalmente u horizontalmente, o contraer un panel completamente:

Visual Studio para Mac


El Preview botón se muestra en el editor cuando se abre una página XAML. Mostrar u ocultar la vista previa
presionando el Preview situado en la esquina superior derecha de cualquier ventana de documento XAML:

Opciones de vista previa de XAML


Las opciones en la parte superior del panel de vista previa son:
Android : mostrar la versión de Android de la pantalla
iOS : mostrar la versión de iOS de la pantalla (Nota: Si usa Visual Studio en Windows, debe ser emparejado
con un equipo Mac para usar este modo)
Dispositivo -lista desplegable de dispositivos Android o iOS, incluido el tamaño de pantalla y resolución
Vertical (icono) – utiliza orientación vertical para la versión preliminar
Horizontal (icono) – utiliza orientación para la versión preliminar de horizontal

Detectar el modo de diseño


Estático DesignMode.IsDesignModeEnabled propiedad indica si la aplicación se está ejecutando en el controlador de
vista previa. Con esta opción, puede especificar el código que solo se ejecutará cuando la aplicación es o no se está
ejecutando en el controlador de vista previa:

if (DesignMode.IsDesignModeEnabled)
{
// Previewer only code
}

if (!DesignMode.IsDesignModeEnabled)
{
// Don't run in the Previewer
}

Esta propiedad es útil si inicializar una biblioteca en el constructor de la página que no se puede ejecutar en
tiempo de diseño.

Solución de problemas
Compruebe los siguientes problemas y la foros de Xamarin, si el controlador de vista previa no funciona.
Controlador de vista previa de XAML no está visible o muestra un error
Puede tardar algún tiempo para el controlador de vista previa en iniciarse, verá "Inicializando Render" hasta
que esté listo.
Intente cerrar y volver a abrir el archivo XAML.
Asegúrese de que su App clase tiene un constructor sin parámetros.
Comprobar la versión de Xamarin.Forms: debe ser al menos 3.6 de Xamarin.Forms. Puede actualizar a la
última versión de Xamarin.Forms a través de NuGet.
Comprobación de la instalación de JDK - vista previa de Android requiere al menos JDK 8.
Pruebe cualquier ajuste inicializa las clases en la página C# código subyacente en
if (!DesignMode.IsDesignModeEnabled) .

Controles personalizados no están procesando


Intente compilar el proyecto. El controlador de vista previa muestra la clase base del control si se produce un error
presentar el control, o si el creador del control elegido horizontal de representación en tiempo de diseño. Para
obtener más información, consulte representar controles personalizados en la vista previa de XAML.
Usar datos en tiempo de diseño con el controlador
de vista previa XAML
11/07/2019 • 5 minutes to read • Edit Online

Algunos diseños son difíciles de visualizar sin datos. Utilice estos consejos para sacar el máximo partido de la vista
previa de las páginas de datos con mucha actividad en la vista previa de XAML.

Conceptos básicos de datos de tiempo de diseño


Datos en tiempo de diseño están datos falsos que se establece para facilitar los controles visualizar en la vista
previa de XAML. Para empezar, agregue las siguientes líneas de código para el encabezado de la página XAML:

xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"

Después de agregar los espacios de nombres, puede colocar d: delante de cualquier atributo o un control para
mostrarlo en la vista previa de XAML. Elementos con d: no se muestran en tiempo de ejecución.
Por ejemplo, puede agregar texto a una etiqueta que normalmente tiene datos enlazados a él.

<Label Text="{Binding Name}" d:Text="Name!" />

En este ejemplo, sin d:Text , el controlador de vista previa de XAML mostraría nada para la etiqueta. En su lugar,
muestra "Name". donde la etiqueta tendrá datos reales en tiempo de ejecución.
Puede usar d: con cualquier atributo para un control de Xamarin.Forms, como colores, tamaños de fuente y
espaciado. Incluso puede agregarlo al propio control:

<d:Button Text="Design Time Button" />


En este ejemplo, el botón aparece sólo en tiempo de diseño. Use este método para colocar un marcador de
posición en una control personalizado no compatible con el controlador de vista previa de XAML.

Imágenes de vista previa en tiempo de diseño


Puede establecer un origen de tiempo de diseño para las imágenes que se enlaza a la página o se carga
dinámicamente en. En el proyecto Android, agregue la imagen que desea mostrar en la vista previa de XAML para
el recursos > Drawable carpeta. En el proyecto de iOS, agregue la imagen a la recursos carpeta. A continuación,
puede mostrar esa imagen en la vista previa de XAML en tiempo de diseño:

<Image Source={Binding ProfilePicture} d:Source="DesignTimePicture.jpg" />


Datos de tiempo de diseño para ListView
ListView es una forma popular para mostrar datos en una aplicación móvil. Sin embargo, son difíciles de visualizar
sin datos reales. Para utilizar datos en tiempo de diseño con ellos, tendrá que crear una matriz de tiempo de diseño
que se usará como ItemsSource. El controlador de vista previa de XAML muestra lo que está en esa matriz en la
ListView en tiempo de diseño.

<StackLayout>
<ListView ItemsSource="{Binding Items}">
<d:ListView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Item One</x:String>
<x:String>Item Two</x:String>
<x:String>Item Three</x:String>
</x:Array>
</d:ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding ItemName}"
d:Text="{Binding .}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Este ejemplo muestra un ListView de tres TextCells en la vista previa de XAML. Puede cambiar x:String a un
modelo de datos existente en el proyecto.
Consulte app de Hanselman.Forms de James Montemagno para obtener un ejemplo más complejo.

Alternativa: Codificar un ViewModel estático


Si no desea agregar datos en tiempo de diseño a controles individuales, puede configurar un almacén de datos
ficticios para enlazar a la página. Hacer referencia a la de James Montemagno entrada de blog sobre cómo agregar
datos en tiempo de diseño para ver cómo enlazar a una clase ViewModel estático en XAML.

Solución de problemas
Requisitos
Datos en tiempo de diseño requieren una versión mínima de Xamarin.Forms 3.6.
IntelliSense muestra con líneas onduladas Mis datos en tiempo de diseño
Esto es un problema conocido y se corregirá en una próxima versión de Visual Studio. El proyecto aún se
compilará sin errores.
El controlador de vista previa de XAML ha dejado de funcionar
Intente cerrar y volver a abrir el archivo XAML y limpiar y recompilar el proyecto.
Representar controles personalizados en la vista
previa XAML
11/07/2019 • 3 minutes to read • Edit Online

A veces, los controles personalizados no funcionan según lo previsto en la vista previa de XAML. Use las
instrucciones de este artículo para conocer las limitaciones de vista previa de los controles personalizados.

Modo de vista previa básico


Incluso si no ha creado el proyecto, el controlador de vista previa de XAML se representarán las páginas. Hasta
que se compile, cualquier control que se basa en el código subyacente mostrará su tipo base de Xamarin.Forms.
Cuando se compila el proyecto, el controlador de vista previa de XAML intentará mostrar controles
personalizados con la representación en tiempo de diseño habilitada. Si se produce un error en la representación,
mostrará el tipo base de Xamarin.Forms.

Habilitar la representación en tiempo de diseño para controles


personalizados
Si se realiza sus propios controles personalizados o usar los controles de una biblioteca de terceros, el controlador
de vista previa aparecer incorrectamente. Controles personalizados deben participar en el tiempo de
representación para aparecer en la vista previa, si escribió el control o se importa desde una biblioteca de diseño.
Con los controles que ha creado, agregue el [DesignTimeVisible(true)] a la clase del control para mostrarlo en la
vista previa:

namespace MyProject
{
[DesignTimeVisible(true)]
public class MyControl : BaseControl
{
// Your control's code here
}

Use clase base de ImageCirclePlugin de James Montemagno como ejemplo.

Controles de SkiaSharp
Actualmente, solo se admiten controles de SkiaSharp, cuando es una vista previa en iOS. No se representarán en
la versión preliminar de Android.

Solución de problemas
Comprobar la versión de Xamarin.Forms
Asegúrese de tener al menos 3.6 Xamarin.Forms instalado. Puede actualizar su versión de Xamarin.Forms en
NuGet.
Incluso con [DesignTimeVisible(true)] , mi control personalizado no está representando correctamente.
Controles personalizados que dependen en gran medida de los datos de código subyacente o de back-end no
siempre funcionan en la vista previa de XAML. Puede probar:
Mover el control, por lo que no inicializa si está habilitado el modo de diseño
Configurar datos en tiempo de diseño para mostrar datos falsos desde el back-end
El controlador de vista previa de XAML muestra el error "Los controles personalizados no están representar
correctamente"
Intente limpiar y recompilar el proyecto, o cerrar y volver a abrir el archivo XAML.
Espacios de nombres XAML en Xamarin.Forms
11/07/2019 • 8 minutes to read • Edit Online

XAML usa el atributo xmlns XML para las declaraciones de espacio de nombres. Este artículo presenta la sintaxis
del espacio de nombres XAML y muestra cómo declarar un espacio de nombres XAML para tener acceso a un
tipo.

Información general
Hay dos declaraciones de espacio de nombres XAML que siempre están dentro del elemento raíz de un archivo
XAML. La primera define el espacio de nombres predeterminado, tal como se muestra en el ejemplo de código
XAML siguiente:

xmlns="http://xamarin.com/schemas/2014/forms"

El espacio de nombres predeterminado especifica que los elementos definidos dentro del archivo XAML con
ningún prefijo hace referencia a clases de Xamarin.Forms, como ContentPage .
La segunda declaración de espacio de nombres usa el x prefijo, como se muestra en el ejemplo de código
XAML siguiente:

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

XAML usa los prefijos para declarar los espacios de nombres no predeterminados, con el prefijo que se va a usar
cuando se hace referencia a tipos del espacio de nombres. El x declaración de espacio de nombres especifica
que los elementos definen en el XAML con el prefijo x se usan para los elementos y atributos que son
intrínsecos de XAML (específicamente la especificación de XAML 2009).
La siguiente tabla se describen los x atributos de espacio de nombres compatibles con Xamarin.Forms:

CONSTRUCCIÓN DESCRIPCIÓN

x:Arguments Especifica los argumentos de constructor para un constructor


no predeterminado, o para una declaración de objeto del
método de fábrica.

x:Class Especifica el nombre de clase y espacio de nombres para una


clase definida en XAML. El nombre de clase debe coincidir con
el nombre de clase del archivo de código subyacente. Tenga
en cuenta que esta construcción sólo puede aparecer en el
elemento raíz de un archivo XAML.

x:DataType Especifica el tipo del objeto que se enlazan el elemento XAML


y sus elementos secundarios, a.

x:FactoryMethod Especifica un método de fábrica que puede usarse para


inicializar un objeto.

x:FieldModifier Especifica el nivel de acceso para los campos generados para


los elementos XAML con nombre.
CONSTRUCCIÓN DESCRIPCIÓN

x:Key Especifica una clave única definida por el usuario para cada
recurso en un ResourceDictionary . El valor de clave se usa
para recuperar el recurso XAML y se utiliza normalmente
como argumento para el StaticResource extensión de
marcado.

x:Name Especifica un nombre de objeto en tiempo de ejecución para


el elemento XAML. Establecer x:Name es similar a declarar
una variable en el código.

x:TypeArguments Especifica los argumentos de tipo genérico para el


constructor de un tipo genérico.

Para obtener más información sobre la x:DataType atributo, vea compilado enlaces. Para obtener más
información sobre la x:FieldModifier atributo, vea modificadores de campo. Para obtener más información
sobre la x:Arguments , x:FactoryMethod , y x:TypeArguments atributos, vea pasar argumentos en XAML.

NOTE
Además de los atributos de espacio de nombres enumerados anteriormente, Xamarin.Forms también incluye las
extensiones de marcado que se pueden consumir mediante el x prefijo de espacio de nombres. Para obtener más
información, consulte las extensiones de marcado XAML de consumo.

En XAML, las declaraciones de espacio de nombres se heredan de elemento primario al elemento secundario. Por
lo tanto, al definir un espacio de nombres en el elemento raíz de un archivo XAML, todos los elementos dentro de
ese archivo heredan la declaración de espacio de nombres.

Declarar espacios de nombres para tipos


Pueden hacer referencia a tipos en XAML mediante la declaración de un espacio de nombres XAML con un
prefijo, con la declaración de espacio de nombres que especifica el nombre del espacio de nombres de Common
Language Runtime (CLR ) y, opcionalmente, un nombre de ensamblado. Esto se logra mediante la definición de
los valores de las siguientes palabras clave dentro de la declaración de espacio de nombres:
clr-namespace: o mediante: : el espacio de nombres CLR declarado dentro del ensamblado que contiene los
tipos que se exponen como elementos XAML. Esta palabra clave es necesaria.
ensamblado = : el ensamblado que contiene el espacio de nombres CLR que se hace referencia. Este valor es
el nombre del ensamblado, sin la extensión de archivo. La ruta de acceso al ensamblado se debe establecer
como referencia en el archivo de proyecto que contiene el archivo XAML que hará referencia el ensamblado.
Esta palabra clave se puede omitir si el clr-namespace valor está dentro del mismo ensamblado que el
código de aplicación que hace referencia a los tipos.
Tenga en cuenta que el carácter que separa el clr-namespace o using símbolo (token) de su valor es un signo de
dos puntos, mientras que el carácter que separa el assembly símbolo (token) de su valor es un signo igual. El
carácter que se va a usar entre los dos tokens es un punto y coma.
El ejemplo de código siguiente muestra una declaración de espacio de nombres XAML:

<ContentPage ... xmlns:local="clr-namespace:HelloWorld" ...>


...
</ContentPage>
Como alternativa, esto puede escribirse como:

<ContentPage ... xmlns:local="using:HelloWorld" ...>


...
</ContentPage>

El local prefijo es una convención que se utiliza para indicar que los tipos del espacio de nombres son locales
de la aplicación. Como alternativa, si los tipos están en un ensamblado diferente, el nombre del ensamblado debe
también se define en la declaración de espacio de nombres, como se muestra en el ejemplo de código XAML
siguiente:

<ContentPage ... xmlns:behaviors="clr-namespace:Behaviors;assembly=BehaviorsLibrary" ...>


...
</ContentPage>

El prefijo de espacio de nombres, a continuación, se especifica al declarar una instancia de un tipo a partir de un
espacio de nombres importado, como se muestra en el ejemplo de código XAML siguiente:

<ListView ...>
<ListView.Behaviors>
<behaviors:EventToCommandBehavior EventName="ItemSelected" ... />
</ListView.Behaviors>
</ListView>

Para obtener información acerca de cómo definir un esquema del espacio de nombres personalizado, vea
esquemas de XAML personalizados Namespace.

Resumen
En este artículo introdujo la sintaxis del espacio de nombres XAML y se muestra cómo declarar un espacio de
nombres XAML para tener acceso a un tipo. XAML usa la xmlns puede hacer referencia a las declaraciones de
espacio de nombres y tipos de atributo XML en XAML mediante la declaración de un espacio de nombres XAML
con un prefijo.

Vínculos relacionados
Pasar argumentos en XAML
Esquemas personalizados Namespace XAML en
Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

descargar el ejemplo
Pueden consultar en una biblioteca de tipos de XAML al declarar un espacio de nombres XAML para la biblioteca,
con la declaración de espacio de nombres especificando el nombre de espacio de nombres de Common Language
Runtime (CLR ) y un nombre de ensamblado:

<ContentPage ...
xmlns:controls="clr-namespace:MyCompany.Controls;assembly="MyCompany.Controls">
...
</ContentPage>

Sin embargo, al especificar un nombre de espacio de nombres y ensamblado CLR en un xmlns definición puede
ser difícil y propenso a errores. Además, varias declaraciones de espacio de nombres XAML pueden ser necesarias
si la biblioteca contiene tipos en varios espacios de nombres.
Un enfoque alternativo es definir un esquema del espacio de nombres personalizado, como
http://mycompany.com/schemas/controls , que se asigna a uno o varios espacios de nombres CLR. Esto permite una
sola declaración de espacio de nombres XAML hacer referencia a todos los tipos en un ensamblado, incluso si se
encuentran en diferentes espacios de nombres. También permite una sola declaración de espacio de nombres
XAML a tipos de referencia en varios ensamblados.
Para obtener más información acerca de los espacios de nombres XAML, vea espacios de nombres XAML en
Xamarin.Forms.

Definir un esquema del espacio de nombres personalizado


La aplicación de ejemplo contiene una biblioteca que expone algunos controles simples, tales como CircleButton :

using Xamarin.Forms;

namespace MyCompany.Controls
{
public class CircleButton : Button
{
...
}
}

Todos los controles de la biblioteca que residen en el MyCompany.Controls espacio de nombres. Estos controles
pueden exponerse a un ensamblado que realiza la llamada a través de un esquema del espacio de nombres
personalizado.
Un esquema del espacio de nombres personalizado se define con el XmlnsDefinitionAttribute (clase), que
especifica la asignación entre un espacio de nombres XAML y uno o varios espacios de nombres CLR. El
XmlnsDefinitionAttribute toma dos argumentos: el nombre de espacio de nombres XAML y el nombre del
espacio de nombres CLR. El nombre de espacio de nombres XAML se almacena en el
XmlnsDefinitionAttribute.XmlNamespace propiedad y el nombre del espacio de nombres CLR se almacena en el
XmlnsDefinitionAttribute.ClrNamespace propiedad.

NOTE
El XmlnsDefinitionAttribute clase también tiene una propiedad denominada AssemblyName , que se puede establecer
opcionalmente para el nombre del ensamblado. Esto solo es necesario cuando un espacio de nombres CLR al que hace
referencia desde un XmlnsDefinitionAttribute está en un ensamblado externo.

El XmlnsDefinitionAttribute debe definirse en el nivel de ensamblado en el proyecto que contiene los espacios de
nombres CLR que se asignará en el esquema del espacio de nombres personalizado. El ejemplo siguiente se
muestra el AssemblyInfo.cs archivo desde la aplicación de ejemplo:

using Xamarin.Forms;
using MyCompany.Controls;

[assembly: Preserve]
[assembly: XmlnsDefinition("http://mycompany.com/schemas/controls", "MyCompany.Controls")]

Este código crea un esquema del espacio de nombres personalizado que se asigna el
http://mycompany.com/schemas/controls dirección URL a la MyCompany.Controls espacio de nombres CLR. Además,
el Preserve atributo se especifica en el ensamblado, para asegurarse de que el vinculador conserva todos los
tipos del ensamblado.

IMPORTANT
El Preserve atributo se debe aplicar a las clases del ensamblado que se asignan mediante el esquema del espacio de
nombres personalizado o aplicado a todo el ensamblado.

A continuación, el esquema del espacio de nombres personalizado puede utilizarse para la resolución de tipos en
archivos XAML.

Consumo de un esquema del espacio de nombres personalizado


Para consumir tipos desde el esquema del espacio de nombres personalizado, el compilador XAML requiere que
hay una referencia de código desde el ensamblado que consume los tipos, al ensamblado que define los tipos.
Esto puede realizarse mediante la adición de una clase que contiene un Init método para el ensamblado que
define los tipos que van a consumir a través de XAML:

namespace MyCompany.Controls
{
public static class Controls
{
public static void Init()
{
}
}
}

El Init , a continuación, se puede llamar al método desde el ensamblado que consume los tipos del esquema del
espacio de nombres personalizado:
using Xamarin.Forms;
using MyCompany.Controls;

namespace CustomNamespaceSchemaDemo
{
public partial class MainPage : ContentPage
{
public MainPage()
{
Controls.Init();
InitializeComponent();
}
}
}

WARNING
Se producirá un error al incluir este tipo de referencia de código en el compilador XAML que no se pueda localizar el
ensamblado que contiene los tipos de esquema del espacio de nombres personalizado.

Para consumir el CircleButton control, se declara un espacio de nombres XAML, con la declaración de espacio de
nombres que especifica la dirección URL de esquema de espacio de nombres personalizado:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="http://mycompany.com/schemas/controls"
x:Class="CustomNamespaceSchemaDemo.MainPage">
<StackLayout Margin="20,35,20,20">
...
<controls:CircleButton Text="+"
BackgroundColor="Fuchsia"
BorderColor="Black"
CircleDiameter="100" />
<controls:CircleButton Text="-"
BackgroundColor="Teal"
BorderColor="Silver"
CircleDiameter="70" />
...
</StackLayout>
</ContentPage>

CircleButton las instancias, a continuación, se pueden agregar a la ContentPage declarando con el controls
prefijo de espacio de nombres.
Para buscar el espacio de nombres personalizado de tipos de esquema, buscará en ensamblados de referencia
para Xamarin.Forms XmlnsDefinitionAttribute instancias. Si el xmlns atributo para un elemento en un archivo
XAML coincide con el XmlNamespace valor de propiedad en un XmlnsDefinitionAttribute , Xamarin.Forms
intentará usar la XmlnsDefinitionAttribute.ClrNamespace valor de propiedad para la resolución del tipo. Si se
produce un error en la resolución de tipos, Xamarin.Forms seguirá tratando de resolución de tipos en función de
cualquier coincidencia adicional XmlnsDefinitionAttribute instancias.
El resultado es que dos CircleButton se muestran las instancias:
Vínculos relacionados
Esquemas personalizados de Namespace (ejemplo)
Prefijos recomendados de espacio de nombres de XAML
Espacios de nombres XAML en Xamarin.Forms
XAML Namespace recomienda prefijos en
Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

La XmlnsPrefixAttribute clase puede ser usada por los autores de controles para especificar un prefijo
recomendado para asociar a un espacio de nombres XAML para el uso XAML. El prefijo es útil cuando se admiten
la serialización del árbol de objetos para XAML, o al interactuar con un entorno de diseño que tiene características
de edición de XAML. Por ejemplo:
Editores de texto XAML podrían utilizar el XmlnsPrefixAttribute como una sugerencia para un espacio de
nombres XAML inicial xmlns asignación.
Los entornos de diseño XAML podrían utilizar el XmlnsPrefixAttribute se agreguen asignaciones para el
XAML al arrastrar objetos fuera de un cuadro de herramientas y a una superficie de diseño visual.
Recomienda que los prefijos de espacio de nombres deben definirse en el nivel de ensamblado con el
XmlnsPrefixAttribute constructor, que toma dos argumentos: una cadena que especifica el identificador de un
espacio de nombres XAML y una cadena que especifica un prefijo recomendado:

[assembly: XmlnsPrefix("http://xamarin.com/schemas/2014/forms", "xf")]

Los prefijos deben utilizar las cadenas cortas, porque el prefijo se aplica normalmente a todos los elementos
serializados que proceden del espacio de nombres XAML. Por lo tanto, la longitud de cadena del prefijo puede
tener un efecto notable en el tamaño de la salida XAML serializada.

NOTE
Más de un XmlnsPrefixAttribute pueden aplicarse a un ensamblado. Por ejemplo, si tiene un ensamblado que define los
tipos de más de un espacio de nombres XAML, podría definir los valores de prefijo diferente para cada espacio de nombres
XAML.

Vínculos relacionados
Esquemas de espacio de nombres personalizado XAML
Espacios de nombres XAML en Xamarin.Forms
Extensiones de marcado XAML
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Las extensiones de marcado XAML ayudar a ampliar la eficacia y flexibilidad de XAML al permitir que los
atributos del elemento que se pueden establecer desde orígenes que no sean cadenas de texto literal.
Por ejemplo, normalmente, debe establecer el Color propiedad de BoxView similar al siguiente:

<BoxView Color="Blue" />

O bien, puede establecerlo en un valor hexadecimal del color RGB:

<BoxView Color="#FF0080" />

En cualquier caso, la cadena de texto se establece en el Color atributo se convierte en un Color valor por el
ColorTypeConverter clase.

Es posible que prefiera en su lugar, establezca el Color desde un valor almacenado en un diccionario de recursos,
o desde el valor de una propiedad estática de una clase que ha creado o una propiedad de tipo de atributo Color
de otro elemento en la página, o construido desde Separe los valores de matiz, saturación y luminosidad.
Todas estas opciones son posibles mediante las extensiones de marcado XAML. Pero no permita que la frase
"extensiones de marcado" lo abrume: Extensiones de marcado XAML son no extensiones a XML. Incluso con las
extensiones de marcado XAML, XAML es siempre XML legal.
Una extensión de marcado es simplemente una manera diferente para expresar un atributo de un elemento. Las
extensiones de marcado XAML son normalmente identificables mediante una configuración de atributo que se
encierra entre llaves:

<BoxView Color="{StaticResource themeColor}" />

Es cualquier valor de atributo están entre llaves siempre una extensión de marcado XAML. Sin embargo, como
verá, las extensiones de marcado XAML también pueden hacer referencia sin el uso de llaves.
En este artículo se divide en dos partes:

Consumo de extensiones de marcado XAML


Use las extensiones de marcado XAML definidas en Xamarin.Forms.

Creación de extensiones de marcado XAML


Escribir sus propias extensiones de marcado XAML personalizados.

Vínculos relacionados
Extensiones de marcado (ejemplo)
Capítulo de extensiones de marcado XAML de Xamarin.Forms libro
Diccionarios de recursos
Estilos dinámicos
Enlace de datos
Consumo de las extensiones de marcado XAML
11/07/2019 • 29 minutes to read • Edit Online

descargar el ejemplo
Las extensiones de marcado XAML que ayudan a mejorar la eficacia y flexibilidad de XAML al permitir que los
atributos del elemento debe establecerse en una variedad de orígenes. Varias extensiones de marcado XAML
forman parte de la especificación de XAML 2009. Esta información aparece en los archivos XAML con el habitual
x prefijo de espacio de nombres y se conocen comúnmente por este prefijo. En este artículo se describe las
extensiones de marcado siguiente:
x:Static : hacer referencia a propiedades estáticas, campos o los miembros de enumeración.
x:Reference – con el nombre de elementos de la página de referencia.
x:Type : establezca un atributo en un System.Type objeto.
x:Array : construya una matriz de objetos de un tipo determinado.
x:Null : establezca un atributo en un null valor.
OnPlatform : personalizar la apariencia de la interfaz de usuario en forma de acuerdo con la plataforma.
OnIdiom : personalizar la apariencia de la interfaz de usuario en función de la expresión del dispositivo se está
ejecutando la aplicación en.
DataTemplate : convierte un tipo en un DataTemplate .

Las extensiones de marcado XAML adicionales históricamente han sido compatible con otras implementaciones
de XAML y también son compatibles con Xamarin.Forms. Estos se describen con más detalle en otros artículos:
StaticResource – hacer referencia a objetos de un diccionario de recursos, como se describe en el artículo los
diccionarios de recursos.
DynamicResource – responder a cambios en los objetos en un diccionario de recursos, como se describe en el
artículo estilos dinámicos.
Binding – establecer un vínculo entre las propiedades de dos objetos, como se describe en el artículo enlace
de datos.
TemplateBinding – realiza el enlace de datos de una plantilla de control, como se describe en el artículo desde
una plantilla de Control de enlace.
El RelativeLayout diseño hace uso de la extensión de marcado personalizada ConstraintExpression . Esta
extensión de marcado se describe en el artículo RelativeLayout.

Extensiones de marcado x:Static


El x:Static extensión de marcado es compatible con la StaticExtension clase. La clase tiene una propiedad única
denominada Member de tipo string establecer en el nombre de una constante pública, una propiedad estática,
campo estático o miembro de enumeración.
Una forma común de usar x:Static consiste en definir primero una clase con algunas constantes o variables
estáticas, como Este diminuto AppConstants clase en el MarkupExtensions programa:

static class AppConstants


{
public static double NormalFontSize = 18;
}
El x: Static demostración página muestra varias maneras de usar el x:Static extensión de marcado. El enfoque
más detallado, crea una instancia del StaticExtension clase entre Label.FontSize etiquetas de elemento de
propiedad:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:MarkupExtensions"
x:Class="MarkupExtensions.StaticDemoPage"
Title="x:Static Demo">
<StackLayout Margin="10, 0">
<Label Text="Label No. 1">
<Label.FontSize>
<x:StaticExtension Member="local:AppConstants.NormalFontSize" />
</Label.FontSize>
</Label>

···

</StackLayout>
</ContentPage>

El analizador XAML también permite la StaticExtension clase va a abreviar como x:Static :

<Label Text="Label No. 2">


<Label.FontSize>
<x:Static Member="local:AppConstants.NormalFontSize" />
</Label.FontSize>
</Label>

Esto se puede simplificar aún más, pero el cambio presenta parte de la nueva sintaxis: Consta de colocar el
StaticExtension clase y la configuración entre llaves del miembro. La expresión resultante se establece
directamente en el FontSize atributo:

<Label Text="Label No. 3"


FontSize="{x:StaticExtension Member=local:AppConstants.NormalFontSize}" />

Tenga en cuenta que hay ningún comillas encerrado entre llaves. El Member propiedad de StaticExtension ya no
es un atributo XML. En realidad es parte de la expresión para la extensión de marcado.
Tal como se puede abreviar x:StaticExtension a x:Static cuando se usa como un elemento de objeto, también
puede abreviar en la expresión entre llaves:

<Label Text="Label No. 4"


FontSize="{x:Static Member=local:AppConstants.NormalFontSize}" />

El StaticExtension clase tiene un ContentProperty que hacen referencia a la propiedad de atributo Member , que
marca esta propiedad como propiedad de contenido de la clase predeterminada. Para las extensiones de marcado
XAML que expresa entre llaves, puede eliminar el Member= forma parte de la expresión:

<Label Text="Label No. 5"


FontSize="{x:Static local:AppConstants.NormalFontSize}" />

Se trata de la forma más común de la x:Static extensión de marcado.


El demostración estático página contiene dos ejemplos de otros. La etiqueta a la raíz del archivo XAML contiene
una declaración de espacio de nombres XML para .NET System espacio de nombres:

xmlns:sys="clr-namespace:System;assembly=mscorlib"

Esto permite la Label tamaño de fuente debe establecerse en el campo estático Math.PI . Esto da como resultado
texto pequeño en su lugar, por lo que la Scale propiedad está establecida en Math.E :

<Label Text="&#x03C0; &#x00D7; E sized text"


FontSize="{x:Static sys:Math.PI}"
Scale="{x:Static sys:Math.E}"
HorizontalOptions="Center" />

El último ejemplo se muestra la Device.RuntimePlatform valor. El Environment.NewLine propiedad estática se utiliza


para insertar un carácter de nueva línea entre los dos Span objetos:

<Label HorizontalTextAlignment="Center"
FontSize="{x:Static local:AppConstants.NormalFontSize}">
<Label.FormattedText>
<FormattedString>
<Span Text="Runtime Platform: " />
<Span Text="{x:Static sys:Environment.NewLine}" />
<Span Text="{x:Static Device.RuntimePlatform}" />
</FormattedString>
</Label.FormattedText>
</Label>

Este es el ejemplo en ejecución:

x:Reference (extensión de marcado)


El x:Reference extensión de marcado es compatible con la ReferenceExtension clase. La clase tiene una
propiedad única denominada Name typu string establecer en el nombre de un elemento en la página que se
asignó un nombre con x:Name . Esto Name propiedad es la propiedad content de ReferenceExtension , por lo que
Name= no es necesaria cuando x:Reference aparece entre llaves.

El x:Reference extensión de marcado se utiliza exclusivamente con los enlaces de datos, que se describen con
más detalle en el artículo enlace de datos.
El x: Reference demostración página muestra dos usos de x:Reference con enlaces de datos, la primera que se
usa para establecer el Source propiedad de la Binding objeto y el segundo donde se usa para establecer el
BindingContext propiedad para los enlaces de datos de dos:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MarkupExtensions.ReferenceDemoPage"
x:Name="page"
Title="x:Reference Demo">

<StackLayout Margin="10, 0">

<Label Text="{Binding Source={x:Reference page},


StringFormat='The type of this page is {0}'}"
FontSize="18"
VerticalOptions="CenterAndExpand"
HorizontalTextAlignment="Center" />

<Slider x:Name="slider"
Maximum="360"
VerticalOptions="Center" />

<Label BindingContext="{x:Reference slider}"


Text="{Binding Value, StringFormat='{0:F0}&#x00B0; rotation'}"
Rotation="{Binding Value}"
FontSize="24"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

</StackLayout>
</ContentPage>

Ambos x:Reference expresiones usan la versión abreviada de la ReferenceExtension nombre de clase y elimine el
Name= forma parte de la expresión. En el primer ejemplo, el x:Reference extensión de marcado está incrustada en
el Binding extensión de marcado. Tenga en cuenta que el Source y StringFormat configuración está separada
por comas. Este es el programa que se ejecuta:

x:Type (Extensión de marcado)


El extensión de marcado es el equivalente XAML de C# typeof palabra clave. Es compatible con la
x:Type
TypeExtension (clase), que define una propiedad denominada TypeName de tipo string que se establece en un
nombre de clase o estructura. El x:Type devuelve de la extensión de marcado el System.Type objeto de esa clase
o estructura. TypeName es la propiedad content de TypeExtension , por lo que TypeName= no es necesaria cuando
x:Type aparece entre llaves.

Dentro de Xamarin.Forms, hay varias propiedades que tienen argumentos de tipo Type . Algunos ejemplos son el
TargetType propiedad de Style y el x: TypeArguments atributo utilizado para especificar los argumentos en las
clases genéricas. Sin embargo, el analizador XAML se realiza el typeof operación automáticamente y el x:Type
extensión de marcado no se usa en estos casos.
Un único lugar donde x:Type es necesarios es con el x:Array extensión de marcado, como se describe en el
siguiente sección.
El x:Type extensión de marcado también es útil al construir un menú donde cada elemento de menú
correspondiente a un objeto de un tipo determinado. Puede asociar un Type con cada elemento de menú de
objetos y, a continuación, cree una instancia del objeto cuando se selecciona el elemento de menú.
Se trata cómo el menú de navegación en MainPage en el las extensiones de marcado programa works. El
MainPage.xaml archivo contiene un TableView con cada TextCell correspondiente a una página determinada
en el programa:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MarkupExtensions"
x:Class="MarkupExtensions.MainPage"
Title="Markup Extensions"
Padding="10">
<TableView Intent="Menu">
<TableRoot>
<TableSection>
<TextCell Text="x:Static Demo"
Detail="Access constants or statics"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:StaticDemoPage}" />

<TextCell Text="x:Reference Demo"


Detail="Reference named elements on the page"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:ReferenceDemoPage}" />

<TextCell Text="x:Type Demo"


Detail="Associate a Button with a Type"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:TypeDemoPage}" />

<TextCell Text="x:Array Demo"


Detail="Use an array to fill a ListView"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:ArrayDemoPage}" />

···

</TableRoot>
</TableView>
</ContentPage>

Aquí está la página principal de apertura en las extensiones de marcado:


Cada CommandParameter propiedad está establecida en un x:Type extensión de marcado que se hace referencia a
una de las otras páginas. El Command propiedad está enlazada a una propiedad denominada NavigateCommand . Esta
propiedad se define en el MainPage archivo de código subyacente:

public partial class MainPage : ContentPage


{
public MainPage()
{
InitializeComponent();

NavigateCommand = new Command<Type>(async (Type pageType) =>


{
Page page = (Page)Activator.CreateInstance(pageType);
await Navigation.PushAsync(page);
});

BindingContext = this;
}

public ICommand NavigateCommand { private set; get; }


}

El NavigateCommand propiedad es un Command objeto que implementa un comando execute con un argumento de
tipo Type — el valor de CommandParameter . Usa el método Activator.CreateInstance para crear una instancia de
la página y, a continuación, navega a ella. El constructor concluye estableciendo el BindingContext de la página a sí
mismo, lo que permite el Binding en Command funcione. Consulte la enlace de datos artículo y especialmente los
Commanding artículo para obtener más detalles sobre este tipo de código.
El x: Type demostración página usa una técnica similar para crear instancias de elementos de Xamarin.Forms y
agregarlos a un StackLayout . El archivo XAML inicialmente consta de tres Button elementos con sus Command
propiedades establecidas en un Binding y CommandParameter propiedades establecidas en los tipos de las tres
vistas de Xamarin.Forms:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MarkupExtensions.TypeDemoPage"
Title="x:Type Demo">

<StackLayout x:Name="stackLayout"
Padding="10, 0">

<Button Text="Create a Slider"


HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Command="{Binding CreateCommand}"
CommandParameter="{x:Type Slider}" />

<Button Text="Create a Stepper"


HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Command="{Binding CreateCommand}"
CommandParameter="{x:Type Stepper}" />

<Button Text="Create a Switch"


HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Command="{Binding CreateCommand}"
CommandParameter="{x:Type Switch}" />
</StackLayout>
</ContentPage>

El archivo de código subyacente define e inicializa la CreateCommand propiedad:

public partial class TypeDemoPage : ContentPage


{
public TypeDemoPage()
{
InitializeComponent();

CreateCommand = new Command<Type>((Type viewType) =>


{
View view = (View)Activator.CreateInstance(viewType);
view.VerticalOptions = LayoutOptions.CenterAndExpand;
stackLayout.Children.Add(view);
});

BindingContext = this;
}

public ICommand CreateCommand { private set; get; }


}

El método que se ejecuta cuando un Button se presiona crea una nueva instancia del argumento, se establece su
VerticalOptions propiedad y lo agrega a la StackLayout . Los tres Button elementos, a continuación, compartan
la página con las vistas creadas dinámicamente:
x:Array (Extensión de marcado)
El x:Array extensión de marcado permite definir una matriz en el marcado. Es compatible con la ArrayExtension
(clase), que define dos propiedades:
Type de tipo Type , lo que indica el tipo de los elementos de la matriz.
Items de tipo IList , que es una colección de los propios elementos. Se trata de la propiedad content de
ArrayExtension .

El x:Array propia extensión de marcado nunca aparece entre llaves. En su lugar, x:Array etiquetas inicial y final
delimitan la lista de elementos. Establecer el Type propiedad a un x:Type extensión de marcado.
El x: Array demostración página muestra cómo usar x:Array para agregar elementos a un ListView
estableciendo el ItemsSource propiedad a una matriz:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MarkupExtensions.ArrayDemoPage"
Title="x:Array Demo Page">
<ListView Margin="10">
<ListView.ItemsSource>
<x:Array Type="{x:Type Color}">
<Color>Aqua</Color>
<Color>Black</Color>
<Color>Blue</Color>
<Color>Fuchsia</Color>
<Color>Gray</Color>
<Color>Green</Color>
<Color>Lime</Color>
<Color>Maroon</Color>
<Color>Navy</Color>
<Color>Olive</Color>
<Color>Pink</Color>
<Color>Purple</Color>
<Color>Red</Color>
<Color>Silver</Color>
<Color>Teal</Color>
<Color>White</Color>
<Color>Yellow</Color>
</x:Array>
</ListView.ItemsSource>

<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<BoxView Color="{Binding}"
Margin="3" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>

El ViewCell crea un simple BoxView para cada entrada de color:

Hay varias maneras de especificar la persona Color elementos de esta matriz. Puede usar un x:Static extensión
de marcado:
<x:Static Member="Color.Blue" />

O bien, puede usar StaticResource para recuperar un color de un diccionario de recursos:

<StaticResource Key="myColor" />

Hacia el final de este artículo, verá una extensión de marcado XAML personalizada que también crea un nuevo
valor de color:

<local:HslColor H="0.5" S="1.0" L="0.5" />

Al definir matrices de tipos comunes, como cadenas o números, use las etiquetas enumeradas en el pasar
argumentos de Constructor artículo para delimitar los valores.

x:Null (Extensión de marcado)


El x:Null extensión de marcado es compatible con la NullExtension clase. No tiene ninguna propiedad lo que es
simplemente el equivalente XAML de C# null palabra clave.
El x:Null extensión de marcado es rara vez es necesario y se usan en contadas ocasiones, pero si lo encuentra lo
necesita, será muy contento de que existe.
El x: Null demostración página ilustra un escenario cuando x:Null podría ser conveniente. Supongamos que
define un modo implícito Style para Label que incluye un Setter que establece el FontFamily propiedad a un
nombre de familia depende de la plataforma:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MarkupExtensions.NullDemoPage"
Title="x:Null Demo">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="FontSize" Value="48" />
<Setter Property="FontFamily">
<Setter.Value>
<OnPlatform x:TypeArguments="x:String">
<On Platform="iOS" Value="Times New Roman" />
<On Platform="Android" Value="serif" />
<On Platform="UWP" Value="Times New Roman" />
</OnPlatform>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<ContentPage.Content>
<StackLayout Padding="10, 0">
<Label Text="Text 1" />
<Label Text="Text 2" />

<Label Text="Text 3"


FontFamily="{x:Null}" />

<Label Text="Text 4" />


<Label Text="Text 5" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

A continuación, verá que para uno de los Label elementos, desea que todos los valores de propiedades en la
parte implícita Style , excepto para el FontFamily , que desee que sea el valor predeterminado. Puede definir otra
Style para ese propósito, pero un enfoque más sencillo consiste simplemente en establecer el FontFamily
propiedad de la instancia concreta Label a x:Null , tal y como se muestra en el centro de Label .
Este es el programa que se ejecuta:

Tenga en cuenta que cuatro de los Label elementos tienen una fuente de serif, pero el centro Label tiene la
fuente sans serif de forma predeterminada.
Extensión de marcado OnPlatform
El OnPlatform extensión de marcado permite personalizar la apariencia de la interfaz de usuario en forma de
acuerdo con la plataforma. Proporciona la misma funcionalidad que el OnPlatform y On clases, pero con una
representación más concisa.
El OnPlatform extensión de marcado es compatible con la OnPlatformExtension (clase), que define las siguientes
propiedades:
Default de tipo object , establecido en un valor predeterminado que se aplicará a las propiedades que
representan las plataformas.
Android de tipo object , que se establece en un valor que se aplicará de Android.
GTK de tipo object , que se establece en un valor que se aplicará de plataformas de GTK +.
iOS de tipo object , que se establece en un valor que se aplicará de iOS.
macOS de tipo object , que se establece en un valor que se aplicará de macOS.
Tizen de tipo object , que se establece en un valor que se aplicará de la plataforma Tizen.
UWP de tipo object , que se establece en un valor que se aplicará de la plataforma Universal de Windows.
WPF de tipo object , que se establece en un valor que se aplicará de la plataforma Windows Presentation
Foundation.
Converter de tipo IValueConverter , que se establece en un IValueConverter implementación.
ConverterParameter de tipo object , que se establecen en un valor para pasar el IValueConverter
implementación.

NOTE
El analizador XAML permite la OnPlatformExtension clase va a abreviar como OnPlatform .

El Default propiedad es la propiedad content de OnPlatformExtension . Por lo tanto, para las expresiones de
marcado XAML expresadas con llaves, puede eliminar el Default= forma parte de la expresión siempre que sea el
primer argumento.

IMPORTANT
El analizador XAML espera que se proporcionen valores del tipo correcto a las propiedades que se consume el OnPlatform
extensión de marcado. Si es necesaria, la conversión de tipos la OnPlatform extensión de marcado se intentará realizar
mediante los convertidores predeterminados proporcionados por Xamarin.Forms. Sin embargo, hay algunas conversiones de
tipos que no se puede realizar los convertidores de forma predeterminada y en estos casos el Converter propiedad debe
establecerse en un IValueConverter implementación.

El OnPlatform demostración página muestra cómo usar el OnPlatform extensión de marcado:

<BoxView Color="{OnPlatform Yellow, iOS=Red, Android=Green, UWP=Blue}"


WidthRequest="{OnPlatform 250, iOS=200, Android=300, UWP=400}"
HeightRequest="{OnPlatform 250, iOS=200, Android=300, UWP=400}"
HorizontalOptions="Center" />

En este ejemplo, las tres OnPlatform expresiones usan la versión abreviada de la OnPlatformExtension nombre de
clase. Los tres OnPlatform conjunto de extensiones de marcado el Color , WidthRequest , y HeightRequest
propiedades de la BoxView en valores distintos en iOS, Android y UWP. Las extensiones de marcado también
proporcionan valores predeterminados para estas propiedades en las plataformas que no están especificadas, al
tiempo que elimina la Default= forma parte de la expresión. Tenga en cuenta que las propiedades de extensión de
marcado que se establecen están separadas por comas.
Este es el programa que se ejecuta:

Extensión de marcado OnIdiom


El OnIdiom permite personalizar la apariencia de la interfaz de usuario en función de la expresión de la aplicación
se está ejecutando en el dispositivo de extensión de marcado. Es compatible con la OnIdiomExtension (clase), que
define las siguientes propiedades:
Default de tipo object , establecido en un valor predeterminado que se aplicará a las propiedades que
representan las expresiones de dispositivo.
Phone de tipo object , que se establece en un valor que se aplicará de teléfonos.
Tablet de tipo object , que se establece en un valor que se aplicará de tabletas.
Desktop de tipo object , que se establece en un valor que se aplicará de plataformas de escritorio.
TV de tipo object , que se establece en un valor que se aplicará de plataformas de TV.
Watch de tipo object , que se establece en un valor que se aplicará de plataformas de inspección.
Converter de tipo IValueConverter , que se establece en un IValueConverter implementación.
ConverterParameter de tipo object , que se establecen en un valor para pasar el IValueConverter
implementación.

NOTE
El analizador XAML permite la OnIdiomExtension clase va a abreviar como OnIdiom .

El Default propiedad es la propiedad content de OnIdiomExtension . Por lo tanto, para las expresiones de marcado
XAML expresadas con llaves, puede eliminar el Default= forma parte de la expresión siempre que sea el primer
argumento.
IMPORTANT
El analizador XAML espera que se proporcionen valores del tipo correcto a las propiedades que se consume el OnIdiom
extensión de marcado. Si es necesaria, la conversión de tipos la OnIdiom extensión de marcado se intentará realizar
mediante los convertidores predeterminados proporcionados por Xamarin.Forms. Sin embargo, hay algunas conversiones de
tipos que no se puede realizar los convertidores de forma predeterminada y en estos casos el Converter propiedad debe
establecerse en un IValueConverter implementación.

El OnIdiom demostración página muestra cómo usar el OnIdiom extensión de marcado:

<BoxView Color="{OnIdiom Yellow, Phone=Red, Tablet=Green, Desktop=Blue}"


WidthRequest="{OnIdiom 100, Phone=200, Tablet=300, Desktop=400}"
HeightRequest="{OnIdiom 100, Phone=200, Tablet=300, Desktop=400}"
HorizontalOptions="Center" />

En este ejemplo, las tres OnIdiom expresiones usan la versión abreviada de la OnIdiomExtension nombre de clase.
Los tres OnIdiom conjunto de extensiones de marcado el Color , WidthRequest , y HeightRequest propiedades de
la BoxView en valores distintos en el teléfono, tableta y escritorio expresiones. Las extensiones de marcado
también proporcionan valores predeterminados para estas propiedades en las expresiones que no se especifican,
mientras se elimina el Default= forma parte de la expresión. Tenga en cuenta que las propiedades de extensión
de marcado que se establecen están separadas por comas.
Este es el programa que se ejecuta:

Extensión de marcado de DataTemplate


El extensión de marcado permite convertir un tipo en un DataTemplate . Es compatible con la
DataTemplate
DataTemplateExtension (clase), que define un TypeName propiedad de tipo string , es decir se establece en el
nombre del tipo para convertirse en un DataTemplate . El TypeName propiedad es la propiedad content de
DataTemplateExtension . Por lo tanto, para las expresiones de marcado XAML expresadas con llaves, puede
eliminar el TypeName= forma parte de la expresión.

NOTE
El analizador XAML permite la DataTemplateExtension clase va a abreviar como DataTemplate .
Un uso típico de esta extensión de marcado está en una aplicación de Shell, como se muestra en el ejemplo
siguiente:

<ShellContent Title="Monkeys"
Icon="monkey.png"
ContentTemplate="{DataTemplate views:MonkeysPage}" />

En este ejemplo, MonkeysPage se convierte de un ContentPage a un DataTemplate , que se establece como el valor
de la ShellContent.ContentTemplate propiedad. Esto garantiza que MonkeysPage es sólo cuando tenga lugar la
navegación a la página se han creado, en lugar de al iniciarse la aplicación.
Para obtener más información acerca de las aplicaciones de Shell, consulte Xamarin.Forms Shell.

Definir sus propias extensiones de marcado


Si se ha encontrado una necesidad de una extensión de marcado XAML que no está disponible en Xamarin.Forms,
puede crear las suyas propias.

Vínculos relacionados
Extensiones de marcado (ejemplo)
Capítulo de extensiones de marcado XAML de Xamarin.Forms libro
Diccionarios de recursos
Estilos dinámicos
Enlace de datos
Shell de Xamarin.Forms.
Creación de extensiones de marcado XAML
11/07/2019 • 10 minutes to read • Edit Online

descargar el ejemplo
En el nivel de programación, una extensión de marcado XAML es una clase que implementa el IMarkupExtension
o IMarkupExtension<T> interfaz. Puede explorar el código fuente de las extensiones de marcado estándar se
describe a continuación, en el MarkupExtensions directory del repositorio de Xamarin.Forms GitHub.
También es posible definir sus propias extensiones de marcado XAML personalizados derivando de
IMarkupExtension o IMarkupExtension<T> . Use el formulario genérico si la extensión de marcado Obtiene un valor
de un tipo determinado. Este es el caso con varias de las extensiones de marcado de Xamarin.Forms:
TypeExtension se deriva de IMarkupExtension<Type>
ArrayExtension se deriva de IMarkupExtension<Array>
DynamicResourceExtension se deriva de IMarkupExtension<DynamicResource>
BindingExtension se deriva de IMarkupExtension<BindingBase>
ConstraintExpression se deriva de IMarkupExtension<Constraint>

Los dos IMarkupExtension interfaces definen solo un método denominado ProvideValue :

public interface IMarkupExtension


{
object ProvideValue(IServiceProvider serviceProvider);
}

public interface IMarkupExtension<out T> : IMarkupExtension


{
new T ProvideValue(IServiceProvider serviceProvider);
}

Puesto que IMarkupExtension<T> se deriva de IMarkupExtension e incluye el new palabra clave en ProvideValue ,
contiene ambos ProvideValue métodos.
Muy a menudo, las extensiones de marcado XAML definen propiedades que contribuyen al valor devuelto. (La
excepción obvia es NullExtension , en el que ProvideValue simplemente devuelve null .) El ProvideValue
método tiene un único argumento de tipo IServiceProvider que se tratará más adelante en este artículo.

Una extensión de marcado para especificar Color


La siguiente extensión de marcado XAML le permite construir un Color valor mediante los componentes de
matiz, saturación y luminosidad. Define las cuatro propiedades de los cuatro componentes del color, incluido un
componente alfa que se inicializa en 1. La clase se deriva de IMarkupExtension<Color> para indicar un Color valor
devuelto:
public class HslColorExtension : IMarkupExtension<Color>
{
public double H { set; get; }

public double S { set; get; }

public double L { set; get; }

public double A { set; get; } = 1.0;

public Color ProvideValue(IServiceProvider serviceProvider)


{
return Color.FromHsla(H, S, L, A);
}

object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)


{
return (this as IMarkupExtension<Color>).ProvideValue(serviceProvider);
}
}

Dado que IMarkupExtension<T> deriva IMarkupExtension , la clase debe contener dos ProvideValue métodos, uno
que devuelve Color y otra que devuelve object , pero el segundo método simplemente puede llamar primero al
método.
El demostración de Color HSL página muestra una variedad de formas que HslColorExtension puede aparecer
en un archivo XAML para especificar el color para un BoxView :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MarkupExtensions"
x:Class="MarkupExtensions.HslColorDemoPage"
Title="HSL Color Demo">

<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="BoxView">
<Setter Property="WidthRequest" Value="80" />
<Setter Property="HeightRequest" Value="80" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<StackLayout>
<BoxView>
<BoxView.Color>
<local:HslColorExtension H="0" S="1" L="0.5" A="1" />
</BoxView.Color>
</BoxView>

<BoxView>
<BoxView.Color>
<local:HslColor H="0.33" S="1" L="0.5" />
</BoxView.Color>
</BoxView>

<BoxView Color="{local:HslColorExtension H=0.67, S=1, L=0.5}" />

<BoxView Color="{local:HslColor H=0, S=0, L=0.5}" />

<BoxView Color="{local:HslColor A=0.5}" />


</StackLayout>
</ContentPage>

Tenga en cuenta que, cuando HslColorExtension es una etiqueta XML, las cuatro propiedades se establecen como
atributos, pero cuando aparece entre llaves, las cuatro propiedades están separadas por comas sin comillas. Los
valores predeterminados de H , S , y L son 0 y el valor predeterminado de A es 1, por lo que esas propiedades
se pueden omitir si quiere que estén establecidas en valores predeterminados. El último ejemplo muestra un
ejemplo donde la luminosidad es 0, lo que normalmente se produce en negro, pero el canal alfa es 0,5, por lo que
es semitransparente y aparece atenuado en el fondo blanco de la página:
Una extensión de marcado para tener acceso a los mapas de bits
El argumento ProvideValue es un objeto que implementa el IServiceProvider interfaz, que se define en .NET
System espacio de nombres. Esta interfaz tiene un miembro, un método denominado GetService con un Type
argumento.
El ImageResourceExtension clase se muestra a continuación muestra un uso posible de IServiceProvider y
GetService para obtener un IXmlLineInfoProvider objeto que puede proporcionar información de línea y el
carácter que indica donde se detectó un error determinado. En este caso, se produce una excepción cuando el
Source no se estableció la propiedad:

[ContentProperty("Source")]
class ImageResourceExtension : IMarkupExtension<ImageSource>
{
public string Source { set; get; }

public ImageSource ProvideValue(IServiceProvider serviceProvider)


{
if (String.IsNullOrEmpty(Source))
{
IXmlLineInfoProvider lineInfoProvider = serviceProvider.GetService(typeof(IXmlLineInfoProvider))
as IXmlLineInfoProvider;
IXmlLineInfo lineInfo = (lineInfoProvider != null) ? lineInfoProvider.XmlLineInfo : new
XmlLineInfo();
throw new XamlParseException("ImageResourceExtension requires Source property to be set",
lineInfo);
}

string assemblyName = GetType().GetTypeInfo().Assembly.GetName().Name;


return ImageSource.FromResource(assemblyName + "." + Source,
typeof(ImageResourceExtension).GetTypeInfo().Assembly);
}

object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)


{
return (this as IMarkupExtension<ImageSource>).ProvideValue(serviceProvider);
}
}

ImageResourceExtension es útil cuando un archivo XAML necesita acceder a un archivo de imagen almacenado
como un recurso incrustado en el proyecto de biblioteca .NET Standard. Usa el Source propiedad para llamar a
estático ImageSource.FromResource método. Este método requiere un nombre de recurso completo, que está
formado por el nombre del ensamblado, el nombre de carpeta y el nombre de archivo separadas por puntos. El
segundo argumento para el ImageSource.FromResource método proporciona el nombre del ensamblado y solo es
necesaria para la versión se basa en UWP. No obstante, ImageSource.FromResource debe llamarse desde el
ensamblado que contiene el mapa de bits, lo que significa que esta extensión de recursos XAML no puede formar
parte de una biblioteca externa a menos que las imágenes también se encuentran en dicha biblioteca. (Consulte la
imágenes incrustadas artículo para obtener más información sobre cómo acceder a los mapas de bits que se
almacenan como recursos incrustados.)
Aunque ImageResourceExtension requiere la Source propiedad se establece, el Source propiedad se indica en un
atributo como la propiedad de contenido de la clase. Esto significa que el Source= se puede omitir la parte de la
expresión entre llaves. En el demostración del recurso de imagen página, el Image elementos capturar dos
imágenes con el nombre de carpeta y el nombre de archivo separadas por puntos:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MarkupExtensions"
x:Class="MarkupExtensions.ImageResourceDemoPage"
Title="Image Resource Demo">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<Image Source="{local:ImageResource Images.SeatedMonkey.jpg}"


Grid.Row="0" />

<Image Source="{local:ImageResource Images.FacePalm.jpg}"


Grid.Row="1" />

</Grid>
</ContentPage>

Este es el programa que se ejecuta:

Proveedores de servicios
Mediante el uso de la IServiceProvider argumento ProvideValue , las extensiones de marcado XAML pueden
obtener acceso a información útil sobre el archivo XAML en el que está usando. Pero para usar el
IServiceProvider argumento correctamente, deberá saber qué tipo de servicios están disponibles en contextos
determinados. La mejor manera para obtener una descripción de esta característica es estudiar el código fuente de
las extensiones de marcado XAML existentes en el MarkupExtensions carpeta en el repositorio de
Xamarin.Forms en GitHub. Tenga en cuenta que algunos tipos de servicios son internos para Xamarin.Forms.
En algunas extensiones de marcado XAML, este servicio puede ser útil:

IProvideValueTarget provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as


IProvideValueTarget;

El IProvideValueTarget interfaz define dos propiedades, TargetObject y TargetProperty . Cuando se obtiene esta
información en el ImageResourceExtension (clase), TargetObject es el Image y TargetProperty es un
BindableProperty de objeto para el Source propiedad de Image . Se trata de la propiedad en el que se ha
establecido la extensión de marcado XAML.
El GetServicellamada con un argumento de typeof(IProvideValueTarget) realmente devuelve un objeto de tipo
SimpleValueTargetProvider , que se define en el Xamarin.Forms.Xaml.Internals espacio de nombres. Si convierte el
valor devuelto de GetService a ese tipo, también puede acceder un ParentObjects propiedad, que es una matriz
que contiene el Image elemento, el Grid primario y el ImageResourceDemoPage primario de la Grid .

Conclusión
Las extensiones de marcado XAML desempeñan un papel fundamental en XAML mediante la extensión de la
capacidad de establecer los atributos de una variedad de orígenes. Además, si las extensiones de marcado XAML
existentes no proporcionan exactamente lo que necesita, también puede escribir su propio.

Vínculos relacionados
Extensiones de marcado (ejemplo)
Capítulo de extensiones de marcado XAML de Xamarin.Forms libro
Modificadores de campo XAML en Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

El x:FieldModifier namespace (atributo ) especifica el nivel de acceso para los campos generados para los
elementos XAML con nombre.

Información general
Los valores válidos del atributo son:
Public : Especifica que el campo generado para el elemento XAML es public .
NotPublic : Especifica que el campo generado para el elemento XAML es internal al ensamblado.

Si no se establece el valor del atributo, el campo generado para el elemento será private .
Las siguientes condiciones deben cumplirse para una x:FieldModifier atributo procesarse:
El elemento XAML debe ser válido x:Class .
El elemento actual de XAML tiene un x:Name especificado.
El XAML siguiente muestra ejemplos de establecer el atributo:

<Label x:Name="privateLabel" />


<Label x:Name="internalLabel" x:FieldModifier="NotPublic" />
<Label x:Name="publicLabel" x:FieldModifier="Public" />

NOTE
El x:FieldModifier atributo no puede utilizarse para especificar el nivel de acceso de una clase XAML.
Pasar argumentos en XAML
11/07/2019 • 6 minutes to read • Edit Online

descargar el ejemplo
En este artículo muestra cómo utilizar los atributos XAML que pueden utilizarse para pasar argumentos a los
constructores no predeterminados, para llamar a métodos de fábrica y para especificar el tipo de argumento
genérico.

Información general
A menudo es necesario crear instancias de objetos con constructores que requieren argumentos, o mediante una
llamada a un método de creación estático. Esto puede lograrse en XAML mediante el uso de la x:Arguments y
x:FactoryMethod atributos:

El x:Arguments atributo se utiliza para especificar los argumentos de constructor para un constructor no
predeterminado, o para una declaración de objeto del método de fábrica. Para obtener más información,
consulte pasar argumentos de Constructor.
El x:FactoryMethod atributo se utiliza para especificar un método de fábrica que puede usarse para inicializar
un objeto. Para obtener más información, consulte llamar a métodos de generador.
Además, el x:TypeArguments atributo puede utilizarse para especificar los argumentos de tipo genérico para el
constructor de un tipo genérico. Para obtener más información, consulte especificando un argumento de tipo
genérico.

Pasar argumentos de Constructor


Se pueden pasar argumentos a un constructor no predeterminado mediante el x:Arguments atributo. Cada
argumento de constructor debe estar delimitado dentro de un elemento XML que representa el tipo del
argumento. Xamarin.Forms es compatible con los siguientes elementos para los tipos básicos:
x:Object
x:Boolean
x:Byte
x:Int16
x:Int32
x:Int64
x:Single
x:Double
x:Decimal
x:Char
x:String
x:TimeSpan
x:Array
x:DateTime

En el ejemplo de código siguiente se muestra cómo utilizar el x:Arguments atributo con tres Color constructores:
<BoxView HeightRequest="150" WidthRequest="150" HorizontalOptions="Center">
<BoxView.Color>
<Color>
<x:Arguments>
<x:Double>0.9</x:Double>
</x:Arguments>
</Color>
</BoxView.Color>
</BoxView>
<BoxView HeightRequest="150" WidthRequest="150" HorizontalOptions="Center">
<BoxView.Color>
<Color>
<x:Arguments>
<x:Double>0.25</x:Double>
<x:Double>0.5</x:Double>
<x:Double>0.75</x:Double>
</x:Arguments>
</Color>
</BoxView.Color>
</BoxView>
<BoxView HeightRequest="150" WidthRequest="150" HorizontalOptions="Center">
<BoxView.Color>
<Color>
<x:Arguments>
<x:Double>0.8</x:Double>
<x:Double>0.5</x:Double>
<x:Double>0.2</x:Double>
<x:Double>0.5</x:Double>
</x:Arguments>
</Color>
</BoxView.Color>
</BoxView>

El número de elementos dentro de la x:Arguments etiqueta y los tipos de estos elementos, deben coincidir con
uno de los Color constructores. El Color constructor con un solo parámetro requiere un valor de escala de
grises de 0 (negro) a 1 (blanco). El Color constructor con tres parámetros requiere un valor de color rojo, verde y
azul comprendida entre 0 y 1. El Color constructor con cuatro parámetros agrega un canal alfa como cuarto
parámetro.
Las capturas de pantalla siguientes muestran el resultado de llamar a cada uno de ellos Color constructor con
los valores de argumento especificado:
Llamar a métodos de fábrica
Pueden llamar a los métodos de fábrica en XAML mediante la especificación del método con el nombre del
x:FactoryMethod atributo y sus argumentos mediante el x:Arguments atributo. Un método de fábrica es un
public static método que devuelve objetos o valores del mismo tipo que la clase o estructura que define los
métodos.
El Color estructura define una serie de métodos de generador y el código siguiente muestra que realiza la
llamada tres de ellas:
<BoxView HeightRequest="150" WidthRequest="150" HorizontalOptions="Center">
<BoxView.Color>
<Color x:FactoryMethod="FromRgba">
<x:Arguments>
<x:Int32>192</x:Int32>
<x:Int32>75</x:Int32>
<x:Int32>150</x:Int32>
<x:Int32>128</x:Int32>
</x:Arguments>
</Color>
</BoxView.Color>
</BoxView>
<BoxView HeightRequest="150" WidthRequest="150" HorizontalOptions="Center">
<BoxView.Color>
<Color x:FactoryMethod="FromHsla">
<x:Arguments>
<x:Double>0.23</x:Double>
<x:Double>0.42</x:Double>
<x:Double>0.69</x:Double>
<x:Double>0.7</x:Double>
</x:Arguments>
</Color>
</BoxView.Color>
</BoxView>
<BoxView HeightRequest="150" WidthRequest="150" HorizontalOptions="Center">
<BoxView.Color>
<Color x:FactoryMethod="FromHex">
<x:Arguments>
<x:String>#FF048B9A</x:String>
</x:Arguments>
</Color>
</BoxView.Color>
</BoxView>

El número de elementos dentro de la x:Arguments etiqueta y los tipos de estos elementos, deben coincidir con los
argumentos de la llamada al método de fábrica. El FromRgba método de fábrica requiere cuatro Int32
parámetros, que representan los valores de rojos, verdes, azules y alfabéticos, comprendida entre 0 y 255,
respectivamente. El FromHsla método de fábrica requiere cuatro Double parámetros, que representan el matiz,
saturación, luminosidad y los valores alfa, comprendida entre 0 y 1 respectivamente. El FromHex método factory
requiere un String que representa el formato hexadecimal (A) color RGB.
Las capturas de pantalla siguientes muestran el resultado de llamar a cada uno de ellos Color método de fábrica
con los valores de argumento especificado:
Si se especifica un argumento de tipo genérico
Argumentos de tipo genérico para el constructor de un tipo genérico pueden especificarse mediante la
x:TypeArguments de atributo, como se muestra en el ejemplo de código siguiente:

<ContentPage ...>
<StackLayout>
<StackLayout.Margin>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0,20,0,0" />
<On Platform="Android" Value="5, 10" />
<On Platform="UWP" Value="10" />
</OnPlatform>
</StackLayout.Margin>
</StackLayout>
</ContentPage>

El OnPlatform clase es una clase genérica y se deben crear instancias con un x:TypeArguments atributo que
coincida con el tipo de destino. En la clase On , el atributo Platform puede aceptar un único valor string o
varios valores string delimitados por comas. En este ejemplo, el StackLayout.Margin propiedad está establecida
en una plataforma específica Thickness .

Resumen
En este artículo se muestra usando los atributos XAML que pueden utilizarse para pasar argumentos a los
constructores no predeterminados, para llamar a métodos de fábrica y para especificar el tipo de argumento
genérico.
Vínculos relacionados
Espacios de nombres XAML
Pasar argumentos de Constructor (ejemplo)
Llamar a métodos de fábrica (ejemplo)
Propiedades enlazables
11/07/2019 • 16 minutes to read • Edit Online

descargar el ejemplo
En Xamarin.Forms, la funcionalidad de las propiedades de common language runtime (CLR ) se extiende por las
propiedades enlazables. Una propiedad enlazable es un tipo especial de propiedad, donde el valor de propiedad
se realiza el seguimiento por el sistema de propiedades de Xamarin.Forms. Este artículo proporciona una
introducción a las propiedades enlazables y muestra cómo crear y consumirlos.

Información general
Propiedades enlazables amplían la funcionalidad de propiedad CLR de seguridad de una propiedad con un
BindableProperty tipo, en lugar de una propiedad con un campo de respaldo. El propósito de las propiedades
enlazables es proporcionar un sistema de propiedades que admite el enlace de datos, estilos, plantillas y valores se
establecen a través de relaciones de elementos primarios y secundarios. Además, las propiedades enlazables
pueden proporcionar valores predeterminados, validación de los valores de propiedad y las devoluciones de
llamada que supervisen los cambios de propiedad.
Las propiedades deben implementarse como propiedades enlazables para admitir una o varias de las siguientes
características:
Que actúa como válido destino propiedad para el enlace de datos.
Establecer la propiedad a través de un estilo.
Proporcionar un valor de propiedad predeterminado que es diferente del valor predeterminado para el tipo de
la propiedad.
Validar el valor de la propiedad.
Supervisión de los cambios de propiedad.
Ejemplos de Xamarin.Forms que las propiedades enlazables Label.Text , Button.BorderRadius , y
StackLayout.Orientation . Cada propiedad enlazable le corresponde una public static readonly propiedad de
tipo BindableProperty que se expone en la misma clase y que es el identificador de la propiedad enlazable. Por
ejemplo, el identificador de propiedad enlazable correspondiente para el Label.Text propiedad es
Label.TextProperty .

Creación y consumo de una propiedad enlazable


El proceso de creación de una propiedad enlazable es como sigue:
1. Crear un BindableProperty instancia con uno de los BindableProperty.Create sobrecargas del método.
2. Definir descriptores de acceso de propiedad para el BindableProperty instancia.
Tenga en cuenta que todos los BindableProperty instancias deben crearse en el subproceso de interfaz de usuario.
Esto significa que sólo el código que se ejecuta en el subproceso de interfaz de usuario puede obtener o establecer
el valor de una propiedad enlazable. Sin embargo, BindableProperty instancias pueden tener acceso desde otros
subprocesos por el cálculo de referencias para el subproceso de interfaz de usuario con el
Device.BeginInvokeOnMainThread método.

Creación de una propiedad


Para crear un BindableProperty instancia, debe derivar la clase contenedora de la BindableObject clase. Sin
embargo, la BindableObject clase es alta en la jerarquía de clases, por lo que la mayoría de las clases que se usa
para las propiedades enlazables para compatibilidad con la funcionalidad de interfaz de usuario.
Se puede crear una propiedad enlazable declarando un public static readonly propiedad de tipo
BindableProperty . La propiedad enlazable debe establecerse en el valor devuelto de uno de los
BindableProperty.Create sobrecargas del método. La declaración debe estar dentro del cuerpo de BindableObject
clase derivada, pero fuera de las definiciones de miembros.
Como mínimo, debe especificarse un identificador al crear un BindableProperty , junto con los parámetros
siguientes:
El nombre de la BindableProperty .
Tipo de la propiedad.
El tipo del objeto propietario.
Valor predeterminado de la propiedad. Esto garantiza que la propiedad siempre devuelve un valor
determinado de forma predeterminada cuando no está establecido, y puede ser diferente del valor
predeterminado para el tipo de la propiedad. El valor predeterminado será cuando restaura la ClearValue se
llama al método en la propiedad enlazable.
El código siguiente muestra un ejemplo de una propiedad enlazable, con un identificador y los valores de los
cuatro campos obligatorios:

public static readonly BindableProperty EventNameProperty =


BindableProperty.Create ("EventName", typeof(string), typeof(EventToCommandBehavior), null);

Esto crea un BindablePropertyinstancia denominada EventName , del tipo string . La propiedad pertenece a la
EventToCommandBehavior clase y tiene un valor predeterminado de null . La convención de nomenclatura para las
propiedades enlazables es que el identificador de propiedad enlazable debe coincidir con el nombre de propiedad
especificado en el Create método con "Property" anexado a él. Por lo tanto, en el ejemplo anterior, el identificador
de propiedad enlazable es EventNameProperty .
Si lo desea, al crear un BindableProperty de instancia, los siguientes parámetros se pueden especificar:
Modo de enlace. Esto se utiliza para especificar la dirección en la que se propaguen los cambios de valor de
propiedad. En el modo de enlace predeterminada, los cambios se propaguen desde el origen a la destino.
Un delegado de validación que se invocará cuando se establece el valor de propiedad. Para obtener más
información, consulte las devoluciones de llamada de validación.
Un delegado de cambio de propiedad que se invoca cuando ha cambiado el valor de propiedad. Para obtener
más información, consulte detectar los cambios de propiedad.
Una propiedad de cambio de delegado que se invoca cuando cambia el valor de propiedad. Este delegado tiene
la misma firma que el delegado de la propiedad ha cambiado.
Un delegado del valor de coerción que se invoca cuando ha cambiado el valor de propiedad. Para obtener más
información, consulte devoluciones de llamada de valor de coerción.
Un Func que se usa para inicializar un valor de propiedad predeterminado. Para obtener más información,
consulte creación de un valor predeterminado con un elemento Func.
Creación de los descriptores de acceso
Los descriptores de acceso de propiedad deben usar la sintaxis de la propiedad para tener acceso a una propiedad
enlazable. El Get descriptor de acceso debe devolver el valor que se encuentra en la propiedad enlazable
correspondiente. Esto puede lograrse mediante una llamada a la GetValue método, pasando el identificador de
propiedad enlazable en la que se va a obtener el valor y, a continuación, convertir el resultado al tipo requerido. El
Set descriptor de acceso debe establecer el valor de la propiedad enlazable correspondiente. Esto puede lograrse
mediante una llamada a la SetValue método, pasando el identificador de propiedad enlazable en la que se va a
establecer el valor y el valor que se va a establecer.
El ejemplo de código siguiente muestra los descriptores de acceso para el EventName propiedad enlazable:

public string EventName {


get { return (string)GetValue (EventNameProperty); }
set { SetValue (EventNameProperty, value); }
}

Consumo de una propiedad enlazable


Una vez creada una propiedad enlazable, se puede consumir desde XAML o código. En XAML, esto se logra
mediante la declaración de un espacio de nombres con un prefijo, con la declaración de espacio de nombres que
indica el nombre del espacio de nombres CLR y, opcionalmente, un nombre de ensamblado. Para obtener más
información, consulte los espacios de nombres XAML.
En el ejemplo de código siguiente se muestra un espacio de nombres XAML para un tipo personalizado que
contiene una propiedad enlazable, que se define dentro del mismo ensamblado que el código de aplicación que se
hace referencia al tipo personalizado:

<ContentPage ... xmlns:local="clr-namespace:EventToCommandBehavior" ...>


...
</ContentPage>

La declaración de espacio de nombres se utiliza al establecer el EventName propiedad enlazable, como se muestra
en el ejemplo de código XAML siguiente:

<ListView ...>
<ListView.Behaviors>
<local:EventToCommandBehavior EventName="ItemSelected" ... />
</ListView.Behaviors>
</ListView>

El código de C# equivalente se muestra en el ejemplo de código siguiente:

var listView = new ListView ();


listView.Behaviors.Add (new EventToCommandBehavior {
EventName = "ItemSelected",
...
});

Escenarios avanzados
Al crear un BindableProperty de la instancia, hay una serie de parámetros opcionales que se pueden establecer
para habilitar escenarios avanzados de propiedad enlazable. Esta sección explora estos escenarios.
Detección de cambios de propiedad
Un static método de devolución de llamada de cambio de propiedad se puede registrar con una propiedad
enlazable especificando el propertyChanged parámetro para el BindableProperty.Create método. El método de
devolución de llamada especificada se invoca cuando cambia el valor de la propiedad enlazable.
El siguiente ejemplo de código muestra cómo el EventName propiedad enlazable registra el OnEventNameChanged
método como un método de devolución de llamada de cambio de propiedad:
public static readonly BindableProperty EventNameProperty =
BindableProperty.Create (
"EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
...

static void OnEventNameChanged (BindableObject bindable, object oldValue, object newValue)


{
// Property changed implementation goes here
}

En el método de devolución de llamada de cambio de propiedad, el BindableObject parámetro se usa para indicar
qué instancia de la clase propietaria informó de un cambio y los valores de los dos object parámetros
representan los valores antiguos y nuevos de la propiedad enlazable.
Devoluciones de llamada de validación
Un static método de devolución de llamada de validación se puede registrar con una propiedad enlazable
especificando el validateValue parámetro para el BindableProperty.Create método. Se invocará el método de
devolución de llamada especificada cuando se establece el valor de la propiedad enlazable.
El siguiente ejemplo de código muestra cómo el Angle propiedad enlazable registra el IsValidValue método
como método de devolución de llamada de validación:

public static readonly BindableProperty AngleProperty =


BindableProperty.Create ("Angle", typeof(double), typeof(HomePage), 0.0, validateValue: IsValidValue);
...

static bool IsValidValue (BindableObject view, object value)


{
double result;
bool isDouble = double.TryParse (value.ToString (), out result);
return (result >= 0 && result <= 360);
}

Las devoluciones de llamada de validación se proporcionan con un valor y debe devolver true si el valor es
válido para la propiedad, de lo contrario false . Se producirá una excepción si una devolución de llamada de
validación devuelve false , que debe controlarse por el desarrollador. Un uso típico de un método de devolución
de llamada de validación es restringir los valores de números enteros o dobles cuando se establece la propiedad
enlazable. Por ejemplo, el IsValidValue método comprueba que el valor de propiedad es un double dentro del
intervalo de 0 a 360.
CoerceValue Callbacks
Un static convertir el valor del método de devolución de llamada se puede registrar con una propiedad
enlazable especificando el coerceValue parámetro para el BindableProperty.Create método. El método de
devolución de llamada especificada se invoca cuando cambia el valor de la propiedad enlazable.
Las devoluciones de llamada se usan para forzar una reevaluación de una propiedad enlazable cuando cambia el
valor de la propiedad de valor de coerción. Por ejemplo, puede utilizarse una devolución de llamada de valor de
coerción para asegurarse de que el valor de una propiedad enlazable no es mayor que el valor de otra propiedad
enlazable.
El siguiente ejemplo de código muestra cómo el Angle propiedad enlazable registra el CoerceAngle método
como un método de devolución de llamada de valor de coerción:
public static readonly BindableProperty AngleProperty = BindableProperty.Create (
"Angle", typeof(double), typeof(HomePage), 0.0, coerceValue: CoerceAngle);
public static readonly BindableProperty MaximumAngleProperty = BindableProperty.Create (
"MaximumAngle", typeof(double), typeof(HomePage), 360.0);
...

static object CoerceAngle (BindableObject bindable, object value)


{
var homePage = bindable as HomePage;
double input = (double)value;

if (input > homePage.MaximumAngle) {


input = homePage.MaximumAngle;
}

return input;
}

El CoerceAngle método comprueba el valor de la MaximumAngle propiedad y si el Angle es mayor el valor de


propiedad, convierte el valor en el MaximumAngle valor de propiedad.
Creación de un valor predeterminado con un elemento Func
Un Func puede usarse para inicializar el valor predeterminado de una propiedad enlazable, como se muestra en
el ejemplo de código siguiente:

public static readonly BindableProperty SizeProperty =


BindableProperty.Create ("Size", typeof(double), typeof(HomePage), 0.0,
defaultValueCreator: bindable => Device.GetNamedSize (NamedSize.Large, (Label)bindable));

El defaultValueCreator parámetro se establece en un Func que invoca la Device.GetNamedSize método devuelva


un double que representa el nombre de tamaño para la fuente que se usa en un Label en la plataforma nativa.

Resumen
En este artículo proporciona una introducción a las propiedades enlazables y se muestra cómo crear y
consumirlos. Una propiedad enlazable es un tipo especial de propiedad, donde el valor de propiedad se realiza el
seguimiento por el sistema de propiedades de Xamarin.Forms.

Vínculos relacionados
Espacios de nombres XAML
Evento para el comportamiento de comandos (ejemplo)
Devolución de llamada de validación (ejemplo)
Convertir el valor de devolución de llamada (ejemplo)
BindableProperty
BindableObject
Propiedades asociadas
11/07/2019 • 10 minutes to read • Edit Online

descargar el ejemplo
Una propiedad adjunta es un tipo especial de propiedad enlazable, definido en una clase pero conectado a
otros objetos y reconocible en XAML como un atributo que contiene una clase y un nombre de propiedad
separados por un punto. Este artículo proporciona una introducción a las propiedades adjuntas y muestra cómo
crear y consumirlos.

Información general
Adjunta propiedades habilitar un objeto para asignar un valor para una propiedad que no define su propia clase.
Por ejemplo, secundario, pueden usar los elementos vinculados propiedades para informar a su elemento
primario de cómo se presentarán en la interfaz de usuario. El Grid control permite la fila y columna de un
elemento secundario que se especifica estableciendo el Grid.Row y Grid.Column propiedades adjuntas.
Grid.Row y Grid.Column son las propiedades adjuntas porque se definen los elementos que son elementos
secundarios de un Grid , en lugar de en el Grid propio.
Propiedades enlazables deben implementarse como propiedades adjuntas en los escenarios siguientes:
Cuando no hay necesidad de tener un mecanismo de configuración de propiedades disponible para las clases
distintas de la definición de clase.
Cuando la clase representa un servicio que necesita para integrarse fácilmente con otras clases.
Para obtener más información acerca de las propiedades enlazables, vea propiedades enlazables.

Creación y consumo de una propiedad adjunta


El proceso de creación de una propiedad adjunta es como sigue:
1. Crear un BindableProperty instancia con uno de los CreateAttached sobrecargas del método.
2. Proporcionar static Get PropertyName y Set PropertyName métodos como descriptores de acceso para
la propiedad adjunta.
Creación de una propiedad
Al crear una propiedad adjunta para su uso en otros tipos, la clase donde se crea la propiedad no tiene que
derivar BindableObject . Sin embargo, el destino debe ser de propiedad para los descriptores de acceso o
derivarse de ella BindableObject .
Una propiedad adjunta se puede crear mediante la declaración de un public static readonly propiedad de tipo
BindableProperty . La propiedad enlazable debe establecerse en el valor devuelto de uno de los
BindableProperty.CreateAttached sobrecargas del método. La declaración debe estar dentro del cuerpo de la
clase propietaria, pero fuera de las definiciones de miembros.
El código siguiente muestra un ejemplo de una propiedad asociada:

public static readonly BindableProperty HasShadowProperty =


BindableProperty.CreateAttached ("HasShadow", typeof(bool), typeof(ShadowEffect), false);

Esto crea una propiedad adjunta mencionada HasShadow , del tipo bool . La propiedad pertenece a la
ShadowEffect clase y tiene un valor predeterminado de false . La convención de nomenclatura para las
propiedades adjuntas es que el identificador de propiedad adjunta debe coincidir con el nombre de propiedad
especificado en el CreateAttached método con "Property" anexado a él. Por lo tanto, en el ejemplo anterior, el
identificador de la propiedad adjunta es HasShadowProperty .
Para obtener más información acerca de cómo crear propiedades enlazables, incluidos los parámetros que se
pueden especificar durante la creación, consulte creación y consumo de una propiedad enlazable.
Creación de los descriptores de acceso
Estática Get PropertyName y Set PropertyName métodos son necesarios como descriptores de acceso para
la propiedad adjunta, en caso contrario, el sistema de propiedades no se puede usar el propiedad adjunta. El
Get PropertyName descriptor de acceso debe ajustarse a la firma siguiente:

public static valueType GetPropertyName(BindableObject target)

El GetPropertyName descriptor de acceso debe devolver el valor que se encuentra en las correspondientes
BindableProperty campo para la propiedad adjunta. Esto puede lograrse mediante una llamada a la GetValue
método, pasando el identificador de propiedad enlazable en la que se va a obtener el valor y, a continuación,
convertir el valor resultante al tipo requerido.
El Set PropertyName descriptor de acceso debe ajustarse a la firma siguiente:

public static void SetPropertyName(BindableObject target, valueType value)

El Set PropertyName descriptor de acceso debe establecer el valor de la correspondiente BindableProperty


campo para la propiedad adjunta. Esto puede lograrse mediante una llamada a la SetValue método, pasando el
identificador de propiedad enlazable en la que se va a establecer el valor y el valor que se va a establecer.
Para ambos descriptores de acceso, el destino debe ser de objeto o derivarse de ella BindableObject .
El ejemplo de código siguiente muestra los descriptores de acceso para el HasShadow propiedad asociada:

public static bool GetHasShadow (BindableObject view)


{
return (bool)view.GetValue (HasShadowProperty);
}

public static void SetHasShadow (BindableObject view, bool value)


{
view.SetValue (HasShadowProperty, value);
}

Consumo de una propiedad adjunta


Una vez creada una propiedad adjunta, se puede consumir desde XAML o código. En XAML, esto se logra
mediante la declaración de un espacio de nombres con un prefijo, con la declaración de espacio de nombres que
indica el nombre del espacio de nombres de Common Language Runtime (CLR ) y, opcionalmente, un nombre
de ensamblado. Para obtener más información, consulte los espacios de nombres XAML.
En el ejemplo de código siguiente se muestra un espacio de nombres XAML para un tipo personalizado que
contiene una propiedad adjunta, que se define dentro del mismo ensamblado que el código de aplicación que se
hace referencia al tipo personalizado:
<ContentPage ... xmlns:local="clr-namespace:EffectsDemo" ...>
...
</ContentPage>

La declaración de espacio de nombres, a continuación, se utiliza al establecer la propiedad adjunta en un control


específico, como se muestra en el ejemplo de código XAML siguiente:

<Label Text="Label Shadow Effect" local:ShadowEffect.HasShadow="true" />

El código de C# equivalente se muestra en el ejemplo de código siguiente:

var label = new Label { Text = "Label Shadow Effect" };


ShadowEffect.SetHasShadow (label, true);

Consumo de una propiedad adjunta con un estilo


Las propiedades adjuntas también pueden agregarse a un control por un estilo. El ejemplo de código XAML
siguiente muestra un explícita estilo que usa el HasShadow propiedad adjunta, que se puede aplicar a Label
controles:

<Style x:Key="ShadowEffectStyle" TargetType="Label">


<Style.Setters>
<Setter Property="local:ShadowEffect.HasShadow" Value="true" />
</Style.Setters>
</Style>

El puede aplicarse a un Label estableciendo su Style propiedad a la Style instancia mediante el


Style
StaticResource extensión de marcado, como se muestra en el ejemplo de código siguiente:

<Label Text="Label Shadow Effect" Style="{StaticResource ShadowEffectStyle}" />

Para obtener más información sobre los estilos, consulte estilos.

Escenarios avanzados
Al crear una propiedad adjunta, hay una serie de parámetros opcionales que se pueden establecer para habilitar
escenarios avanzados de propiedad adjunta. Esto incluye la detección de cambios de propiedad, validar los
valores de propiedad y la conversión de valores de propiedad. Para obtener más información, consulte
escenarios avanzados.

Resumen
En este artículo proporciona una introducción a las propiedades adjuntas y se muestra cómo crear y
consumirlos. Una propiedad adjunta es un tipo especial de propiedad enlazable, definida en una clase, pero
Unidos a otros objetos y reconocible en XAML como atributos que contienen una clase y un nombre de
propiedad separados por un punto.

Vínculos relacionados
Propiedades enlazables
Espacios de nombres XAML
Efecto de sombra paralela (ejemplo)
BindableProperty
BindableObject
Diccionarios de recursos
11/07/2019 • 16 minutes to read • Edit Online

descargar el ejemplo
Recursos XAML son definiciones de objetos que pueden compartir y volver a utilizarse en toda una aplicación de
Xamarin.Forms.
Estos objetos de recursos se almacenan en un diccionario de recursos. En este artículo se describe cómo crear y
consumir un diccionario de recursos y cómo se combinan los diccionarios de recursos.

Información general
Un ResourceDictionary es un repositorio para los recursos utilizados por una aplicación de Xamarin.Forms.
Recursos típicos que se almacenan en un ResourceDictionary incluyen estilos, plantillas de control, plantillas de
datos, colores y los convertidores de tipos.
En XAML, los recursos que se almacenan en un ResourceDictionary puedan recuperarse y aplica a los elementos
mediante el StaticResource extensión de marcado. En C#, también se pueden definir los recursos en un
ResourceDictionary y, a continuación, recuperar y aplicar a los elementos mediante el uso de un indexador de
cadena. Sin embargo, existen muy pocas ventajas al uso de un ResourceDictionary en C#, como objetos
compartidos simplemente se pueden almacenar como campos o propiedades y obtener acceso a directamente
sin tener que primero recuperarlos de un diccionario.

Creación y consumo de un objeto ResourceDictionary


Los recursos se definen en un ResourceDictionary es decir, se establece en uno de los siguientes Resources
propiedades:
El Resources propiedad de cualquier clase que deriva de Application
El Resources propiedad de cualquier clase que deriva de VisualElement

Un programa de Xamarin.Forms contiene sólo una clase que derive de Application pero a menudo hace uso de
muchas clases que derivan de VisualElement , incluidas las páginas, diseños y controles. Cualquiera de estos
objetos pueden tener su Resources propiedad establecida en un ResourceDictionary . Elegir dónde colocar un
determinado ResourceDictionary impactos donde se pueden usar los recursos:
Los recursos en un ResourceDictionary que está asociado a una vista como Button o Label sólo puede
aplicarse a ese objeto concreto, por lo que esto no es muy útil.
Los recursos en un ResourceDictionary adjunta a un diseño, como StackLayout o Grid se pueden aplicar
para el diseño y todos los elementos secundarios de ese diseño.
Los recursos en un ResourceDictionary definido en la página de nivel se puede aplicar a la página y a todos
sus elementos secundarios.
Los recursos en un ResourceDictionary definido en la aplicación de nivel se puede aplicar a lo largo de la
aplicación.
El siguiente XAML muestra los recursos definidos en un nivel de aplicación ResourceDictionary en el App.xaml
archivo creado como parte del programa de Xamarin.Forms estándar:
<Application ...>
<Application.Resources>
<ResourceDictionary>
<Color x:Key="PageBackgroundColor">Yellow</Color>
<Color x:Key="HeadingTextColor">Black</Color>
<Color x:Key="NormalTextColor">Blue</Color>
<Style x:Key="LabelPageHeadingStyle" TargetType="Label">
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="TextColor" Value="{StaticResource HeadingTextColor}" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

Esto ResourceDictionary define tres Color recursos y un Style recursos. Para obtener más información sobre
la App de clases, vea clase App.
A partir de 3.0 de Xamarin.Forms, la configuración explícita ResourceDictionary las etiquetas no son necesarias.
El ResourceDictionary objeto se crea automáticamente y los recursos puede insertar directamente entre el
Resources etiquetas de elemento de propiedad:

<Application ...>
<Application.Resources>
<Color x:Key="PageBackgroundColor">Yellow</Color>
<Color x:Key="HeadingTextColor">Black</Color>
<Color x:Key="NormalTextColor">Blue</Color>
<Style x:Key="LabelPageHeadingStyle" TargetType="Label">
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="TextColor" Value="{StaticResource HeadingTextColor}" />
</Style>
</Application.Resources>
</Application>

Cada recurso tiene una clave que se especifica utilizando el x:Key atributo, que se convierte en la clave de
diccionario en el ResourceDictionary . La clave se usa para recuperar un recurso desde el ResourceDictionary por
la StaticResource extensión de marcado, como se muestra en el siguiente ejemplo de código XAML que se
muestra recursos adicionales definidos en el StackLayout :
<StackLayout Margin="0,20,0,0">
<StackLayout.Resources>
<ResourceDictionary>
<Style x:Key="LabelNormalStyle" TargetType="Label">
<Setter Property="TextColor" Value="{StaticResource NormalTextColor}" />
</Style>
<Style x:Key="MediumBoldText" TargetType="Button">
<Setter Property="FontSize" Value="Medium" />
<Setter Property="FontAttributes" Value="Bold" />
</Style>
</ResourceDictionary>
</StackLayout.Resources>
<Label Text="ResourceDictionary Demo" Style="{StaticResource LabelPageHeadingStyle}" />
<Label Text="This app demonstrates consuming resources that have been defined in resource dictionaries."
Margin="10,20,10,0"
Style="{StaticResource LabelNormalStyle}" />
<Button Text="Navigate"
Clicked="OnNavigateButtonClicked"
TextColor="{StaticResource NormalTextColor}"
Margin="0,20,0,0"
HorizontalOptions="Center"
Style="{StaticResource MediumBoldText}" />
</StackLayout>

La primera Label instancia recupera y consume el LabelPageHeadingStyle recurso definido en el nivel de


aplicación ResourceDictionary , con la segunda Label instancia recuperar y consumir el LabelNormalStyle
recurso definido en el nivel de control ResourceDictionary . De forma similar, el Button instancia recupera y
consume el NormalTextColor recurso definido en el nivel de aplicación ResourceDictionary y el MediumBoldText
recurso definido en el nivel de control ResourceDictionary . El resultado es el aspecto que se muestra en las
capturas de pantalla siguiente:
NOTE
Recursos que son específicos a una sola página no deberían incluirse en un aplicación nivel diccionario de recursos, por lo
tanto los recursos, a continuación, se analizarán al iniciarse la aplicación en lugar de cuando se solicite una página. Para
obtener más información, consulte reducir el tamaño del diccionario de recursos de aplicación.

Reemplazar los recursos


Cuando ResourceDictionary compartan recursos x:Key los valores de atributo definidos más abajo en la
jerarquía de vistas de recursos tienen prioridad sobre aquellas definidas mayor seguridad. Por ejemplo, si se
establece la PageBackgroundColor recurso Blue en la aplicación de nivel serán reemplazado por un nivel de
página PageBackgroundColor del conjunto de recursos Yellow . De forma similar, un nivel de página
PageBackgroundColor recursos serán reemplazados por un nivel de control PageBackgroundColor recursos. Esta
prioridad se muestra en el ejemplo de código XAML siguiente:

<ContentPage ... BackgroundColor="{StaticResource PageBackgroundColor}">


<ContentPage.Resources>
<ResourceDictionary>
<Color x:Key="PageBackgroundColor">Blue</Color>
<Color x:Key="NormalTextColor">Yellow</Color>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="0,20,0,0">
...
<Label Text="ResourceDictionary Demo" Style="{StaticResource LabelPageHeadingStyle}" />
<Label Text="This app demonstrates consuming resources that have been defined in resource
dictionaries."
Margin="10,20,10,0"
Style="{StaticResource LabelNormalStyle}" />
<Button Text="Navigate"
Clicked="OnNavigateButtonClicked"
TextColor="{StaticResource NormalTextColor}"
Margin="0,20,0,0"
HorizontalOptions="Center"
Style="{StaticResource MediumBoldText}" />
</StackLayout>
</ContentPage>

La versión original PageBackgroundColor y NormalTextColor instancias, definidas en el nivel de aplicación, se


reemplazan por la PageBackgroundColor y NormalTextColor instancias definidas en el nivel de página. Por lo
tanto, el color de fondo de página se convierte en azul y el texto en la página, se convierte en amarillo, como se
muestra en las capturas de pantalla siguiente:
Sin embargo, tenga en cuenta que la barra de fondo de la NavigationPage está de color amarillo todavía, porque
el BarBackgroundColor propiedad está establecida en el valor de la PageBackgroundColor recursos definidos en la
aplicación nivel ResourceDictionary .
Ésta es otra manera de pensar en ResourceDictionary prioridad: Cuando el analizador XAML encuentra un
StaticResource , busca una clave coincidente recorriendo la seguridad a través del árbol visual, con la primera
coincidencia que encuentre. Si esta búsqueda finaliza en la página y la clave aún no se ha encontrado, el
analizador XAML busca el ResourceDictionary conectados a la App objeto. Si todavía no se encuentra la clave,
se produce una excepción.

Diccionarios de recursos independiente


Una clase derivada de ResourceDictionary también pueden estar en un archivo independiente independiente.
(Más concretamente, una clase derivada de ResourceDictionary requiere normalmente un par de archivos
debido a los recursos se definen en un archivo XAML, pero un archivo de código subyacente con un
InitializeComponent también es necesario llamar a.) El archivo resultante, a continuación, se puede compartir
entre aplicaciones.
Para crear este archivo, agregue un nuevo vista contenido o página de contenido al proyecto (pero no un
vista contenido o página de contenido con solo un archivo de C#). En el archivo XAML y archivo de C#,
cambie el nombre de la clase base desde ContentView o ContentPage a ResourceDictionary . En el archivo XAML,
el nombre de la clase base es el elemento de nivel superior.
El siguiente ejemplo XAML se muestra un ResourceDictionary denominado MyResourceDictionary :
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResourceDictionaryDemo.MyResourceDictionary">
<DataTemplate x:Key="PersonDataTemplate">
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" TextColor="{StaticResource NormalTextColor}"
FontAttributes="Bold" />
<Label Grid.Column="1" Text="{Binding Age}" TextColor="{StaticResource NormalTextColor}" />
<Label Grid.Column="2" Text="{Binding Location}" TextColor="{StaticResource NormalTextColor}"
HorizontalTextAlignment="End" />
</Grid>
</ViewCell>
</DataTemplate>
</ResourceDictionary>

Esto ResourceDictionary contiene un único recurso, que es un objeto de tipo DataTemplate .


Puede crear una instancia MyResourceDictionary colocándola entre un par de Resources elementos de propiedad
etiquetas, por ejemplo, en un ContentPage :

<ContentPage ...>
<ContentPage.Resources>
<local:MyResourceDictionary />
</ContentPage.Resources>
...
</ContentPage>

Una instancia de MyResourceDictionary está establecido en el Resources propiedad de la ContentPage objeto.


Sin embargo, este enfoque tiene algunas limitaciones: El Resources propiedad de la ContentPage hace referencia
a este ejemplar ResourceDictionary . En la mayoría de los casos, desea que la opción de incluir otros
ResourceDictionary instancias y quizás también otros recursos.

Esta tarea requiere diccionarios de recursos combinados.

Diccionarios de recursos combinados


Diccionarios de recursos combinados combinan uno o varios ResourceDictionary instancias en otro
ResourceDictionary . Puede hacerlo en un archivo XAML estableciendo el MergedDictionaries propiedad en uno
o varios diccionarios de recursos que se incorporará a la aplicación, la página o el nivel de control
ResourceDictionary .

IMPORTANT
ResourceDictionary También define un MergedWith propiedad. No utilice esta propiedad; se ha quedado obsoleta en
Xamarin.Forms 3.0.

Una instancia de MyResourceDictionaryse pueden combinar en cualquier nivel de control, página o aplicación
ResourceDictionary . El ejemplo de código XAML siguiente muestra lo que se va a combinar en un nivel de
página ResourceDictionary utilizando el MergedDictionaries propiedad:
<ContentPage ...>
<ContentPage.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<local:MyResourceDictionary />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>

Ese marcado muestra solo una instancia de MyResourceDictionary que se agrega a la ResourceDictionary , pero
también puede hacer referencia otros ResourceDictionary instancias dentro de la MergedDictionary etiquetas de
elemento de propiedad y otros recursos fuera de esas etiquetas:

<ContentPage ...>
<ContentPage.Resources>
<ResourceDictionary>

<!-- Add more resources here -->

<ResourceDictionary.MergedDictionaries>

<!-- Add more resource dictionaries here -->

<local:MyResourceDictionary />

<!-- Add more resource dictionaries here -->

</ResourceDictionary.MergedDictionaries>

<!-- Add more resources here -->

</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>

Puede haber solo un MergedDictionaries sección un ResourceDictionary , pero se puede colocar tantos
ResourceDictionary instancias allí como desee.

Cuando combina ResourceDictionary recursos comparten idénticos x:Key valores de atributo, Xamarin.Forms
utiliza la prioridad de recursos siguientes:
1. Los recursos locales para el diccionario de recursos.
2. Los recursos contenidos en el diccionario de recursos que se combinó a través del objeto desusado
MergedWith propiedad.
3. Los recursos contenidos en los diccionarios de recursos que se han combinado a través de la
MergedDictionaries colección, en el orden inverso que aparecen en la MergedDictionaries propiedad.

NOTE
La búsqueda de diccionarios de recursos puede ser una tarea de cálculo intensiva si una aplicación contiene varios
diccionarios de recursos grande. Por lo tanto, para evitar búsquedas innecesarias, debe asegurarse de que cada página en
una aplicación solo usa los diccionarios de recursos que son adecuados para la página.

Combinación de los diccionarios en Xamarin.Forms 3.0


Desde el 3.0 de Xamarin.Forms, el proceso de combinar ResourceDictionary instancias se ha convertido en algo
más fácil y más flexible. El MergedDictionaries etiquetas de elemento de propiedad ya no son necesarias. En su
lugar, agrega al diccionario de recursos otro ResourceDictionary etiqueta con el nuevo Source propiedad
establecida en el nombre de archivo del archivo XAML con los recursos:

<ContentPage ...>
<ContentPage.Resources>
<ResourceDictionary>

<!-- Add more resources here -->

<ResourceDictionary Source="MyResourceDictionary.xaml" />

<!-- Add more resources here -->

</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>

Dado que Xamarin.Forms 3.0 crea automáticamente una instancia el ResourceDictionary , esos dos externas
ResourceDictionary etiquetas ya no son necesarias:

<ContentPage ...>
<ContentPage.Resources>

<!-- Add more resources here -->

<ResourceDictionary Source="MyResourceDictionary.xaml" />

<!-- Add more resources here -->

</ContentPage.Resources>
...
</ContentPage>

Esta nueva sintaxis no crear una instancia de la MyResourceDictionary clase. En su lugar, hace referencia al
archivo XAML. Por esa razón el archivo de código subyacente (MyResourceDictionary.xaml.cs) ya no es
necesario. También puede quitar el x:Class atributo de la etiqueta a la raíz de la MyResourceDictionary.xaml
archivo.

Resumen
En este artículo se explica cómo crear y consumir un ResourceDictionary y cómo se combinan los diccionarios
de recursos. Un ResourceDictionary permite que los recursos definidos en una sola ubicación y volver a
utilizarse en toda una aplicación de Xamarin.Forms.

Vínculos relacionados
Diccionarios de recursos (ejemplo)
Estilos
ResourceDictionary

Vídeo relacionado
Encuentre más vídeos de Xamarin en Channel 9 y YouTube.
XAML estándar (versión preliminar)
11/07/2019 • 2 minutes to read • Edit Online

Siga estos pasos para experimentar con el estándar de XAML en Xamarin.Forms:


Visual Studio
Visual Studio para Mac
1. Descargue el obtener una vista previa de los paquetes de NuGet aquí.
2. Agregar el Xamarin.Forms.Alias paquete NuGet a los proyectos de Xamarin.Forms .NET Standard y
plataforma.
3. Inicializar el paquete con Alias.Init()
4. Agregar un xmlns:a referencia xmlns:a="clr-namespace:Xamarin.Forms.Alias;assembly=Xamarin.Forms.Alias"
5. Usar los tipos de XAML, consulte el referencia controles para obtener más información.
El siguiente XAML muestra algunos controles XAML estándar que se usan en un objeto Xamarin.Forms
ContentPage :

<?xml version="1.0" encoding="utf-8"?>


<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:a="clr-namespace:Xamarin.Forms.Alias;assembly=Xamarin.Forms.Alias"
x:Class="XAMLStandardSample.ItemsPage"
Title="{Binding Title}" x:Name="BrowseItemsPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Clicked="AddItem_Clicked" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<a:StackPanel>
<ListView x:Name="ItemsListView" ItemsSource="{Binding Items}" VerticalOptions="FillAndExpand"
HasUnevenRows="true" RefreshCommand="{Binding LoadItemsCommand}" IsPullToRefreshEnabled="true" IsRefreshing="
{Binding IsBusy, Mode=OneWay}" CachingStrategy="RecycleElement" ItemSelected="OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10">
<a:TextBlock Text="{Binding Text}" LineBreakMode="NoWrap" Style="{DynamicResource
ListItemTextStyle}" FontSize="16" />
<a:TextBlock Text="{Binding Description}" LineBreakMode="NoWrap" Style="{DynamicResource
ListItemDetailTextStyle}" FontSize="13" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</a:StackPanel>
</ContentPage.Content>
</ContentPage>
NOTE
Requerir xmlns a: prefijo en los controles estándar de XAML es una limitación de la vista previa actual.

Vínculos relacionados
NuGet de versión preliminar
Controls Reference (Referencia de controles)
Controles XAML estándar (versión preliminar)
11/07/2019 • 2 minutes to read • Edit Online

Esta página enumera los controles XAML estándar disponibles en la vista previa, junto con su control equivalente
de Xamarin.Forms.
También hay una lista de los controles que tienen los nuevos nombres de propiedad y enumeración en XAML
estándar.

Controles
XAMARIN.FORMS XAML ESTÁNDAR

Fotograma Borde

Selector ComboBox

ActivityIndicator ProgressRing

StackLayout StackPanel

Etiqueta TextBlock

Entrada TextBox

Modificador ToggleSwitch

ContentView Control de usuario

Las propiedades y enumeraciones


CONTROLES DE XAMARIN.FORMS CON LAS PROPIEDAD XAMARIN.FORMS O
PROPIEDADES ACTUALIZADAS ENUMERACIÓN XAML ESTÁNDAR EQUIVALENTE

Botón, entrada, etiqueta, DatePicker, TextColor Primer plano


Editor, SearchBar, TimePicker

VisualElement BackgroundColor En segundo plano *

Selector de botón BorderColor, OutlineColor BorderBrush

Botón BorderWidth BorderThickness

ProgressBar Progreso Valor


CONTROLES DE XAMARIN.FORMS CON LAS PROPIEDAD XAMARIN.FORMS O
PROPIEDADES ACTUALIZADAS ENUMERACIÓN XAML ESTÁNDAR EQUIVALENTE

Botón, entrada, etiqueta, Editor, FontAttributesBold, Italic, None FontStyleItalic, Normal


SearchBar, intervalo, fuente

Botón, entrada, etiqueta, Editor, FontAttributes FontWeights * Normal, en negrita


SearchBar, intervalo, fuente

InputView KeyboardDefault, dirección Url, InputScopeNameValue *


número, telefónica, texto, Chat, enviar predeterminado, dirección Url, número,
por correo electrónico TelephoneNumber, texto, Chat,
EmailNameOrAddress

StackPanel StackOrientation Orientación *

IMPORTANT
Los elementos marcados con * están incompletos en la vista previa actual

Vínculos relacionados
NuGet de versión preliminar
Cargar XAML en tiempo de ejecución en
Xamarin.Forms
11/07/2019 • 4 minutes to read • Edit Online

descargar el ejemplo
El Xamarin.Forms.Xaml espacio de nombres incluye dos LoadFromXaml métodos de extensión que se pueden utilizar
para cargar y analizar XAML en tiempo de ejecución.

Fondo
Cuando se construye una clase de XAML de Xamarin.Forms, el LoadFromXaml indirectamente se llama al método.
Esto ocurre porque el archivo de código subyacente para un XAML llama a la clase el InitializeComponent método
desde su constructor:

public partial class MainPage : ContentPage


{
public MainPage()
{
InitializeComponent();
}
}

Cuando Visual Studio compila un proyecto que contiene un archivo XAML, analiza el archivo XAML para generar
un C# archivo de código (por ejemplo, MainPage.xaml.g.cs) que contiene la definición de la InitializeComponent
método:

private void InitializeComponent()


{
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(MainPage));
...
}

El InitializeComponent llamadas al método el LoadFromXaml método para extraer el archivo XAML (o el binario
compilado) de la biblioteca .NET Standard. Después de la extracción, inicializa todos los objetos definidos en el
archivo XAML, se conectan todos juntos en las relaciones de elementos primarios y secundarios, adjunta los
controladores de eventos definidos en el código para los eventos que se establece en el archivo XAML y establece
el árbol resultante de objetos como el contenido de la página.

Cargar XAML en tiempo de ejecución


El LoadFromXaml métodos son public , por lo tanto, pueden llamarse desde las aplicaciones de Xamarin.Forms
para cargar y analizar XAML en tiempo de ejecución. Esto permite escenarios como una aplicación XAML de
descarga de un servicio web, crear la vista necesaria desde el XAML y mostrarla en la aplicación.

WARNING
Cargar XAML en tiempo de ejecución tiene un gran costo de rendimiento y, por lo general debe evitarse.
El ejemplo de código siguiente muestra un uso sencillo:

using Xamarin.Forms.Xaml;
...

string navigationButtonXAML = "<Button Text=\"Navigate\" />";


Button navigationButton = new Button().LoadFromXaml(navigationButtonXAML);
...
_stackLayout.Children.Add(navigationButton);

En este ejemplo, un Button se crea la instancia, con su Text define un valor de propiedad que se establece desde
el XAML en el string . El Button , a continuación, se agrega a un StackLayout que se ha definido en el XAML
para la página.

NOTE
El LoadFromXaml métodos de extensión permiten especificar un argumento de tipo genérico. Sin embargo, es no suele ser
necesario especificar el argumento de tipo, ya que será deriva del tipo de la instancia de su funcionamiento en.

El LoadFromXaml método puede utilizarse para inflado cualquier XAML, con el siguiente ejemplo se infla un
ContentPage y, a continuación, vaya a él:

using Xamarin.Forms.Xaml;
...

// See the sample for the full XAML string


string pageXAML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<ContentPage
xmlns=\"http://xamarin.com/schemas/2014/forms\"\nxmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\nx:C
lass=\"LoadRuntimeXAML.CatalogItemsPage\"\nTitle=\"Catalog Items\">\n</ContentPage>";

ContentPage page = new ContentPage().LoadFromXaml(pageXAML);


await Navigation.PushAsync(page);

Tener acceso a elementos


Cargar XAML en tiempo de ejecución con el LoadFromXaml método no permite el acceso fuertemente tipado a los
elementos XAML que se ha especificado los nombres de objeto en tiempo de ejecución (mediante x:Name ). Sin
embargo, estos elementos XAML se pueden recuperar mediante el FindByName método y, a continuación, acceder
a ellos según sea necesario:

// See the sample for the full XAML string


string pageXAML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<ContentPage
xmlns=\"http://xamarin.com/schemas/2014/forms\"\nxmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\nx:C
lass=\"LoadRuntimeXAML.CatalogItemsPage\"\nTitle=\"Catalog Items\">\n<StackLayout>\n<Label
x:Name=\"monkeyName\"\n />\n</StackLayout>\n</ContentPage>";
ContentPage page = new ContentPage().LoadFromXaml(pageXAML);

Label monkeyLabel = page.FindByName<Label>("monkeyName");


monkeyLabel.Text = "Seated Monkey";
...

En este ejemplo, el XAML para un ContentPage se aumenta. Este XAML incluye una Label denominado
monkeyName , que se recupera mediante la FindByName método antes de que tenga Text se establece la propiedad.

Vínculos relacionados
LoadRuntimeXAML (ejemplo)
Aspectos básicos de la aplicación de Xamarin.Forms
11/07/2019 • 4 minutes to read • Edit Online

Accesibilidad
Sugerencias para incorporar características accesibles (como por ejemplo, la compatibilidad con herramientas de
lectura de pantalla) con Xamarin.Forms.

Clase de aplicación
La clase Application es el punto de partida para Xamarin.Forms: todas las aplicaciones necesitan implementar una
subclase App para establecer la página inicial. También proporciona la recopilación Properties para el
almacenamiento sencillo de datos. Se puede definir en C# o XAML.

Ciclo de vida de la aplicación


La clase Application y los métodos OnStart , OnSleep y OnResume , así como los eventos de navegación modal,
permiten controlar los eventos del ciclo de vida de la aplicación con código personalizado.

Indexación de la aplicación y vinculación en profundidad


La indexación de la aplicación permite a aplicaciones que de lo contrario se olvidarían tras unos pocos usos
mantener su pertinencia al aparecer en los resultados de la búsqueda. La vinculación en profundidad permite a las
aplicaciones responder a un resultado de la búsqueda que contiene datos de aplicación, normalmente al navegar a
una página a la que se hace referencia a partir de un vínculo profundo.

Comportamientos
Los controles de la interfaz de usuario se pueden extender fácilmente sin incluir subclases mediante
comportamientos para agregar funcionalidad.

Representadores personalizados
Los representadores personalizados permiten a los desarrolladores reemplazar la representación predeterminada
de los controles de Xamarin.Forms para personalizar su apariencia y comportamiento en cada plataforma (usando
SDK nativos si lo prefiere).

Enlace de datos
El enlace de datos vincula las propiedades de dos objetos para que los cambios en una propiedad se reflejen
automáticamente en la otra propiedad. El enlace de datos es una parte integral de la arquitectura de aplicación
Model-View -ViewModel (MVVM ).

Servicio de dependencia
DependencyService proporciona un localizador simple para que pueda incluir código para interfaces en el código
compartido y proporcionar implementaciones específicas de la plataforma que se resuelvan automáticamente, lo
que facilita la referencia a funcionalidades específicas de la plataforma en Xamarin.Forms.
Efectos
Con los efectos se pueden personalizar los controles nativos de cada plataforma y normalmente se usan para
pequeños cambios de estilo.

Gestos
La clase GestureRecognizer de Xamarin.Forms admite los gestos de pulsar, reducir y desplazar lateralmente en
controles de interfaz de usuario.

Localización
El marco de trabajo de localización integrado de .NET puede usarse para generar aplicaciones multilingües
multiplataforma con Xamarin.Forms.

Centro de mensajería
MessagingCenter de Xamarin.Forms permite que los modelos de vista y otros componentes se comuniquen sin
tener que saber nada sobre los demás, salvo un sencillo contrato de mensajería.

Navegación
Xamarin.Forms proporciona una serie de experiencias de navegación de páginas diferentes, en función del tipo de
Page que se use.

Shell
Xamarin.Forms Shell reduce la complejidad del desarrollo de aplicaciones móviles al proporcionar las
características fundamentales que requieren la mayoría de aplicaciones móviles. Esto incluye una experiencia de
usuario de navegación común, un esquema de navegación basado en URI y un controlador de búsqueda integrada.

Templates (Plantillas [C++])


Las plantillas de control proporcionan la capacidad de aplicar temas y cambiar el tema de las páginas de aplicación
fácilmente en tiempo de ejecución, mientras que las plantillas de datos proporcionan la capacidad de definir la
presentación de datos en los controles admitidos.

Desencadenadores
Actualice los controles respondiendo a los cambios de propiedades y eventos en XAML.
Accesibilidad de Xamarin.Forms
11/07/2019 • 3 minutes to read • Edit Online

Crear una aplicación accesible garantiza que la aplicación podrá ser usada por personas que interaccionan con la
interfaz de usuario con una variedad de necesidades y experiencias.
Hacer que una aplicación de Xamarin.Forms sea accesible significa pensar en el diseño de muchos elementos de
interfaz de usuario. Para obtener información sobre cuestiones que hay que tener en cuenta, vea la lista de
comprobación de accesibilidad. Ya se pueden solucionar muchos problemas de accesibilidad como las fuentes
grandes y la configuración adecuada de color y contraste mediante las API de Xamarin.Forms.
Las guías Accesibilidad para Android y Accesibilidad para iOS contienen información detallada de las API nativas
expuestas por Xamarin, y la Guía de accesibilidad UWP en MSDN explica el enfoque nativo en esa plataforma.
Estas API se usan para implementar por completo aplicaciones accesibles en cada plataforma.
Actualmente, Xamarin.Forms no tiene compatibilidad integrada con todas las API de accesibilidad que hay
disponibles en cada una de las plataformas subyacentes. Pero sí admite configurar las propiedades de
automatización en los elementos de la interfaz de usuario para admitir las herramientas de ayuda a la navegación y
el lector de pantalla, que es uno de los aspectos más importantes al crear aplicaciones accesibles. Para más
información, vea Propiedades de automatización.
En las aplicaciones de Xamarin.Forms también se puede especificar el orden de tabulación de los controles, con el
fin de mejorar la facilidad de uso y la accesibilidad. Para más información, consulte Accesibilidad del teclado.
Otras API de accesibilidad (como PostNotification en iOS ) pueden ser más adecuadas para una implementación
de DependencyService o Representador personalizado. No se tratarán en esta guía.

Probar la accesibilidad
Las aplicaciones de Xamarin.Forms normalmente van dirigidas a varias plataformas, lo que significa que las
pruebas de las características de accesibilidad se realizan según la plataforma. Siga estos vínculos para saber más
sobre cómo probar la accesibilidad en cada plataforma:
Pruebas en iOS
Pruebas en Android
Windows AccScope (MSDN )

Vínculos relacionados
Accesibilidad multiplataforma
Propiedades de automatización
Accesibilidad de teclado

Vídeo relacionado
Encuentre más vídeos de Xamarin en Channel 9 y YouTube.
Propiedades de automatización en Xamarin.Forms
11/07/2019 • 12 minutes to read • Edit Online

Descargar el ejemplo
Xamarin.Forms permite que los valores de accesibilidad se establezcan en elementos de la interfaz de usuario
mediante el uso de las propiedades adjuntas de la clase AutomationProperties, que a su vez establece valores de
accesibilidad nativa. Este artículo explica cómo usar la clase AutomationProperties, para que un lector de pantalla
pueda leer los elementos en la página.
Xamarin.Forms permite que las propiedades de automatización se establezcan en elementos de la interfaz de
usuario a través de las propiedades adjuntas siguientes:
AutomationProperties.IsInAccessibleTree : indica si el elemento está disponible para una aplicación accesible.
Para obtener más información, consulte AutomationProperties.IsInAccessibleTree.
AutomationProperties.Name : una breve descripción del elemento que actúa como un identificador con capacidad
de lectura para el elemento. Para obtener más información, consulte AutomationProperties.Name.
AutomationProperties.HelpText : una descripción más larga del elemento, que puede considerarse como el texto
de información sobre herramientas asociado al elemento. Para obtener más información, consulte
AutomationProperties.HelpText.
AutomationProperties.LabeledBy : permite que otro elemento defina la información de accesibilidad para el
elemento actual. Para obtener más información, consulte AutomationProperties.LabeledBy.
Estas propiedades adjuntas establecen los valores de accesibilidad nativa para que un lector de pantalla pueda leer
el elemento. Para más información sobre las propiedades adjuntas, consulte Propiedades asociadas.

IMPORTANT
El uso de las propiedades adjuntas AutomationProperties puede afectar a la ejecución de prueba de IU en Android. Las
propiedades AutomationId , AutomationProperties.Name y AutomationProperties.HelpText , establecen la propiedad
ContentDescription nativa, con los valores de propiedad AutomationProperties.Name y
AutomationProperties.HelpText con prioridad sobre el valor AutomationId (si tanto AutomationProperties.Name
como AutomationProperties.HelpText están establecidos, los valores se concatenarán). Esto significa que cualquier
prueba que busque AutomationId fallará si AutomationProperties.Name o AutomationProperties.HelpText también
están establecidos en el elemento. En este escenario, las pruebas de IU deben modificarse para buscar el valor de
AutomationProperties.Name o AutomationProperties.HelpText , o una concatenación de ambos.

Cada plataforma tiene un lector de pantalla diferente para leer los valores de accesibilidad:
iOS tiene VoiceOver. Para obtener más información, consulte Test Accessibility on Your Device with VoiceOver
(Probar la accesibilidad en un dispositivo con VoiceOver), en developer.apple.com.
Android tiene TalkBack. Para obtener más información, consulte Testing Your App's Accessibility (Prueba de la
accesibilidad de una aplicación), en developer.android.com.
Windows tiene el Narrador. Para obtener más información, consulte Verify main app scenarios by using
Narrator (Comprobar escenarios de aplicaciones principales mediante el uso del Narrador).
Sin embargo, el comportamiento exacto de un lector de pantalla depende del software y de la configuración del
usuario en él. Por ejemplo, la mayoría de los lectores de pantalla leen el texto asociado con un control cuando
queda focalizado, permitiendo a los usuarios orientarse a medida que se mueven entre los controles en la página.
Algunos lectores de pantalla leen también la interfaz de usuario de la aplicación completa cuando aparece una
página, lo cual permite que el usuario reciba todo de contenido informativo disponible de la página antes de
intentar navegar por ella.
Los lectores de pantalla también leen valores de accesibilidad distintos. En la aplicación de ejemplo:
VoiceOver leerá el valor Placeholder de Entry , seguido de las instrucciones para usar el control.
TalkBack leerá el valor Placeholder de Entry , seguido del valor AutomationProperties.HelpText , seguido de las
instrucciones para usar el control.
El Narrador leerá el valor AutomationProperties.LabeledBy de Entry , seguido de las instrucciones para usar el
control.
Además, el Narrador dará prioridad a AutomationProperties.Name , AutomationProperties.LabeledBy y, a
continuación, AutomationProperties.HelpText . En Android, TalkBack puede combinar los valores
AutomationProperties.Name y AutomationProperties.HelpText . Por lo tanto, se recomienda llevar a cabo pruebas de
accesibilidad exhaustivas en cada plataforma para garantizar una experiencia óptima.

AutomationProperties.IsInAccessibleTree
La propiedad adjunta AutomationProperties.IsInAccessibleTree es un valor boolean que determina si el elemento
es accesible, y por lo tanto visible, para los lectores de pantalla. Debe establecerse en true para usar las otras
propiedades adjuntas de accesibilidad. Esto se puede lograr en XAML de la siguiente manera:

<Entry AutomationProperties.IsInAccessibleTree="true" />

Como alternativa, puede establecerse en C# de la siguiente manera:

var entry = new Entry();


AutomationProperties.SetIsInAccessibleTree(entry, true);

NOTE
Tenga en cuenta que el método SetValue también se puede utilizar para establecer la propiedad adjunta de
AutomationProperties.IsInAccessibleTree ,
entry.SetValue(AutomationProperties.IsInAccessibleTreeProperty, true);

AutomationProperties.Name
El valor de la propiedad adjunta AutomationProperties.Name debe ser una cadena de texto corta y descriptiva que
usa un lector de pantalla anunciar un elemento. Esta propiedad debe establecerse para los elementos que tengan
un significado que sea importante para comprender el contenido o interactuar con la interfaz de usuario. Esto se
puede lograr en XAML de la siguiente manera:

<ActivityIndicator AutomationProperties.IsInAccessibleTree="true"
AutomationProperties.Name="Progress indicator" />

Como alternativa, puede establecerse en C# de la siguiente manera:

var activityIndicator = new ActivityIndicator();


AutomationProperties.SetIsInAccessibleTree(activityIndicator, true);
AutomationProperties.SetName(activityIndicator, "Progress indicator");
NOTE
Tenga en cuenta que el método SetValue también se puede utilizar para establecer la propiedad adjunta de
AutomationProperties.Name ,
activityIndicator.SetValue(AutomationProperties.NameProperty, "Progress indicator");

AutomationProperties.HelpText
La propiedad adjunta de AutomationProperties.HelpText debe establecerse en el texto que describe el elemento de
interfaz de usuario y puede ser considerarse el texto de información sobre herramientas asociado al elemento.
Esto se puede lograr en XAML de la siguiente manera:

<Button Text="Toggle ActivityIndicator"


AutomationProperties.IsInAccessibleTree="true"
AutomationProperties.HelpText="Tap to toggle the activity indicator" />

Como alternativa, puede establecerse en C# de la siguiente manera:

var button = new Button { Text = "Toggle ActivityIndicator" };


AutomationProperties.SetIsInAccessibleTree(button, true);
AutomationProperties.SetHelpText(button, "Tap to toggle the activity indicator");

NOTE
Tenga en cuenta que el método SetValue también se puede utilizar para establecer la propiedad adjunta de
AutomationProperties.HelpText ,
button.SetValue(AutomationProperties.HelpTextProperty, "Tap to toggle the activity indicator");

En algunas plataformas, para editar controles, como un elemento Entry , la propiedad HelpText a veces puede
omitirse y reemplazarse por el texto de marcador de posición. Por ejemplo, "Escriba aquí su nombre" es un buen
candidato para la propiedad Entry.Placeholder que coloca el texto en el control antes de la entrada real del
usuario.

AutomationProperties.LabeledBy
La propiedad adjunta AutomationProperties.LabeledBy permite que otro elemento defina la información de
accesibilidad para el elemento actual. Por ejemplo, un elemento Label junto a un elemento Entry puede
utilizarse para describir lo que representa Entry . Esto se puede lograr en XAML de la siguiente manera:

<Label x:Name="label" Text="Enter your name: " />


<Entry AutomationProperties.IsInAccessibleTree="true"
AutomationProperties.LabeledBy="{x:Reference label}" />

Como alternativa, puede establecerse en C# de la siguiente manera:

var nameLabel = new Label { Text = "Enter your name: " };


var entry = new Entry();
AutomationProperties.SetIsInAccessibleTree(entry, true);
AutomationProperties.SetLabeledBy(entry, nameLabel);
NOTE
Tenga en cuenta que el método también se puede utilizar para establecer la propiedad adjunta de
SetValue
AutomationProperties.IsInAccessibleTree ,
entry.SetValue(AutomationProperties.LabeledByProperty, nameLabel);

Pormenores relativos a la accesibilidad


En las secciones siguientes se describen los pormenores que supone el hecho de establecer valores de
accesibilidad en ciertos controles.
NavigationPage
En Android, para establecer el texto que el lector de pantalla leerá para la flecha Atrás de la barra de acciones
NavigationPage , defina las propiedades AutomationProperties.Name y AutomationProperties.HelpText en Page . Sin
embargo, tenga en cuenta que esto no se aplicará a los botones Atrás del sistema operativo.
MasterDetailPage
En iOS y la Plataforma Universal de Windows (UWP ), para establecer el texto que el lector de pantalla leerá para
el botón de alternancia en MasterDetailPage , defina las propiedades AutomationProperties.Name y
AutomationProperties.HelpText en MasterDetailPage , o bien en la propiedad IconImageSource de la página
Master .

En Android, para establecer el texto que el lector de pantalla leerá para el botón de alternancia en
MasterDetailPage , agregue los recursos de cadena al proyecto de Android:

<resources>
<string name="app_name">Xamarin Forms Control Gallery</string>
<string name="btnMDPAutomationID_open">Open Side Menu message</string>
<string name="btnMDPAutomationID_close">Close Side Menu message</string>
</resources>

A continuación, defina la AutomationId propiedad de la propiedad IconImageSource de la página Master en la


cadena que corresponda:

var master = new ContentPage { ... };


master.IconImageSource.AutomationId = "btnMDPAutomationID";

ToolbarItem
En iOS, Android y UWP, los lectores de pantalla leerán el valor de propiedad Text de las instancias de
ToolbarItem , siempre que no haya definido los valores AutomationProperties.Name y
AutomationProperties.HelpText .

En iOS y UWP, el valor de propiedad AutomationProperties.Name reemplazará el valor de propiedad Text que el
lector de pantalla lee.
En Android, los valores de propiedad AutomationProperties.Name o AutomationProperties.HelpText reemplazarán
por completo el valor de propiedad Text , que es visible y el lector de pantalla leerá. Tenga en cuenta que se trata
de una limitación de API inferior a 26.

Vínculos relacionados
Propiedades asociadas
Accesibilidad (ejemplo)
Accesibilidad del teclado en Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

Descargar el ejemplo
Los usuarios que emplean lectores de pantalla, o que tienen problemas de movilidad, pueden tener dificultades
para usar aplicaciones que no proporcionan acceso adecuado con el teclado. En las aplicaciones de Xamarin.Forms
se puede especificar un orden de tabulación esperado para mejorar su facilidad de uso y accesibilidad. La
especificación de un orden de tabulación para los controles permite la navegación con el teclado, prepara las
páginas de la aplicación para recibir la entrada en un orden concreto y permite que los lectores de pantalla lean
elementos activables para el usuario.
De forma predeterminada, el orden de tabulación de los controles es el mismo orden en que se están indicados en
XAML o agregados mediante programación a una colección secundaria. Este es el orden en que se navegará por
los controles con un teclado y en que se leerán con un lector de pantalla; a menudo, este orden predeterminado es
el mejor orden posible. Sin embargo, el orden predeterminado no es siempre el mismo que el orden previsto,
como se muestra en el ejemplo de código XAML siguiente:

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Text="You"
HorizontalOptions="Center" />
<Label Grid.Column="1"
Text="Manager"
HorizontalOptions="Center" />
<Entry Grid.Row="1"
Placeholder="Enter forename" />
<Entry Grid.Column="1"
Grid.Row="1"
Placeholder="Enter forename" />
<Entry Grid.Row="2"
Placeholder="Enter surname" />
<Entry Grid.Column="1"
Grid.Row="2"
Placeholder="Enter surname" />
</Grid>

La captura de pantalla siguiente muestra el orden de tabulación predeterminado para este ejemplo de código:
Aquí, el orden de tabulación está basado en filas y es el orden en el cual los controles se indican en el XAML. Por
lo tanto, al presionar la tecla TAB se navega por instancias Entry de nombre, seguidas por instancias Entry de
apellido. Sin embargo, una experiencia más intuitiva sería usar una navegación por tabulación de la columna en
primer lugar, para que al presionar la tecla TAB se navegara por los pares de nombre y apellido. Esto puede
lograrse mediante la especificación del orden de tabulación de los controles de entrada.

NOTE
En la Plataforma universal de Windows, se pueden definir métodos abreviados de teclado que proporcionen una manera
intuitiva para que los usuarios puedan navegar rápidamente e interactuar con la interfaz de usuario visible de la aplicación a
través de un teclado, en lugar de a través de funciones táctiles o un mouse. Para obtener más información, consulte Setting
VisualElement Access Keys (Configuración de claves de acceso de VisualElement).

Configuración del orden de tabulación


La propiedad VisualElement.TabIndex se utiliza para indicar el orden en que las instancias VisualElement reciben
el foco cuando el usuario navega por los controles presionando la tecla TAB. El valor predeterminado de la
propiedad es 0, y se puede establecer en cualquier valor int .
Las reglas siguientes aplican cuando se usa el orden de tabulación predeterminado, o cuando se establece la
propiedad TabIndex :
Las instancias de VisualElement con TabIndex igual a 0 se agregan al orden de tabulación en función de su
orden de declaración en colecciones secundarias o XAML.
Las instancias de VisualElement con TabIndex mayor que 0 se agregan al orden de tabulación en función de
su valor TabIndex .
Las instancias de VisualElement con TabIndex menor que 0 se agregan al orden de tabulación y aparecen
antes que cualquier valor 0.
Los conflictos relacionados con TabIndex se resuelven por orden de declaración.
Tras definirse un orden de tabulación, al presionar la tecla TAB se recorrerá cíclicamente el foco a través de
controles en orden de TabIndex ascendente, con un ajuste alrededor del principio una vez alcanzado el control
final.
El siguiente ejemplo de XAML muestra la propiedad TabIndex establecida en los controles de entrada para
habilitar la navegación por tabulación de la columna en primer lugar:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Text="You"
HorizontalOptions="Center" />
<Label Grid.Column="1"
Text="Manager"
HorizontalOptions="Center" />
<Entry Grid.Row="1"
Placeholder="Enter forename"
TabIndex="1" />
<Entry Grid.Column="1"
Grid.Row="1"
Placeholder="Enter forename"
TabIndex="3" />
<Entry Grid.Row="2"
Placeholder="Enter surname"
TabIndex="2" />
<Entry Grid.Column="1"
Grid.Row="2"
Placeholder="Enter surname"
TabIndex="4" />
</Grid>

La captura de pantalla siguiente muestra el orden de tabulación para este ejemplo de código:

Aquí, el orden de tabulación está basado en columnas. Por lo tanto, al presionar la tecla TAB se navega por pares
Entry de nombre y apellido.

IMPORTANT
Los lectores de pantalla de iOS y Android respetarán la propiedad TabIndex de un objeto VisualElement al leer los
elementos accesible en la pantalla.

Exclusión de controles del orden de tabulación


Además de establecer el orden de tabulación de los controles, puede ser necesario excluir los controles del orden
de tabulación. Una manera de conseguirlo es estableciendo la propiedad IsEnabled de los controles en false , ya
que los controles deshabilitados se excluyen del orden de tabulación.
Sin embargo, puede ser necesario excluir los controles del orden de tabulación incluso cuando no están
deshabilitados. Esto puede lograrse con la propiedad VisualElement.IsTapStop , que indica si un elemento
VisualElement se incluye en la navegación por tabulación. Su valor predeterminado es true , y cuando su valor es
false , la infraestructura de navegación mediante tabulación omite el control, independientemente de si se ha
definido un TabIndex .

Controles admitidos
Las propiedades TabIndex y IsTapStop se admiten en los siguientes controles, que aceptan entradas de teclado
en una o varias plataformas:
Button
DatePicker
Editor
Entry
NavigationPage
Picker
ProgressBar
SearchBar
Slider
Stepper
Switch
TabbedPage
TimePicker

NOTE
Cada uno de estos controles no puede recibir el foco del tabulador en todas las plataformas.

Vínculos relacionados
Accesibilidad (ejemplo)
Clase App de Xamarin.Forms
11/07/2019 • 8 minutes to read • Edit Online

La clase base Application ofrece las siguientes características, que se exponen en la subclase predeterminada
App de los proyectos:

Una propiedad MainPage , que es donde se establece la página inicial de la aplicación.


Un diccionario Properties persistente para almacenar valores simples a lo largo de los cambios de estado del
ciclo de vida.
Una propiedad estática Current que contiene una referencia al objeto de aplicación actual.
También expone Métodos del ciclo de vida como OnStart , OnSleep y OnResume , así como eventos de navegación
modales.
Según la plantilla que se elija, la clase App puede definirse de alguna de estas dos maneras:
C#, o bien
XAML y C#
Para crear una clase App mediante XAML, se debe reemplazar la clase App predeterminada por una clase App
de XAML y el código subyacente asociado, como se muestra en el ejemplo de código siguiente:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Photos.App">

</Application>

El ejemplo de código siguiente muestra el código subyacente asociado:

public partial class App : Application


{
public App ()
{
InitializeComponent ();
MainPage = new HomePage ();
}
...
}

Además de establecer la propiedad MainPage , el código subyacente también debe llamar al método
InitializeComponent para cargar y analizar el XAML asociado.

Propiedad MainPage
La propiedad MainPage de la clase Application establece la página raíz de la aplicación.
Por ejemplo, puede crear lógica en la clase App para mostrar otra página en función de si el usuario ha iniciado
sesión o no.
La propiedad MainPage debe establecerse en el constructor App ,
public class App : Xamarin.Forms.Application
{
public App ()
{
MainPage = new ContentPage { Title = "App Lifecycle Sample" }; // your page here
}
}

Diccionario de propiedades
La subclase Application tiene un diccionario estático Properties que se puede usar para almacenar datos, en
particular para su uso en los métodos OnStart , OnSleep y OnResume . Se puede acceder a él desde cualquier lugar
del código de Xamarin.Forms con Application.Current.Properties .
El diccionario Properties usa una clave string y almacena un valor object .
Por ejemplo, puede establecer una propiedad persistente "id" en cualquier lugar del código (al seleccionar un
elemento, en el método OnDisappearing de una página, o en el método OnSleep ) del modo siguiente:

Application.Current.Properties ["id"] = someClass.ID;

En los métodos OnStart u OnResume puede usar este valor para volver a crear la experiencia del usuario de
alguna manera. El diccionario Properties almacena elementos object , por lo que debe convertir su valor antes
de usarlo.

if (Application.Current.Properties.ContainsKey("id"))
{
var id = Application.Current.Properties ["id"] as int;
// do something with id
}

Verifique siempre la presencia de la clave antes de acceder para evitar errores inesperados.

NOTE
El diccionario Properties solo puede serializar tipos primitivos para el almacenamiento. El intento de almacenar otros tipos
(como List<string> ) puede producir un error en modo silencioso.

Persistencia
El diccionario Properties se guarda automáticamente en el dispositivo. Los datos agregados al diccionario están
disponibles cuando la aplicación vuelve desde el segundo plano o incluso después de su reinicio.
Xamarin.Forms 1.4 ha incorporado un método adicional a la clase Application ( SavePropertiesAsync() ) al que se
puede llamar para conservar de forma proactiva el diccionario Properties . Esto permite guardar propiedades
después de actualizaciones importantes en lugar de arriesgarse a que no se serialicen debido a un bloqueo o a su
eliminación por parte del sistema operativo.
Puede encontrar referencias al uso del diccionario Properties en los capítulos 6, 15 y 20 del libro Creating
Mobile Apps with Xamarin.Forms (Creación de aplicaciones móviles con Xamarin.Forms) y en los ejemplos
asociados.

La clase Application
A continuación se muestra una completa implementación de la clase Application como referencia:

public class App : Xamarin.Forms.Application


{
public App ()
{
MainPage = new ContentPage { Title = "App Lifecycle Sample" }; // your page here
}

protected override void OnStart()


{
// Handle when your app starts
Debug.WriteLine ("OnStart");
}

protected override void OnSleep()


{
// Handle when your app sleeps
Debug.WriteLine ("OnSleep");
}

protected override void OnResume()


{
// Handle when your app resumes
Debug.WriteLine ("OnResume");
}
}

Luego se crean instancias de esta clase en cada proyecto específico de plataforma y se pasa al método
LoadApplication , que es donde se carga MainPage y se muestra al usuario. El código para cada plataforma se
muestra en las secciones siguientes. Las plantillas de solución de Xamarin.Forms más recientes ya contienen todo
este código, preconfigurado para la aplicación.
Proyecto de iOS
La clase AppDelegate de iOS hereda de FormsApplicationDelegate . Debe:
Llamar a LoadApplication con una instancia de la clase App .
Devolver siempre base.FinishedLaunching (app, options); .

[Register ("AppDelegate")]
public partial class AppDelegate :
global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate // superclass new in 1.3
{
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init ();

LoadApplication (new App ()); // method is new in 1.3

return base.FinishedLaunching (app, options);


}
}

Proyecto de Android
MainActivity de Android hereda de FormsAppCompatActivity . En la invalidación OnCreate , se llama al método
LoadApplication con una instancia de la clase App .
[Activity (Label = "App Lifecycle Sample", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher =
true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : FormsAppCompatActivity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);

global::Xamarin.Forms.Forms.Init (this, bundle);

LoadApplication (new App ()); // method is new in 1.3


}
}

Proyecto universal de Windows (UWP) para Windows 10


La página principal del proyecto de UWP debe heredar de WindowsPage :

<forms:WindowsPage
...
xmlns:forms="using:Xamarin.Forms.Platform.UWP"
...>
</forms:WindowsPage>

El código de C# detrás de la construcción debe llamar a LoadApplication para crear una instancia del elemento
App de Xamarin.Forms. Tenga en cuenta que es recomendable usar explícitamente el espacio de nombres de
aplicación para calificar a App , dado que las aplicaciones de UWP también tienen su propia clase App no
relacionada con Xamarin.Forms.

public sealed partial class MainPage


{
public MainPage()
{
InitializeComponent();

LoadApplication(new YOUR_NAMESPACE.App());
}
}

Tenga en cuenta que Forms.Init() debe llamarse desde App.xaml.cs en el proyecto UWP.
Para obtener más información, consulte Proyectos de instalación de Windows, donde se incluyen instrucciones
para agregar un proyecto UWP a una solución existente de Xamarin.Forms cuyo destino no es UWP.
Ciclo de vida de aplicaciones de Xamarin.Forms
17/07/2019 • 3 minutes to read • Edit Online

La clase base Application proporciona las características siguientes:


Métodos de ciclo de vida OnStart , OnSleep y OnResume .
Eventos de navegación de página PageAppearing , PageDisappearing .
Eventos de navegación modal ModalPushing , ModalPushed , ModalPopping y ModalPopped .

Métodos de ciclo de vida


La clase Application contiene tres métodos virtuales que pueden reemplazarse para responder a cambios en el
ciclo de vida:
OnStart : se llama cuando se inicia la aplicación.
OnSleep : se llama cada vez que la aplicación pasa a segundo plano.
OnResume : se llama cuando se reanuda la aplicación, después de haberla enviado a segundo plano.

NOTE
No hay ningún método para la finalización de aplicaciones. En circunstancias normales (es decir, si no se produce un
bloqueo), la finalización de la aplicación se producirá desde el estado OnSleep, sin que se muestren notificaciones en el
código.

Para observar cuándo se llama a estos métodos, implemente una llamada WriteLine en cada uno (como se
muestra a continuación) y pruébelos en cada plataforma.

protected override void OnStart()


{
Debug.WriteLine ("OnStart");
}
protected override void OnSleep()
{
Debug.WriteLine ("OnSleep");
}
protected override void OnResume()
{
Debug.WriteLine ("OnResume");
}

IMPORTANT
En Android, se llama al método OnStart por rotación y también cuando la aplicación se inicia por primera vez si la
actividad principal carece de ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation en el
atributo [Activity()] .

Eventos de notificación de página


Hay dos eventos en la clase Application que proporcionan notificaciones de páginas que aparecen y
desaparecen:
PageAppearing : se genera cuando una página va a aparecer en la pantalla.
PageDisappearing : se genera cuando una página va a desaparecer de la pantalla.

Estos eventos pueden usarse en escenarios en los que quiera realizar un seguimiento de las páginas a medida que
aparezcan en la pantalla.

NOTE
Los eventos PageAppearing y PageDisappearing se generan a partir de la clase base Page inmediatamente después de
los eventos Page.Appearing y Page.Disappearing , respectivamente.

Eventos de navegación modal


Hay cuatro eventos en la clase Application , cada uno con sus propios argumentos de evento, que le permiten
responder a páginas modales que empiezan a mostrarse y se descartan:
ModalPushing : se genera cuando se inserta una página de forma modal.
ModalPushed : se genera después de que se ha insertado una página de forma modal.
ModalPopping : se genera cuando se extrae una página de forma modal.
ModalPopped : se genera después de que se ha extraído una página de forma modal.

NOTE
Los argumentos de evento ModalPopping de tipo ModalPoppingEventArgs contienen una propiedad Cancel . Cuando
Cancel se establece en true , se cancela la ventana emergente modal.
Indexación de la aplicación y vinculación en
profundidad
11/07/2019 • 18 minutes to read • Edit Online

Descargar el ejemplo
La indexación de la aplicación permite a aplicaciones que de lo contrario se olvidarían tras unos pocos usos
mantener su pertinencia al aparecer en los resultados de la búsqueda. La vinculación en profundidad permite a las
aplicaciones responder a un resultado de la búsqueda que contiene datos de aplicación, normalmente al navegar
a una página a la que se hace referencia a partir de un vínculo profundo. En este artículo se explica cómo usar la
indexación de la aplicación y la vinculación en profundidad para que el contenido de las aplicaciones
Xamarin.Forms permita efectuar búsquedas en dispositivos iOS y Android.

Vídeo de vinculación en profundidad con Xamarin.Forms y Azure


La indexación de la aplicación y la vinculación en profundidad de Xamarin.Forms proporcionan una API para la
publicación de metadatos y la indexación de las aplicaciones a medida que los usuarios navegan por ellas.
Posteriormente, el contenido indexado se puede buscar en una búsqueda web, de Spotlight o de Google. Al pulsar
en un resultado de la búsqueda que contenga un vínculo profundo, se desencadenará un evento que normalmente
se usa para navegar a la página a la que se hace referencia desde el vínculo profundo y que puede controlarse
mediante una aplicación.
La aplicación de ejemplo muestra una aplicación de lista de tareas pendientes en la que los datos se almacenan en
una base de datos SQLite local, tal como aparece en las siguientes capturas de pantalla:
Se indexan todas las instancias del elemento TodoItem que crea el usuario. A continuación, se puede usar una
búsqueda específica de la plataforma para localizar los datos indexados de la aplicación. Cuando el usuario pulsa
un resultado de la búsqueda de la aplicación, esta se inicia, se navega al elemento TodoItemPage y se muestra el
elemento TodoItem al que se hace referencia desde el vínculo profundo.
Para obtener más información sobre el uso de una base de datos SQLite, consulte Bases de datos locales de
Xamarin.Forms.

NOTE
La funcionalidad de la indexación de la aplicación y de la vinculación en profundidad de Xamarin.Forms solo está disponible
en las plataformas iOS y Android, y requiere como mínimo iOS 9 y API 23, respectivamente.

Programa de instalación
En las siguientes secciones se proporcionan las instrucciones de instalación adicionales para usar esta característica
en las plataformas iOS y Android.
iOS
En la plataforma iOS, asegúrese de que el proyecto de plataforma de iOS establezca el archivo Entitlements.plist
como de derechos personalizados para firmar el conjunto.
Para usar vínculos universales de iOS, realice lo siguiente:
1. Agregue un derecho de dominios asociados a la aplicación con la clave applinks , incluidos todos los dominios
que admitirá la aplicación.
2. Agregue un archivo de asociación de sitios de la aplicación de Apple a su sitio web.
3. Agregue la clave applinks al archivo de asociación de sitios de la aplicación de Apple.
Para obtener más información, vea Allowing Apps and Websites to Link to Your Content (Permitir la vinculación de
contenido a aplicaciones y sitios web) en developer.apple.com.
Android
En la plataforma Android, se deben cumplir los siguientes requisitos previos para usar la funcionalidad de la
indexación de la aplicación y de la vinculación en profundidad:
1. Es necesario tener una versión de la aplicación publicada en Google Play.
2. Es necesario haber registrado un sitio web complementario para la aplicación en la consola del desarrollador de
Google. Una vez que la aplicación esté asociada a un sitio web, las direcciones URL se pueden indexar de forma
que funcionen tanto para el sitio web como para la aplicación. De este modo, podrán aparecer después en los
resultados de la búsqueda. Para obtener más información, vea Indexar aplicaciones en la Búsqueda de Google
en el sitio web de Google.
3. La aplicación debe admitir las intenciones de dirección URL HTTP en la clase MainActivity , que indican a la
indexación de la aplicación los tipos de esquemas de datos de direcciones URL a los que esta puede dar
respuesta. Para obtener más información, vea Configuración del filtro de intención.
Una vez cumplidos estos requisitos previos, para usar la indexación de la aplicación y la vinculación en
profundidad de Xamarin.Forms en la plataforma Android se requiere la siguiente configuración adicional:
1. Instale el paquete NuGet Xamarin.Forms.AppLinks en el proyecto de la aplicación de Android.
2. En el archivo MainActivity.cs, agregue una declaración para usar el espacio de nombres
Xamarin.Forms.Platform.Android.AppLinks .
3. En el archivo MainActivity.cs, agregue una declaración para usar el espacio de nombres Firebase .
4. En un explorador web, cree un nuevo proyecto con la consola Firebase.
5. En la consola Firebase, agregue Firebase a la aplicación Android y escriba los datos necesarios.
6. Descargue el archivo google-services.json resultante.
7. Agregue el archivo google-services.json al directorio raíz del proyecto de Android y establezca el valor
Acción de compilación en GoogleServicesJson.
8. En la invalidación del elemento MainActivity.OnCreate , agregue la siguiente línea de código debajo de
Forms.Init(this, bundle) :

FirebaseApp.InitializeApp(this);
AndroidAppLinks.Init(this);

Al agregar google-services.json al proyecto (y al establecer la acción de compilación GoogleServicesJson*), el


proceso de compilación extraerá la clave de API y el id. de cliente. A continuación, las credenciales se agregarán al
archivo de manifiesto generado.
Para obtener más información, vea Deep Link Content with Xamarin.Forms URL Navigation (Contenido de
vínculos profundos con la navegación de direcciones URL Xamarin.Forms) en el blog de Xamarin.

Indexación de una página


El proceso de indexación de una página y de exposición en una búsqueda de Spotlight y Google es el que sigue:
1. Cree un elemento AppLinkEntry que contenga los metadatos necesarios para indexar la página y un vínculo
profundo para volver a ella cuando el usuario seleccione el contenido indexado en los resultados de la
búsqueda.
2. Registre la instancia AppLinkEntry con el fin de indexarla para la búsqueda.
En el siguiente ejemplo de código se muestra cómo crear una instancia AppLinkEntry :
AppLinkEntry GetAppLink(TodoItem item)
{
var pageType = GetType().ToString();
var pageLink = new AppLinkEntry
{
Title = item.Name,
Description = item.Notes,
AppLinkUri = new Uri($"http://{App.AppName}/{pageType}?id={item.ID}", UriKind.RelativeOrAbsolute),
IsLinkActive = true,
Thumbnail = ImageSource.FromFile("monkey.png")
};

pageLink.KeyValues.Add("contentType", "TodoItemPage");
pageLink.KeyValues.Add("appName", App.AppName);
pageLink.KeyValues.Add("companyName", "Xamarin");

return pageLink;
}

La instancia AppLinkEntry contiene un número de propiedades cuyos valores son necesarios para indexar la
página y crear un vínculo profundo. Las propiedades Title , Description y Thumbnail se usan para identificar el
contenido indexado cuando este aparece en los resultados de la búsqueda. La propiedad IsLinkActive se
establece en true para indicar que se está visualizando el contenido indexado. La propiedad AppLinkUri es un
elemento Uri que contiene la información necesaria para volver a la página actual y mostrar el elemento
TodoItem actual. A continuación encontrará un elemento Uri de muestra para la aplicación de ejemplo:

http://deeplinking/DeepLinking.TodoItemPage?id=2

Este elemento contiene toda la información necesaria para iniciar la aplicación


Uri deeplinking , navegar a
DeepLinking.TodoItemPage y mostrar TodoItem , que tiene un valor ID de 2.

Registro del contenido para la indexación


Una vez que la instancia AppLinkEntry se ha creado, esta se debe registrar para la indexación con el fin de que
aparezca en los resultados de la búsqueda. Esto se consigue con el método RegisterLink , como se muestra en el
ejemplo de código siguiente:

Application.Current.AppLinks.RegisterLink (appLink);

Esto agrega la instancia AppLinkEntry a la colección AppLinks de la aplicación.

NOTE
El método RegisterLink también se puede utilizar para actualizar el contenido que se ha indexado para una página.

Una vez que la instancia AppLinkEntry se ha registrado para la indexación, esta puede aparecer en los resultados
de la búsqueda. La siguiente captura de pantalla muestra el contenido indexado que aparece en los resultados de la
búsqueda en la plataforma iOS:
Anulación del registro del contenido indexado
El método DeregisterLink se usa para eliminar el contenido indexado de los resultados de la búsqueda, tal como
se muestra en el siguiente ejemplo de código:

Application.Current.AppLinks.DeregisterLink (appLink);

Esto elimina la instancia AppLinkEntry de la colección AppLinks de la aplicación.

NOTE
En Android no es posible eliminar contenido indexado de los resultados de la búsqueda.

Respuesta a un vínculo profundo


Cuando aparece contenido indexado en los resultados de la búsqueda y un usuario lo selecciona, la clase App de
la aplicación recibirá una solicitud para controlar el elemento Uri incluido en dicho contenido indexado. Esta
solicitud se puede procesar en la invalidación del elemento OnAppLinkRequestReceived , tal como se muestra en el
siguiente ejemplo de código:
public class App : Application
{
...
protected override async void OnAppLinkRequestReceived(Uri uri)
{
string appDomain = "http://" + App.AppName.ToLowerInvariant() + "/";
if (!uri.ToString().ToLowerInvariant().StartsWith(appDomain, StringComparison.Ordinal))
return;

string pageUrl = uri.ToString().Replace(appDomain, string.Empty).Trim();


var parts = pageUrl.Split('?');
string page = parts[0];
string pageParameter = parts[1].Replace("id=", string.Empty);

var formsPage = Activator.CreateInstance(Type.GetType(page));


var todoItemPage = formsPage as TodoItemPage;
if (todoItemPage != null)
{
var todoItem = await App.Database.GetItemAsync(int.Parse(pageParameter));
todoItemPage.BindingContext = todoItem;
await MainPage.Navigation.PushAsync(formsPage as Page);
}
base.OnAppLinkRequestReceived(uri);
}
}

El método OnAppLinkRequestReceived comprueba que el elemento Uri recibido esté dirigido a la aplicación, antes
de analizar el elemento Uri para la página a la que se navegará y el parámetro que se pasará a la página. Luego,
se crea una instancia de la página a la que se navegará y se recupera el elemento TodoItem representado por el
parámetro de la página. Posteriormente, el elemento BindingContext de la página a la que se navegará se
establece en TodoItem . Esto garantiza que, cuando el método PushAsync muestre TodoItemPage , se muestre
también el elemento TodoItem cuyo valor ID se encuentra en el vínculo profundo.

Disponibilidad del contenido para la indexación de la búsqueda


Cada vez que se muestra la página representada por un vínculo profundo, la propiedad AppLinkEntry.IsLinkActive
se puede establecer en true . En iOS y Android, esto hace que la instancia AppLinkEntry esté disponible para la
indexación de la búsqueda y, solo en iOS, también facilita la instancia AppLinkEntry para Handoff. Para obtener
más información sobre Handoff, vea la introducción a Handoff.
El siguiente ejemplo de código muestra cómo establecer la propiedad AppLinkEntry.IsLinkActive en true en la
invalidación del elemento Page.OnAppearing :

protected override void OnAppearing()


{
appLink = GetAppLink(BindingContext as TodoItem);
if (appLink != null)
{
appLink.IsLinkActive = true;
}
}

Asimismo, al salir de una página representada por un vínculo profundo, la propiedad AppLinkEntry.IsLinkActive
se puede establecer en false . En iOS y Android, esto evita que la instancia AppLinkEntry se anuncie en la
indexación de la búsqueda y, solo en iOS, también evita anunciar la instancia AppLinkEntry para Handoff. Esto se
puede conseguir en la invalidación del elemento Page.OnDisappearing , tal como se muestra en el siguiente ejemplo
de código:
protected override void OnDisappearing()
{
if (appLink != null)
{
appLink.IsLinkActive = false;
}
}

Envío de datos a Handoff


En iOS, los datos específicos de la aplicación se pueden almacenar al realizar la indexación de la página. Para
hacerlo, agregue datos a la colección KeyValues , que es un elemento Dictionary<string, string> para almacenar
los pares clave-valor que se usan en Handoff. Handoff es una forma que tiene el usuario de iniciar una actividad en
uno de sus dispositivos y continuar con ella en otro (de acuerdo con la identificación de la cuenta de iCloud del
usuario). El siguiente código muestra un ejemplo de almacenamiento de pares clave-valor específicos de la
aplicación:

var pageLink = new AppLinkEntry


{
...
};
pageLink.KeyValues.Add("appName", App.AppName);
pageLink.KeyValues.Add("companyName", "Xamarin");

Los valores almacenados en la colección KeyValues se almacenarán en los metadatos de la página indexada y se
restaurarán cuando el usuario pulse un resultado de la búsqueda que contenga un vínculo profundo, o bien al
utilizar Handoff para ver el contenido en otro dispositivo en el que haya iniciado sesión.
Además, se pueden especificar valores para las siguientes claves:
contentType : string que especifica el identificador de tipo uniforme del contenido indexado. La convención
que se recomienda usar para este valor es el nombre del tipo de la página que contiene el contenido indexado.
associatedWebPage : string que representa la página web que se va a visitar si el contenido indexado también
se puede ver en la Web, o bien si la aplicación admite vínculos profundos de Safari.
shouldAddToPublicIndex : string de true o false que controla si se debe agregar el contenido indexado al
índice de la nube pública de Apple y que, posteriormente, se puede presentar a los usuarios que no hayan
instalado la aplicación en su dispositivo iOS. Sin embargo, el hecho de que el contenido se haya establecido
para la indexación pública no significa que se agregue automáticamente al índice de la nube pública de Apple.
Para obtener más información, vea Indexación de búsqueda pública. Tenga en cuenta que, al agregar datos
personales a la colección KeyValues , esta clave se debe establecer en false .

NOTE
La colección KeyValues no se usa en la plataforma Android.

Para obtener más información sobre Handoff, vea la introducción a Handoff.

Resumen
En este artículo se ha explicado cómo usar la indexación de la aplicación y la vinculación en profundidad para que
el contenido de las aplicaciones Xamarin.Forms permita efectuar búsquedas en dispositivos iOS y Android. La
indexación de la aplicación permite a aplicaciones que de lo contrario se olvidarían tras unos pocos usos mantener
su pertinencia al aparecer en los resultados de la búsqueda.
Vínculos relacionados
Muestra de vinculación en profundidad
API de búsqueda iOS
Vinculación de aplicaciones en Android 6.0
AppLinkEntry
IAppLinkEntry
IAppLinks
Comportamientos de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Los comportamientos permiten agregar funciones a los controles de interfaz de usuario sin tener que incluir
subclases. Los comportamientos se escriben en código y se agregan a controles en XAML o código.

Introducción a comportamientos
Los comportamientos permiten implementar el código que normalmente tendría que escribir como código
subyacente, ya que interactúa directamente con la API del control de manera que puede asociarse al control de
manera concisa. En este artículo se proporciona una introducción a los comportamientos.

Comportamientos asociados
Los comportamientos asociados son clases static con una o varias propiedades adjuntas. En este artículo se
explica cómo crear y consumir comportamientos asociados.

Comportamientos de Xamarin.Forms
Los comportamientos de Xamarin.Forms se crean derivando de la clase Behavior o Behavior<T> . En este artículo
se explica cómo crear y consumir comportamientos de Xamarin.Forms.

Comportamientos reutilizables
Los comportamientos se pueden reutilizar en más de una aplicación. En estos artículos se explica cómo crear
comportamientos útiles para realizar funciones usadas con frecuencia.
Introducción a los comportamientos
11/07/2019 • 2 minutes to read • Edit Online

Los comportamientos permiten agregar funciones a los controles de la interfaz de usuario sin tener que incluirlos
en subclases. En su lugar, la función se implementa en una clase de comportamiento y se asocia al control como si
fuera parte de este. En este artículo, se proporciona una introducción a los comportamientos.
Los comportamientos permiten implementar código que normalmente tendría que escribirse como código
subyacente, ya que interactúan directamente con la API del control, de manera que pueden asociarse al control de
manera concisa y empaquetarse para reutilizarlos en más de una aplicación. Pueden usarse para proporcionar una
amplia variedad de funciones para los controles, como:
Agregar un validador de correo electrónico a un elemento Entry .
Crear un control de calificación mediante un reconocedor de gestos de pulsar.
Controlar una animación.
Agregar un efecto a un control.
Los comportamientos también permiten escenarios más avanzados. En el contexto de los comandos, los
comportamientos son un método útil para conectar un control a un comando. Además, también pueden usarse
para asociar comandos a los controles que no se han diseñado para interactuar con los comandos. Por ejemplo,
pueden usarse para invocar un comando en respuesta a la activación de un evento.
Xamarin.Forms admite dos estilos de comportamiento:
Comportamientos de Xamarin.Forms: clases que derivan de la clase Behavior o Behavior<T> , donde T es
el tipo del control en el que tiene que aplicarse el comportamiento. Para obtener más información sobre los
comportamientos de Xamarin.Forms, vea Comportamientos de Xamarin.Forms y Comportamientos
reutilizables.
Comportamientos asociados: clases de static con una o varias propiedades asociadas. Para obtener más
información sobre los comportamientos asociados, vea Comportamientos asociados.
Esta guía se centra en los comportamientos de Xamarin.Forms, ya que son el método preferido para la creación de
comportamientos.

Vínculos relacionados
Comportamiento
Comportamiento<T>
Comportamientos asociados
11/07/2019 • 7 minutes to read • Edit Online

Descargar el ejemplo
Los comportamientos asociados son clases estáticas con una o varias propiedades asociadas. En este artículo, se
explica cómo crear y usar comportamientos asociados.

Información general
Una propiedad asociada es un tipo especial de propiedad enlazable. Se define en una clase, pero se asocia a otros
objetos y puede reconocerse en XAML como un atributo que contiene una clase y un nombre de propiedad
separado por un punto.
Una propiedad asociada puede definir un delegado propertyChanged que se ejecutará cuando cambie el valor de
la propiedad (por ejemplo, cuando la propiedad se establezca en un control). Cuando se ejecute el delegado
propertyChanged , se pasará una referencia al control donde esté asociado, así como los parámetros que contienen
los valores nuevos y anteriores de la propiedad. Este delegado puede usarse para agregar nuevas funciones al
control al que se asocie la propiedad; para hacerlo, se manipula la referencia en la que se pasa, como se muestra a
continuación:
1. El delegado propertyChanged transmite la referencia del control, que se recibe como un elemento
BindableObject , al tipo de control cuyo comportamiento se ha diseñado para mejorar.
2. El delegado propertyChanged modifica las propiedades del control, llama a métodos del control o registra
controladores de eventos expuestos por el control para implementar la función básica de comportamiento.
Un problema con los comportamientos asociados es que se definen en una clase static , con propiedades y
métodos de static . Esto hace que sea difícil crear comportamientos asociados que tengan un estado. Además,
los comportamientos de Xamarin.Forms han reemplazado a los comportamientos asociados como el método
preferido para la creación de comportamientos. Para obtener más información sobre los comportamientos de
Xamarin.Forms, vea Comportamientos de Xamarin.Forms y Comportamientos reutilizables.

Crear un comportamiento asociado


En la aplicación de ejemplo, se muestra un elemento NumericValidationBehavior , que resalta el valor especificado
por el usuario en un control Entry en color rojo, si no es del tipo double . El comportamiento se muestra en el
siguiente ejemplo de código:
public static class NumericValidationBehavior
{
public static readonly BindableProperty AttachBehaviorProperty =
BindableProperty.CreateAttached (
"AttachBehavior",
typeof(bool),
typeof(NumericValidationBehavior),
false,
propertyChanged:OnAttachBehaviorChanged);

public static bool GetAttachBehavior (BindableObject view)


{
return (bool)view.GetValue (AttachBehaviorProperty);
}

public static void SetAttachBehavior (BindableObject view, bool value)


{
view.SetValue (AttachBehaviorProperty, value);
}

static void OnAttachBehaviorChanged (BindableObject view, object oldValue, object newValue)


{
var entry = view as Entry;
if (entry == null) {
return;
}

bool attachBehavior = (bool)newValue;


if (attachBehavior) {
entry.TextChanged += OnEntryTextChanged;
} else {
entry.TextChanged -= OnEntryTextChanged;
}
}

static void OnEntryTextChanged (object sender, TextChangedEventArgs args)


{
double result;
bool isValid = double.TryParse (args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
}
}

La clase NumericValidationBehavior contiene una propiedad asociada denominada AttachBehavior con un


captador y establecedor de static , que controla la adición o eliminación del comportamiento del control al que
se va a asociar. Esta propiedad asociada registra el método OnAttachBehaviorChanged que se ejecutará cuando
cambie el valor de la propiedad. Este método registra o anula el registro de un controlador de eventos para el
evento TextChanged basándose en el valor de la propiedad asociada AttachBehavior . El método
OnEntryTextChanged proporciona la función básica del comportamiento, que analiza el valor especificado por el
usuario en el elemento y establece la propiedad Entry TextColor en rojo si el valor no es del tipo double .

Usar un comportamiento asociado


Para usar la clase NumericValidationBehavior , se puede agregar la propiedad asociada AttachBehavior a un
control Entry , como se muestra en el siguiente ejemplo de código de XAML:

<ContentPage ... xmlns:local="clr-namespace:WorkingWithBehaviors;assembly=WorkingWithBehaviors" ...>


...
<Entry Placeholder="Enter a System.Double" local:NumericValidationBehavior.AttachBehavior="true" />
...
</ContentPage>
En el siguiente ejemplo de código, se muestra el control Entry equivalente en C#:

var entry = new Entry { Placeholder = "Enter a System.Double" };


NumericValidationBehavior.SetAttachBehavior (entry, true);

En tiempo de ejecución, el comportamiento responderá a la interacción con el control, según la implementación


del comportamiento. En las capturas de pantalla siguientes, se muestra la respuesta del comportamiento asociado
en respuesta a entradas no válidas:

NOTE
Los comportamientos asociados se escriben para un tipo de control específico (o una superclase que se puede aplicar a
muchos controles) y solo se tienen que agregar a un control compatible. Al intentar asociar un comportamiento a un control
incompatible, se producirá un comportamiento desconocido que depende de la implementación del comportamiento.

Quitar un comportamiento asociado de un control


Para quitar la clase NumericValidationBehavior de un control, establezca la propiedad asociada AttachBehavior en
false , como se muestra en el siguiente ejemplo de código de XAML:

<Entry Placeholder="Enter a System.Double" local:NumericValidationBehavior.AttachBehavior="false" />

En el siguiente ejemplo de código, se muestra el control Entry equivalente en C#:

var entry = new Entry { Placeholder = "Enter a System.Double" };


NumericValidationBehavior.SetAttachBehavior (entry, false);

En tiempo de ejecución, el método OnAttachBehaviorChanged se ejecutará cuando el valor de la propiedad asociada


AttachBehavior se establezca en false . Después, el método OnAttachBehaviorChanged anulará el registro del
controlador de eventos del evento TextChanged con el fin de garantizar que el comportamiento no se ejecute
cuando el usuario interactúe con el control.

Resumen
En este artículo, se explica cómo crear y consumir comportamientos asociados. Los comportamientos asociados
son clases static con una o varias propiedades asociadas.
Vínculos relacionados
Comportamientos asociados (ejemplo)
Creación de comportamientos de Xamarin.Forms
11/07/2019 • 11 minutes to read • Edit Online

Descargar el ejemplo
Los comportamientos de Xamarin.Forms se crean mediante la derivación de la clase Behavior o Behavior<T>. En
este artículo se explica cómo crear y consumir comportamientos de Xamarin.Forms.

Información general
El proceso de creación de un comportamiento de Xamarin.Forms es el siguiente:
1. Cree una clase que herede de la clase Behavior o Behavior<T> , donde T sea el tipo del control al que se debe
aplicar el comportamiento.
2. Invalide el método OnAttachedTo para realizar cualquier configuración necesaria.
3. Invalide el método OnDetachingFrom para realizar cualquier limpieza necesaria.
4. Implemente la funcionalidad básica del comportamiento.
El resultado es la estructura que se muestra en el ejemplo de código siguiente:

public class CustomBehavior : Behavior<View>


{
protected override void OnAttachedTo (View bindable)
{
base.OnAttachedTo (bindable);
// Perform setup
}

protected override void OnDetachingFrom (View bindable)


{
base.OnDetachingFrom (bindable);
// Perform clean up
}

// Behavior implementation
}

El método OnAttachedTo se desencadena justo después de que se adjunte el comportamiento a un control. Este
método recibe una referencia al control al que se adjunta, y se puede usar para registrar controladores de eventos
o realizar otra configuración necesaria para admitir la funcionalidad del comportamiento. Por ejemplo, se podría
suscribir a un evento en un control. Después, se implementaría la funcionalidad del comportamiento en el
controlador de eventos para el evento.
El método OnDetachingFrom se desencadena cuando se quita el comportamiento del control. Este método recibe
una referencia al control al que se adjunta y se usa para realizar cualquier limpieza necesaria. Por ejemplo, podría
cancelar la suscripción a un evento en un control para evitar fugas de memoria.
Después, el comportamiento se puede consumir si se adjunta a la colección Behaviors del control adecuado.

Creación de un comportamiento de Xamarin.Forms


En la aplicación de ejemplo se muestra un elemento NumericValidationBehavior , que resalta el valor especificado
por el usuario en un control Entry en color rojo, si no es de tipo double . El comportamiento se muestra en el
ejemplo de código siguiente:

public class NumericValidationBehavior : Behavior<Entry>


{
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}

protected override void OnDetachingFrom(Entry entry)


{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}

void OnEntryTextChanged(object sender, TextChangedEventArgs args)


{
double result;
bool isValid = double.TryParse (args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
}
}

NumericValidationBehavior se deriva de la clase Behavior<T> , donde T es un elemento Entry . El método


OnAttachedTo registra un controlador de eventos para el evento TextChanged , y el método OnDetachingFrom anula
el registro del evento TextChanged para evitar fugas de memoria. El método OnEntryTextChanged proporciona la
funcionalidad básica del comportamiento y analiza el valor especificado por el usuario en el elemento Entry y
establece la propiedad TextColor en rojo si el valor no es de tipo double .

NOTE
Xamarin.Forms no establece el elemento BindingContext de un comportamiento, ya que los comportamientos se pueden
compartir y aplicar a varios controles mediante estilos.

Consumo de un comportamiento de Xamarin.Forms


Todos los controles de Xamarin.Forms tienen una colección Behaviors , a la que se pueden agregar uno o varios
comportamientos, como se muestra en el ejemplo de código XAML siguiente:

<Entry Placeholder="Enter a System.Double">


<Entry.Behaviors>
<local:NumericValidationBehavior />
</Entry.Behaviors>
</Entry>

El control Entry equivalente en C# se muestra en el ejemplo de código siguiente:

var entry = new Entry { Placeholder = "Enter a System.Double" };


entry.Behaviors.Add (new NumericValidationBehavior ());

En tiempo de ejecución, el comportamiento responderá a la interacción con el control, en función de la


implementación del comportamiento. En las capturas de pantalla siguientes se muestra la respuesta del
comportamiento a entradas no válidas:
NOTE
Los comportamientos se escriben para un tipo de control específico (o una superclase que se puede aplicar a muchos
controles), y solo se deben agregar a un control compatible. El intento de adjuntar un comportamiento a un control
incompatible iniciará una excepción.

Consumo de un comportamiento de Xamarin.Forms con un estilo


Los comportamientos también se pueden consumir mediante un estilo explícito o implícito. Pero no se puede
crear un estilo que establece la propiedad Behaviors de un control porque la propiedad es de solo lectura. La
solución consiste en agregar una propiedad adjunta a la clase de comportamiento que controle la adición y
eliminación del comportamiento. El proceso es el siguiente:
1. Se agrega una propiedad adjunta a la clase de comportamiento que se usará para controlar la adición o
eliminación del comportamiento del control al que se va a conectar el comportamiento. Se asegura que la
propiedad adjunta registra un delegado propertyChanged que se ejecutará cuando cambie el valor de la
propiedad.
2. Se crea un captador y establecedor static para la propiedad adjunta.
3. Se implementa la lógica en el delegado propertyChanged para agregar y quitar el comportamiento.

En el ejemplo de código siguiente se muestra una propiedad adjunta que controla la adición y eliminación de
NumericValidationBehavior :
public class NumericValidationBehavior : Behavior<Entry>
{
public static readonly BindableProperty AttachBehaviorProperty =
BindableProperty.CreateAttached ("AttachBehavior", typeof(bool), typeof(NumericValidationBehavior),
false, propertyChanged: OnAttachBehaviorChanged);

public static bool GetAttachBehavior (BindableObject view)


{
return (bool)view.GetValue (AttachBehaviorProperty);
}

public static void SetAttachBehavior (BindableObject view, bool value)


{
view.SetValue (AttachBehaviorProperty, value);
}

static void OnAttachBehaviorChanged (BindableObject view, object oldValue, object newValue)


{
var entry = view as Entry;
if (entry == null) {
return;
}

bool attachBehavior = (bool)newValue;


if (attachBehavior) {
entry.Behaviors.Add (new NumericValidationBehavior ());
} else {
var toRemove = entry.Behaviors.FirstOrDefault (b => b is NumericValidationBehavior);
if (toRemove != null) {
entry.Behaviors.Remove (toRemove);
}
}
}
...
}

La clase NumericValidationBehavior contiene una propiedad adjunta denominada AttachBehavior con un


captador y establecedor static , que controla la adición o eliminación del comportamiento del control al que se
va a adjuntar. Esta propiedad adjunta registra el método OnAttachBehaviorChanged que se ejecutará cuando cambie
el valor de la propiedad. Este método agrega o quita el comportamiento del control, en función del valor de la
propiedad adjunta AttachBehavior .
En el ejemplo de código siguiente se muestra un estilo explícito para el elemento NumericValidationBehavior que
usa la propiedad adjunta AttachBehavior y que se puede aplicar a controles Entry :

<Style x:Key="NumericValidationStyle" TargetType="Entry">


<Style.Setters>
<Setter Property="local:NumericValidationBehavior.AttachBehavior" Value="true" />
</Style.Setters>
</Style>

Style se puede aplicar a un control Entry si se establece su propiedad Style en la instancia de Style
mediante la extensión de marcado StaticResource , como se muestra en el ejemplo de código siguiente:

<Entry Placeholder="Enter a System.Double" Style="{StaticResource NumericValidationStyle}">

Para obtener más información sobre los estilos, vea Estilos.


NOTE
Aunque se pueden agregar propiedades enlazables a un comportamiento que se establece o se consulta en XAML, si se
crean comportamientos que tienen estado, no se deberían compartir entre los controles en un elemento Style de un
objeto ResourceDictionary .

Eliminación de un comportamiento de un control


El método OnDetachingFrom se desencadena cuando se quita un comportamiento de un control, y se usa para
realizar cualquier limpieza necesaria como cancelar la suscripción de un evento para evitar una fuga de memoria.
Pero los comportamientos no se quitan de forma implícita de los controles a menos que un método Remove o
Clear modifique la colección Behaviors del control. En el ejemplo de código siguiente se muestra cómo quitar
un comportamiento específico de la colección Behaviors de un control:

var toRemove = entry.Behaviors.FirstOrDefault (b => b is NumericValidationBehavior);


if (toRemove != null) {
entry.Behaviors.Remove (toRemove);
}

Como alternativa, se puede borrar la colección Behaviors del control, como se muestra en el ejemplo de código
siguiente:

entry.Behaviors.Clear();

Además, tenga en cuenta que los comportamientos no se quitan de forma implícita de los controles cuando se
extraen páginas de la pila de navegación. En su lugar, se deben quitar explícitamente antes de que las páginas
queden fuera del ámbito.

Resumen
En este artículo se ha explicado cómo crear y consumir comportamientos de Xamarin.Forms. Los
comportamientos de Xamarin.Forms se crean mediante la derivación de la clase Behavior o Behavior<T> .

Vínculos relacionados
Comportamiento de Xamarin.Forms (ejemplo)
Comportamiento de Xamarin.Forms aplicado con un estilo (ejemplo)
Behavior
Behavior
Comportamientos reutilizables
11/07/2019 • 2 minutes to read • Edit Online

Los comportamientos se pueden reutilizar en más de una aplicación. En estos artículos se explica cómo crear
comportamientos útiles para realizar funciones usadas con frecuencia.

EffectBehavior reutilizable
Los comportamientos son una manera útil de agregar un efecto a un control, quitando el código de control de
efecto reutilizable de los archivos de código subyacente. En este artículo se explica cómo crear y consumir un
comportamiento de Xamarin.Forms para agregar un efecto a un control.

EventToCommandBehavior reutilizable
Los comportamientos sirven para asociar comandos a los controles que no se han diseñado para interactuar con
los comandos. En este artículo se explica cómo crear y consumir un comportamiento de Xamarin.Forms para
invocar un comando cuando se desencadena un evento.
EffectBehavior reutilizable
11/07/2019 • 7 minutes to read • Edit Online

Descargar el ejemplo
Los comportamientos son una manera útil de agregar un efecto a un control, quitando el código de control de
efecto reutilizable de los archivos de código subyacente. En este artículo se explica cómo crear y consumir un
comportamiento de Xamarin.Forms para agregar un efecto a un control.

Información general
La clase EffectBehavior es un comportamiento personalizado de Xamarin.Forms reutilizable que agrega una
instancia de Effect a un control cuando el comportamiento se asocia al control, y quita la instancia de Effect
cuando el comportamiento se desasocia del control.
Para usar el comportamiento, se deben establecer las propiedades de comportamiento siguientes:
Group: el valor del atributo ResolutionGroupName para la clase del efecto.
Name: el valor del atributo ExportEffect para la clase del efecto.
Para obtener más información sobre los efectos, vea Efectos.

NOTE
EffectBehavior es una clase personalizada que se puede encontrar en el ejemplo Comportamiento de un efecto, y que no
forma parte de Xamarin.Forms.

Creación del comportamiento


La clase EffectBehavior se deriva de la clase Behavior<T> , donde T es un objeto View . Esto significa que la clase
EffectBehavior se puede asociar a cualquier control de Xamarin.Forms.
Implementación de propiedades enlazables
La clase EffectBehavior define dos instancias de BindableProperty , que se usan para agregar un elemento
Effect a un control cuando el comportamiento está asociado al control. Estas propiedades se muestran en el
ejemplo de código siguiente:
public class EffectBehavior : Behavior<View>
{
public static readonly BindableProperty GroupProperty =
BindableProperty.Create ("Group", typeof(string), typeof(EffectBehavior), null);
public static readonly BindableProperty NameProperty =
BindableProperty.Create ("Name", typeof(string), typeof(EffectBehavior), null);

public string Group {


get { return (string)GetValue (GroupProperty); }
set { SetValue (GroupProperty, value); }
}

public string Name {


get { return(string)GetValue (NameProperty); }
set { SetValue (NameProperty, value); }
}
...
}

Cuando se consume EffectBehavior , la propiedad Group se debe establecer en el valor del atributo
ResolutionGroupName para el efecto. Además, la propiedad Name se debe establecer en el valor del atributo
ExportEffect para la clase de efecto.
Implementación de las invalidaciones
La clase EffectBehavior invalida los métodos OnAttachedTo y OnDetachingFrom de la clase Behavior<T> , como se
muestra en el ejemplo de código siguiente:

public class EffectBehavior : Behavior<View>


{
...
protected override void OnAttachedTo (BindableObject bindable)
{
base.OnAttachedTo (bindable);
AddEffect (bindable as View);
}

protected override void OnDetachingFrom (BindableObject bindable)


{
RemoveEffect (bindable as View);
base.OnDetachingFrom (bindable);
}
...
}

El método OnAttachedTo realiza la instalación mediante una llamada al método AddEffect , pasando el control
asociado como un parámetro. El método OnDetachingFrom realiza la limpieza mediante una llamada al método
RemoveEffect , pasando el control asociado como un parámetro.

Implementación de la funcionalidad del comportamiento


El propósito del comportamiento es agregar el elemento Effect definido en las propiedades Group y Name a un
control cuando el comportamiento está asociado al control, y quitar el elemento Effect cuando es el
comportamiento se desasocia del control. La funcionalidad básica del comportamiento se muestra en el ejemplo
de código siguiente:
public class EffectBehavior : Behavior<View>
{
...
void AddEffect (View view)
{
var effect = GetEffect ();
if (effect != null) {
view.Effects.Add (GetEffect ());
}
}

void RemoveEffect (View view)


{
var effect = GetEffect ();
if (effect != null) {
view.Effects.Remove (GetEffect ());
}
}

Effect GetEffect ()
{
if (!string.IsNullOrWhiteSpace (Group) && !string.IsNullOrWhiteSpace (Name)) {
return Effect.Resolve (string.Format ("{0}.{1}", Group, Name));
}
return null;
}
}

El método AddEffect se ejecuta en respuesta a la asociación de EffectBehavior a un control y recibe el control


adjunto como un parámetro. Después, el método agrega el efecto recuperado a la colección Effects del control.
El método RemoveEffect se ejecuta en respuesta a la desasociación de EffectBehavior de un control y recibe el
control adjunto como un parámetro. Después, el método quita el efecto de la colección Effects del control.
El método GetEffect usa el método Effect.Resolve para recuperar el elemento Effect . El efecto se localiza a
través de una concatenación de los valores de propiedad Group y Name . Si una plataforma no proporciona el
efecto, el método Effect.Resolve devolverá un valor que no es null .

Consumo del comportamiento


La clase EffectBehavior se puede adjuntar a la colección Behaviors de un control, como se muestra en el ejemplo
de código XAML siguiente:

<Label Text="Label Shadow Effect" ...>


<Label.Behaviors>
<local:EffectBehavior Group="Xamarin" Name="LabelShadowEffect" />
</Label.Behaviors>
</Label>

El código de C# equivalente se muestra en el ejemplo de código siguiente:

var label = new Label {


Text = "Label Shadow Effect",
...
};
label.Behaviors.Add (new EffectBehavior {
Group = "Xamarin",
Name = "LabelShadowEffect"
});
Las propiedades Group y del comportamiento se establecen en los valores de los atributos
Name
ResolutionGroupName y ExportEffect para la clase de efecto de cada proyecto específico de la plataforma.
En tiempo de ejecución, cuando el comportamiento se asocia al control Label , se agregará
Xamarin.LabelShadowEffect a la colección Effects del control. Como resultado, se agrega una sombra al texto
mostrado por el control Label , como se muestra en las capturas de pantalla siguientes:

La ventaja de usar este comportamiento para agregar y quitar efectos de los controles es que se puede quitar el
código de control de efecto reutilizable de los archivos de código subyacente.

Resumen
En este artículo se ha mostrado el uso de un comportamiento para agregar un efecto a un control. La clase
EffectBehavior es un comportamiento personalizado de Xamarin.Forms reutilizable que agrega una instancia de
Effect a un control cuando el comportamiento se asocia al control, y quita la instancia de Effect cuando el
comportamiento se desasocia del control.

Vínculos relacionados
Efectos
Comportamiento de un efecto (ejemplo)
Comportamiento
Comportamiento<T>
EventToCommandBehavior reutilizable
11/07/2019 • 11 minutes to read • Edit Online

Descargar el ejemplo
Los comportamientos se pueden usar para asociar comandos a los controles que no se han diseñado para
interactuar con los comandos. En este artículo se explica cómo crear y consumir un comportamiento de
Xamarin.Forms para invocar un comando cuando se desencadena un evento.

Información general
La clase EventToCommandBehavior es un comportamiento personalizado de Xamarin.Forms reutilizable que ejecuta
un comando como respuesta al desencadenamiento de cualquier evento. De forma predeterminada, los
argumentos para el evento se pasarán al comando y, opcionalmente, se pueden convertir mediante una
implementación IValueConverter .
Para usar el comportamiento, se deben establecer las propiedades de comportamiento siguientes:
EventName: el nombre del evento que escucha el comportamiento.
Command: el elemento ICommand que se va a ejecutar. El comportamiento espera encontrar la instancia de
ICommand en el elemento BindingContext del control adjunto, que se puede heredar de un elemento primario.

También se pueden establecer las siguientes propiedades de comportamiento opcionales:


CommandParameter: un elemento object que se va a pasar al comando.
Converter: una implementación IValueConverter que cambiará el formato de los datos de argumento de
evento mientras el motor de enlace los pasa entre el origen y el destino.

NOTE
EventToCommandBehaviores una clase personalizada que se puede encontrar en el ejemplo de comportamiento
EventToCommand y que no forma parte de Xamarin.Forms.

Creación del comportamiento


La clase EventToCommandBehavior se deriva de la clase BehaviorBase<T> , que a su vez se deriva de la clase
Behavior<T> . El propósito de la clase BehaviorBase<T> es proporcionar una clase base para cualquier
comportamiento de Xamarin.Forms que requiera que el elemento BindingContext del comportamiento se
establezca en el control adjunto. Esto garantiza que el comportamiento puede enlazar y ejecutar la interfaz
ICommand especificada por la propiedad Command cuando se consume el comportamiento.

La clase proporciona un método OnAttachedTo reemplazable que establece el elemento


BehaviorBase<T>
BindingContext del comportamiento y un método OnDetachingFrom reemplazable que limpia BindingContext .
Además, la clase almacena una referencia al control adjunto en la propiedad AssociatedObject .
Implementación de propiedades enlazables
La clase EventToCommandBehavior define cuatro instancias de BindableProperty , que ejecutan un comando definido
por el usuario cuando se desencadena un evento. Estas propiedades se muestran en el ejemplo de código
siguiente:
public class EventToCommandBehavior : BehaviorBase<View>
{
public static readonly BindableProperty EventNameProperty =
BindableProperty.Create ("EventName", typeof(string), typeof(EventToCommandBehavior), null,
propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty =
BindableProperty.Create ("Command", typeof(ICommand), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty CommandParameterProperty =
BindableProperty.Create ("CommandParameter", typeof(object), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty InputConverterProperty =
BindableProperty.Create ("Converter", typeof(IValueConverter), typeof(EventToCommandBehavior), null);

public string EventName { ... }


public ICommand Command { ... }
public object CommandParameter { ... }
public IValueConverter Converter { ... }
...
}

Cuando se consume la clase EventToCommandBehavior , la propiedad Command debe estar enlazada a datos con una
interfaz ICommand que se ejecutará en respuesta al desencadenamiento de eventos que se define en la propiedad
EventName . El comportamiento espera encontrar la interfaz ICommand en el elemento BindingContext del control
adjunto.
De forma predeterminada, los argumentos de evento para el evento se pasarán al comando. Estos datos se
pueden convertir, opcionalmente, cuando el motor de enlace los pasa entre el origen y el destino, mediante la
especificación de una implementación IValueConverter como valor de la propiedad Converter . Como alternativa,
se puede pasar un parámetro para el comando si se especifica el valor de propiedad CommandParameter .
Implementación de las invalidaciones
La clase EventToCommandBehavior invalida los métodos OnAttachedTo y OnDetachingFrom de la clase
BehaviorBase<T> , como se muestra en el ejemplo de código siguiente:

public class EventToCommandBehavior : BehaviorBase<View>


{
...
protected override void OnAttachedTo (View bindable)
{
base.OnAttachedTo (bindable);
RegisterEvent (EventName);
}

protected override void OnDetachingFrom (View bindable)


{
DeregisterEvent (EventName);
base.OnDetachingFrom (bindable);
}
...
}

El método OnAttachedTo realiza la instalación mediante una llamada al método RegisterEvent , pasando el valor
de la propiedad EventName como un parámetro. El método OnDetachingFrom realiza la limpieza mediante una
llamada al método DeregisterEvent , pasando el valor de la propiedad EventName como un parámetro.
Implementación de la funcionalidad del comportamiento
El propósito del comportamiento es ejecutar el comando definido por la propiedad Command en respuesta al
desencadenamiento del evento definido por la propiedad EventName . La funcionalidad básica del comportamiento
se muestra en el ejemplo de código siguiente:
public class EventToCommandBehavior : BehaviorBase<View>
{
...
void RegisterEvent (string name)
{
if (string.IsNullOrWhiteSpace (name)) {
return;
}

EventInfo eventInfo = AssociatedObject.GetType ().GetRuntimeEvent (name);


if (eventInfo == null) {
throw new ArgumentException (string.Format ("EventToCommandBehavior: Can't register the '{0}' event.",
EventName));
}
MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo ().GetDeclaredMethod ("OnEvent");
eventHandler = methodInfo.CreateDelegate (eventInfo.EventHandlerType, this);
eventInfo.AddEventHandler (AssociatedObject, eventHandler);
}

void OnEvent (object sender, object eventArgs)


{
if (Command == null) {
return;
}

object resolvedParameter;
if (CommandParameter != null) {
resolvedParameter = CommandParameter;
} else if (Converter != null) {
resolvedParameter = Converter.Convert (eventArgs, typeof(object), null, null);
} else {
resolvedParameter = eventArgs;
}

if (Command.CanExecute (resolvedParameter)) {
Command.Execute (resolvedParameter);
}
}
...
}

El método RegisterEvent se ejecuta en respuesta a la asociación de EventToCommandBehavior a un control y recibe


el valor de la propiedad EventName como un parámetro. Después, el método intenta localizar el evento definido en
la propiedad EventName , en el control adjunto. Siempre que el evento se pueda localizar, el método OnEvent se
registra para que sea el método de controlador para el evento.
El método OnEventse ejecuta en respuesta al desencadenamiento del evento que se define en la propiedad
EventName . Siempre que la propiedad Command haga referencia a una interfaz ICommand válida, el método intenta
recuperar un parámetro para pasarlo a ICommand como sigue:
Si la propiedad CommandParameter define un parámetro, se recuperará.
De lo contrario, si la propiedad Converter define una implementación IValueConverter , el convertidor se
ejecuta y convierte los datos del argumento de evento mientras el motor de enlace los pasa entre el origen y el
destino.
En caso contrario, se asume que los argumentos del evento son el parámetro.
Después, se ejecuta la interfaz ICommand enlazada a datos, y se pasa el parámetro al comando, siempre que el
método CanExecute devuelva true .
Aunque aquí no se muestre, EventToCommandBehavior también incluye un método DeregisterEvent que ejecuta el
método OnDetachingFrom . El método DeregisterEvent se usa para localizar y anular el registro del evento definido
en la propiedad EventName , para limpiar las posibles fugas de memoria.

Consumo del comportamiento


La clase EventToCommandBehavior se puede adjuntar a la colección Behaviors de un control, como se muestra en el
ejemplo de código XAML siguiente:

<ListView ItemsSource="{Binding People}">


<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Behaviors>
<local:EventToCommandBehavior EventName="ItemSelected" Command="{Binding OutputAgeCommand}"
Converter="{StaticResource SelectedItemConverter}" />
</ListView.Behaviors>
</ListView>
<Label Text="{Binding SelectedItemText}" />

El código de C# equivalente se muestra en el ejemplo de código siguiente:

var listView = new ListView();


listView.SetBinding(ItemsView<Cell>.ItemsSourceProperty, "People");
listView.ItemTemplate = new DataTemplate(() =>
{
var textCell = new TextCell();
textCell.SetBinding(TextCell.TextProperty, "Name");
return textCell;
});
listView.Behaviors.Add(new EventToCommandBehavior
{
EventName = "ItemSelected",
Command = ((HomePageViewModel)BindingContext).OutputAgeCommand,
Converter = new SelectedItemEventArgsToSelectedItemConverter()
});

var selectedItemLabel = new Label();


selectedItemLabel.SetBinding(Label.TextProperty, "SelectedItemText");

La propiedad Command del comportamiento está enlazada a datos a la propiedad OutputAgeCommand del modelo de
vista asociado, mientras que la propiedad Converter se establece en la instancia de SelectedItemConverter , que
devuelve el objeto SelectedItem del control ListView del elemento SelectedItemChangedEventArgs .
En tiempo de ejecución, el comportamiento responderá a la interacción con el control. Cuando se selecciona un
elemento en el control ListView , se desencadena el evento ItemSelected , que ejecutará OutputAgeCommand en el
modelo de vista. A su vez, esto actualiza la propiedad SelectedItemText del modelo de vista a la que se enlaza
Label , como se muestra en las capturas de pantalla siguientes:
La ventaja de usar este comportamiento para ejecutar un comando cuando se desencadena un evento es que se
pueden asociar comandos con controles que no se han diseñado para interactuar con los comandos. Además, esto
quita el código de control de eventos reutilizable de los archivos de código subyacente.

Resumen
En este artículo se ha explicado el uso de un comportamiento de Xamarin.Forms para invocar un comando cuando
se desencadena un evento. Los comportamientos se pueden usar para asociar comandos a los controles que no se
han diseñado para interactuar con los comandos.

Vínculos relacionados
Comportamiento de EventToCommand (ejemplo)
Behavior
Comportamiento<T>
Representadores personalizados de Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

Las interfaces de usuario de Xamarin.Forms se representan mediante los controles nativos de la plataforma de
destino, lo que permite que las aplicaciones de Xamarin.Forms conserven la apariencia adecuada para cada
plataforma. Los representadores personalizados permiten que los desarrolladores reemplacen este proceso
para personalizar la apariencia y el comportamiento de los controles de Xamarin.Forms en cada plataforma.

Introducción a los representadores personalizados


Los representadores personalizados proporcionan un método eficaz para personalizar la apariencia y el
comportamiento de los controles de Xamarin.Forms. Se pueden usar para pequeños cambios de estilo o para
una personalización sofisticada del diseño y el comportamiento específicos de una plataforma. En este artículo
se proporciona una introducción a los representadores personalizados y se describe el proceso para crear un
representador personalizado.

Clases base y controles nativos del representador


Todos los controles de Xamarin.Forms tienen un representador que lo acompaña para cada plataforma y que
crea una instancia de un control nativo. En este artículo se enumeran las clases de representador y control
nativo que implementan cada página, diseño, vista y celda de Xamarin.Forms.

Personalización de una entrada


El control Entry de Xamarin.Forms permite que se edite una sola línea de texto. En este artículo se muestra
cómo crear un representador personalizado para el control Entry , lo que permite que los desarrolladores
reemplacen la representación nativa de forma predeterminada con su propia personalización específica de la
plataforma.

Personalización de una página de contenido


Un ContentPage es un elemento visual que muestra una vista única y ocupa la mayor parte de la pantalla. En
este artículo se muestra cómo crear un representador personalizado para la página ContentPage , lo que
permite que los desarrolladores reemplacen la representación nativa de forma predeterminada con su propia
personalización específica de la plataforma.

Personalización de un mapa
Xamarin.Forms.Maps proporciona una abstracción multiplataforma para mostrar mapas que usan la API de
mapa nativo en cada plataforma y proporcionar una experiencia de mapa rápida y familiar para los usuarios.
En este tema se muestra cómo crear representadores personalizados para el control Map , lo que permite que
los desarrolladores reemplacen la representación nativa de forma predeterminada por una personalización
propia específica de la plataforma.

Personalización de la clase ListView


ListView de Xamarin.Forms es una vista que muestra una colección de datos como una lista vertical. En este
artículo se muestra cómo crear un representador personalizado que encapsula los controles de lista específica
de la plataforma y los diseños de celda nativa, lo que permite tener más control sobre el rendimiento del
control de lista nativa.

Personalización de la clase ViewCell


ViewCell de Xamarin.Forms es una celda que se puede agregar a ListView o TableView , y que contiene una
vista definida por el desarrollador. En este artículo se muestra cómo crear un representador personalizado para
un ViewCell que se hospeda dentro de un control ListView de Xamarin.Forms. Esto impide que se llame
varias veces a los cálculos de diseño de Xamarin.Forms durante el desplazamiento de ListView .

Implementación de una vista


Los controles de interfaces de usuario personalizadas de Xamarin.Forms deben derivar de la clase View , que
se usa para colocar los diseños y los controles en la pantalla. En este artículo se muestra cómo crear un
representador personalizado para un control personalizado de Xamarin.Forms que se usa para mostrar una
secuencia de vídeo de vista previa de la cámara del dispositivo.

Implementación de una clase HybridWebView


En este artículo se muestra cómo crear un representador personalizado para un control personalizado
HybridWebView , que muestra cómo mejorar los controles específicos de la plataforma web para permitir que el
código C# se invoque desde JavaScript.

Implementación de un reproductor de vídeo


En este artículo se muestra cómo escribir los representadores para implementar un control personalizado
VideoPlayer que puede reproducir vídeos de la web, vídeos insertados como recursos de la aplicación o
vídeos almacenados en la biblioteca de vídeos en el dispositivo del usuario. Se muestran varias técnicas,
incluida la implementación de métodos y propiedades enlazables de solo lectura.

Vínculos relacionados
Efectos
Introducción a los representadores personalizados
11/07/2019 • 9 minutes to read • Edit Online

Los representadores personalizados proporcionan un método eficaz para personalizar la apariencia y el


comportamiento de los controles de Xamarin.Forms. Se pueden usar para realizar pequeños cambios de estilo o
para una personalización sofisticada del diseño y el comportamiento específicos de una plataforma. En este
artículo, se proporciona una introducción a los representadores personalizados y se describe el proceso para crear
un representador personalizado.
En Páginas, diseños y controles de Xamarin.Forms, se presenta una API común para describir interfaces de usuario
móviles multiplataforma. Cada página, diseño y control se representa de forma distinta en cada plataforma
mediante una clase Renderer que, a su vez, crea un control nativo (correspondiente a la representación de
Xamarin.Forms), lo coloca en la pantalla y agrega el comportamiento especificado en el código compartido.
Los desarrolladores pueden implementar sus propias clases Renderer personalizadas para personalizar la
apariencia o el comportamiento de un control. Los representadores personalizados para un tipo específico pueden
agregarse a un proyecto de aplicación para personalizar el control en un lugar, mientras se permite el
comportamiento predeterminado en otras plataformas; también pueden agregarse diferentes representadores
personalizados a cada proyecto de aplicación para crear una apariencia distinta en iOS, Android y la Plataforma
universal de Windows (UWP ). Pero la implementación de una clase de representador personalizado para llevar a
cabo una personalización de controles simples suele ser una respuesta compleja. Los efectos simplifican este
proceso y suelen usarse para pequeños cambios de estilo. Para obtener más información, consulte Effects (Efectos).

Examinar por qué es necesario usar representadores personalizados


Cambiar la apariencia de un control de Xamarin.Forms, sin usar un representador personalizado, es un proceso de
dos pasos en el que se crea un control personalizado mediante subclases y, después, se usa el control
personalizado en lugar del control original. En el siguiente ejemplo de código, se muestra cómo crear una subclase
del control Entry :

public class MyEntry : Entry


{
public MyEntry ()
{
BackgroundColor = Color.Gray;
}
}

El control MyEntry es un control de Entry en el que BackgroundColor se establece en gris y al que puede hacerse
referencia en XAML mediante la declaración de un espacio de nombres para su ubicación y el uso del prefijo de
espacio de nombres en el elemento de control. En el siguiente ejemplo de código, se muestra cómo usar el control
personalizado MyEntry en una ContentPage :

<ContentPage
...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
...
<local:MyEntry Text="In Shared Code" />
...
</ContentPage>
El prefijo de espacio de nombres local puede ser cualquier texto. Pero los valores namespace y assembly tienen
que coincidir con los detalles del control personalizado. Después de declarar el espacio de nombres, el prefijo se
usa para hacer referencia al control personalizado.

NOTE
Definir el elemento xmlns es mucho más fácil en proyectos de biblioteca de .NET Standard que en proyectos compartidos.
Una biblioteca de .NET Standard se compila en un ensamblado, por lo que resulta más sencillo determinar cuál tendría que
ser el valor de assembly=CustomRenderer . Al usar proyectos compartidos, todos los activos compartidos (incluido el código
XAML) se compilan en cada uno de los proyectos a los que se hace referencia, lo que significa que, si los proyectos de iOS,
Android y UWP tienen sus propios nombres de ensamblado, resulta imposible escribir la declaración xmlns , ya que el valor
tiene que ser distinto para cada aplicación. Para usar los controles personalizados en XAML para proyectos compartidos, es
necesario configurar cada proyecto de aplicación con el mismo nombre de ensamblado.

Después, el control personalizado MyEntry se representa en cada plataforma, con un fondo gris, como se muestra
en las capturas de pantalla siguientes:

El cambio del color de fondo del control en cada plataforma se ha obtenido simplemente mediante la creación de
subclases del control. Pero los resultados que pueden obtenerse con esta técnica son limitados, ya que no es
posible aprovechar las mejoras y personalizaciones específicas de cada plataforma. Cuando sea necesario usarlos,
tendrán que implementarse representadores personalizados.

Crear una clase de representador personalizada


Siga este procedimiento para crear una clase de representador personalizada:
1. Cree una subclase de la clase de representador que represente el control nativo.
2. Reemplace el método que representa el control nativo y escriba la lógica para personalizar el control. Con
frecuencia, el método OnElementChanged se usa para representar el control nativo, al que se llama cuando se
crea el control Xamarin.Forms correspondiente.
3. Agregue un atributo ExportRenderer a la clase de representador personalizada para especificar que se usará
para representar el control de Xamarin.Forms. Este atributo se usa para registrar al representador
personalizado con Xamarin.Forms.

NOTE
Para la mayoría de los elementos de Xamarin.Forms, proporcionar un representador personalizado en cada proyecto de la
plataforma es un paso opcional. Si no se registra un representador personalizado, se usará el representador predeterminado
de la clase base del control. Pero los representadores personalizados son necesarios en cada proyecto de la plataforma al
representar un elemento View o ViewCell.
En los temas de esta serie, se proporcionarán demostraciones y explicaciones de este proceso para distintos
elementos de Xamarin.Forms.

Solución de problemas
Si se incluye un control personalizado en un proyecto de biblioteca de .NET Standard agregado a la solución (es
decir, no la biblioteca de .NET Standard creada por la plantilla de proyecto de aplicación de Xamarin.Forms de
Visual Studio o Visual Studio para Mac), puede producirse una excepción en iOS al intentar acceder al control
personalizado. Si se produce este problema, puede solucionarse mediante la creación de una referencia al control
personalizado desde la clase AppDelegate :

var temp = new ClassInPCL(); // in AppDelegate, but temp not used anywhere

Esto obliga al compilador a reconocer el tipo ClassInPCL y solucionarlo. Como alternativa, el atributo Preserve
puede agregarse a la clase AppDelegate para obtener el mismo resultado:

[assembly: Preserve (typeof (ClassInPCL))]

De esta forma, se crea una referencia al tipo ClassInPCL , lo que indica que se necesita en tiempo de ejecución. Para
obtener más información, vea Conservar código.

Resumen
En este artículo, se proporciona una introducción a los representadores personalizados y se describe el proceso
para crear un representador personalizado. Los representadores personalizados proporcionan un método eficaz
para personalizar la apariencia y el comportamiento de los controles de Xamarin.Forms. Se pueden usar para
realizar pequeños cambios de estilo o para una personalización sofisticada del diseño y el comportamiento
específicos de una plataforma.

Vínculos relacionados
Efectos
Clases base y controles nativos del representador
11/07/2019 • 5 minutes to read • Edit Online

Todos los controles de Xamarin.Forms tienen un representador que los acompaña para cada plataforma y que
crea una instancia de un control nativo. En este artículo se enumeran las clases de representador y control
nativo que implementan cada página, diseño, vista y celda de Xamarin.Forms.
A excepción de la clase MapRenderer , los representadores específicos de la plataforma se pueden encontrar en
los espacios de nombres siguientes:
iOS: Xamarin.Forms.Platform.iOS
Android: Xamarin.Forms.Platform.Android
Android (AppCompat) : Xamarin.Forms.Platform.Android.AppCompat
Plataforma universal de Windows (UWP ) : Xamarin.Forms.Platform.UWP
La clase MapRenderer se puede encontrar en los espacios de nombres siguientes:
iOS: Xamarin.Forms.Maps.iOS
Android: Xamarin.Forms.Maps.Android
Plataforma universal de Windows (UWP ) : Xamarin.Forms.Maps.UWP

NOTE
Para información sobre cómo crear representadores personalizados para aplicaciones de Shell, consulte Representadores
personalizados de Xamarin.Forms Shell.

Páginas
En la tabla siguiente se enumeran las clases de representador y control nativo que implementan cada tipo Page
de Xamarin.Forms:

ANDROID
PÁGINA REPRESENTADOR IOS ANDROID (APPCOMPAT) UWP

ContentPage PageRenderer UIViewController ViewGroup FrameworkEleme


nt

MasterDetailPage PhoneMasterDet UIViewController DrawerLayout DrawerLayout FrameworkEleme


ailRenderer (iOS: (teléfono), (v4) (v4) nt (Control
teléfono), UISplitViewContr personalizado)
TabletMasterDet oller (tableta)
ailPageRenderer
(iOS: tableta),
MasterDetailRen
derer (Android),
MasterDetailPag
eRenderer
(Android
AppCompat),
MasterDetailPag
eRenderer (UWP)
ANDROID
PÁGINA REPRESENTADOR IOS ANDROID (APPCOMPAT) UWP

NavigationPage NavigationRende UIToolbar ViewGroup ViewGroup FrameworkEleme


rer (iOS y nt (Control
Android), personalizado)
NavigationPageR
enderer
(AppCompat
Android),
NavigationPageR
enderer (UWP)

TabbedPage TabbedRenderer UIView ViewPager ViewPager FrameworkEleme


(iOS y Android), nt (Pivot)
TabbedPageRend
erer (Android
AppCompat),
TabbedPageRend
erer (UWP)

TemplatedPage PageRenderer UIViewController ViewGroup FrameworkEleme


nt

CarouselPage CarouselPageRen UIScrollView ViewPager ViewPager FrameworkEleme


derer nt (FlipView)

Diseños
En la tabla siguiente se enumeran las clases de representador y control nativo que implementan cada tipo
Layout de Xamarin.Forms:

DISEÑO REPRESENTADOR IOS ANDROID UWP

ContentPresenter ViewRenderer UIView Ver FrameworkElement

ContentView ViewRenderer UIView Ver FrameworkElement

FlexLayout ViewRenderer UIView Ver FrameworkElement

Frame FrameRenderer UIView ViewGroup Borde

ScrollView ScrollViewRenderer UIScrollView ScrollView ScrollViewer

TemplatedView ViewRenderer UIView Ver FrameworkElement

AbsoluteLayout ViewRenderer UIView Ver FrameworkElement

Grid ViewRenderer UIView Ver FrameworkElement

RelativeLayout ViewRenderer UIView Ver FrameworkElement

StackLayout ViewRenderer UIView Ver FrameworkElement


Vistas
En la tabla siguiente se enumeran las clases de representador y control nativo que implementan cada tipo View
de Xamarin.Forms:

ANDROID
VISTAS REPRESENTADOR IOS ANDROID (APPCOMPAT) UWP

ActivityIndicator ActivityIndicator UIActivityIndicat ProgressBar ProgressBar


Renderer or

BoxView BoxRenderer (iOS UIView ViewGroup Rectángulo


y Android),
BoxViewRenderer
(UWP)

Button ButtonRenderer UIButton Botón AppCompatButt Botón


on

CheckBox CheckBoxRender UIButton AppCompatChec CheckBox


er kBox

CollectionView CollectionViewRe UICollectionView RecyclerView


nderer

DatePicker DatePickerRende UITextField EditText DatePicker


rer

Editor EditorRenderer UITextView EditText TextBox

Entry EntryRenderer UITextField EditText TextBox

Image ImageRenderer UIImageView ImageView Imagen

ImageButton ImageButtonRen UIButton AppCompatImag Botón


derer eButton

Label LabelRenderer UILabel TextView TextBlock

ListView ListViewRenderer UITableView ListView ListView

Map MapRenderer MKMapView MapView MapControl

Picker PickerRenderer UITextField EditText EditText ComboBox

ProgressBar ProgressBarRend UIProgressView ProgressBar ProgressBar


erer

SearchBar SearchBarRender UISearchBar SearchView AutoSuggestBox


er

Slider SliderRenderer UISlider SeekBar Slider

Stepper StepperRenderer UIStepper LinearLayout Control


ANDROID
VISTAS REPRESENTADOR IOS ANDROID (APPCOMPAT) UWP

Switch SwitchRenderer UISwitch Modificador SwitchCompat ToggleSwitch

TableView TableViewRender UITableView ListView ListView


er

TimePicker TimePickerRende UITextField EditText TimePicker


rer

WebView WebViewRender UIWebView WebView WebView


er

Celdas
En la tabla siguiente se enumeran las clases de representador y control nativo que implementan cada tipo Cell
de Xamarin.Forms:

CELDAS REPRESENTADOR IOS ANDROID UWP

EntryCell EntryCellRenderer UITableViewCell con LinearLayout con DataTemplate con un


UITextField TextView y EditText control TextBox

SwitchCell SwitchCellRenderer UITableViewCell con Modificador DataTemplate con un


UISwitch elemento Grid que
contiene controles
TextBlock y
ToggleSwitch

TextCell TextCellRenderer UITableViewCell LinearLayout con dos DataTemplate con un


objetos TextView elemento StackPanel
que contiene dos
elementos TextBlock

ImageCell ImageCellRenderer UITableViewCell con LinearLayout con dos DataTemplate con un


UIImage objetos TextView y un elemento Grid que
objeto ImageView contiene un control
Image y dos
TextBlock

ViewCell ViewCellRenderer UITableViewCell Ver DataTemplate con un


elemento
ContentPresenter

Resumen
En este artículo se han enumerado las clases de representador y control nativo que implementan cada página,
diseño, vista y celda de Xamarin.Forms. Todos los controles de Xamarin.Forms tienen un representador que lo
acompaña para cada plataforma y que crea una instancia de un control nativo.
Personalización de una entrada
11/07/2019 • 12 minutes to read • Edit Online

Descargar el ejemplo
El control Entry de Xamarin.Forms permite que se edite una sola línea de texto. En este artículo se muestra cómo
crear un representador personalizado para el control Entry, lo que permite que los desarrolladores reemplacen la
representación nativa de forma predeterminada con su propia personalización específica de la plataforma.
Todos los controles de Xamarin.Forms tienen un representador que lo acompaña para cada plataforma y que crea
una instancia de un control nativo. Cuando una aplicación de Xamarin.Forms representa un control Entry , se crea
una instancia de la clase EntryRenderer en iOS que, a su vez, crea una instancia de un control UITextField nativo.
En la plataforma Android, la clase EntryRenderer crea una instancia de un control EditText . En la Plataforma
universal de Windows (UWP ), la clase EntryRenderer crea una instancia de un control TextBox . Para obtener más
información sobre el representador y las clases de control nativo a las que se asignan los controles de
Xamarin.Forms, vea Renderer Base Classes and Native Controls (Clases base y controles nativos del
representador).
El siguiente diagrama muestra la relación entre el control Entry y los controles nativos correspondientes que lo
implementan:

El proceso de representación puede aprovecharse para implementar las personalizaciones específicas de la


plataforma creando un representador personalizado para el control Entry en cada plataforma. Para hacerlo, siga
este procedimiento:
1. Cree un control personalizado de Xamarin.Forms.
2. Use el control personalizado de Xamarin.Forms.
3. Cree el representador personalizado para el control en cada plataforma.
Ahora se analizará en detalle cada elemento, para implementar un control Entry que tiene un color de fondo
diferente en cada plataforma.
IMPORTANT
En este artículo se explica cómo crear un representador personalizado simple. Empero, no es necesario crear un
representador personalizado para implementar un Entry que tiene un color de fondo diferente en cada plataforma. Esto
puede realizarse más fácilmente usando la clase Device o la extensión de marcado OnPlatform , para proporcionar los
valores específicos de la plataforma. Para obtener más información, vea Providing Platform-Specific Values (Proporcionar
valores específicos de la plataforma) y OnPlatform Markup Extension (Extensión de marcado OnPlatform).

Creación de un control Entry personalizado


Se puede crear un control Entry personalizado mediante la creación de subclases del control Entry , como se
muestra en el siguiente ejemplo de código:

public class MyEntry : Entry


{
}

El control MyEntry se crea en el proyecto de biblioteca de .NET Standard y es simplemente un control Entry . La
personalización del control se llevará a cabo en el representador personalizado, por lo que no se requiere ninguna
implementación adicional en el control MyEntry .

Uso del control personalizado


En XAML puede hacerse referencia al control MyEntry en el proyecto de biblioteca de .NET Standard declarando
un espacio de nombres para su ubicación y usando el prefijo del espacio de nombres en el elemento de control. El
siguiente ejemplo de código muestra cómo el control personalizado MyEntry puede utilizarse en una página
XAML:

<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
...
<local:MyEntry Text="In Shared Code" />
...
</ContentPage>

El prefijo de espacio de nombres local puede tener cualquier nombre. Pero los valores clr-namespace y
assembly deben coincidir con los detalles del control personalizado. Una vez que se declara el espacio de
nombres, el prefijo se usa para hacer referencia al control personalizado.
El siguiente ejemplo de código muestra cómo el control personalizado MyEntry puede utilizarse en una página C#:
public class MainPage : ContentPage
{
public MainPage ()
{
Content = new StackLayout {
Children = {
new Label {
Text = "Hello, Custom Renderer !",
},
new MyEntry {
Text = "In Shared Code",
}
},
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
};
}
}

Este código crea una instancia de un nuevo objeto ContentPage que mostrará un Label y un control MyEntry
centrado tanto vertical como horizontalmente en la página.
Ahora se puede agregar un representador personalizado a cada proyecto de aplicación para personalizar la
apariencia del control en cada plataforma.

Creación del representador personalizado en cada plataforma


El proceso para crear la clase del representador personalizado es el siguiente:
1. Cree una subclase de la clase EntryRenderer que representa el control nativo.
2. Invalide el método OnElementChanged que representa el control nativo y escriba lógica para personalizar el
control. Se llama a este método cuando se crea el correspondiente control de Xamarin.Forms.
3. Agregue un atributo ExportRenderer a la clase de representador personalizada para especificar que se utilizará
para representar el control de Xamarin.Forms. Este atributo se usa para registrar el representador
personalizado con Xamarin.Forms.

NOTE
Proporcionar un representador personalizado en cada proyecto de la plataforma es un paso opcional. Si no hay un
representador personalizado registrado, se usa el representador predeterminado de la clase base del control.

El siguiente diagrama muestra las responsabilidades de cada proyecto de la aplicación de ejemplo, junto con las
relaciones entre ellos:

Las clases MyEntryRendererdel representador específico de la plataforma, que se derivan de la clase


EntryRenderer para cada plataforma, representan el control MyEntry . Esto da como resultado que cada control
MyEntry se represente con un color de fondo específico de la plataforma, como se muestra en las siguientes
capturas de pantalla:

La clase EntryRenderer expone el método OnElementChanged , al que se llama cuando se crea el control de
Xamarin.Forms para representar el control nativo correspondiente. Este método toma un parámetro
ElementChangedEventArgs que contiene propiedades OldElement y NewElement . Estas propiedades representan al
elemento de Xamarin.Forms al que estaba asociado el representador y al elemento de Xamarin.Forms al que está
asociado el representador, respectivamente. En la aplicación de ejemplo, la propiedad OldElement es null y la
propiedad NewElement contiene una referencia al control de MyEntry .
El lugar para realizar la personalización del control nativo es una versión reemplazada del método
OnElementChanged en la clase MyEntryRenderer . Una referencia con tipo para el control nativo que se usa en la
plataforma puede obtenerse a través de la propiedad Control . Además, se puede obtener una referencia al
control de Xamarin.Forms que se representa mediante la propiedad Element , aunque no se usa en la aplicación de
ejemplo.
Cada clase de representador personalizado se decora con un atributo ExportRenderer que registra el
representador con Xamarin.Forms. El atributo toma dos parámetros: el nombre de tipo del control de
Xamarin.Forms que se va a representar y el nombre de tipo del representador personalizado. El prefijo assembly
para el atributo especifica que el atributo se aplica a todo el ensamblado.
En las secciones siguientes se describe la implementación de cada clase de representador personalizado
MyEntryRenderer específico de plataforma.

Creación del representador personalizado en iOS


El siguiente ejemplo de código muestra el representador personalizado para la plataforma iOS:

using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer (typeof(MyEntry), typeof(MyEntryRenderer))]


namespace CustomRenderer.iOS
{
public class MyEntryRenderer : EntryRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged (e);

if (Control != null) {
// do whatever you want to the UITextField here!
Control.BackgroundColor = UIColor.FromRGB (204, 153, 255);
Control.BorderStyle = UITextBorderStyle.Line;
}
}
}
}
La llamada al método OnElementChanged de la clase base crea una instancia de un control UITextField de iOS, con
una referencia al control que se asigna en la propiedad Control del representador. Después se establece el color
de fondo en púrpura claro con el método UIColor.FromRGB .
Creación del representador personalizado en Android
En el ejemplo de código siguiente se muestra el representador personalizado para la plataforma Android:

using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(MyEntry), typeof(MyEntryRenderer))]


namespace CustomRenderer.Android
{
class MyEntryRenderer : EntryRenderer
{
public MyEntryRenderer(Context context) : base(context)
{
}

protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)


{
base.OnElementChanged(e);

if (Control != null)
{
Control.SetBackgroundColor(global::Android.Graphics.Color.LightGreen);
}
}
}
}

La llamada al método OnElementChanged de la clase base crea una instancia de un control EditText de Android,
con una referencia al control que se asigna en la propiedad Control del representador. Después se establece el
color de fondo en verde claro con el método Control.SetBackgroundColor .
Creación del representador personalizado en UWP
En el siguiente ejemplo de código se muestra el representador personalizado para UWP:

[assembly: ExportRenderer(typeof(MyEntry), typeof(MyEntryRenderer))]


namespace CustomRenderer.UWP
{
public class MyEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);

if (Control != null)
{
Control.Background = new SolidColorBrush(Colors.Cyan);
}
}
}
}

La llamada al método OnElementChanged de la clase base crea una instancia de un control TextBox , con una
referencia al control que se asigna en la propiedad Control del representador. Después se establece el color de
fondo en cian mediante la creación de una instancia de SolidColorBrush .

Resumen
En este artículo se mostró cómo crear un representador de control personalizado para el control Entry de
Xamarin.Forms, lo que permite que los desarrolladores reemplacen la representación nativa de forma
predeterminada con su propia representación específica de la plataforma. Los representadores personalizados
proporcionan un método eficaz para personalizar la apariencia de los controles de Xamarin.Forms. Se pueden usar
para pequeños cambios de estilo o para una personalización sofisticada del diseño y el comportamiento
específicos de una plataforma.

Vínculos relacionados
CustomRendererEntry (sample) (CustomRendererEntry [ejemplo])
Personalización de una página de contenido
11/07/2019 • 14 minutes to read • Edit Online

Descargar el ejemplo
Un ContentPage es un elemento visual que muestra una vista única y ocupa la mayor parte de la pantalla. En este
artículo se muestra cómo crear un representador personalizado para la página ContentPage, lo que permite que
los desarrolladores reemplacen la representación nativa de forma predeterminada con su propia personalización
específica de la plataforma.
Todos los controles de Xamarin.Forms tienen un representador que lo acompaña para cada plataforma y que crea
una instancia de un control nativo. Cuando una aplicación de Xamarin.Forms representa una ContentPage , en iOS
se crea la instancia de la clase PageRenderer , que a su vez crea una instancia del control UIViewController nativo.
En la plataforma Android, la clase PageRenderer crea una instancia de un control ViewGroup . En la Plataforma
universal de Windows (UWP ), la clase PageRenderer crea una instancia de un control FrameworkElement . Para
obtener más información sobre el representador y las clases de control nativo a las que se asignan los controles de
Xamarin.Forms, vea Renderer Base Classes and Native Controls (Clases base y controles nativos del
representador).
El siguiente diagrama muestra la relación entre la clase ContentPage y los controles nativos correspondientes que
la implementan:

El proceso de representación puede aprovecharse para implementar las personalizaciones específicas de la


plataforma creando un representador personalizado para una ContentPage en cada plataforma. Para hacerlo, siga
este procedimiento:
1. Cree una página de Xamarin.Forms.
2. Use la página de Xamarin.Forms.
3. Cree el representador personalizado para la página en cada plataforma.
Ahora se analizará en detalle cada elemento, para implementar un CameraPage que proporciona una fuente de la
cámara en vivo y la capacidad de capturar una foto.

Creación de una página de Xamarin.Forms


Puede agregarse una ContentPage al proyecto de Xamarin.Forms compartido, como se muestra en el siguiente
ejemplo de código XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CustomRenderer.CameraPage">
<ContentPage.Content>
</ContentPage.Content>
</ContentPage>

De forma similar, el archivo de código subyacente para el ContentPage también debe permanecer sin
modificaciones, como se muestra en el siguiente ejemplo de código:

public partial class CameraPage : ContentPage


{
public CameraPage ()
{
// A custom renderer is used to display the camera UI
InitializeComponent ();
}
}

En el siguiente ejemplo de código se muestra cómo puede crearse la página en C#:

public class CameraPageCS : ContentPage


{
public CameraPageCS ()
{
}
}

Una instancia de la CameraPage se usará para mostrar la fuente de la cámara en directo en cada plataforma. La
personalización del control se llevará a cabo en el representador personalizado, por lo que no se requiere ninguna
implementación adicional en la clase CameraPage .

Uso de una página de Xamarin.Forms


La aplicación de Xamarin.Forms debe mostrar la página CameraPage vacía. Esto se produce cuando se pulsa un
botón en la instancia de MainPage , lo que a su vez ejecuta el método OnTakePhotoButtonClicked , como se muestra
en el siguiente ejemplo de código:

async void OnTakePhotoButtonClicked (object sender, EventArgs e)


{
await Navigation.PushAsync (new CameraPage ());
}

Este código simplemente se desplaza a la CameraPage , en la que representadores personalizados personalizarán el


aspecto de la página en cada plataforma.

Creación del representador de página en cada plataforma


El proceso para crear la clase del representador personalizado es el siguiente:
1. Se crea una subclase de la clase PageRenderer .
2. Invalide el método OnElementChanged que representa la página nativa y escriba lógica para personalizar la
página. Se llama al método OnElementChanged cuando se crea el correspondiente control de Xamarin.Forms.
3. Agregue un atributo ExportRenderer a la clase de representador para especificar que se utilizará para
representar la página de Xamarin.Forms. Este atributo se usa para registrar el representador personalizado con
Xamarin.Forms.

NOTE
Proporcionar un representador de página en cada proyecto de la plataforma es un paso opcional. Si no hay un representador
de página registrado, se usa el representador predeterminado de la página.

El siguiente diagrama muestra las responsabilidades de cada proyecto de la aplicación de ejemplo, junto con las
relaciones entre ellos:

Las clases CameraPageRendererdel representador específico de la plataforma, que se derivan de la clase


PageRenderer para esa plataforma, representan la instancia CameraPage . Esto da como resultado que cada
instancia de CameraPage se represente con una fuente de la cámara en directo, como se muestra en las siguientes
capturas de pantalla:

La clase PageRenderer expone el método OnElementChanged , al que se llama cuando se crea una página de
Xamarin.Forms para representar el control nativo correspondiente. Este método toma un parámetro
ElementChangedEventArgs que contiene propiedades OldElement y NewElement . Estas propiedades representan al
elemento de Xamarin.Forms al que estaba asociado el representador y al elemento de Xamarin.Forms al que está
asociado el representador, respectivamente. En la aplicación de ejemplo, la propiedad OldElement es null y la
propiedad NewElement contiene una referencia a la instancia de CameraPage .
El lugar para realizar la personalización de páginas nativas es una versión reemplazada del método
OnElementChanged en la clase CameraPageRenderer . Se puede obtener una referencia a la instancia de la página de
Xamarin.Forms que se representa mediante la propiedad Element .
Cada clase de representador personalizado se decora con un atributo ExportRenderer que registra el
representador con Xamarin.Forms. El atributo toma dos parámetros: el nombre de tipo de la página de
Xamarin.Forms que se representa y el nombre de tipo del representador personalizado. El prefijo assembly para el
atributo especifica que el atributo se aplica a todo el ensamblado.
En las secciones siguientes se describe la implementación del representador personalizado de CameraPageRenderer
para cada plataforma.
Creación del representador de página en iOS
El siguiente ejemplo de código muestra el representador de página para la plataforma iOS:

[assembly:ExportRenderer (typeof(CameraPage), typeof(CameraPageRenderer))]


namespace CustomRenderer.iOS
{
public class CameraPageRenderer : PageRenderer
{
...

protected override void OnElementChanged (VisualElementChangedEventArgs e)


{
base.OnElementChanged (e);

if (e.OldElement != null || Element == null) {


return;
}

try {
SetupUserInterface ();
SetupEventHandlers ();
SetupLiveCameraStream ();
AuthorizeCameraUse ();
} catch (Exception ex) {
System.Diagnostics.Debug.WriteLine (@" ERROR: ", ex.Message);
}
}
...
}
}

La llamada al método OnElementChanged de la clase base crea una instancia de un control UIViewController de
iOS. Solo se procesa la secuencia en directo de cámara si el representador aún no está unido a un elemento
existente de Xamarin.Forms, y siempre que exista una instancia de la página que se representa mediante el
representador personalizado.
Después, se personaliza la página mediante una serie de métodos que usan la API de AVCapture para
proporcionar la secuencia en directo desde la cámara y la capacidad para capturar una foto.
Creación del representador de página en Android
En el ejemplo de código siguiente se muestra el representador de página para la plataforma Android:
[assembly: ExportRenderer(typeof(CameraPage), typeof(CameraPageRenderer))]
namespace CustomRenderer.Droid
{
public class CameraPageRenderer : PageRenderer, TextureView.ISurfaceTextureListener
{
...
public CameraPageRenderer(Context context) : base(context)
{
}

protected override void OnElementChanged(ElementChangedEventArgs<Page> e)


{
base.OnElementChanged(e);

if (e.OldElement != null || Element == null)


{
return;
}

try
{
SetupUserInterface();
SetupEventHandlers();
AddView(view);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(@" ERROR: ", ex.Message);
}
}
...
}
}

La llamada al método de la clase base OnElementChanged crea una instancia de un control ViewGroup de Android,
que es un grupo de vistas. Solo se procesa la secuencia en directo de cámara si el representador aún no está unido
a un elemento existente de Xamarin.Forms, y siempre que exista una instancia de la página que se representa
mediante el representador personalizado.
Después se personaliza la página mediante la invocación de una serie de métodos que usan la API de Camera para
proporcionar la secuencia en directo desde la cámara y la capacidad de capturar una foto, antes de que se invoque
al método AddView para agregar la interfaz de usuario de la transmisión de cámara en vivo al ViewGroup . Tenga
en cuenta que en Android también es necesario reemplazar el método OnLayout para realizar operaciones de
medida y de diseño en la vista. Para obtener más información, vea el ejemplo de representador de ContentPage.
Creación del representador de página en UWP
En el siguiente ejemplo de código se muestra el representador de página para UWP:
[assembly: ExportRenderer(typeof(CameraPage), typeof(CameraPageRenderer))]
namespace CustomRenderer.UWP
{
public class CameraPageRenderer : PageRenderer
{
...
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Page> e)
{
base.OnElementChanged(e);

if (e.OldElement != null || Element == null)


{
return;
}

try
{
...
SetupUserInterface();
SetupBasedOnStateAsync();

this.Children.Add(page);
}
...
}

protected override Size ArrangeOverride(Size finalSize)


{
page.Arrange(new Windows.Foundation.Rect(0, 0, finalSize.Width, finalSize.Height));
return finalSize;
}
...
}
}

La llamada al método OnElementChanged de la clase base crea una instancia de un control FrameworkElement , en el
que se representa la página. Solo se procesa la secuencia en directo de cámara si el representador aún no está
unido a un elemento existente de Xamarin.Forms, y siempre que exista una instancia de la página que se
representa mediante el representador personalizado. Después se personaliza la página mediante la invocación de
una serie de métodos que usan la API de MediaCapture para proporcionar la secuencia en directo desde la cámara
y la capacidad de capturar una foto, antes de que se agregue la página personalizada a la colección Children para
mostrarla.
Al implementar un representador personalizado que derive de PageRenderer en UWP, el método ArrangeOverride
también debe implementarse para organizar los controles de página, ya que el representador de base no sabe qué
hacer con ellos. En caso contrario, da como resultado una página en blanco. Por lo tanto, en este ejemplo el
método ArrangeOverride llama al método Arrange en la instancia de Page .

NOTE
Es importante detener y eliminar los objetos que proporcionan acceso a la cámara en una aplicación de UWP. Si no lo hace
puede interferir con otras aplicaciones que intentan acceder a la cámara del dispositivo. Para obtener más información, vea
Display the camera preview (Mostar la vista previa de la cámara).

Resumen
En este artículo se mostró cómo crear un representador personalizado para la página ContentPage , lo que permite
que los desarrolladores reemplacen la representación nativa de forma predeterminada con su propia
personalización específica de la plataforma. Un ContentPage es un elemento visual que muestra una vista única y
ocupa la mayor parte de la pantalla.

Vínculos relacionados
CustomRendererContentPage (sample) (CustomRendererContentPage [ejemplo])
Personalizar un mapa de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Xamarin.Forms.Maps proporciona una abstracción multiplataforma para mostrar mapas que usan la API de
mapa nativo en cada plataforma y proporcionar una experiencia de mapa rápida y familiar para los usuarios.

Personalización de un anclado de mapa


En este artículo se explica cómo crear un representador personalizado para el control Map , que muestra un mapa
nativo con un PIN personalizado y una vista personalizada de los datos de PIN en cada plataforma.

Resaltado de un área circular en un mapa


En este artículo se explica cómo agregar una superposición circular a un mapa, para resaltar un área circular del
mapa.

Resaltado de una región en un mapa


En este artículo se explica cómo agregar una superposición de polígono a un mapa, para resaltar una región del
mapa. Los polígonos son una forma cerrada y tienen relleno en su interior.

Resaltado de una ruta en un mapa


En este artículo se explica cómo agregar una superposición de polilínea a un mapa. Una superposición de polilínea
es una serie de segmentos de línea conectados que se usan normalmente para mostrar una ruta en un mapa o
crear cualquier forma que se necesite.
Personalización de un anclado de mapa
11/07/2019 • 39 minutes to read • Edit Online

Descargar el ejemplo
En este artículo se explica cómo crear un representador personalizado para el control de mapa, que muestra un
mapa nativo con una marca personalizada y una vista personalizada de los datos de marca en cada plataforma.
Todos las vistas de Xamarin.Forms tienen un representador que lo acompaña para cada plataforma y que crea
una instancia de un control nativo. Cuando una aplicación de Xamarin.Forms representa una Map en iOS se crea
la instancia de la clase MapRenderer , que a su vez crea una instancia del control MKMapView nativo. En la
plataforma Android, la clase MapRenderer crea una instancia del control MapView nativo. En la Plataforma
Universal de Windows (UWP ), la clase MapRenderer crea una instancia de MapControl nativa. Para obtener más
información sobre las clases de control nativo que se asignan a los controles de Xamarin.Forms y el
representador, vea Renderer Base Classes and Native Controls (Controles nativos y clases base del
representador).
El siguiente diagrama ilustra la relación entre la Map y los controles nativos correspondientes que la
implementan:

El proceso de representación puede usarse para implementar las personalizaciones específicas de la plataforma
creando un representador personalizado para una Map en cada plataforma. El proceso para hacer esto es el
siguiente:
1. Cree un mapa personalizado de Xamarin.Forms.
2. Consuma el mapa personalizado de Xamarin.Forms.
3. Cree el representador personalizado para el mapa en cada plataforma.
Se explicará cada elemento uno por uno, para implementar un representador CustomMap que muestra un mapa
nativo con una marca personalizada y una vista personalizada de los datos de marcas en cada plataforma.

NOTE
Xamarin.Forms.Maps debe inicializarse y configurarse antes de su uso. Para obtener más información, vea Maps Control .

Creación del mapa personalizado


Se puede crear un control de mapa personalizado mediante la creación de subclases de la clase Map , como se
muestra en el siguiente ejemplo de código:

public class CustomMap : Map


{
public List<CustomPin> CustomPins { get; set; }
}

El control CustomMap se crea en el proyecto de biblioteca de .NET Standard y define la API para el mapa
personalizado. El mapa personalizado expone la propiedad CustomPins que representa la colección de objetos
CustomPin que va a representar el control de mapa nativo en cada plataforma. La clase CustomPin se muestra en
el siguiente ejemplo de código:

public class CustomPin : Pin


{
public string Url { get; set; }
}

Esta clase define que un CustomPin hereda las propiedades de la clase Pin y agrega una propiedad Url .

Consumo del mapa personalizado


En XAML puede hacerse referencia al control personalizado CustomMap en el proyecto de biblioteca de .NET
Standard declarando un espacio de nombres para su ubicación y usando el prefijo del espacio de nombres en el
control de mapa personalizado. El siguiente ejemplo de código muestra cómo el control personalizado CustomMap
puede utilizarse en una página XAML:

<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer">
<ContentPage.Content>
<local:CustomMap x:Name="myMap" MapType="Street"
WidthRequest="{x:Static local:App.ScreenWidth}"
HeightRequest="{x:Static local:App.ScreenHeight}" />
</ContentPage.Content>
</ContentPage>

El prefijo del espacio de nombres local puede tener cualquier nombre. Empero, los valores deben
clr-namespace y assembly coincidir con los detalles del mapa personalizado. Una vez que se declare el espacio
de nombres, el prefijo se utiliza para hacer referencia al mapa personalizado.
El siguiente ejemplo de código muestra cómo el control personalizado CustomMap puede utilizarse en una página
C#:
public class MapPageCS : ContentPage
{
public MapPageCS ()
{
var customMap = new CustomMap {
MapType = MapType.Street,
WidthRequest = App.ScreenWidth,
HeightRequest = App.ScreenHeight
};
...

Content = customMap;
}
}

La instancia CustomMap se usará para mostrar el mapa nativo en cada plataforma. La propiedad MapType
establece el estilo de presentación del Map y los valores posibles que se definen en la enumeración MapType .
Para iOS y Android, el ancho y alto del mapa se establece a través de las propiedades de la clase App que se
inicializan en los proyectos específicos de la plataforma.
La ubicación del mapa y las marcas que contiene se inicializan como se muestra en el siguiente ejemplo de
código:

public MapPage ()
{
...
var pin = new CustomPin {
Type = PinType.Place,
Position = new Position (37.79752, -122.40183),
Label = "Xamarin San Francisco Office",
Address = "394 Pacific Ave, San Francisco CA",
Id = "Xamarin",
Url = "http://xamarin.com/about/"
};

customMap.CustomPins = new List<CustomPin> { pin };


customMap.Pins.Add (pin);
customMap.MoveToRegion (MapSpan.FromCenterAndRadius (
new Position (37.79752, -122.40183), Distance.FromMiles (1.0)));
}

Esta inicialización agrega una marca personalizada y coloca la vista del mapa con el método MoveToRegion , que
cambia la posición y el nivel de zoom del mapa mediante la creación de un MapSpan desde un Position y un
Distance .

Ahora se puede agregar un representador personalizado a cada proyecto de aplicación para personalizar los
controles de mapa nativos.

Creación del representador personalizado en cada plataforma


El proceso de creación de la clase de representador personalizada es el siguiente:
1. Cree una subclase de la clase MapRenderer que represente el mapa personalizado.
2. Invalide el método OnElementChanged que representa el mapa personalizado y escriba una lógica para
personalizarlo. Se llama a este método cuando se crea el mapa personalizado de Xamarin.Forms
correspondiente.
3. Agregue un atributo ExportRenderer a la clase de representador personalizada para especificar que se utilizará
para representar el mapa personalizado de Xamarin.Forms. Este atributo se usa para registrar al representador
personalizado con Xamarin.Forms.
NOTE
Proporcionar un representador personalizado en cada proyecto de la plataforma es un paso opcional. Si no hay un
representador personalizado registrado, se usa el representador predeterminado de la clase base del control.

El siguiente diagrama ilustra las responsabilidades de cada proyecto en la aplicación de ejemplo, junto con las
relaciones entre ellos:

Las clases del representador específico de la plataforma, que se derivan de la clase MapRenderer para cada
plataforma, representan el control CustomMap . Esto da como resultado que cada control CustomMap se represente
con controles específicos de la plataforma, como se muestra en las siguientes capturas de pantalla:

La clase MapRenderer expone el método OnElementChanged , al que se llama cuando se crea un mapa personalizado
de Xamarin.Forms para representar el control nativo correspondiente. Este método toma un parámetro
ElementChangedEventArgs que contiene propiedades OldElement y NewElement . Estas propiedades representan al
elemento de Xamarin.Forms al que estaba asociado el representador y al elemento de Xamarin.Forms al que está
asociado el representador, respectivamente. En la aplicación de ejemplo la propiedad OldElement será null y la
propiedad NewElement contendrá una referencia a la instancia CustomMap .
El lugar para realizar la personalización de controles nativos es una versión reemplazada del método
OnElementChanged en cada clase de representador específica de la plataforma. Una referencia con tipo para el
control nativo que se usa en la plataforma puede obtenerse a través de la propiedad Control . Además, se puede
obtener una referencia al control de Xamarin.Forms que se representa mediante la propiedad Element .
Debe tener cuidado al suscribirse a los controladores de eventos en el método OnElementChanged , como se
muestra en el siguiente ejemplo de código:
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged (e);

if (e.OldElement != null) {
// Unsubscribe from event handlers
}

if (e.NewElement != null) {
// Configure the native control and subscribe to event handlers
}
}

Solo se debe configurar el control nativo y suscribir los controladores de eventos cuando se adjunta el
presentador personalizado a un nuevo elemento de Xamarin.Forms. De forma similar, solo se debe cancelar la
suscripción de los controladores de eventos que se han suscrito cuando cambia el elemento al que está asociado
el presentador. Adoptar este enfoque facilita crear un presentador personalizado que no sufra pérdidas de
memoria.
Cada clase de presentador personalizado se decora con un atributo ExportRenderer que registra el representador
con Xamarin.Forms. El atributo toma dos parámetros: el nombre de tipo del control personalizado de
Xamarin.Forms que se va a representar y el nombre de tipo del representador personalizado. El prefijo assembly
para el atributo especifica que el atributo se aplica a todo el ensamblado.
En las secciones siguientes se describe la implementación de cada clase de representador personalizado
específico de plataforma.
Creación del representador personalizado en iOS
Las siguientes capturas de pantalla muestran el mapa antes y después de la personalización:

En iOS la marca se denomina anotación y puede ser una imagen personalizada o una marca definida por el
sistema de varios colores. Opcionalmente las anotaciones pueden mostrar una llamada, que se muestra en
respuesta a que el usuario seleccione la anotación. La llamada muestra las propiedades Label y Address de la
instancia Pin , con vistas adicionales a la derecha y a la izquierda. En la captura de pantalla anterior, la vista
adicional izquierda es la imagen de un mono y la vista adicional derecha es el botón Información.
El siguiente ejemplo de código muestra el representador personalizado para la plataforma de iOS:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace CustomRenderer.iOS
{
public class CustomMapRenderer : MapRenderer
{
UIView customPinView;
List<CustomPin> customPins;

protected override void OnElementChanged(ElementChangedEventArgs<View> e)


{
base.OnElementChanged(e);

if (e.OldElement != null) {
var nativeMap = Control as MKMapView;
if (nativeMap != null) {
nativeMap.RemoveAnnotations(nativeMap.Annotations);
nativeMap.GetViewForAnnotation = null;
nativeMap.CalloutAccessoryControlTapped -= OnCalloutAccessoryControlTapped;
nativeMap.DidSelectAnnotationView -= OnDidSelectAnnotationView;
nativeMap.DidDeselectAnnotationView -= OnDidDeselectAnnotationView;
}
}

if (e.NewElement != null) {
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MKMapView;
customPins = formsMap.CustomPins;

nativeMap.GetViewForAnnotation = GetViewForAnnotation;
nativeMap.CalloutAccessoryControlTapped += OnCalloutAccessoryControlTapped;
nativeMap.DidSelectAnnotationView += OnDidSelectAnnotationView;
nativeMap.DidDeselectAnnotationView += OnDidDeselectAnnotationView;
}
}
...
}
}

El método OnElementChanged realiza la siguiente configuración de la instancia MKMapView , siempre que se adjunte
el presentador personalizado a un nuevo elemento de Xamarin.Forms:
La propiedad GetViewForAnnotation se establece en el método GetViewForAnnotation . Se llama a este método
cuando la ubicación de la anotación se vuelve visible en el mapa y se usa para personalizar la anotación antes
de mostrarla.
Los controladores de eventos para los eventos CalloutAccessoryControlTapped , DidSelectAnnotationView y
DidDeselectAnnotationView se registran. Estos eventos se activan cuando el usuario pulsa el accesorio derecho
de la llamada y cuando el usuario selecciona y anula la selección de la anotación, respectivamente. Se cancela
la suscripción de los eventos solo cuando cambia el representador al que está adjunto el elemento.
Mostrar la anotación
Se llama al método GetViewForAnnotation cuando la ubicación de la anotación se vuelve visible en el mapa y se
usa para personalizar la anotación antes de mostrarla. Una anotación tiene dos partes:
MkAnnotation: incluye el título, el subtítulo y la ubicación de la anotación.
MkAnnotationView : contiene la imagen para representar la anotación y, opcionalmente, una llamada que se
muestra cuando el usuario pulsa la anotación.
El método GetViewForAnnotation acepta un IMKAnnotation que contiene los datos de la anotación y devuelve un
MKAnnotationView para su presentación en el mapa. Se muestra en el siguiente ejemplo de código:
protected override MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation)
{
MKAnnotationView annotationView = null;

if (annotation is MKUserLocation)
return null;

var customPin = GetCustomPin(annotation as MKPointAnnotation);


if (customPin == null) {
throw new Exception("Custom pin not found");
}

annotationView = mapView.DequeueReusableAnnotation(customPin.Id.ToString());
if (annotationView == null) {
annotationView = new CustomMKAnnotationView(annotation, customPin.Id.ToString());
annotationView.Image = UIImage.FromFile("pin.png");
annotationView.CalloutOffset = new CGPoint(0, 0);
annotationView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile("monkey.png"));
annotationView.RightCalloutAccessoryView = UIButton.FromType(UIButtonType.DetailDisclosure);
((CustomMKAnnotationView)annotationView).Id = customPin.Id.ToString();
((CustomMKAnnotationView)annotationView).Url = customPin.Url;
}
annotationView.CanShowCallout = true;

return annotationView;
}

Este método garantiza que la anotación se muestre como una imagen personalizada, en lugar de como una marca
definida por el sistema y que, cuando se pulsa la anotación, se muestre una llamada que incluye contenido
adicional a la izquierda y a la derecha del título y la dirección de la anotación. Esto se logra de la siguiente manera:
1. Se llama al método GetCustomPin para devolver los datos de marca personalizada para la anotación.
2. Para ahorrar memoria, la vista de la anotación se agrupa para volver a usarla con la llamada a
DequeueReusableAnnotation .
3. La clase CustomMKAnnotationView extiende la clase MKAnnotationView con las propiedades Id y Url que
corresponden a las propiedades idénticas en la instancia CustomPin . Se crea una nueva instancia de la
CustomMKAnnotationView , siempre que la anotación sea null :
La propiedad CustomMKAnnotationView.Image se establece en la imagen que representará la anotación en
el mapa.
La propiedad CustomMKAnnotationView.CalloutOffset se establece en un CGPoint que especifica que la
llamada se centrará por encima de la anotación.
La propiedad CustomMKAnnotationView.LeftCalloutAccessoryView se establece en una imagen de un
mono que aparecerá a la izquierda del título y la dirección de la anotación.
La propiedad CustomMKAnnotationView.RightCalloutAccessoryView se establece en un botón Información
que aparecerá a la derecha del título y la dirección de la anotación.
La propiedad CustomMKAnnotationView.Id se establece en la propiedad CustomPin.Id devuelta por el
método GetCustomPin . Esto permite que la anotación pueda identificarse de forma que su llamada
pueda personalizarse aún más si así lo desea.
La propiedad CustomMKAnnotationView.Url se establece en la propiedad CustomPin.Url devuelta por el
método GetCustomPin . La dirección URL se abrirá cuando el usuario pulse el botón que se muestra en
la vista de accesorios de llamada correcta.
4. La propiedad MKAnnotationView.CanShowCallout se establece en true para que se muestre la llamada cuando
se pulsa la anotación.
5. La anotación se devuelve para su visualización en el mapa.
Seleccionar la anotación
Cuando el usuario pulsa en la anotación, se desencadena el evento DidSelectAnnotationView , que a su vez ejecuta
el método OnDidSelectAnnotationView :

void OnDidSelectAnnotationView (object sender, MKAnnotationViewEventArgs e)


{
var customView = e.View as CustomMKAnnotationView;
customPinView = new UIView ();

if (customView.Id == "Xamarin") {
customPinView.Frame = new CGRect (0, 0, 200, 84);
var image = new UIImageView (new CGRect (0, 0, 200, 84));
image.Image = UIImage.FromFile ("xamarin.png");
customPinView.AddSubview (image);
customPinView.Center = new CGPoint (0, -(e.View.Frame.Height + 75));
e.View.AddSubview (customPinView);
}
}

Este método extiende la llamada existente (que contiene las vistas adicionales izquierdas y derechas) mediante la
adición de una instancia de UIView a ella que contiene una imagen del logotipo de Xamarin, siempre que la
anotación seleccionada tenga su propiedad Id establecida en Xamarin . Esto permite escenarios donde se
pueden mostrar llamadas diferentes para distintas anotaciones. La instancia UIView se mostrará centrada por
encima de la llamada existente.
Pulsar en la vista adicional de llamada derecha
Cuando el usuario pulsa el botón Información en la vista adicional de llamada derecha, se desencadena el evento
CalloutAccessoryControlTapped , que a su vez ejecuta el método OnCalloutAccessoryControlTapped :

void OnCalloutAccessoryControlTapped (object sender, MKMapViewAccessoryTappedEventArgs e)


{
var customView = e.View as CustomMKAnnotationView;
if (!string.IsNullOrWhiteSpace (customView.Url)) {
UIApplication.SharedApplication.OpenUrl (new Foundation.NSUrl (customView.Url));
}
}

Este método abre un explorador web y navega a la dirección almacenada en la propiedad


CustomMKAnnotationView.Url . Tenga en cuenta que la dirección se definió al crear la colección CustomPin en el
proyecto de biblioteca de .NET Standard.
Anule la selección de la anotación
Cuando la anotación se muestra y el usuario pulsa en el mapa, se desencadena el evento
DidDeselectAnnotationView , que a su vez ejecuta el método OnDidDeselectAnnotationView :

void OnDidDeselectAnnotationView (object sender, MKAnnotationViewEventArgs e)


{
if (!e.View.Selected) {
customPinView.RemoveFromSuperview ();
customPinView.Dispose ();
customPinView = null;
}
}

Este método garantiza que cuando la llamada existente no está seleccionada, la parte extendida de la llamada (la
imagen del logotipo de Xamarin) también dejará de mostrarse y se liberarán sus recursos.
Para obtener más información sobre cómo personalizar una instancia de MKMapView , vea Maps in Xamarin.iOS
(Mapas en Xamarin.iOS ).
Creación del representador personalizado en Android
Las siguientes capturas de pantalla muestran el mapa antes y después de la personalización:

En Android la marca se denomina marcador y puede ser una imagen personalizada o un marcador definido por
el sistema de varios colores. Los marcadores pueden mostrar una ventana de información, que se muestra en la
respuesta para el usuario que pulsa en el marcador. Muestra la ventana de información de las propiedades Label
y Address de la instancia Pin y se pueden personalizar para incluir otro tipo de contenido. Con todo, solo una
ventana de información puede mostrarse al mismo tiempo.
El siguiente ejemplo de código muestra el representador personalizado para la plataforma de Android:

[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]


namespace CustomRenderer.Droid
{
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter
{
List<CustomPin> customPins;

public CustomMapRenderer(Context context) : base(context)


{
}

protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map>


e)
{
base.OnElementChanged(e);

if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}

if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
Control.GetMapAsync(this);
}
}

protected override void OnMapReady(GoogleMap map)


{
base.OnMapReady(map);

NativeMap.InfoWindowClick += OnInfoWindowClick;
NativeMap.SetInfoWindowAdapter(this);
}
...
}
}
Siempre que se adjunta el representador personalizado a un nuevo elemento de Xamarin.Forms, el método
OnElementChanged llama al método MapView.GetMapAsync , que obtiene la interfaz GoogleMap que está asociada a la
vista. Una vez que la instancia GoogleMap esté disponible, se invocará la invalidación OnMapReady . Este método
registra un controlador de eventos para el evento InfoWindowClick , que se desencadena cuando se hace clic en la
ventana de información y cuya suscripción solo se cancela cuando cambia el elemento al que está adjunto el
representador. La invalidación OnMapReady también llama al método SetInfoWindowAdapter para especificar que la
instancia de la clase CustomMapRenderer proporcionará los métodos para personalizar la ventana de información.
La clase CustomMapRenderer implementa la interfaz GoogleMap.IInfoWindowAdapter para personalizar la ventana de
información. Esta interfaz especifica que se deben implementar los siguientes métodos:
public Android.Views.View GetInfoWindow(Marker marker) : se llama a este método para devolver una ventana de
información personalizada para un marcador. Si se devuelve null , se usará la representación de la ventana
predeterminada. Si se devuelve un View , View se colocará dentro del marco de la ventana de información.
public Android.Views.View GetInfoContents(Marker marker) : se llama a este método para devolver un View que
contiene el contenido de la ventana de información, y solo se llamará si el método GetInfoWindow devuelve
null . Si devuelve null , se usará la representación predeterminada del contenido de la ventana de
información.
En la aplicación de ejemplo, solo se personaliza el contenido de la ventana de información, de forma que el
método GetInfoWindow devuelve null para habilitar esto.
Personalización del marcador
El icono utilizado para representar un marcador puede personalizarse mediante una llamada al método
MarkerOptions.SetIcon . Esto puede realizarse invalidando el método CreateMarker , que se invoca para cada Pin
que se agrega al mapa:

protected override MarkerOptions CreateMarker(Pin pin)


{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
marker.SetTitle(pin.Label);
marker.SetSnippet(pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
return marker;
}

Este método crea una nueva instancia de MarkerOption para cada instancia de Pin . Después de establecer la
posición, la etiqueta y la dirección del marcador, su icono se establece con el método SetIcon . Este método toma
un objeto BitmapDescriptor que contiene los datos necesarios para representar el icono y la clase
BitmapDescriptorFactory proporciona métodos auxiliares para simplificar la creación de la BitmapDescriptor .
Para obtener más información sobre el uso de la clase BitmapDescriptorFactory para personalizar un marcador,
vea Customizing a Marker (Personalización de un marcador).

NOTE
Si es necesario, el método GetMarkerForPin se puede invocar en el representador de mapa para recuperar un Marker de
una Pin .

Personalización de la ventana de información


Cuando un usuario pulsa en el marcador, el método GetInfoContents se ejecuta, siempre que el método
GetInfoWindow devuelva null . El siguiente ejemplo de código muestra el método GetInfoContents :
public Android.Views.View GetInfoContents (Marker marker)
{
var inflater = Android.App.Application.Context.GetSystemService (Context.LayoutInflaterService) as
Android.Views.LayoutInflater;
if (inflater != null) {
Android.Views.View view;

var customPin = GetCustomPin (marker);


if (customPin == null) {
throw new Exception ("Custom pin not found");
}

if (customPin.Id.ToString() == "Xamarin") {
view = inflater.Inflate (Resource.Layout.XamarinMapInfoWindow, null);
} else {
view = inflater.Inflate (Resource.Layout.MapInfoWindow, null);
}

var infoTitle = view.FindViewById<TextView> (Resource.Id.InfoWindowTitle);


var infoSubtitle = view.FindViewById<TextView> (Resource.Id.InfoWindowSubtitle);

if (infoTitle != null) {
infoTitle.Text = marker.Title;
}
if (infoSubtitle != null) {
infoSubtitle.Text = marker.Snippet;
}

return view;
}
return null;
}

Este método devuelve un View con el contenido de la ventana de información. Esto se logra de la siguiente
manera:
Se recupera una instancia de LayoutInflater . Esta se usa para crear una instancia de un archivo XML de
diseño en su View correspondiente.
Se llama al método GetCustomPin para devolver los datos de marca personalizada para la ventana de
información.
Se aumenta el diseño de XamarinMapInfoWindow si la propiedad CustomPin.Id es igual a Xamarin . En caso
contrario, se aumenta el diseño de MapInfoWindow . Esto permite escenarios donde se pueden mostrar
diferentes diseños de ventana de información para distintos marcadores.
Se recuperan los recursos InfoWindowTitle y InfoWindowSubtitle desde el diseño aumentado y sus
propiedades Text se establecen en los datos correspondientes de la instancia de Marker , siempre que los
recursos no sean null .
La instancia de View se devuelve para su visualización en el mapa.

NOTE
Una ventana de información no es una View dinámica. En su lugar, Android convertirá la View a mapa de bits estático y
la mostrará como una imagen. Esto significa que, mientras que una ventana de información puede responder a un evento
de clic, no puede responder a los eventos de toque o gestos, y los controles individuales en la ventana de información no
pueden responder a sus propios eventos de clic.

Hacer clic en la ventana de información


Cuando el usuario hace clic en la ventana de información, se desencadena el evento InfoWindowClick , que a su
vez ejecuta el método OnInfoWindowClick :
void OnInfoWindowClick (object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin (e.Marker);
if (customPin == null) {
throw new Exception ("Custom pin not found");
}

if (!string.IsNullOrWhiteSpace (customPin.Url)) {
var url = Android.Net.Uri.Parse (customPin.Url);
var intent = new Intent (Intent.ActionView, url);
intent.AddFlags (ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity (intent);
}
}

Este método abre un explorador web y navega a la dirección almacenada en la propiedad Url de la instancia
CustomPin recuperada para el Marker . Tenga en cuenta que la dirección se definió al crear la colección CustomPin
en el proyecto de biblioteca de .NET Standard.
Para obtener más información sobre cómo personalizar una instancia de MapView , vea Using the Google Maps
API in your application (Uso de la API de Google Maps en su aplicación).
Creación de un representador personalizado en la Plataforma universal de Windows
Las siguientes capturas de pantalla muestran el mapa antes y después de la personalización:

En la UWP la marca se denomina icono de mapa y puede ser una imagen personalizada o la imagen
predeterminada definida por el sistema. Un icono de mapa puede mostrar un UserControl , que se muestra en la
respuesta para el usuario que pulsa en el icono de mapa. El UserControl puede mostrar cualquier contenido,
incluyendo las propiedades Label y Address de la instancia Pin .
El siguiente ejemplo de código muestra el representador personalizado de UWP:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace CustomRenderer.UWP
{
public class CustomMapRenderer : MapRenderer
{
MapControl nativeMap;
List<CustomPin> customPins;
XamarinMapOverlay mapOverlay;
bool xamarinOverlayShown = false;

protected override void OnElementChanged(ElementChangedEventArgs<Map> e)


{
base.OnElementChanged(e);

if (e.OldElement != null)
{
nativeMap.MapElementClick -= OnMapElementClick;
nativeMap.Children.Clear();
mapOverlay = null;
nativeMap = null;
}

if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
nativeMap = Control as MapControl;
customPins = formsMap.CustomPins;

nativeMap.Children.Clear();
nativeMap.MapElementClick += OnMapElementClick;

foreach (var pin in customPins)


{
var snPosition = new BasicGeoposition { Latitude = pin.Pin.Position.Latitude, Longitude =
pin.Pin.Position.Longitude };
var snPoint = new Geopoint(snPosition);

var mapIcon = new MapIcon();


mapIcon.Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///pin.png"));
mapIcon.CollisionBehaviorDesired = MapElementCollisionBehavior.RemainVisible;
mapIcon.Location = snPoint;
mapIcon.NormalizedAnchorPoint = new Windows.Foundation.Point(0.5, 1.0);

nativeMap.MapElements.Add(mapIcon);
}
}
}
...
}
}

El método OnElementChanged realiza las siguientes operaciones, siempre que se adjunte el presentador
personalizado a un nuevo elemento de Xamarin.Forms:
Borra la colección de MapControl.Children para quitar los elementos de interfaz de usuario existentes del
mapa y después registra un controlador de eventos para el evento de MapElementClick . Este evento se
desencadena cuando el usuario pulsa o hace clic en un MapElement en el MapControl y solo se cancela su
suscripción cuando cambia el elemento al que está adjunto el representador.
Cada marca en la colección de customPins se muestra en la ubicación geográfica correcta en el mapa como
sigue:
La ubicación para la marca se crea como una instancia de Geopoint .
Una instancia de MapIcon se crea para representar la marca.
La imagen utilizada para representar el MapIcon se especifica estableciendo la propiedad
MapIcon.Image . Con todo, no siempre se puede garantizar que se muestre la imagen del icono de mapa,
ya que puede estar ocultada por otros elementos del mapa. Por lo tanto, la propiedad
CollisionBehaviorDesired del icono del mapa se establece en
MapElementCollisionBehavior.RemainVisible , para asegurarse de que está visible.
La ubicación del MapIcon se especifica configurando la propiedad MapIcon.Location .
La propiedad MapIcon.NormalizedAnchorPoint se establece en la ubicación aproximada del puntero en la
imagen. Si esta propiedad conserva su valor predeterminado de (0,0), que representa la esquina
superior izquierda de la imagen, los cambios en el nivel de zoom del mapa pueden dar lugar a que la
imagen apunte a una ubicación distinta.
La instancia MapIcon se agrega a la colección MapControl.MapElements . Esto da como resultado que el
icono de mapa se muestre en el MapControl .

NOTE
Cuando se usa la misma imagen para varios iconos de mapa, la instancia de RandomAccessStreamReference debe
declararse en el nivel de página o aplicación para mejorar el rendimiento.

Mostrar el UserControl
El método OnMapElementClick se ejecuta cuando un usuario pulsa en el icono de mapa. El siguiente ejemplo de
código muestra este método:

private void OnMapElementClick(MapControl sender, MapElementClickEventArgs args)


{
var mapIcon = args.MapElements.FirstOrDefault(x => x is MapIcon) as MapIcon;
if (mapIcon != null)
{
if (!xamarinOverlayShown)
{
var customPin = GetCustomPin(mapIcon.Location.Position);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}

if (customPin.Id.ToString() == "Xamarin")
{
if (mapOverlay == null)
{
mapOverlay = new XamarinMapOverlay(customPin);
}

var snPosition = new BasicGeoposition { Latitude = customPin.Pin.Position.Latitude, Longitude


= customPin.Pin.Position.Longitude };
var snPoint = new Geopoint(snPosition);

nativeMap.Children.Add(mapOverlay);
MapControl.SetLocation(mapOverlay, snPoint);
MapControl.SetNormalizedAnchorPoint(mapOverlay, new Windows.Foundation.Point(0.5, 1.0));
xamarinOverlayShown = true;
}
}
else
{
nativeMap.Children.Remove(mapOverlay);
xamarinOverlayShown = false;
}
}
}
Este método crea una instancia de UserControl que muestra información sobre la marca. Esto se logra de la
siguiente manera:
Se recupera la instancia de MapIcon .
Se llama al método GetCustomPin para devolver los datos de marca personalizada que se mostrarán.
Se crea una instancia de XamarinMapOverlay para mostrar los datos de marca personalizada. Esta clase es un
control de usuario.
Se crea la ubicación geográfica en la que se mostrará la instancia de XamarinMapOverlay en el MapControl
como una instancia de Geopoint .
La instancia XamarinMapOverlay se agrega a la colección MapControl.Children . Esta colección contiene
elementos de interfaz de usuario de XAML que se mostrarán en el mapa.
La ubicación geográfica de la instancia de XamarinMapOverlay en el mapa se establece mediante una llamada al
método SetLocation .
La ubicación relativa de la instancia de XamarinMapOverlay que corresponde a la ubicación especificada se
establece mediante una llamada al método SetNormalizedAnchorPoint . Esto garantiza que los cambios en el
nivel de zoom del mapa tendrán como resultado que la instancia de XamarinMapOverlay siempre se muestre en
la ubicación correcta.
Como alternativa, si ya se muestra información sobre la marca en el mapa, pulsar en el mapa quita la instancia de
XamarinMapOverlay de la colección de MapControl.Children .

Pulsar en el botón Información


Cuando el usuario pulsa el botón Información en el control de usuario de XamarinMapOverlay , se desencadena el
evento Tapped , que a su vez ejecuta el método OnInfoButtonTapped :

private async void OnInfoButtonTapped(object sender, TappedRoutedEventArgs e)


{
await Launcher.LaunchUriAsync(new Uri(customPin.Url));
}

Este método abre un explorador web y navega a la dirección almacenada en la propiedad Url de la instancia de
CustomPin . Tenga en cuenta que la dirección se definió al crear la colección CustomPin en el proyecto de
biblioteca de .NET Standard.
Para obtener más información sobre cómo personalizar una instancia de MapControl , vea Introducción a
ubicación y mapas en MSDN.

Resumen
En este artículo se muestra cómo crear un representador personalizado para el control Map , lo que permite que
los desarrolladores reemplacen la representación nativa de forma predeterminada con su propia personalización
específica de la plataforma. Xamarin.Forms.Maps proporciona una abstracción multiplataforma para mostrar
mapas que usan la API de mapa nativo en cada plataforma y proporcionar una experiencia de mapa rápida y
familiar para los usuarios.

Vínculos relacionados
Xamarin.Forms Map (Mapa de Xamarin.Forms)
Maps in Xamarin.iOS (Mapas en Xamarin.iOS )
API de Maps
Customized Pin (sample) (Marca personalizada [ejemplo])
Resaltado de un área circular en un mapa
11/07/2019 • 10 minutes to read • Edit Online

Descargar el ejemplo
En este artículo se explica cómo agregar una superposición circular a un mapa, para resaltar un área circular del
mapa.

Información general
Una superposición es un gráfico superpuesto en una capa en un mapa. Las superposiciones admiten dibujar
contenido gráfico que se escala con el mapa a medida que se amplía. En las capturas de pantalla siguientes se
muestra el resultado de agregar una superposición circular a un mapa:

Cuando una aplicación de Xamarin.Forms representa un control Map , en iOS se crea la instancia de la clase
MapRenderer , que a su vez crea una instancia del control MKMapView nativo. En la plataforma de Android, la clase
MapRenderer crea una instancia de un control MapView nativo. En la Plataforma universal de Windows ( UWP ), la
clase MapRenderer crea una instancia de un elemento MapControl nativo. El proceso de representación se puede
aprovechar para implementar personalizaciones de mapa específicas de cada plataforma mediante la creación de
un representador personalizado para un elemento Map en cada plataforma. Para hacerlo, siga este procedimiento:
1. Cree un mapa personalizado de Xamarin.Forms.
2. Use el mapa personalizado desde Xamarin.Forms.
3. Personalice el mapa; para hacerlo, cree un representador personalizado para el mapa en cada plataforma.

NOTE
Xamarin.Forms.Maps debe inicializarse y configurarse antes de su uso. Para obtener más información, consulte
Maps Control

Para obtener información sobre cómo personalizar un mapa mediante un representador personalizado, consulte
Personalización de un anclado de mapa.
Creación del mapa personalizado
Cree una clase CustomCircle que tenga las propiedades Position y Radius :

public class CustomCircle


{
public Position Position { get; set; }
public double Radius { get; set; }
}

A continuación, cree una subclase de la clase Map que agregue una propiedad de tipo CustomCircle :

public class CustomMap : Map


{
public CustomCircle Circle { get; set; }
}

Consumo del mapa personalizado


Para usar el control CustomMap , declare una instancia de este en la instancia de la página de XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MapOverlay;assembly=MapOverlay"
x:Class="MapOverlay.MapPage">
<ContentPage.Content>
<local:CustomMap x:Name="customMap" MapType="Street" WidthRequest="{x:Static local:App.ScreenWidth}"
HeightRequest="{x:Static local:App.ScreenHeight}" />
</ContentPage.Content>
</ContentPage>

También puede usar el control CustomMap mediante la declaración de una instancia de este en la instancia de la
página de C#:

public class MapPageCS : ContentPage


{
public MapPageCS ()
{
var customMap = new CustomMap {
MapType = MapType.Street,
WidthRequest = App.ScreenWidth,
HeightRequest = App.ScreenHeight
};
...
Content = customMap;
}
}

Inicialice el control CustomMap como sea necesario:


public partial class MapPage : ContentPage
{
public MapPage ()
{
...
var pin = new Pin {
Type = PinType.Place,
Position = new Position (37.79752, -122.40183),
Label = "Xamarin San Francisco Office",
Address = "394 Pacific Ave, San Francisco CA"
};

var position = new Position (37.79752, -122.40183);


customMap.Circle = new CustomCircle {
Position = position,
Radius = 1000
};

customMap.Pins.Add (pin);
customMap.MoveToRegion (MapSpan.FromCenterAndRadius (position, Distance.FromMiles (1.0)));
}
}

Esta inicialización agrega instancias de Pin y CustomCircle al mapa personalizado y coloca la vista del mapa con
el método MoveToRegion , que cambia la posición y el nivel de zoom del mapa mediante la creación de un MapSpan
desde una Position y una Distance .
Personalizar el mapa
Ahora debe agregarse un representador personalizado a cada proyecto de aplicación para agregar la
superposición circular al mapa.
Creación del representador personalizado en iOS
Cree una subclase de la clase MapRenderer e invalide su método OnElementChanged para agregar la superposición
circular:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapOverlay.iOS
{
public class CustomMapRenderer : MapRenderer
{
MKCircleRenderer circleRenderer;

protected override void OnElementChanged(ElementChangedEventArgs<View> e)


{
base.OnElementChanged(e);

if (e.OldElement != null) {
var nativeMap = Control as MKMapView;
if (nativeMap != null) {
nativeMap.RemoveOverlays(nativeMap.Overlays);
nativeMap.OverlayRenderer = null;
circleRenderer = null;
}
}

if (e.NewElement != null) {
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MKMapView;
var circle = formsMap.Circle;

nativeMap.OverlayRenderer = GetOverlayRenderer;

var circleOverlay = MKCircle.Circle(new


CoreLocation.CLLocationCoordinate2D(circle.Position.Latitude, circle.Position.Longitude), circle.Radius);
nativeMap.AddOverlay(circleOverlay);
}
}
...
}
}

Este método realiza la configuración siguiente, siempre que el representador personalizado se adjunte a un
elemento de Xamarin.Forms nuevo:
La propiedad MKMapView.OverlayRenderer se establece en un delegado correspondiente.
Para crear el círculo, se debe establecer un objeto MKCircle estático que especifica el centro del círculo y su
radio en metros.
Para agregar el círculo al mapa, llame al método MKMapView.AddOverlay .

A continuación, implemente el método GetOverlayRenderer para personalizar la representación de la


superposición:
public class CustomMapRenderer : MapRenderer
{
MKCircleRenderer circleRenderer;
...

MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)


{
if (circleRenderer == null && !Equals(overlayWrapper, null)) {
var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
circleRenderer = new MKCircleRenderer(overlay as MKCircle) {
FillColor = UIColor.Red,
Alpha = 0.4f
};
}
return circleRenderer;
}
}

Creación del representador personalizado en Android


Cree una subclase de la clase MapRenderer e invalide sus métodos OnElementChanged y OnMapReady para agregar la
superposición circular:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapOverlay.Droid
{
public class CustomMapRenderer : MapRenderer
{
CustomCircle circle;

public CustomMapRenderer(Context context) : base(context)


{
}

protected override void


OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.Maps.Map> e)
{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// Unsubscribe
}

if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
circle = formsMap.Circle;
Control.GetMapAsync(this);
}
}

protected override void OnMapReady(Android.Gms.Maps.GoogleMap map)


{
base.OnMapReady(map);

var circleOptions = new CircleOptions();


circleOptions.InvokeCenter(new LatLng(circle.Position.Latitude, circle.Position.Longitude));
circleOptions.InvokeRadius(circle.Radius);
circleOptions.InvokeFillColor(0X66FF0000);
circleOptions.InvokeStrokeColor(0X66FF0000);
circleOptions.InvokeStrokeWidth(0);

NativeMap.AddCircle(circleOptions);
}
}
}

El método OnElementChanged llama al método MapView.GetMapAsync , que obtiene el GoogleMap subyacente asociado
a la vista, siempre que el representador personalizado se adjunte a un nuevo elemento de Xamarin.Forms. Una vez
la instancia de GoogleMap está disponible, se invoca el método OnMapReady , en que el círculo se crea mediante la
instancia de un objeto CircleOptions que especifica el centro del círculo y su radio en metros. A continuación, se
llama al método NativeMap.AddCircle para agregar el círculo al mapa.
Creación de un representador personalizado en la Plataforma universal de Windows
Cree una subclase de la clase MapRenderer e invalide su método OnElementChanged para agregar la superposición
circular:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapOverlay.UWP
{
public class CustomMapRenderer : MapRenderer
{
const int EarthRadiusInMeteres = 6371000;

protected override void OnElementChanged(ElementChangedEventArgs<Map> e)


{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MapControl;
var circle = formsMap.Circle;

var coordinates = new List<BasicGeoposition>();


var positions = GenerateCircleCoordinates(circle.Position, circle.Radius);
foreach (var position in positions)
{
coordinates.Add(new BasicGeoposition { Latitude = position.Latitude, Longitude =
position.Longitude });
}

var polygon = new MapPolygon();


polygon.FillColor = Windows.UI.Color.FromArgb(128, 255, 0, 0);
polygon.StrokeColor = Windows.UI.Color.FromArgb(128, 255, 0, 0);
polygon.StrokeThickness = 5;
polygon.Path = new Geopath(coordinates);
nativeMap.MapElements.Add(polygon);
}
}
// GenerateCircleCoordinates helper method (below)
}
}

Este método realiza las operaciones siguientes, siempre que el representador personalizado se adjunte a un nuevo
elemento de Xamarin.Forms:
La posición del círculo y el radio se recuperan de la propiedad CustomMap.Circle y se pasan al método
GenerateCircleCoordinates , que genera las coordenadas de latitud y longitud para el perímetro del círculo. El
código para este método auxiliar se muestra a continuación.
Las coordenadas del perímetro del círculo se convierten en coordenadas de una List de BasicGeoposition .
El círculo se crea mediante la instancia de un objeto MapPolygon . La clase MapPolygon se utiliza para mostrar
una forma de varios puntos en el mapa; para ello, se establece su propiedad Path en un objeto Geopath que
contiene las coordenadas de la forma.
Para representar el polígono en el mapa, se agrega a la colección MapControl.MapElements .
List<Position> GenerateCircleCoordinates(Position position, double radius)
{
double latitude = position.Latitude.ToRadians();
double longitude = position.Longitude.ToRadians();
double distance = radius / EarthRadiusInMeteres;
var positions = new List<Position>();

for (int angle = 0; angle <=360; angle++)


{
double angleInRadians = ((double)angle).ToRadians();
double latitudeInRadians = Math.Asin(Math.Sin(latitude) * Math.Cos(distance) + Math.Cos(latitude) *
Math.Sin(distance) * Math.Cos(angleInRadians));
double longitudeInRadians = longitude + Math.Atan2(Math.Sin(angleInRadians) * Math.Sin(distance) *
Math.Cos(latitude), Math.Cos(distance) - Math.Sin(latitude) * Math.Sin(latitudeInRadians));

var pos = new Position(latitudeInRadians.ToDegrees(), longitudeInRadians.ToDegrees());


positions.Add(pos);
}

return positions;
}

Resumen
En este artículo se explica cómo agregar una superposición circular a un mapa, para resaltar un área circular del
mapa.

Vínculos relacionados
Superposición de mapa circular (ejemplo)
Personalización de un anclado de mapa
Xamarin.Forms.Maps
Resaltado de una región en un mapa
11/07/2019 • 10 minutes to read • Edit Online

Descargar el ejemplo
En este artículo se explica cómo agregar una superposición de polígono a un mapa para resaltar una región del
mapa. Los polígonos son una forma cerrada y tienen relleno en su interior.

Información general
Una superposición es un gráfico superpuesto en una capa en un mapa. Las superposiciones permiten dibujar
contenido gráfico que se escala con el mapa al ampliarlo o reducirlo. En las capturas de pantalla siguientes, se
muestra el resultado de agregar una superposición de polígono a un mapa:

Cuando se representa un control Map mediante una aplicación de Xamarin.Forms, se crea una instancia de la clase
MapRenderer en iOS que, a su vez, crea una instancia de un control MKMapView nativo. En la plataforma de Android,
la clase MapRenderer crea una instancia de un control MapView nativo. En la Plataforma universal de Windows
(UWP ), la clase MapRenderer crea una instancia de un elemento MapControl nativo. El proceso de representación
se puede aprovechar para implementar personalizaciones de mapa específicas de cada plataforma mediante la
creación de un representador personalizado para un elemento Map en cada plataforma. Para hacerlo, siga este
procedimiento:
1. Cree un mapa personalizado de Xamarin.Forms.
2. Use el mapa personalizado desde Xamarin.Forms.
3. Personalice el mapa; para hacerlo, cree un representador personalizado para el mapa en cada plataforma.

NOTE
Xamarin.Forms.Maps tiene que inicializarse y configurarse antes de cada uso. Para obtener más información, vea
Maps Control .

Para obtener información sobre cómo personalizar un mapa mediante un representador personalizado, vea
Customizing a Map Pin (Personalizar un marcador de mapa).
Crear un mapa personalizado
Cree una subclase de la clase Map que agregue una propiedad ShapeCoordinates :

public class CustomMap : Map


{
public List<Position> ShapeCoordinates { get; set; }

public CustomMap ()
{
ShapeCoordinates = new List<Position> ();
}
}

La propiedad ShapeCoordinates almacenará una colección de coordenadas que definen la región que se resaltará.
Usar el mapa personalizado
Para usar el control CustomMap , declare una instancia de este en la instancia de la página de XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MapOverlay;assembly=MapOverlay"
x:Class="MapOverlay.MapPage">
<ContentPage.Content>
<local:CustomMap x:Name="customMap" MapType="Street" WidthRequest="{x:Static local:App.ScreenWidth}"
HeightRequest="{x:Static local:App.ScreenHeight}" />
</ContentPage.Content>
</ContentPage>

También puede usar el control CustomMap mediante la declaración de una instancia de este en la instancia de la
página de C#:

public class MapPageCS : ContentPage


{
public MapPageCS ()
{
var customMap = new CustomMap {
MapType = MapType.Street,
WidthRequest = App.ScreenWidth,
HeightRequest = App.ScreenHeight
};
...
Content = customMap;
}
}

Inicialice el control CustomMap según sea necesario:

public partial class MapPage : ContentPage


{
public MapPage ()
{
...
customMap.ShapeCoordinates.Add (new Position (37.797513, -122.402058));
customMap.ShapeCoordinates.Add (new Position (37.798433, -122.402256));
customMap.ShapeCoordinates.Add (new Position (37.798582, -122.401071));
customMap.ShapeCoordinates.Add (new Position (37.797658, -122.400888));

customMap.MoveToRegion (MapSpan.FromCenterAndRadius (new Position (37.79752, -122.40183),


Distance.FromMiles (0.1)));
}
}
Esta inicialización especifica una serie de coordenadas de latitud y longitud para definir la región que se resaltará
en el mapa. Después, coloque la vista del mapa con el método MoveToRegion , que cambia la posición y el nivel de
zoom del mapa mediante la creación de un elemento MapSpan desde los elementos Position y Distance .
Personalización del mapa
Ahora, es necesario agregar un representador personalizado a cada proyecto de aplicación para agregar la
superposición de polígono al mapa.
Creación del representador personalizado en iOS
Cree una subclase de la clase MapRenderer y reemplace su método OnElementChanged para agregar la
superposición de polígono:

[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]


namespace MapOverlay.iOS
{
public class CustomMapRenderer : MapRenderer
{
MKPolygonRenderer polygonRenderer;

protected override void OnElementChanged(ElementChangedEventArgs<View> e)


{
base.OnElementChanged(e);

if (e.OldElement != null) {
var nativeMap = Control as MKMapView;
if (nativeMap != null) {
nativeMap.RemoveOverlays(nativeMap.Overlays);
nativeMap.OverlayRenderer = null;
polygonRenderer = null;
}
}

if (e.NewElement != null) {
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MKMapView;

nativeMap.OverlayRenderer = GetOverlayRenderer;

CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[formsMap.ShapeCoordinates.Count];

int index = 0;
foreach (var position in formsMap.ShapeCoordinates)
{
coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
index++;
}

var blockOverlay = MKPolygon.FromCoordinates(coords);


nativeMap.AddOverlay(blockOverlay);
}
}
...
}
}

Este método realiza la siguiente configuración, siempre que el representador personalizado esté asociado a un
nuevo elemento de Xamarin.Forms:
La propiedad MKMapView.OverlayRenderer se establece en un delegado correspondiente.
La colección de coordenadas de latitud y longitud se recupera desde la propiedad CustomMap.ShapeCoordinates y
se almacena como una matriz de instancias de CLLocationCoordinate2D .
Para crear el polígono, se realiza una llamada al método MKPolygon.FromCoordinates estático, que especifica la
latitud y longitud de cada punto.
El polígono se agrega al mapa mediante una llamada al método MKMapView.AddOverlay . Este método cierra
automáticamente el polígono dibujando una línea que conecta el primer y el último punto.
Después, se implementa el método GetOverlayRenderer para personalizar la representación de la superposición:

public class CustomMapRenderer : MapRenderer


{
MKPolygonRenderer polygonRenderer;
...

MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)


{
if (polygonRenderer == null && !Equals(overlayWrapper, null)) {
var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
polygonRenderer = new MKPolygonRenderer(overlay as MKPolygon) {
FillColor = UIColor.Red,
StrokeColor = UIColor.Blue,
Alpha = 0.4f,
LineWidth = 9
};
}
return polygonRenderer;
}
}

Creación del representador personalizado en Android


Cree una subclase de la clase MapRenderer y reemplace sus métodos OnElementChanged y OnMapReady para agregar
la superposición de polígono:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapOverlay.Droid
{
public class CustomMapRenderer : MapRenderer
{
List<Position> shapeCoordinates;

public CustomMapRenderer(Context context) : base(context)


{
}

protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map>


e)
{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// Unsubscribe
}

if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
shapeCoordinates = formsMap.ShapeCoordinates;
Control.GetMapAsync(this);
}
}

protected override void OnMapReady(Android.Gms.Maps.GoogleMap map)


{
base.OnMapReady(map);

var polygonOptions = new PolygonOptions();


polygonOptions.InvokeFillColor(0x66FF0000);
polygonOptions.InvokeStrokeColor(0x660000FF);
polygonOptions.InvokeStrokeWidth(30.0f);

foreach (var position in shapeCoordinates)


{
polygonOptions.Add(new LatLng(position.Latitude, position.Longitude));
}
NativeMap.AddPolygon(polygonOptions);
}
}
}

El método OnElementChanged recupera la colección de coordenadas de latitud y longitud de la propiedad


CustomMap.ShapeCoordinates y las almacena en una variable miembro. Después, realiza una llamada al método
MapView.GetMapAsync , que obtiene el GoogleMap subyacente vinculado a la vista, siempre que el representador
personalizado esté asociado a un nuevo elemento de Xamarin.Forms. Cuando la instancia de GoogleMap esté
disponible, se invocará al método OnMapReady , que genera el polígono mediante la creación de una instancia de un
objeto PolygonOptions que especifica la latitud y longitud de cada punto. Después el polígono se agrega al mapa
mediante una llamada al método NativeMap.AddPolygon . Este método cierra automáticamente el polígono
dibujando una línea que conecta el primer y el último punto.
Creación del representador personalizado en la Plataforma universal de Windows
Cree una subclase de la clase MapRenderer y reemplace su método OnElementChanged para agregar la
superposición de polígono:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapOverlay.UWP
{
public class CustomMapRenderer : MapRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// Unsubscribe
}

if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MapControl;

var coordinates = new List<BasicGeoposition>();


foreach (var position in formsMap.ShapeCoordinates)
{
coordinates.Add(new BasicGeoposition() { Latitude = position.Latitude, Longitude =
position.Longitude });
}

var polygon = new MapPolygon();


polygon.FillColor = Windows.UI.Color.FromArgb(128, 255, 0, 0);
polygon.StrokeColor = Windows.UI.Color.FromArgb(128, 0, 0, 255);
polygon.StrokeThickness = 5;
polygon.Path = new Geopath(coordinates);
nativeMap.MapElements.Add(polygon);
}
}
}
}

Este método realiza las operaciones siguientes, siempre que el representador personalizado esté asociado a un
nuevo elemento de Xamarin.Forms:
La colección de coordenadas de latitud y longitud se recupera desde la propiedad CustomMap.ShapeCoordinates y
se convierte a un elemento List de coordenadas de BasicGeoposition .
El polígono se genera mediante la creación de una instancia de un objeto MapPolygon . La clase MapPolygon se
utiliza para mostrar una forma de varios puntos en el mapa; para ello, se establece su propiedad Path en un
objeto Geopath que contiene las coordenadas de la forma.
Para representar el polígono en el mapa, se agrega a la colección MapControl.MapElements . Tenga en cuenta que
el polígono se cerrará automáticamente dibujando una línea que conecta el primer y el último punto.

Resumen
En este artículo se explicó cómo agregar una superposición de polígono a un mapa para resaltar una región del
mapa. Los polígonos son una forma cerrada y tienen relleno en su interior.

Vínculos relacionados
Polygon Map Overlay (sample) (Superposición de mapa de polígono [ejemplo])
Personalización de un anclado de mapa
Xamarin.Forms.Maps
Resaltar una ruta en un mapa
11/07/2019 • 10 minutes to read • Edit Online

Descargar el ejemplo
En este artículo, se explica cómo agregar una superposición de polilínea a un mapa. Una superposición de
polilínea es una serie de segmentos de línea conectados que suelen usarse para mostrar una ruta en un mapa o
para componer cualquier forma necesaria.

Información general
Una superposición es un gráfico superpuesto en una capa en un mapa. Las superposiciones permiten dibujar
contenido gráfico que se escala con el mapa al ampliarlo o reducirlo. En las capturas de pantalla siguientes, se
muestra el resultado de agregar una superposición de polilínea a un mapa:

Cuando se representa un control Map mediante una aplicación de Xamarin.Forms, se crea una instancia de la clase
MapRenderer en iOS que, a su vez, crea una instancia de un control MKMapView nativo. En la plataforma de Android,
la clase MapRenderer crea una instancia de un control MapView nativo. En la Plataforma universal de Windows
(UWP ), la clase MapRenderer crea una instancia de un elemento MapControl nativo. El proceso de representación
se puede aprovechar para implementar personalizaciones de mapa específicas de cada plataforma mediante la
creación de un representador personalizado para un elemento Map en cada plataforma. Para hacerlo, siga este
procedimiento:
1. Cree un mapa personalizado de Xamarin.Forms.
2. Use el mapa personalizado desde Xamarin.Forms.
3. Personalice el mapa; para hacerlo, cree un representador personalizado para el mapa en cada plataforma.

NOTE
Xamarin.Forms.Maps tiene que inicializarse y configurarse antes de cada uso. Para obtener más información, vea
Maps Control .

Para obtener información sobre cómo personalizar un mapa mediante un representador personalizado, vea
Customizing a Map Pin (Personalizar un marcador de mapa).
Crear un mapa personalizado
Cree una subclase de la clase Map que agregue una propiedad RouteCoordinates :

public class CustomMap : Map


{
public List<Position> RouteCoordinates { get; set; }

public CustomMap ()
{
RouteCoordinates = new List<Position> ();
}
}

La propiedad RouteCoordinates almacenará una colección de coordenadas que definen la ruta que se resaltará.
Usar el mapa personalizado
Para usar el control CustomMap , declare una instancia de este en la instancia de la página de XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MapOverlay;assembly=MapOverlay"
x:Class="MapOverlay.MapPage">
<ContentPage.Content>
<local:CustomMap x:Name="customMap" MapType="Street" WidthRequest="{x:Static local:App.ScreenWidth}"
HeightRequest="{x:Static local:App.ScreenHeight}" />
</ContentPage.Content>
</ContentPage>

También puede usar el control CustomMap mediante la declaración de una instancia de este en la instancia de la
página de C#:

public class MapPageCS : ContentPage


{
public MapPageCS ()
{
var customMap = new CustomMap {
MapType = MapType.Street,
WidthRequest = App.ScreenWidth,
HeightRequest = App.ScreenHeight
};
...
Content = customMap;
}
}

Inicialice el control CustomMap según sea necesario:


public partial class MapPage : ContentPage
{
public MapPage ()
{
...
customMap.RouteCoordinates.Add (new Position (37.785559, -122.396728));
customMap.RouteCoordinates.Add (new Position (37.780624, -122.390541));
customMap.RouteCoordinates.Add (new Position (37.777113, -122.394983));
customMap.RouteCoordinates.Add (new Position (37.776831, -122.394627));

customMap.MoveToRegion (MapSpan.FromCenterAndRadius (new Position (37.79752, -122.40183),


Distance.FromMiles (1.0)));
}
}

Esta inicialización especifica una serie de coordenadas de latitud y longitud para definir la ruta que se resaltará en
el mapa. Después, coloca la vista del mapa con el método MoveToRegion , que cambia la posición y el nivel de zoom
del mapa mediante la creación de un elemento MapSpan desde los elementos Position y Distance .
Personalizar el mapa
Ahora, es necesario agregar un representador personalizado a cada proyecto de aplicación para agregar la
superposición de polilínea al mapa.
Crear un representador personalizado en iOS
Cree una subclase de la clase MapRenderer y reemplace su método OnElementChanged para agregar la
superposición de polilínea:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapOverlay.iOS
{
public class CustomMapRenderer : MapRenderer
{
MKPolylineRenderer polylineRenderer;

protected override void OnElementChanged(ElementChangedEventArgs<View> e)


{
base.OnElementChanged(e);

if (e.OldElement != null) {
var nativeMap = Control as MKMapView;
if (nativeMap != null) {
nativeMap.RemoveOverlays(nativeMap.Overlays);
nativeMap.OverlayRenderer = null;
polylineRenderer = null;
}
}

if (e.NewElement != null) {
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MKMapView;
nativeMap.OverlayRenderer = GetOverlayRenderer;

CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[formsMap.RouteCoordinates.Count];


int index = 0;
foreach (var position in formsMap.RouteCoordinates)
{
coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
index++;
}

var routeOverlay = MKPolyline.FromCoordinates(coords);


nativeMap.AddOverlay(routeOverlay);
}
}
...
}
}

Este método realiza la siguiente configuración, siempre que el representador personalizado esté asociado a un
nuevo elemento de Xamarin.Forms:
La propiedad MKMapView.OverlayRenderer se establece en un delegado correspondiente.
La colección de coordenadas de latitud y longitud se recupera desde la propiedad CustomMap.RouteCoordinates y
se almacena como una matriz de instancias de CLLocationCoordinate2D .
Para crear la polilínea, se realiza una llamada al método MKPolyline.FromCoordinates estático, que especifica la
latitud y longitud de cada punto.
La polilínea se agrega al mapa mediante una llamada al método MKMapView.AddOverlay .

Después, se implementa el método GetOverlayRenderer para personalizar la representación de la superposición:


public class CustomMapRenderer : MapRenderer
{
MKPolylineRenderer polylineRenderer;
...

MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)


{
if (polylineRenderer == null && !Equals(overlayWrapper, null)) {
var overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
polylineRenderer = new MKPolylineRenderer(overlay as MKPolyline) {
FillColor = UIColor.Blue,
StrokeColor = UIColor.Red,
LineWidth = 3,
Alpha = 0.4f
};
}
return polylineRenderer;
}
}

Crear un representador personalizado en Android


Cree una subclase de la clase MapRenderer y reemplace sus métodos OnElementChanged y OnMapReady para agregar
la superposición de polilínea:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapOverlay.Droid
{
public class CustomMapRenderer : MapRenderer
{
List<Position> routeCoordinates;

public CustomMapRenderer(Context context) : base(context)


{
}

protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map>


e)
{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// Unsubscribe
}

if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
routeCoordinates = formsMap.RouteCoordinates;
Control.GetMapAsync(this);
}
}

protected override void OnMapReady(Android.Gms.Maps.GoogleMap map)


{
base.OnMapReady(map);

var polylineOptions = new PolylineOptions();


polylineOptions.InvokeColor(0x66FF0000);

foreach (var position in routeCoordinates)


{
polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
}

NativeMap.AddPolyline(polylineOptions);
}
}
}

El método OnElementChanged recupera la colección de coordenadas de latitud y longitud de la propiedad


CustomMap.RouteCoordinates y las almacena en una variable miembro. Después, realiza una llamada al método
MapView.GetMapAsync , que obtiene el elemento GoogleMap subyacente vinculado a la vista, siempre que el
representador personalizado esté asociado a un nuevo elemento de Xamarin.Forms. Cuando la instancia de
GoogleMap esté disponible, se invocará al método OnMapReady , que genera la polilínea mediante la creación de una
instancia de un objeto PolylineOptions que especifica la latitud y longitud de cada punto. Después, la polilínea se
agrega al mapa mediante una llamada al método NativeMap.AddPolyline .
Crear el representador personalizado en la Plataforma universal de Windows
Cree una subclase de la clase MapRenderer y reemplace su método OnElementChanged para agregar la
superposición de polilínea:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace MapOverlay.UWP
{
public class CustomMapRenderer : MapRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// Unsubscribe
}

if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MapControl;

var coordinates = new List<BasicGeoposition>();


foreach (var position in formsMap.RouteCoordinates)
{
coordinates.Add(new BasicGeoposition() { Latitude = position.Latitude, Longitude =
position.Longitude });
}

var polyline = new MapPolyline();


polyline.StrokeColor = Windows.UI.Color.FromArgb(128, 255, 0, 0);
polyline.StrokeThickness = 5;
polyline.Path = new Geopath(coordinates);
nativeMap.MapElements.Add(polyline);
}
}
}
}

Este método realiza las operaciones siguientes, siempre que el representador personalizado esté asociado a un
nuevo elemento de Xamarin.Forms:
La colección de coordenadas de latitud y longitud se recupera desde la propiedad CustomMap.RouteCoordinates y
se convierte a un elemento List de coordenadas de BasicGeoposition .
La polilínea se genera mediante la creación de una instancia de un objeto MapPolyline . La clase MapPolygon se
usa para mostrar una línea en el mapa mediante el ajuste de su propiedad Path en un objeto Geopath que
contiene las coordenadas de línea.
Para representar la polilínea en el mapa, se agrega a la colección MapControl.MapElements .

Resumen
En este artículo, se ha explicado cómo agregar una superposición de polilínea a un mapa para mostrar una ruta en
un mapa o para componer cualquier forma necesaria.

Vínculos relacionados
Superposición de mapa de polilínea (ejemplo)
Personalización de un anclado de mapa
Xamarin.Forms.Maps
Personalización de una ListView
11/07/2019 • 28 minutes to read • Edit Online

Descargar el ejemplo
Una ListView de Xamarin.Forms es una vista que muestra una colección de datos como una lista vertical. En este
artículo se muestra cómo crear un representador personalizado que encapsula los controles de lista específica de
la plataforma y los diseños de celda nativa, lo que permite tener más control sobre el rendimiento del control de
lista nativa.
Todas las vistas de Xamarin.Forms tienen un representador adjunto para cada plataforma que crea una instancia
de un control nativo. Cuando una aplicación de Xamarin.Forms representa una ListView , en iOS se crea la
instancia de la clase ListViewRenderer , que a su vez crea una instancia del control UITableView nativo. En la
plataforma de Android, la clase ListViewRenderer crea una instancia de un control ListView nativo. En
Plataforma universal de Windows (UWP ), la clase ListViewRenderer crea una instancia de un control ListView
nativo. Para obtener más información sobre el representador y las clases de control nativo a las que se asignan los
controles de Xamarin.Forms, vea Renderer Base Classes and Native Controls (Clases base y controles nativos del
representador).
El siguiente diagrama muestra la relación entre el control ListView y los controles nativos correspondientes que
lo implementan:

El proceso de representación puede aprovecharse para implementar las personalizaciones específicas de la


plataforma creando un representador personalizado para una ListView en cada plataforma. Para hacerlo, siga
este procedimiento:
1. Cree un control personalizado de Xamarin.Forms.
2. Use el control personalizado de Xamarin.Forms.
3. Cree el representador personalizado para el control en cada plataforma.
Ahora se analizará en detalle cada elemento, para implementar un representador NativeListView que aproveche
las ventajas de los diseños de celda nativos y los controles de lista específicos de la plataforma. Este escenario es
útil al migrar una aplicación nativa existente que contiene la lista y el código de la celda que se puede volver a usar.
Además, permite la personalización detallada de las características de control de lista que pueden afectar al
rendimiento, como la virtualización de datos.

Crear el control ListView personalizado


Se puede crear un control personalizado ListView mediante la creación de subclases de la clase ListView , como
se muestra en el siguiente ejemplo de código:

public class NativeListView : ListView


{
public static readonly BindableProperty ItemsProperty =
BindableProperty.Create ("Items", typeof(IEnumerable<DataSource>), typeof(NativeListView), new
List<DataSource> ());

public IEnumerable<DataSource> Items {


get { return (IEnumerable<DataSource>)GetValue (ItemsProperty); }
set { SetValue (ItemsProperty, value); }
}

public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;

public void NotifyItemSelected (object item)


{
if (ItemSelected != null) {
ItemSelected (this, new SelectedItemChangedEventArgs (item));
}
}
}

El NativeListView se crea en el proyecto de biblioteca de .NET Standard y define la API para el control
personalizado. Este control expone una propiedad Items que se usa para rellenar el ListView con los datos y que
puede enlazarse a datos para fines de presentación. También expone un evento ItemSelected que se desencadena
cada vez que se selecciona un elemento en un control de lista nativo específico de la plataforma. Para más
información sobre el enlace de datos, consulte Data Binding Basics (Aspectos básicos del enlace de datos).

Uso del control personalizado


En XAML puede hacerse referencia al control personalizado NativeListView en el proyecto de biblioteca de .NET
Standard declarando un espacio de nombres para su ubicación y usando el prefijo del espacio de nombres en el
control. El siguiente ejemplo de código muestra cómo se puede usar el control personalizado NativeListView en
una página XAML:

<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
...
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="{x:Static local:App.Description}" HorizontalTextAlignment="Center" />
<local:NativeListView Grid.Row="1" x:Name="nativeListView" ItemSelected="OnItemSelected"
VerticalOptions="FillAndExpand" />
</Grid>
</ContentPage.Content>
</ContentPage>

El prefijo de espacio de nombres local puede tener cualquier nombre. Pero los valores clr-namespace y
assembly deben coincidir con los detalles del control personalizado. Una vez que se declara el espacio de
nombres, el prefijo se usa para hacer referencia al control personalizado.
El siguiente ejemplo de código muestra cómo se puede usar el control personalizado NativeListView en una
página C#:
public class MainPageCS : ContentPage
{
NativeListView nativeListView;

public MainPageCS()
{
nativeListView = new NativeListView
{
Items = DataSource.GetList(),
VerticalOptions = LayoutOptions.FillAndExpand
};

switch (Device.RuntimePlatform)
{
case Device.iOS:
Padding = new Thickness(0, 20, 0, 0);
break;
case Device.Android:
case Device.UWP:
Padding = new Thickness(0);
break;
}

Content = new Grid


{
RowDefinitions = {
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = new GridLength (1, GridUnitType.Star) }
},
Children = {
new Label { Text = App.Description, HorizontalTextAlignment = TextAlignment.Center },
nativeListView
}
};
nativeListView.ItemSelected += OnItemSelected;
}
...
}

El control personalizado NativeListView utiliza los representadores personalizados específicos de la plataforma


para mostrar una lista de datos que se rellena mediante la propiedad Items . Cada fila de la lista contiene tres
elementos de datos, un nombre, una categoría y un nombre de archivo de imagen. El diseño de cada fila de la lista
se define mediante el representador personalizado específico de la plataforma.

NOTE
Dado que el control personalizado NativeListView se representa mediante controles de lista específicos de la plataforma
que incluyen capacidad de desplazamiento, el control personalizado no debe hospedarse en los controles de diseño
desplazable, como ScrollView .

Ahora se puede agregar un representador personalizado a cada proyecto de aplicación para crear controles de
lista específicos de la plataforma y diseños de celda nativos.

Creación del representador personalizado en cada plataforma


El proceso para crear la clase del representador personalizado es el siguiente:
1. Cree una subclase de la clase ListViewRenderer que representa el control personalizado.
2. Invalide el método OnElementChanged que representa el control personalizado y escriba lógica para
personalizarlo. Se llama a este método cuando se crea el ListView de Xamarin.Forms correspondiente.
3. Agregue un atributo ExportRenderer a la clase del representador personalizado para especificar que se va a
usar para representar el control personalizado de Xamarin.Forms. Este atributo se usa para registrar el
representador personalizado con Xamarin.Forms.

NOTE
Proporcionar un representador personalizado en cada proyecto de la plataforma es un paso opcional. Si no hay un
representador personalizado registrado, se usa el representador predeterminado de la clase base de la celda.

El siguiente diagrama muestra las responsabilidades de cada proyecto de la aplicación de ejemplo, junto con las
relaciones entre ellos:

El control personalizado NativeListView se representa mediante clases de representador específicas de la


plataforma, que se derivan de la clase ListViewRenderer de cada plataforma. Esto da lugar a que cada control
personalizado NativeListView se represente con diseños de celda nativos y controles de lista específicos de la
plataforma, como se muestra en las siguientes capturas de pantalla:

La clase ListViewRenderer expone el método OnElementChanged , al que se llama cuando se crea el control
personalizado de Xamarin.Forms para representar el control nativo correspondiente. Este método toma un
parámetro ElementChangedEventArgs que contiene propiedades OldElement y NewElement . Estas propiedades
representan al elemento de Xamarin.Forms al que estaba asociado el representador y al elemento de
Xamarin.Forms al que está asociado el representador, respectivamente. En la aplicación de ejemplo, la propiedad
OldElement es null y la propiedad NewElement contiene una referencia a la instancia de NativeListView .

El lugar para realizar la personalización de controles nativos es una versión reemplazada del método
OnElementChanged en cada clase de representador específica de la plataforma. Una referencia con tipo para el
control nativo que se usa en la plataforma puede obtenerse a través de la propiedad Control . Además, se puede
obtener una referencia al control de Xamarin.Forms que se representa mediante la propiedad Element .
Debe tener cuidado al suscribirse a los controladores de eventos en el método OnElementChanged , como se
muestra en el siguiente ejemplo de código:

protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.ListView> e)


{
base.OnElementChanged (e);

if (e.OldElement != null) {
// Unsubscribe from event handlers and cleanup any resources
}

if (e.NewElement != null) {
// Configure the native control and subscribe to event handlers
}
}

Solo se debe configurar el control nativo y suscribir a los controladores de eventos cuando se adjunta el
representador personalizado a un nuevo elemento de Xamarin.Forms. De forma similar, solo se debe cancelar la
suscripción de los controladores de eventos que se han suscrito cuando cambia el elemento al que está asociado
el representador. Adoptar este enfoque facilita crear un representador personalizado que no sufra pérdidas de
memoria.
Una versión invalidada del método OnElementPropertyChanged , en cada clase de representador específico de la
plataforma, es el lugar para responder a los cambios de propiedad enlazable en el control personalizado de
Xamarin.Forms. Siempre se debe realizar una comprobación de la propiedad que ha modificado, ya que esta
invalidación se puede llamar varias veces.
Cada clase de representador personalizado se decora con un atributo ExportRenderer que registra el
representador con Xamarin.Forms. El atributo toma dos parámetros: el nombre de tipo del control personalizado
de Xamarin.Forms que se va a representar y el nombre de tipo del representador personalizado. El prefijo
assembly para el atributo especifica que el atributo se aplica a todo el ensamblado.

En las secciones siguientes se describe la implementación de cada clase de representador personalizado específico
de plataforma.
Creación del representador personalizado en iOS
El siguiente ejemplo de código muestra el representador personalizado para la plataforma iOS:
[assembly: ExportRenderer (typeof(NativeListView), typeof(NativeiOSListViewRenderer))]
namespace CustomRenderer.iOS
{
public class NativeiOSListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged (e);

if (e.OldElement != null) {
// Unsubscribe
}

if (e.NewElement != null) {
Control.Source = new NativeiOSListViewSource (e.NewElement as NativeListView);
}
}
}
}

El control UITableView se configura creando una instancia de la clase NativeiOSListViewSource , siempre que se
adjunte el representador personalizado a un nuevo elemento de Xamarin.Forms. Esta clase proporciona datos
para el control UITableView invalidando los métodos RowsInSection y GetCell desde la clase UITableViewSource
y exponiendo una propiedad Items que contiene la lista de datos que se mostrarán. La clase también proporciona
una invalidación del método RowSelected que invoca el evento ItemSelected proporcionado por el control
personalizado NativeListView . Para obtener más información sobre las invalidaciones de método, vea
Subclassing UITableViewSource (Creación de subclases de UITableViewSource). El método GetCell devuelve un
UITableCellView que se rellena con datos para cada fila de la lista y se muestra en el siguiente ejemplo de código:

public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)


{
// request a recycled cell to save memory
NativeiOSListViewCell cell = tableView.DequeueReusableCell (cellIdentifier) as NativeiOSListViewCell;

// if there are no cells to reuse, create a new one


if (cell == null) {
cell = new NativeiOSListViewCell (cellIdentifier);
}

if (String.IsNullOrWhiteSpace (tableItems [indexPath.Row].ImageFilename)) {


cell.UpdateCell (tableItems [indexPath.Row].Name
, tableItems [indexPath.Row].Category
, null);
} else {
cell.UpdateCell (tableItems [indexPath.Row].Name
, tableItems [indexPath.Row].Category
, UIImage.FromFile ("Images/" + tableItems [indexPath.Row].ImageFilename + ".jpg"));
}

return cell;
}

Este método crea una instancia de NativeiOSListViewCell para cada fila de datos que se mostrará en la pantalla.
La instancia de NativeiOSCell define el diseño de cada celda y los datos de la celda. Cuando una celda
desaparezca de la pantalla debido al desplazamiento, la celda estará disponible para su reutilización. Esto evita
desperdiciar memoria garantizando que solo hay instancias de NativeiOSCell para los datos que se muestran en
la pantalla, en lugar de todos los datos en la lista. Para obtener más información sobre la reutilización de celdas,
vea Cell Reuse (Reutilización de celdas). El método GetCell también lee la propiedad ImageFilename de cada fila
de datos, siempre que exista, y lee la imagen y la almacena como una instancia de UIImage antes de actualizar la
instancia de NativeiOSListViewCell con los datos (nombre, categoría e imagen) de la fila.
La clase NativeiOSListViewCell define el diseño de cada celda y se muestra en el siguiente ejemplo de código:

public class NativeiOSListViewCell : UITableViewCell


{
UILabel headingLabel, subheadingLabel;
UIImageView imageView;

public NativeiOSListViewCell (NSString cellId) : base (UITableViewCellStyle.Default, cellId)


{
SelectionStyle = UITableViewCellSelectionStyle.Gray;

ContentView.BackgroundColor = UIColor.FromRGB (218, 255, 127);

imageView = new UIImageView ();

headingLabel = new UILabel () {


Font = UIFont.FromName ("Cochin-BoldItalic", 22f),
TextColor = UIColor.FromRGB (127, 51, 0),
BackgroundColor = UIColor.Clear
};

subheadingLabel = new UILabel () {


Font = UIFont.FromName ("AmericanTypewriter", 12f),
TextColor = UIColor.FromRGB (38, 127, 0),
TextAlignment = UITextAlignment.Center,
BackgroundColor = UIColor.Clear
};

ContentView.Add (headingLabel);
ContentView.Add (subheadingLabel);
ContentView.Add (imageView);
}

public void UpdateCell (string caption, string subtitle, UIImage image)


{
headingLabel.Text = caption;
subheadingLabel.Text = subtitle;
imageView.Image = image;
}

public override void LayoutSubviews ()


{
base.LayoutSubviews ();

headingLabel.Frame = new CoreGraphics.CGRect (5, 4, ContentView.Bounds.Width - 63, 25);


subheadingLabel.Frame = new CoreGraphics.CGRect (100, 18, 100, 20);
imageView.Frame = new CoreGraphics.CGRect (ContentView.Bounds.Width - 63, 5, 33, 33);
}
}

Esta clase define los controles utilizados para representar el contenido de la celda y su diseño. El constructor
NativeiOSListViewCell crea instancias de controles de UILabel y UIImageView e inicializa su apariencia. Estos
controles se usan para mostrar datos de cada fila, y el método UpdateCell se usa para establecer estos datos en
las instancias de UILabel y UIImageView . El método LayoutSubviews invalidado establece la ubicación de estas
instancias especificando sus coordenadas dentro de la celda.
Responde a un cambio de propiedad en el control personalizado
Si la propiedad NativeListView.Items cambia debido a elementos que se agregan o se quitan de la lista, el
representador personalizado debe responder mostrando los cambios. Esto puede realizarse invalidando el método
OnElementPropertyChanged , que se muestra en el siguiente ejemplo de código:
protected override void OnElementPropertyChanged (object sender,
System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged (sender, e);

if (e.PropertyName == NativeListView.ItemsProperty.PropertyName) {
Control.Source = new NativeiOSListViewSource (Element as NativeListView);
}
}

El método crea una nueva instancia de la clase NativeiOSListViewSource que proporciona datos para el control
UITableView , siempre que la propiedad enlazable NativeListView.Items haya cambiado.

Creación del representador personalizado en Android


En el ejemplo de código siguiente se muestra el representador personalizado para la plataforma Android:

[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeAndroidListViewRenderer))]


namespace CustomRenderer.Droid
{
public class NativeAndroidListViewRenderer : ListViewRenderer
{
Context _context;

public NativeAndroidListViewRenderer(Context context) : base(context)


{
_context = context;
}

protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)


{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// unsubscribe
Control.ItemClick -= OnItemClick;
}

if (e.NewElement != null)
{
// subscribe
Control.Adapter = new NativeAndroidListViewAdapter(_context as Android.App.Activity,
e.NewElement as NativeListView);
Control.ItemClick += OnItemClick;
}
}
...

void OnItemClick(object sender, Android.Widget.AdapterView.ItemClickEventArgs e)


{
((NativeListView)Element).NotifyItemSelected(((NativeListView)Element).Items.ToList()[e.Position -
1]);
}
}
}

El control nativo ListView se configura siempre que el representador personalizado esté asociado a un nuevo
elemento de Xamarin.Forms. Esta configuración implica la creación de una instancia de la clase
NativeAndroidListViewAdapter que proporciona datos al control ListView nativo y el registro de un controlador
de eventos para procesar el evento ItemClick . A su vez, este controlador invocará el evento ItemSelected
proporcionado por el control personalizado NativeListView . Se cancela la suscripción del evento ItemClick solo
si cambia el representador al que está adjunto el elemento de Xamarin.Forms.
El NativeAndroidListViewAdapter deriva de la clase BaseAdapter y expone una propiedad Items que contiene la
lista de datos que se mostrarán, además de invalidar los métodos Count , GetView , GetItemId y this[int] . Para
obtener más información sobre estas invalidaciones de método, vea Implementing a ListAdapter (Implementación
de un ListAdapter). El método GetView devuelve una vista para cada fila, que se rellena con datos y se muestra en
el siguiente ejemplo de código:

public override View GetView (int position, View convertView, ViewGroup parent)
{
var item = tableItems [position];

var view = convertView;


if (view == null) {
// no view to re-use, create new
view = context.LayoutInflater.Inflate (Resource.Layout.NativeAndroidListViewCell, null);
}
view.FindViewById<TextView> (Resource.Id.Text1).Text = item.Name;
view.FindViewById<TextView> (Resource.Id.Text2).Text = item.Category;

// grab the old image and dispose of it


if (view.FindViewById<ImageView> (Resource.Id.Image).Drawable != null) {
using (var image = view.FindViewById<ImageView> (Resource.Id.Image).Drawable as BitmapDrawable) {
if (image != null) {
if (image.Bitmap != null) {
//image.Bitmap.Recycle ();
image.Bitmap.Dispose ();
}
}
}
}

// If a new image is required, display it


if (!String.IsNullOrWhiteSpace (item.ImageFilename)) {
context.Resources.GetBitmapAsync (item.ImageFilename).ContinueWith ((t) => {
var bitmap = t.Result;
if (bitmap != null) {
view.FindViewById<ImageView> (Resource.Id.Image).SetImageBitmap (bitmap);
bitmap.Dispose ();
}
}, TaskScheduler.FromCurrentSynchronizationContext ());
} else {
// clear the image
view.FindViewById<ImageView> (Resource.Id.Image).SetImageBitmap (null);
}

return view;
}

Se llama al método GetView para devolver la celda que se va a representar, como una View , para cada fila de
datos en la lista. Crea una instancia de View para cada fila de datos que se mostrará en la pantalla, con la
apariencia de la instancia de View que se define en un archivo de diseño. Cuando una celda desaparezca de la
pantalla debido al desplazamiento, la celda estará disponible para su reutilización. Esto evita desperdiciar memoria
garantizando que solo hay instancias de View para los datos que se muestran en la pantalla, en lugar de todos los
datos en la lista. Para obtener más información sobre la reutilización de vistas, vea Row View Re-use (Reutilización
de vistas de fila).
El método GetView también rellena la instancia View con datos, incluyendo la lectura de los datos de imagen del
nombre de archivo especificado en la propiedad ImageFilename .
El diseño de cada celda mostrada por el ListView nativo se define en el archivo de diseño
NativeAndroidListViewCell.axml , que aumenta el método LayoutInflater.Inflate . En el siguiente ejemplo de
código se muestra la definición del diseño:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="@drawable/CustomSelector">
<LinearLayout
android:id="@+id/Text"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dip">
<TextView
android:id="@+id/Text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF7F3300"
android:textSize="20dip"
android:textStyle="italic" />
<TextView
android:id="@+id/Text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="#FF267F00"
android:paddingLeft="100dip" />
</LinearLayout>
<ImageView
android:id="@+id/Image"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="5dp"
android:src="@drawable/icon"
android:layout_alignParentRight="true" />
</RelativeLayout>

Este diseño especifica que dos controles de TextView y un control de ImageView se usan para mostrar el
contenido de la celda. Los dos controles de TextView están orientados verticalmente dentro de un control de
LinearLayout , con todos los controles contenidos en un RelativeLayout .

Responde a un cambio de propiedad en el control personalizado


Si la propiedad NativeListView.Items cambia debido a elementos que se agregan o se quitan de la lista, el
representador personalizado debe responder mostrando los cambios. Esto puede realizarse invalidando el método
OnElementPropertyChanged , que se muestra en el siguiente ejemplo de código:

protected override void OnElementPropertyChanged (object sender,


System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged (sender, e);

if (e.PropertyName == NativeListView.ItemsProperty.PropertyName) {
Control.Adapter = new NativeAndroidListViewAdapter (_context as Android.App.Activity, Element as
NativeListView);
}
}

El método crea una nueva instancia de la clase NativeAndroidListViewAdapter que proporciona datos para el
control ListView nativo, siempre que el la propiedad enlazable NativeListView.Items haya cambiado.
Creación del representador personalizado en UWP
En el siguiente ejemplo de código se muestra el representador personalizado para UWP:
[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeUWPListViewRenderer))]
namespace CustomRenderer.UWP
{
public class NativeUWPListViewRenderer : ListViewRenderer
{
ListView listView;

protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)


{
base.OnElementChanged(e);

listView = Control as ListView;

if (e.OldElement != null)
{
// Unsubscribe
listView.SelectionChanged -= OnSelectedItemChanged;
}

if (e.NewElement != null)
{
listView.SelectionMode = ListViewSelectionMode.Single;
listView.IsItemClickEnabled = false;
listView.ItemsSource = ((NativeListView)e.NewElement).Items;
listView.ItemTemplate = App.Current.Resources["ListViewItemTemplate"] as
Windows.UI.Xaml.DataTemplate;
// Subscribe
listView.SelectionChanged += OnSelectedItemChanged;
}
}

void OnSelectedItemChanged(object sender, SelectionChangedEventArgs e)


{
((NativeListView)Element).NotifyItemSelected(listView.SelectedItem);
}
}
}

El control nativo ListView se configura siempre que el representador personalizado esté asociado a un nuevo
elemento de Xamarin.Forms. Esta configuración implica configurar el modo en que el control ListView nativo
responderá a los elementos seleccionados, rellenar los datos mostrados por el control, definir la apariencia y el
contenido de cada celda y registrar un controlador de eventos para procesar el evento SelectionChanged . A su vez,
este controlador invocará el evento ItemSelected proporcionado por el control personalizado NativeListView . Se
cancela la suscripción del evento SelectionChanged solo si cambia el representador al que está adjunto el
elemento de Xamarin.Forms.
La apariencia y el contenido de cada celda ListView nativa se definen mediante un DataTemplate denominado
ListViewItemTemplate . Este DataTemplate se almacena en el diccionario de recursos de nivel de aplicación y se
muestra en el siguiente ejemplo de código:
<DataTemplate x:Key="ListViewItemTemplate">
<Grid Background="#DAFF7F">
<Grid.Resources>
<local:ConcatImageExtensionConverter x:Name="ConcatImageExtensionConverter" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.40*" />
<ColumnDefinition Width="0.40*"/>
<ColumnDefinition Width="0.20*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.ColumnSpan="2" Foreground="#7F3300" FontStyle="Italic" FontSize="22"
VerticalAlignment="Top" Text="{Binding Name}" />
<TextBlock Grid.RowSpan="2" Grid.Column="1" Foreground="#267F00" FontWeight="Bold" FontSize="12"
VerticalAlignment="Bottom" Text="{Binding Category}" />
<Image Grid.RowSpan="2" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center" Source="
{Binding ImageFilename, Converter={StaticResource ConcatImageExtensionConverter}}" Width="50" Height="50" />
<Line Grid.Row="1" Grid.ColumnSpan="3" X1="0" X2="1" Margin="30,20,0,0" StrokeThickness="1"
Stroke="LightGray" Stretch="Fill" VerticalAlignment="Bottom" />
</Grid>
</DataTemplate>

El DataTemplate especifica los controles utilizados para mostrar el contenido de la celda y su diseño y apariencia.
Dos controles de TextBlock y un control de Image se usan para mostrar el contenido de la celda mediante el
enlace de datos. Además, una instancia de ConcatImageExtensionConverter se utiliza para concatenar la extensión
de archivo .jpg para cada nombre de archivo de imagen. Esto garantiza que el control Image puede cargar y
representar la imagen cuando se establece su propiedad Source .
Responde a un cambio de propiedad en el control personalizado
Si la propiedad NativeListView.Items cambia debido a elementos que se agregan o se quitan de la lista, el
representador personalizado debe responder mostrando los cambios. Esto puede realizarse invalidando el método
OnElementPropertyChanged , que se muestra en el siguiente ejemplo de código:

protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs


e)
{
base.OnElementPropertyChanged(sender, e);

if (e.PropertyName == NativeListView.ItemsProperty.PropertyName)
{
listView.ItemsSource = ((NativeListView)Element).Items;
}
}

El método rellena el control ListView nativo con los datos modificados, siempre que la propiedad
NativeListView.Items enlazable haya cambiado.

Resumen
En este artículo se mostró cómo crear un representador personalizado que encapsula los controles de lista
específica de la plataforma y los diseños de celda nativa, lo que permite tener más control sobre el rendimiento del
control de lista nativa.

Vínculos relacionados
CustomRendererListView (sample) (CustomRendererListView [ejemplo])
Personalización de ViewCell
11/07/2019 • 26 minutes to read • Edit Online

Descargar el ejemplo
Un ViewCell de Xamarin.Forms es una celda que se puede agregar a ListView o TableView y que contiene una
vista definida por el desarrollador. En este artículo se muestra cómo crear un representador personalizado para
un ViewCell que se hospeda dentro de un control ListView de Xamarin.Forms. Esto impide que se llame varias
veces a los cálculos de diseño de Xamarin.Forms durante el desplazamiento de ListView.
Todos las celdas de Xamarin.Forms tienen un representador que las acompaña para cada plataforma y que crea
una instancia de un control nativo. Cuando una aplicación de Xamarin.Forms representa una ViewCell , en iOS se
crea la instancia de la clase ViewCellRenderer , que a su vez crea una instancia del control UITableViewCell nativo.
En la plataforma Android, la clase ViewCellRenderer crea una instancia del control View nativo. En la Plataforma
Universal de Windows (UWP ), la clase ViewCellRenderer crea una instancia de DataTemplate nativa. Para obtener
más información sobre las clases de control nativo que se asignan a los controles de Xamarin.Forms y el
representador, vea Renderer Base Classes and Native Controls (Controles nativos y clases base del
representador).
El siguiente diagrama ilustra la relación entre la ViewCell y los controles nativos correspondientes que la
implementan:

El proceso de representación puede aprovecharse para implementar las personalizaciones específicas de la


plataforma creando un representador personalizado para una ViewCell en cada plataforma. El proceso para
hacer esto es el siguiente:
1. Cree una celda personalizada de Xamarin.Forms.
2. Consuma la celda personalizada de Xamarin.Forms.
3. Cree el representador personalizado para la celda en cada plataforma.
Cada elemento ahora se explicará a su vez, para implementar un representador NativeCell que aproveche las
ventajas de un diseño específico de la plataforma para cada celda que se hospeda dentro de un control ListView
de Xamarin.Forms. Esto impide que se llame varias veces a los cálculos de diseño de Xamarin.Forms durante el
desplazamiento de ListView .

Creación de la celda personalizada


Se puede crear un control de celda personalizado mediante la creación de subclases de la clase ViewCell , como
se muestra en el siguiente ejemplo de código:

public class NativeCell : ViewCell


{
public static readonly BindableProperty NameProperty =
BindableProperty.Create ("Name", typeof(string), typeof(NativeCell), "");

public string Name {


get { return (string)GetValue (NameProperty); }
set { SetValue (NameProperty, value); }
}

public static readonly BindableProperty CategoryProperty =


BindableProperty.Create ("Category", typeof(string), typeof(NativeCell), "");

public string Category {


get { return (string)GetValue (CategoryProperty); }
set { SetValue (CategoryProperty, value); }
}

public static readonly BindableProperty ImageFilenameProperty =


BindableProperty.Create ("ImageFilename", typeof(string), typeof(NativeCell), "");

public string ImageFilename {


get { return (string)GetValue (ImageFilenameProperty); }
set { SetValue (ImageFilenameProperty, value); }
}
}

La clase NativeCell se crea en el proyecto de biblioteca de .NET Standard y define la API para la celda
personalizada. La celda personalizada expone las propiedades Name , Category y ImageFilename que se pueden
mostrar mediante el enlace de datos. Para más información sobre el enlace de datos, consulte Data Binding Basics
(Aspectos básicos del enlace de datos).

Consumo de la celda personalizada


En XAML puede hacerse referencia a la celda personalizada NativeCell en el proyecto de biblioteca de .NET
Standard. Para ello, se declara un espacio de nombres para su ubicación y se usa el prefijo del espacio de nombres
en el elemento de celda personalizado. El siguiente ejemplo de código muestra cómo la celda personalizada
NativeCell puede utilizarse en una página XAML:

<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
...
<ContentPage.Content>
<StackLayout>
<Label Text="Xamarin.Forms native cell" HorizontalTextAlignment="Center" />
<ListView x:Name="listView" CachingStrategy="RecycleElement" ItemSelected="OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<local:NativeCell Name="{Binding Name}" Category="{Binding Category}"
ImageFilename="{Binding ImageFilename}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>

El prefijo del espacio de nombres local puede tener cualquier nombre. Empero, los valores clr-namespace y
assembly deben coincidir con los detalles del control personalizado. Una vez que se declare el espacio de
nombres, el prefijo se utiliza para hacer referencia a la celda personalizada.
El siguiente ejemplo de código muestra cómo una página de C# puede consumir la celda personalizada
NativeCell :

public class NativeCellPageCS : ContentPage


{
ListView listView;

public NativeCellPageCS()
{
listView = new ListView(ListViewCachingStrategy.RecycleElement)
{
ItemsSource = DataSource.GetList(),
ItemTemplate = new DataTemplate(() =>
{
var nativeCell = new NativeCell();
nativeCell.SetBinding(NativeCell.NameProperty, "Name");
nativeCell.SetBinding(NativeCell.CategoryProperty, "Category");
nativeCell.SetBinding(NativeCell.ImageFilenameProperty, "ImageFilename");

return nativeCell;
})
};

switch (Device.RuntimePlatform)
{
case Device.iOS:
Padding = new Thickness(0, 20, 0, 0);
break;
case Device.Android:
case Device.UWP:
Padding = new Thickness(0);
break;
}

Content = new StackLayout


{
Children = {
new Label { Text = "Xamarin.Forms native cell", HorizontalTextAlignment =
TextAlignment.Center },
listView
}
};
listView.ItemSelected += OnItemSelected;
}
...
}

Un control ListView de Xamarin.Forms se usa para mostrar una lista de los datos, que se rellena mediante la
propiedad ItemSource . La estrategia de almacenamiento en caché RecycleElement intenta minimizar la velocidad
de ejecución y el consumo de memoria de ListView mediante el reciclaje de las celdas de la lista. Para obtener
más información, vea Estrategia de almacenamiento en caché.
Cada fila de la lista contiene tres elementos de datos: un nombre, una categoría y un nombre de archivo de
imagen. El diseño de cada fila de la lista está definido por el DataTemplate al que se hace referencia mediante la
propiedad enlazable ListView.ItemTemplate . DataTemplate define que cada fila de datos en la lista será una
NativeCell que muestra sus propiedades Name , Category y ImageFilename mediante el enlace de datos. Para
obtener más información sobre el control ListView , vea ListView de Xamarin.Forms.
Ahora se puede agregar un representador personalizado a cada proyecto de aplicación para personalizar el
diseño específico de la plataforma para cada celda.
Creación del representador personalizado en cada plataforma
El proceso de creación de la clase de representador personalizada es el siguiente:
1. Cree una subclase de la clase ViewCellRenderer que represente la celda personalizada.
2. Invalide el método específico de la plataforma que representa la celda personalizada y escriba una lógica para
personalizarla.
3. Agregue un atributo ExportRenderer a la clase de representador personalizada para especificar que se utilizará
para representar la celda personalizada de Xamarin.Forms. Este atributo se usa para registrar al representador
personalizado con Xamarin.Forms.

NOTE
Para la mayoría de los elementos de Xamarin.Forms, proporcionar un representador personalizado en cada proyecto de la
plataforma es un paso opcional. Si no se registra un representador personalizado, se usará el representador predeterminado
de la clase base del control. Con todo, los representadores personalizados son necesarios en cada proyecto de la plataforma
al representar un elemento ViewCell.

El siguiente diagrama ilustra las responsabilidades de cada proyecto en la aplicación de ejemplo, junto con las
relaciones entre ellos:

Las clases del representador específico de la plataforma, que se derivan de la clase ViewCellRenderer para cada
plataforma, representan la celda personalizada NativeCell . Esto da como resultado que cada celda personalizada
NativeCell se represente con diseño específico de la plataforma, como se muestra en las siguientes capturas de
pantalla:
La clase ViewCellRenderer expone métodos específicos de la plataforma para representar la celda personalizada.
Estos son el método GetCell en la plataforma iOS, el método GetCellCore en la plataforma Android y el método
GetTemplate en UWP.

Cada clase de presentador personalizado se decora con un atributo ExportRenderer que registra el representador
con Xamarin.Forms. El atributo toma dos parámetros: el nombre de tipo de la celda de Xamarin.Forms que se
representa y el nombre de tipo del representador personalizado. El prefijo assembly para el atributo especifica
que el atributo se aplica a todo el ensamblado.
En las secciones siguientes se describe la implementación de cada clase de representador personalizado
específico de plataforma.
Creación del representador personalizado en iOS
El siguiente ejemplo de código muestra el representador personalizado para la plataforma de iOS:

[assembly: ExportRenderer(typeof(NativeCell), typeof(NativeiOSCellRenderer))]


namespace CustomRenderer.iOS
{
public class NativeiOSCellRenderer : ViewCellRenderer
{
NativeiOSCell cell;

public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)


{
var nativeCell = (NativeCell)item;

cell = reusableCell as NativeiOSCell;


if (cell == null)
cell = new NativeiOSCell(item.GetType().FullName, nativeCell);
else
cell.NativeCell.PropertyChanged -= OnNativeCellPropertyChanged;

nativeCell.PropertyChanged += OnNativeCellPropertyChanged;
cell.UpdateCell(nativeCell);
return cell;
}
...
}
}

Se llama al método GetCell para crear cada celda que se mostrará. Cada celda es una instancia de
NativeiOSCell que define el diseño de la celda y sus datos. La operación del método GetCell depende de la
estrategia de almacenamiento en caché ListView :
Cuando la estrategia de almacenamiento en caché ListView sea RetainElement , se invocará el método
GetCell para cada celda. Se creará una instancia de NativeiOSCell para cada instancia de NativeCell que
se muestre inicialmente en la pantalla. Cuando el usuario se desplace a través de la ListView , se volverán
a usar las instancias de NativeiOSCell . Para obtener más información sobre la reutilización de celdas de
iOS, vea Reutilización de celda.

NOTE
Este código de representador personalizado llevará a cabo cierta reutilización de celda cuando la ListView se
establezca para conservar las celdas.

El método UpdateCell actualizará los datos mostrados por cada instancia de NativeiOSCell , ya sean recién
creados o se vuelvan a utilizar, con los datos de cada instancia de NativeCell .
NOTE
El método OnNativeCellPropertyChanged nunca se invocará cuando la estrategia de almacenamiento en caché
ListView se establezca para conservar las celdas.

Cuando la estrategia de almacenamiento en caché ListView sea RecycleElement , se invocará el método


GetCell para cada celda que se muestra inicialmente en la pantalla. Se creará una instancia de
NativeiOSCell para cada instancia de NativeCell que se muestre inicialmente en la pantalla. El método
UpdateCell actualizará los datos mostrados por cada instancia de NativeiOSCell con los datos de cada
instancia de NativeCell . Empero, el método GetCell no se invoca cuando el usuario se desplaza por la
ListView . En su lugar, se reutilizarán las instancias de NativeiOSCell . Los eventos de PropertyChanged se
generarán en la instancia de NativeCell cuando cambien sus datos y el controlador de eventos
OnNativeCellPropertyChanged actualizará los datos en cada instancia de NativeiOSCell reutilizada.

El siguiente ejemplo de código muestra el método OnNativeCellPropertyChanged que se invoca cuando se provoca
un evento PropertyChanged :

namespace CustomRenderer.iOS
{
public class NativeiOSCellRenderer : ViewCellRenderer
{
...

void OnNativeCellPropertyChanged(object sender, PropertyChangedEventArgs e)


{
var nativeCell = (NativeCell)sender;
if (e.PropertyName == NativeCell.NameProperty.PropertyName)
{
cell.HeadingLabel.Text = nativeCell.Name;
}
else if (e.PropertyName == NativeCell.CategoryProperty.PropertyName)
{
cell.SubheadingLabel.Text = nativeCell.Category;
}
else if (e.PropertyName == NativeCell.ImageFilenameProperty.PropertyName)
{
cell.CellImageView.Image = cell.GetImage(nativeCell.ImageFilename);
}
}
}
}

Este método actualiza los datos que muestran las instancias de NativeiOSCell reutilizadas. Se realiza una
comprobación para la propiedad que se ha modificado, ya que el método puede llamarse varias veces.
La clase NativeiOSCell define el diseño de cada celda y se muestra en el siguiente ejemplo de código:
internal class NativeiOSCell : UITableViewCell, INativeElementView
{
public UILabel HeadingLabel { get; set; }
public UILabel SubheadingLabel { get; set; }
public UIImageView CellImageView { get; set; }

public NativeCell NativeCell { get; private set; }


public Element Element => NativeCell;

public NativeiOSCell(string cellId, NativeCell cell) : base(UITableViewCellStyle.Default, cellId)


{
NativeCell = cell;

SelectionStyle = UITableViewCellSelectionStyle.Gray;
ContentView.BackgroundColor = UIColor.FromRGB(255, 255, 224);
CellImageView = new UIImageView();

HeadingLabel = new UILabel()


{
Font = UIFont.FromName("Cochin-BoldItalic", 22f),
TextColor = UIColor.FromRGB(127, 51, 0),
BackgroundColor = UIColor.Clear
};

SubheadingLabel = new UILabel()


{
Font = UIFont.FromName("AmericanTypewriter", 12f),
TextColor = UIColor.FromRGB(38, 127, 0),
TextAlignment = UITextAlignment.Center,
BackgroundColor = UIColor.Clear
};

ContentView.Add(HeadingLabel);
ContentView.Add(SubheadingLabel);
ContentView.Add(CellImageView);
}

public void UpdateCell(NativeCell cell)


{
HeadingLabel.Text = cell.Name;
SubheadingLabel.Text = cell.Category;
CellImageView.Image = GetImage(cell.ImageFilename);
}

public UIImage GetImage(string filename)


{
return (!string.IsNullOrWhiteSpace(filename)) ? UIImage.FromFile("Images/" + filename + ".jpg") : null;
}

public override void LayoutSubviews()


{
base.LayoutSubviews();

HeadingLabel.Frame = new CGRect(5, 4, ContentView.Bounds.Width - 63, 25);


SubheadingLabel.Frame = new CGRect(100, 18, 100, 20);
CellImageView.Frame = new CGRect(ContentView.Bounds.Width - 63, 5, 33, 33);
}
}

Esta clase define los controles utilizados para representar el contenido de la celda y su diseño. La clase
implementa la interfaz INativeElementView , que es necesaria cuando ListView usa estrategia de almacenamiento
en caché RecycleElement . Esta interfaz especifica que la clase debe implementar la propiedad Element , que debe
devolver los datos de celda personalizada para las celdas recicladas.
El constructor NativeiOSCell inicializa la apariencia de las propiedades HeadingLabel , SubheadingLabel y
CellImageView . Estas propiedades se utilizan para mostrar los datos almacenados en la instancia NativeCell ,
siendo llamado el método UpdateCell para establecer el valor de cada propiedad. Además, cuando ListView usa
la estrategia de almacenamiento en caché RecycleElement , el método OnNativeCellPropertyChanged puede
actualizar los datos mostrados por las propiedades HeadingLabel , SubheadingLabel y CellImageView en el
representador personalizado.
El diseño de la celda se realiza mediante la invalidación de LayoutSubviews , que establece las coordenadas de
HeadingLabel , SubheadingLabel y CellImageView dentro de la celda.

Creación del representador personalizado en Android


El siguiente ejemplo de código muestra el representador personalizado para la plataforma de Android:

[assembly: ExportRenderer(typeof(NativeCell), typeof(NativeAndroidCellRenderer))]


namespace CustomRenderer.Droid
{
public class NativeAndroidCellRenderer : ViewCellRenderer
{
NativeAndroidCell cell;

protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView,


ViewGroup parent, Context context)
{
var nativeCell = (NativeCell)item;
Console.WriteLine("\t\t" + nativeCell.Name);

cell = convertView as NativeAndroidCell;


if (cell == null)
{
cell = new NativeAndroidCell(context, nativeCell);
}
else
{
cell.NativeCell.PropertyChanged -= OnNativeCellPropertyChanged;
}

nativeCell.PropertyChanged += OnNativeCellPropertyChanged;

cell.UpdateCell(nativeCell);
return cell;
}
...
}
}

Se llama al método para crear cada celda que se mostrará. Cada celda es una instancia de
GetCellCore
NativeAndroidCell que define el diseño de la celda y sus datos. La operación del método GetCellCore depende
de la estrategia de almacenamiento en caché ListView :
Cuando la estrategia de almacenamiento en caché ListView sea RetainElement , se invocará el método
GetCellCore para cada celda. Se creará una NativeAndroidCell para cada instancia de NativeCell que se
muestre inicialmente en la pantalla. Cuando el usuario se desplace a través de la ListView , se volverán a
usar las instancias de NativeAndroidCell . Para obtener más información sobre la reutilización de celdas en
Android, vea Reutilización de vista fila.

NOTE
Tenga en cuenta que este código de representador personalizado llevará a cabo cierta reutilización de celda cuando
la ListView se establezca para conservar las celdas.
El método UpdateCell actualizará los datos mostrados por cada instancia de NativeAndroidCell , ya sean
recién creados o se vuelvan a utilizar, con los datos de cada instancia de NativeCell .

NOTE
Tenga en cuenta que el método OnNativeCellPropertyChanged se invocará cuando ListView se configure para
conservar las celdas, pero no se actualizarán los valores de propiedad de NativeAndroidCell .

Cuando la estrategia de almacenamiento en caché ListView sea RecycleElement , se invocará el método


GetCellCore para cada celda que se muestra inicialmente en la pantalla. Se creará una instancia de
NativeAndroidCell para cada instancia de NativeCell que se muestre inicialmente en la pantalla. El
método UpdateCell actualizará los datos mostrados por cada instancia de NativeAndroidCell con los
datos de cada instancia de NativeCell . Empero, el método GetCellCore no se invoca cuando el usuario se
desplaza por la ListView . En su lugar, se reutilizarán las instancias de NativeAndroidCell . Los eventos de
PropertyChanged se generarán en la instancia de NativeCell cuando cambien sus datos y el controlador de
eventos OnNativeCellPropertyChanged actualizará los datos en cada instancia de NativeAndroidCell
reutilizada.
El siguiente ejemplo de código muestra el método OnNativeCellPropertyChanged que se invoca cuando se provoca
un evento PropertyChanged :

namespace CustomRenderer.Droid
{
public class NativeAndroidCellRenderer : ViewCellRenderer
{
...

void OnNativeCellPropertyChanged(object sender, PropertyChangedEventArgs e)


{
var nativeCell = (NativeCell)sender;
if (e.PropertyName == NativeCell.NameProperty.PropertyName)
{
cell.HeadingTextView.Text = nativeCell.Name;
}
else if (e.PropertyName == NativeCell.CategoryProperty.PropertyName)
{
cell.SubheadingTextView.Text = nativeCell.Category;
}
else if (e.PropertyName == NativeCell.ImageFilenameProperty.PropertyName)
{
cell.SetImage(nativeCell.ImageFilename);
}
}
}
}

Este método actualiza los datos que muestran las instancias de NativeAndroidCell reutilizadas. Se realiza una
comprobación para la propiedad que se ha modificado, ya que el método puede llamarse varias veces.
La clase NativeAndroidCell define el diseño de cada celda y se muestra en el siguiente ejemplo de código:
internal class NativeAndroidCell : LinearLayout, INativeElementView
{
public TextView HeadingTextView { get; set; }
public TextView SubheadingTextView { get; set; }
public ImageView ImageView { get; set; }

public NativeCell NativeCell { get; private set; }


public Element Element => NativeCell;

public NativeAndroidCell(Context context, NativeCell cell) : base(context)


{
NativeCell = cell;

var view = (context as Activity).LayoutInflater.Inflate(Resource.Layout.NativeAndroidCell, null);


HeadingTextView = view.FindViewById<TextView>(Resource.Id.HeadingText);
SubheadingTextView = view.FindViewById<TextView>(Resource.Id.SubheadingText);
ImageView = view.FindViewById<ImageView>(Resource.Id.Image);

AddView(view);
}

public void UpdateCell(NativeCell cell)


{
HeadingTextView.Text = cell.Name;
SubheadingTextView.Text = cell.Category;

// Dispose of the old image


if (ImageView.Drawable != null)
{
using (var image = ImageView.Drawable as BitmapDrawable)
{
if (image != null)
{
if (image.Bitmap != null)
{
image.Bitmap.Dispose();
}
}
}
}

SetImage(cell.ImageFilename);
}

public void SetImage(string filename)


{
if (!string.IsNullOrWhiteSpace(filename))
{
// Display new image
Context.Resources.GetBitmapAsync(filename).ContinueWith((t) =>
{
var bitmap = t.Result;
if (bitmap != null)
{
ImageView.SetImageBitmap(bitmap);
bitmap.Dispose();
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else
{
// Clear the image
ImageView.SetImageBitmap(null);
}
}
}
Esta clase define los controles utilizados para representar el contenido de la celda y su diseño. La clase
implementa la interfaz INativeElementView , que es necesaria cuando ListView usa estrategia de almacenamiento
en caché RecycleElement . Esta interfaz especifica que la clase debe implementar la propiedad Element , que debe
devolver los datos de celda personalizada para las celdas recicladas.
El constructor NativeAndroidCell aumenta el diseño de NativeAndroidCell e inicializa las propiedades
HeadingTextView , SubheadingTextView y ImageView a los controles en el diseño aumentado. Estas propiedades se
utilizan para mostrar los datos almacenados en la instancia NativeCell , siendo llamado el método UpdateCell
para establecer el valor de cada propiedad. Además, cuando la ListView usa la estrategia de almacenamiento en
caché RecycleElement , el método OnNativeCellPropertyChanged puede actualizar los datos mostrados por las
propiedades HeadingTextView , SubheadingTextView y ImageView en el representador personalizado.
El siguiente ejemplo de código muestra la definición de diseño para el archivo de diseño de
NativeAndroidCell.axml :

<?xml version="1.0" encoding="utf-8"?>


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="@drawable/CustomSelector">
<LinearLayout
android:id="@+id/Text"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dip">
<TextView
android:id="@+id/HeadingText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF7F3300"
android:textSize="20dip"
android:textStyle="italic" />
<TextView
android:id="@+id/SubheadingText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="#FF267F00"
android:paddingLeft="100dip" />
</LinearLayout>
<ImageView
android:id="@+id/Image"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="5dp"
android:src="@drawable/icon"
android:layout_alignParentRight="true" />
</RelativeLayout>

Este diseño especifica que dos controles de TextView y un control de ImageView se usan para mostrar el
contenido de la celda. Los dos controles de TextView están orientados verticalmente dentro de un control de
LinearLayout , con todos los controles contenidos en un RelativeLayout .

Creación del representador personalizado en UWP


El siguiente ejemplo de código muestra el representador personalizado para UWP:
[assembly: ExportRenderer(typeof(NativeCell), typeof(NativeUWPCellRenderer))]
namespace CustomRenderer.UWP
{
public class NativeUWPCellRenderer : ViewCellRenderer
{
public override Windows.UI.Xaml.DataTemplate GetTemplate(Cell cell)
{
return App.Current.Resources["ListViewItemTemplate"] as Windows.UI.Xaml.DataTemplate;
}
}
}

Se llama al método GetTemplate para devolver la celda que se va a representar para cada fila de datos en la lista.
Crea un DataTemplate para cada instancia de NativeCell que se mostrará en la pantalla, con el DataTemplate
definiendo la apariencia y el contenido de la celda.
DataTemplate se almacena en el diccionario de recursos de nivel de aplicación y se muestra en el siguiente
ejemplo de código:

<DataTemplate x:Key="ListViewItemTemplate">
<Grid Background="LightYellow">
<Grid.Resources>
<local:ConcatImageExtensionConverter x:Name="ConcatImageExtensionConverter" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.40*" />
<ColumnDefinition Width="0.40*"/>
<ColumnDefinition Width="0.20*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.ColumnSpan="2" Foreground="#7F3300" FontStyle="Italic" FontSize="22"
VerticalAlignment="Top" Text="{Binding Name}" />
<TextBlock Grid.RowSpan="2" Grid.Column="1" Foreground="#267F00" FontWeight="Bold" FontSize="12"
VerticalAlignment="Bottom" Text="{Binding Category}" />
<Image Grid.RowSpan="2" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center"
Source="{Binding ImageFilename, Converter={StaticResource ConcatImageExtensionConverter}}" Width="50"
Height="50" />
<Line Grid.Row="1" Grid.ColumnSpan="3" X1="0" X2="1" Margin="30,20,0,0" StrokeThickness="1"
Stroke="LightGray" Stretch="Fill" VerticalAlignment="Bottom" />
</Grid>
</DataTemplate>

DataTemplate especifica los controles utilizados para mostrar el contenido de la celda y su diseño y apariencia.
Dos controles de TextBlock y un control de Image se usan para mostrar el contenido de la celda mediante el
enlace de datos. Además, una instancia de ConcatImageExtensionConverter se utiliza para concatenar la extensión
de archivo .jpg para cada nombre de archivo de imagen. Esto garantiza que el control Image puede cargar y
representar la imagen cuando se establece su propiedad Source .

Resumen
En este artículo se mostró cómo crear un representador personalizado para un ViewCell que se hospeda dentro
de un control ListView de Xamarin.Forms. Esto impide que se llame varias veces a los cálculos de diseño de
Xamarin.Forms durante el desplazamiento de ListView .

Vínculos relacionados
Rendimiento de ListView
CustomRendererViewCell (sample) (CustomRendererViewCell [ejemplo])
Implementación de una vista
11/07/2019 • 18 minutes to read • Edit Online

Descargar el ejemplo
Los controles de interfaz de usuario personalizados de Xamarin.Forms deben derivar de la clase View, que se usa
para colocar diseños y controles en la pantalla. En este artículo se muestra cómo crear un representador
personalizado para un control personalizado de Xamarin.Forms que se usa para mostrar una secuencia de vídeo
de vista previa de la cámara del dispositivo.
Todas las vistas de Xamarin.Forms tienen un representador adjunto para cada plataforma que crea una instancia
de un control nativo. Cuando una aplicación de Xamarin.Forms representa una View en iOS se crea la instancia de
la clase ViewRenderer , que a su vez crea una instancia del control UIView nativo. En la plataforma de Android, la
clase ViewRenderer crea una instancia de un control View nativo. En Plataforma universal de Windows (UWP ), la
clase ViewRenderer crea una instancia de un control FrameworkElement nativo. Para obtener más información sobre
el representador y las clases de control nativo a las que se asignan los controles de Xamarin.Forms, vea Renderer
Base Classes and Native Controls (Clases base y controles nativos del representador).
El siguiente diagrama muestra la relación entre la clase View y los controles nativos correspondientes que la
implementan:

El proceso de representación puede usarse para implementar personalizaciones específicas de plataforma al crear
un representador personalizado para una clase View en cada plataforma. Para hacerlo, siga este procedimiento:
1. Cree un control personalizado de Xamarin.Forms.
2. Use el control personalizado de Xamarin.Forms.
3. Cree el representador personalizado para el control en cada plataforma.
Ahora se analizará en detalle cada elemento, para implementar un representador CameraPreview que muestre una
secuencia de vídeo de vista previa de la cámara del dispositivo. Pulsar en la secuencia de vídeo la detendrá e
iniciará.

Creación de un control personalizado


Se puede crear un control personalizado mediante la creación de subclases de la clase View , como se muestra en
el siguiente ejemplo de código:
public class CameraPreview : View
{
public static readonly BindableProperty CameraProperty = BindableProperty.Create (
propertyName: "Camera",
returnType: typeof(CameraOptions),
declaringType: typeof(CameraPreview),
defaultValue: CameraOptions.Rear);

public CameraOptions Camera {


get { return (CameraOptions)GetValue (CameraProperty); }
set { SetValue (CameraProperty, value); }
}
}

El control personalizado CameraPreview se crea en el proyecto de biblioteca de .NET Standard y define la API del
control. El control personalizado expone una propiedad Camera que se usa para controlar si se debe mostrar la
secuencia de vídeo desde la cámara delantera o trasera del dispositivo. Si no se especifica un valor para la
propiedad Camera cuando se crea el control, el valor predeterminado es especificar la cámara trasera.

Uso del control personalizado


En XAML, se puede hacer referencia al control personalizado CameraPreview en el proyecto de biblioteca de .NET
Standard mediante la declaración de un espacio de nombres para su ubicación y el uso del prefijo del espacio de
nombres en el elemento de control personalizado. El siguiente ejemplo de código muestra cómo se puede usar el
control personalizado CameraPreview en una página XAML:

<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
<ContentPage.Content>
<StackLayout>
<Label Text="Camera Preview:" />
<local:CameraPreview Camera="Rear"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

El prefijo de espacio de nombres local puede tener cualquier nombre. Pero los valores clr-namespace y
assembly deben coincidir con los detalles del control personalizado. Una vez que se declara el espacio de nombres,
el prefijo se usa para hacer referencia al control personalizado.
El siguiente ejemplo de código muestra cómo se puede usar el control personalizado CameraPreview en una página
C#:
public class MainPageCS : ContentPage
{
public MainPageCS ()
{
...
Content = new StackLayout {
Children = {
new Label { Text = "Camera Preview:" },
new CameraPreview {
Camera = CameraOptions.Rear,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
}
}
};
}
}

Se usará una instancia del control personalizado CameraPreview para mostrar la secuencia de vídeo de vista previa
de la cámara del dispositivo. Aparte de especificar opcionalmente un valor para la propiedad Camera , la
personalización del control se llevará a cabo en el representador personalizado.
Ahora se puede agregar un representador personalizado a cada proyecto de aplicación para crear controles de
vista previa de cámara específicos de la plataforma.

Creación del representador personalizado en cada plataforma


El proceso para crear la clase del representador personalizado es el siguiente:
1. Cree una subclase de la clase ViewRenderer<T1,T2> que representa el control personalizado. El primer
argumento de tipo debe ser el control personalizado para el que es el representador, en este caso
CameraPreview . El segundo argumento de tipo debe ser el control nativo que va a implementar el control
personalizado.
2. Invalide el método OnElementChanged que representa el control personalizado y escriba lógica para
personalizarlo. Se llama a este método cuando se crea el correspondiente control de Xamarin.Forms.
3. Agregue un atributo ExportRenderer a la clase del representador personalizado para especificar que se va a
usar para representar el control personalizado de Xamarin.Forms. Este atributo se usa para registrar el
representador personalizado con Xamarin.Forms.

NOTE
Para la mayoría de los elementos de Xamarin.Forms, proporcionar un representador personalizado en cada proyecto de la
plataforma es un paso opcional. Si no hay un representador personalizado registrado, se usa el representador
predeterminado de la clase base del control. Pero los representadores personalizados son necesarios en cada proyecto de
plataforma al representar un elemento View.

El siguiente diagrama muestra las responsabilidades de cada proyecto de la aplicación de ejemplo, junto con las
relaciones entre ellos:
El control personalizado CameraPreview se representa mediante clases de representador específicas de la
plataforma, que se derivan de la clase ViewRenderer de cada plataforma. Esto da lugar a que cada control
personalizado CameraPreview se represente con controles específicos de la plataforma, como se muestra en las
capturas de pantalla siguientes:

La clase ViewRenderer expone el método OnElementChanged , al que se llama cuando se crea el control
personalizado de Xamarin.Forms para representar el control nativo correspondiente. Este método toma un
parámetro ElementChangedEventArgs que contiene propiedades OldElement y NewElement . Estas propiedades
representan al elemento de Xamarin.Forms al que estaba asociado el representador y al elemento de
Xamarin.Forms al que está asociado el representador, respectivamente. En la aplicación de ejemplo, la propiedad
OldElement es null y la propiedad NewElement contiene una referencia a la instancia de CameraPreview .

Una versión invalidada del método OnElementChanged , en cada clase de representador específica de la plataforma,
es el lugar en el que realizar la personalización y la creación de instancias del control nativo. Se debe usar el
método SetNativeControl para crear instancias del control nativo; además, este método también asigna la
referencia del control a la propiedad Control . Además, se puede obtener una referencia al control de
Xamarin.Forms que se representa mediante la propiedad Element .
En algunas circunstancias, se puede llamar al método OnElementChanged varias veces. Por lo tanto, para evitar
pérdidas de memoria, se debe tener cuidado a la hora de crear instancias de un nuevo control nativo. El enfoque
que usar al crear instancias de un nuevo control nativo en un presentador personalizado se muestra en el ejemplo
de código siguiente:
protected override void OnElementChanged (ElementChangedEventArgs<NativeListView> e)
{
base.OnElementChanged (e);

if (e.OldElement != null) {
// Unsubscribe from event handlers and cleanup any resources
}

if (e.NewElement != null) {
if (Control == null) {
// Instantiate the native control and assign it to the Control property with
// the SetNativeControl method
}
// Configure the control and subscribe to event handlers
}
}

Solo se debe crear una instancia de un nuevo control nativo una vez, cuando la propiedad Control es null .
Además, solo se debe crear, configurar el control y suscribir los controladores de eventos cuando se adjunta el
presentador personalizado a un nuevo elemento de Xamarin.Forms. De forma similar, solo se debe cancelar la
suscripción de los controladores de eventos que se han suscrito cuando cambia el elemento al que está asociado el
representador. La adopción de este enfoque ayuda a crear un representador personalizado eficaz que no sufra
pérdidas de memoria.

IMPORTANT
El método SetNativeControl solo se debe llamar si e.NewElement no es null .

Cada clase de representador personalizado se decora con un atributo ExportRenderer que registra el
representador con Xamarin.Forms. El atributo toma dos parámetros: el nombre de tipo del control personalizado
de Xamarin.Forms que se va a representar y el nombre de tipo del representador personalizado. El prefijo
assembly para el atributo especifica que el atributo se aplica a todo el ensamblado.

En las secciones siguientes se describe la implementación de cada clase de representador personalizado específico
de plataforma.
Creación del representador personalizado en iOS
El siguiente ejemplo de código muestra el representador personalizado para la plataforma iOS:
[assembly: ExportRenderer (typeof(CameraPreview), typeof(CameraPreviewRenderer))]
namespace CustomRenderer.iOS
{
public class CameraPreviewRenderer : ViewRenderer<CameraPreview, UICameraPreview>
{
UICameraPreview uiCameraPreview;

protected override void OnElementChanged (ElementChangedEventArgs<CameraPreview> e)


{
base.OnElementChanged (e);

if (e.OldElement != null) {
// Unsubscribe
uiCameraPreview.Tapped -= OnCameraPreviewTapped;
}
if (e.NewElement != null) {
if (Control == null) {
uiCameraPreview = new UICameraPreview (e.NewElement.Camera);
SetNativeControl (uiCameraPreview);
}
// Subscribe
uiCameraPreview.Tapped += OnCameraPreviewTapped;
}
}

void OnCameraPreviewTapped (object sender, EventArgs e)


{
if (uiCameraPreview.IsPreviewing) {
uiCameraPreview.CaptureSession.StopRunning ();
uiCameraPreview.IsPreviewing = false;
} else {
uiCameraPreview.CaptureSession.StartRunning ();
uiCameraPreview.IsPreviewing = true;
}
}
...
}
}

Siempre que la propiedad Control sea null , se llama al método SetNativeControl para crear instancias de un
nuevo control UICameraPreview y asignar una referencia a él para la propiedad Control . El control
UICameraPreview es un control personalizado específico de la plataforma que utiliza las API de AVCapture para
proporcionar la secuencia de vista previa de la cámara. Expone un evento Tapped que controla el método
OnCameraPreviewTapped para detener e iniciar la vista previa de vídeo cuando se pulsa. Se establece una suscripción
al evento Tapped cuando el representador personalizado se adjunta a un nuevo elemento de Xamarin.Forms y
solo se cancela la suscripción cuando el elemento al que está adjunto el representador cambia.
Creación del representador personalizado en Android
En el ejemplo de código siguiente se muestra el representador personalizado para la plataforma Android:
[assembly: ExportRenderer(typeof(CustomRenderer.CameraPreview), typeof(CameraPreviewRenderer))]
namespace CustomRenderer.Droid
{
public class CameraPreviewRenderer : ViewRenderer<CustomRenderer.CameraPreview,
CustomRenderer.Droid.CameraPreview>
{
CameraPreview cameraPreview;

public CameraPreviewRenderer(Context context) : base(context)


{
}

protected override void OnElementChanged(ElementChangedEventArgs<CustomRenderer.CameraPreview> e)


{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// Unsubscribe
cameraPreview.Click -= OnCameraPreviewClicked;
}
if (e.NewElement != null)
{
if (Control == null)
{
cameraPreview = new CameraPreview(Context);
SetNativeControl(cameraPreview);
}
Control.Preview = Camera.Open((int)e.NewElement.Camera);

// Subscribe
cameraPreview.Click += OnCameraPreviewClicked;
}
}

void OnCameraPreviewClicked(object sender, EventArgs e)


{
if (cameraPreview.IsPreviewing)
{
cameraPreview.Preview.StopPreview();
cameraPreview.IsPreviewing = false;
}
else
{
cameraPreview.Preview.StartPreview();
cameraPreview.IsPreviewing = true;
}
}
...
}
}

Siempre que la propiedad Control sea null , se llama al método SetNativeControl para crear instancias de un
nuevo control CameraPreview y asignar una referencia a él para la propiedad Control . El control CameraPreview es
un control personalizado específico de la plataforma que utiliza la API de Camera para proporcionar la secuencia
de vista previa de la cámara. Después, el control CameraPreview se configura, siempre que el representador
personalizado esté asociado a un nuevo elemento de Xamarin.Forms. Esta configuración implica la creación de un
nuevo objeto Camera nativo para acceder a una cámara de hardware concreto y registrar un controlador de
eventos para procesar el evento de Click . A su vez este controlador detendrá e iniciará la vista previa de vídeo
cuando se pulse. Se cancela la suscripción del evento Click solo si cambia el representador al que está adjunto el
elemento de Xamarin.Forms.
Creación del representador personalizado en UWP
En el siguiente ejemplo de código se muestra el representador personalizado para UWP:

[assembly: ExportRenderer(typeof(CameraPreview), typeof(CameraPreviewRenderer))]


namespace CustomRenderer.UWP
{
public class CameraPreviewRenderer : ViewRenderer<CameraPreview, Windows.UI.Xaml.Controls.CaptureElement>
{
...
CaptureElement _captureElement;
bool _isPreviewing;

protected override void OnElementChanged(ElementChangedEventArgs<CameraPreview> e)


{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// Unsubscribe
Tapped -= OnCameraPreviewTapped;
...
}
if (e.NewElement != null)
{
if (Control == null)
{
...
_captureElement = new CaptureElement();
_captureElement.Stretch = Stretch.UniformToFill;

SetupCamera();
SetNativeControl(_captureElement);
}
// Subscribe
Tapped += OnCameraPreviewTapped;
}
}

async void OnCameraPreviewTapped(object sender, TappedRoutedEventArgs e)


{
if (_isPreviewing)
{
await StopPreviewAsync();
}
else
{
await StartPreviewAsync();
}
}
...
}
}

Siempre que la propiedad Control sea null , se crea una instancia de un nuevo CaptureElement y se llama al
método SetupCamera , que usa la API de MediaCapture para proporcionar la secuencia de vista previa de la cámara.
Después se llama al método SetNativeControl para asignar una referencia a la instancia de CaptureElement para la
propiedad Control . El control CaptureElement expone un evento Tapped que controla el método
OnCameraPreviewTapped para detener e iniciar la vista previa de vídeo cuando se pulsa. Se establece una suscripción
al evento Tapped cuando el representador personalizado se adjunta a un nuevo elemento de Xamarin.Forms y
solo se cancela la suscripción cuando el elemento al que está adjunto el representador cambia.
NOTE
Es importante detener y eliminar los objetos que proporcionan acceso a la cámara en una aplicación de UWP. Si no lo hace
puede interferir con otras aplicaciones que intentan acceder a la cámara del dispositivo. Para obtener más información, vea
Display the camera preview (Mostar la vista previa de la cámara).

Resumen
En este artículo se mostró cómo crear un representador personalizado para un control personalizado de
Xamarin.Forms que se usa para mostrar una secuencia de vídeo de vista previa de la cámara del dispositivo. Los
controles de interfaces de usuario personalizadas de Xamarin.Forms deben derivar de la clase View , que se usa
para colocar los diseños y los controles en la pantalla.

Vínculos relacionados
CustomRendererView (sample) (CustomRendererView [ejemplo])
Implementación de HybridWebView
11/07/2019 • 31 minutes to read • Edit Online

Descargar el ejemplo
Los controles de interfaz de usuario personalizados de Xamarin.Forms deben derivar de la clase View, que se usa
para colocar diseños y controles en la pantalla. En este artículo se muestra cómo crear un representador
personalizado para un control personalizado HybridWebView, lo que mejora los controles web específicos de la
plataforma para permitir la invocación de código de C# desde JavaScript.
Todas las vistas de Xamarin.Forms tienen un representador adjunto para cada plataforma que crea una instancia
de un control nativo. Cuando una aplicación de Xamarin.Forms representa una instancia de View en iOS, se crea
una instancia de la clase ViewRenderer , que a su vez crea una instancia del control UIView nativo. En la plataforma
Android, la clase ViewRenderer crea una instancia de un control View . En Plataforma universal de Windows
(UWP ), la clase ViewRenderer crea una instancia de un control FrameworkElement nativo. Para obtener más
información sobre el representador y las clases de control nativo a las que se asignan los controles de
Xamarin.Forms, vea Renderer Base Classes and Native Controls (Clases base y controles nativos del
representador).
El siguiente diagrama muestra la relación entre la clase View y los controles nativos correspondientes que la
implementan:

El proceso de representación puede usarse para implementar personalizaciones específicas de plataforma al crear
un representador personalizado para una clase View en cada plataforma. Para ello, siga este procedimiento:
1. Cree el control HybridWebView personalizado.
2. Use el elemento HybridWebView de Xamarin.Forms.
3. Cree el representador personalizado para el elemento HybridWebView en cada plataforma.
Ahora se va a hablar de cada elemento para implementar un representador de HybridWebView que mejore los
controles web específicos de la plataforma a fin de permitir la invocación de código de C# desde JavaScript. Se usa
la instancia de HybridWebView para mostrar una página HTML que pide al usuario que escriba su nombre. Luego,
cuando el usuario hace clic en un botón HTML, una función de JavaScript invoca a un elemento Action de C# que
muestra una ventana emergente que contiene el nombre de los usuarios.
Para obtener más información sobre el proceso de invocación de C# desde JavaScript, vea Invocación a C# desde
JavaScript. Para obtener más información sobre la página HTML, vea Creación de la página web.
Creación de HybridWebView
Se puede crear el control personalizado HybridWebView mediante la creación de subclases de la clase View , como
se muestra en el siguiente ejemplo de código:

public class HybridWebView : View


{
Action<string> action;
public static readonly BindableProperty UriProperty = BindableProperty.Create (
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(HybridWebView),
defaultValue: default(string));

public string Uri {


get { return (string)GetValue (UriProperty); }
set { SetValue (UriProperty, value); }
}

public void RegisterAction (Action<string> callback)


{
action = callback;
}

public void Cleanup ()


{
action = null;
}

public void InvokeAction (string data)


{
if (action == null || data == null) {
return;
}
action.Invoke (data);
}
}

El control HybridWebView personalizado se crea en el proyecto de biblioteca de .NET Standard y define la siguiente
API para el control:
Una propiedad Uri que especifica la dirección de la página web que se va a cargar.
Un método RegisterAction que registra un elemento Action con el control. Se invoca a la acción registrada
desde el código de JavaScript incluido en el archivo HTML al que hace referencia la propiedad Uri .
Un método CleanUp que quita la referencia al elemento registrado Action .
Un método InvokeAction que invoca al elemento registrado Action . Se llama a este método desde un
representador personalizado en cada proyecto específico de plataforma.

Uso de HybridWebView
En XAML, se puede hacer referencia al control personalizado HybridWebView en el proyecto de biblioteca de .NET
Standard al declarar un espacio de nombres para su ubicación y usar el prefijo del espacio de nombres en el
control personalizado. El siguiente ejemplo de código muestra cómo se puede usar el control personalizado
HybridWebView en una página XAML:
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
x:Class="CustomRenderer.HybridWebViewPage"
Padding="0,20,0,0">
<ContentPage.Content>
<local:HybridWebView x:Name="hybridWebView" Uri="index.html"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</ContentPage.Content>
</ContentPage>

El prefijo de espacio de nombres local puede tener cualquier nombre. Pero los valores clr-namespace y
assembly deben coincidir con los detalles del control personalizado. Una vez que se declara el espacio de nombres,
el prefijo se usa para hacer referencia al control personalizado.
El siguiente ejemplo de código muestra cómo se puede usar el control personalizado HybridWebView en una página
C#:

public class HybridWebViewPageCS : ContentPage


{
public HybridWebViewPageCS ()
{
var hybridWebView = new HybridWebView {
Uri = "index.html",
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
};
...
Padding = new Thickness (0, 20, 0, 0);
Content = hybridWebView;
}
}

La instancia de HybridWebView se usa para mostrar un control web nativo en cada plataforma. Su propiedad Uri
se establece en un archivo HTML que se almacena en cada proyecto específico de plataforma y que muestra el
control web nativo. El HTML representado pide al usuario que escriba su nombre, con una función de JavaScript
que invoca a un elemento Action de C# en respuesta a un clic de botón HTML.
HybridWebViewPage registra la acción que se va a invocar desde JavaScript, como se muestra en el ejemplo de
código siguiente:

public partial class HybridWebViewPage : ContentPage


{
public HybridWebViewPage ()
{
...
hybridWebView.RegisterAction (data => DisplayAlert ("Alert", "Hello " + data, "OK"));
}
}

Esta acción llama al método DisplayAlert para mostrar un elemento emergente modal que presenta el nombre
especificado en la página HTML que muestra la instancia de HybridWebView .
Ahora se puede agregar un representador personalizado a cada proyecto de aplicación para mejorar los controles
web específicos de la plataforma al permitir la invocación de código de C# desde JavaScript.

Creación del representador personalizado en cada plataforma


El proceso para crear la clase del representador personalizado es el siguiente:
1. Cree una subclase de la clase ViewRenderer<T1,T2> que representa el control personalizado. El primer
argumento de tipo debe ser el control personalizado para el que es el representador, en este caso
HybridWebView . El segundo argumento de tipo debe ser el control nativo que va a implementar la vista
personalizada.
2. Invalide el método OnElementChanged que representa el control personalizado y escriba lógica para
personalizarlo. Se llama a este método cuando se crea el correspondiente control personalizado de
Xamarin.Forms.
3. Agregue un atributo ExportRenderer a la clase del representador personalizado para especificar que se va a
usar para representar el control personalizado de Xamarin.Forms. Este atributo se usa para registrar el
representador personalizado con Xamarin.Forms.

NOTE
Para la mayoría de los elementos de Xamarin.Forms, proporcionar un representador personalizado en cada proyecto de la
plataforma es un paso opcional. Si no hay un representador personalizado registrado, se usa el representador
predeterminado de la clase base del control. Pero los representadores personalizados son necesarios en cada proyecto de
plataforma al representar un elemento View.

El siguiente diagrama muestra las responsabilidades de cada proyecto de la aplicación de ejemplo, junto con las
relaciones entre ellos:

El control personalizado HybridWebView se representa mediante clases de representador específicas de la


plataforma, que se derivan de la clase ViewRenderer de cada plataforma. Esto da lugar a que cada control
personalizado HybridWebView se represente con controles web específicos de la plataforma, como se muestra en
las capturas de pantalla siguientes:

La clase ViewRenderer expone el método OnElementChanged , al que se llama cuando se crea el control
personalizado de Xamarin.Forms para representar el control web nativo correspondiente. Este método toma un
parámetro ElementChangedEventArgs que contiene propiedades OldElement y NewElement . Estas propiedades
representan al elemento de Xamarin.Forms al que estaba asociado el representador y al elemento de
Xamarin.Forms al que está asociado el representador, respectivamente. En la aplicación de ejemplo, la propiedad
OldElement es null y la propiedad NewElement contiene una referencia a la instancia de HybridWebView .

Una versión invalidada del método OnElementChanged , en cada clase de representador específica de la plataforma,
es el lugar en el que realizar la personalización y la creación de instancias del control web nativo. Se debe usar el
método SetNativeControl para crear instancias del control web nativo; además, este método también asigna la
referencia del control a la propiedad Control . Además, se puede obtener una referencia al control de
Xamarin.Forms que se va a representar mediante la propiedad Element .
En algunas circunstancias, se puede llamar al método OnElementChanged varias veces. Por lo tanto, para evitar
pérdidas de memoria, se debe tener cuidado a la hora de crear instancias de un nuevo control nativo. El enfoque
que usar al crear instancias de un nuevo control nativo en un presentador personalizado se muestra en el ejemplo
de código siguiente:

protected override void OnElementChanged (ElementChangedEventArgs<NativeListView> e)


{
base.OnElementChanged (e);

if (e.OldElement != null) {
// Unsubscribe from event handlers and cleanup any resources
}

if (e.NewElement != null) {
if (Control == null) {
// Instantiate the native control and assign it to the Control property with
// the SetNativeControl method
}
// Configure the control and subscribe to event handlers
}
}

Solo se debe crear una instancia de un nuevo control nativo una vez, cuando la propiedad Control es null .
Además, solo se debe crear, configurar el control y suscribir los controladores de eventos cuando se adjunta el
presentador personalizado a un nuevo elemento de Xamarin.Forms. De forma similar, solo se debe cancelar la
suscripción de los controladores de eventos que se han suscrito cuando cambia el elemento al que está asociado el
presentador. La adopción de este enfoque ayuda a crear un representador personalizado eficaz que no sufra
pérdidas de memoria.

IMPORTANT
El método SetNativeControl solo se debe llamar si e.NewElement no es null .

Cada clase de representador personalizado se decora con un atributo ExportRenderer que registra el
representador con Xamarin.Forms. El atributo toma dos parámetros: el nombre de tipo del control personalizado
de Xamarin.Forms que se va a representar y el nombre de tipo del representador personalizado. El prefijo
assembly del atributo especifica que el atributo se aplica a todo el ensamblado.

En las secciones siguientes se habla de la estructura de la página web cargada por cada control web nativo, el
proceso para invocar a C# desde JavaScript y su implementación en cada clase de representador personalizado
específico de la plataforma.
Creación de la página web
El ejemplo de código siguiente muestra la página web que va a mostrar el control personalizado HybridWebView :
<html>
<body>
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<h1>HybridWebView Test</h1>
<br/>
Enter name: <input type="text" id="name">
<br/>
<br/>
<button type="button" onclick="javascript:invokeCSCode($('#name').val());">Invoke C# Code</button>
<br/>
<p id="result">Result:</p>
<script type="text/javascript">
function log(str)
{
$('#result').text($('#result').text() + " " + str);
}

function invokeCSCode(data) {
try {
log("Sending Data:" + data);
invokeCSharpAction(data);
}
catch (err){
log(err);
}
}
</script>
</body>
</html>

La página web permite que un usuario escriba su nombre en un elemento input y proporciona un elemento
button que va a invocar a código de C# cuando se haga clic sobre él. El proceso para lograrlo es el siguiente:

Cuando el usuario hace clic en el elemento button , se llama a la función de JavaScript invokeCSCode y el valor
del elemento input se pasa a la función.
La función invokeCSCode llama a la función log para mostrar los datos que está enviando al elemento Action
de C#. Luego llama al método invokeCSharpAction para invocar al elemento Action de C#, pasando el
parámetro recibido desde el elemento input .

La función de JavaScript invokeCSharpAction no está definida en la página web, así que es cada representador
personalizado el que la inserta en ella.
En iOS, este archivo HTML se encuentra en la carpeta de contenido del proyecto de la plataforma e incluye una
acción de compilación de BundleResource. En Android, este archivo HTML se encuentra en la carpeta de
contenido o recursos del proyecto de la plataforma e incluye una acción de compilación de AndroidAsset.
Invocación de C# desde JavaScript
El proceso para invocar a C# desde JavaScript es idéntico en cada plataforma:
El representador personalizado crea un control web nativo y carga el archivo HTML especificado por la
propiedad HybridWebView.Uri .
Una vez que se ha cargado la página web, el representador personalizado inserta la función de JavaScript
invokeCSharpAction en la página web.
Cuando el usuario escribe su nombre y hace clic en el elemento HTML button , se invoca a la función
invokeCSCode , que a su vez invoca a la función invokeCSharpAction .
La función invokeCSharpAction invoca a un método del representador personalizado, que a su vez invoca al
método HybridWebView.InvokeAction .
El método HybridWebView.InvokeAction invoca al elemento registrado Action .
En las secciones siguientes se habla de cómo se implementa este proceso en cada plataforma.
Creación del representador personalizado en iOS
El ejemplo de código siguiente muestra el representador personalizado para la plataforma iOS:

[assembly: ExportRenderer (typeof(HybridWebView), typeof(HybridWebViewRenderer))]


namespace CustomRenderer.iOS
{
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, WKWebView>, IWKScriptMessageHandler
{
const string JavaScriptFunction = "function invokeCSharpAction(data)
{window.webkit.messageHandlers.invokeAction.postMessage(data);}";
WKUserContentController userController;

protected override void OnElementChanged (ElementChangedEventArgs<HybridWebView> e)


{
base.OnElementChanged (e);

if (e.OldElement != null) {
userController.RemoveAllUserScripts ();
userController.RemoveScriptMessageHandler ("invokeAction");
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup ();
}
if (e.NewElement != null) {
if (Control == null) {
userController = new WKUserContentController ();
var script = new WKUserScript (new NSString (JavaScriptFunction),
WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript (script);
userController.AddScriptMessageHandler (this, "invokeAction");

var config = new WKWebViewConfiguration { UserContentController = userController };


var webView = new WKWebView (Frame, config);
SetNativeControl (webView);
}
string fileName = Path.Combine (NSBundle.MainBundle.BundlePath, string.Format ("Content/{0}",
Element.Uri));
Control.LoadRequest (new NSUrlRequest (new NSUrl (fileName, false)));
}
}

public void DidReceiveScriptMessage (WKUserContentController userContentController, WKScriptMessage


message)
{
Element.InvokeAction (message.Body.ToString ());
}
}
}

La clase HybridWebViewRenderer carga la página web especificada en la propiedad HybridWebView.Uri en un control


nativo WKWebView y la función de JavaScript invokeCSharpAction se inserta en la página web. Una vez que el
usuario escribe su nombre y hace clic en el elemento HTML button , se ejecuta la función de JavaScript
invokeCSharpAction y se llama al método DidReceiveScriptMessage después de que se reciba un mensaje de la
página web. A su vez, este método invoca al método HybridWebView.InvokeAction , que invoca a la acción registrada
para mostrar la ventana emergente.
Esta funcionalidad se logra del siguiente modo:
Siempre que el representador personalizado está asociado a un nuevo elemento de Xamarin.Forms:
Siempre que la propiedad Control es null , se efectúan las siguientes operaciones:
Se crea una instancia de WKUserContentController , lo que permite la publicación de mensajes y la
inserción de scripts de usuario en una página web.
Se crea una instancia de WKUserScript para insertar la función de JavaScript invokeCSharpAction
en la página web una vez cargada la página web.
El método WKUserContentController.AddUserScript agrega la instancia de WKUserScript al
controlador de contenido.
El método WKUserContentController.AddScriptMessageHandler agrega un controlador de mensajes
de script denominado invokeAction a la instancia de WKUserContentController , lo que hace que la
función de JavaScript window.webkit.messageHandlers.invokeAction.postMessage(data) se defina en
todos los marcos de todas las vistas web que van a usar la instancia de WKUserContentController .
Se crea una instancia de WKWebViewConfiguration , con la instancia de WKUserContentController
establecida como controlador de contenido.
Se crean instancias de un control WKWebView y se llama al método SetNativeControl para asignar
una referencia al control WKWebView para la propiedad Control .
El método WKWebView.LoadRequest carga el archivo HTML especificado por la propiedad
HybridWebView.Uri . El código especifica que el archivo se almacena en la carpeta Content del proyecto.
Una vez que se muestra la página web, la función de JavaScript invokeCSharpAction se inserta en la
página web.
Cuando cambia el elemento al que está asociado el representador:
Se liberan recursos.

NOTE
La clase WKWebView solo se admite en iOS 8 y versiones posteriores.

Además, Info.plist debe actualizarse para que incluya los siguientes valores:

<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

Creación del representador personalizado en Android


En el ejemplo de código siguiente se muestra el representador personalizado para la plataforma Android:
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace CustomRenderer.Droid
{
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, Android.Webkit.WebView>
{
const string JavascriptFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
Context _context;

public HybridWebViewRenderer(Context context) : base(context)


{
_context = context;
}

protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)


{
base.OnElementChanged(e);

if (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
if (Control == null)
{
var webView = new Android.Webkit.WebView(_context);
webView.Settings.JavaScriptEnabled = true;
webView.SetWebViewClient(new JavascriptWebViewClient($"javascript:
{JavascriptFunction}"));
SetNativeControl(webView);
}
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
Control.LoadUrl($"file:///android_asset/Content/{Element.Uri}");
}
}
}
}

La clase HybridWebViewRenderer carga la página web especificada en la propiedad HybridWebView.Uri en un control


nativo WebView y la función de JavaScript invokeCSharpAction se inserta en la página web, una vez que la página
web ha terminado de cargarse, con la invalidación OnPageFinished en la clase JavascriptWebViewClient :

public class JavascriptWebViewClient : WebViewClient


{
string _javascript;

public JavascriptWebViewClient(string javascript)


{
_javascript = javascript;
}

public override void OnPageFinished(WebView view, string url)


{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
}
}

Una vez que el usuario escribe su nombre y hace clic en el elemento HTML button , se ejecuta la función de
JavaScript invokeCSharpAction . Esta funcionalidad se logra del siguiente modo:
Siempre que el representador personalizado está asociado a un nuevo elemento de Xamarin.Forms:
Siempre que la propiedad Control es null , se efectúan las siguientes operaciones:
Se crea una instancia nativa de WebView , JavaScript se habilita en el control y se establece una
instancia de JavascriptWebViewClient como la implementación de WebViewClient .
Se llama al método SetNativeControl para asignar una referencia al control nativo WebView para
la propiedad Control .
El método WebView.AddJavascriptInterface inserta una nueva instancia de JSBridge en el marco
principal del contexto de JavaScript de WebView y le asigna el nombre jsBridge . Esto permite acceder a
los métodos de la clase JSBridge desde JavaScript.
El método WebView.LoadUrl carga el archivo HTML especificado por la propiedad HybridWebView.Uri . El
código especifica que el archivo se almacena en la carpeta Content del proyecto.
En la clase JavascriptWebViewClient , la función de JavaScript invokeCSharpAction se inserta en la página
web una vez que esta termina de cargarse.
Cuando cambia el elemento al que está asociado el representador:
Se liberan recursos.
Cuando se ejecuta la función de JavaScript invokeCSharpAction , a su vez invoca al método JSBridge.InvokeAction ,
que se muestra en el ejemplo de código siguiente:

public class JSBridge : Java.Lang.Object


{
readonly WeakReference<HybridWebViewRenderer> hybridWebViewRenderer;

public JSBridge (HybridWebViewRenderer hybridRenderer)


{
hybridWebViewRenderer = new WeakReference <HybridWebViewRenderer> (hybridRenderer);
}

[JavascriptInterface]
[Export ("invokeAction")]
public void InvokeAction (string data)
{
HybridWebViewRenderer hybridRenderer;

if (hybridWebViewRenderer != null && hybridWebViewRenderer.TryGetTarget (out hybridRenderer))


{
hybridRenderer.Element.InvokeAction (data);
}
}
}

La clase debe derivar de Java.Lang.Object y los métodos que se exponen a JavaScript deben decorarse con los
atributos [JavascriptInterface] y [Export] . Por lo tanto, cuando se inserta la función de JavaScript
invokeCSharpAction en la página web y se ejecuta, llama al método JSBridge.InvokeAction , puesto que está
decorado con los atributos [JavascriptInterface] y [Export("invokeAction")] . A su vez, el método InvokeAction
invoca al método HybridWebView.InvokeAction , que invoca a la acción registrada para mostrar la ventana
emergente.

NOTE
Los proyectos que usan el atributo [Export] deben incluir una referencia a Mono.Android.Export , o se produce un error
del compilador.

Tenga en cuenta que la clase JSBridge mantiene un elemento WeakReference para la clase HybridWebViewRenderer .
Esto es para evitar la creación de una referencia circular entre las dos clases. Para obtener más información, vea
Referencias débiles en MSDN.
Creación del representador personalizado en UWP
En el ejemplo de código siguiente se muestra el representador personalizado para UWP:

[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]


namespace CustomRenderer.UWP
{
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, Windows.UI.Xaml.Controls.WebView>
{
const string JavaScriptFunction = "function invokeCSharpAction(data){window.external.notify(data);}";

protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)


{
base.OnElementChanged(e);

if (e.OldElement != null)
{
Control.NavigationCompleted -= OnWebViewNavigationCompleted;
Control.ScriptNotify -= OnWebViewScriptNotify;
}
if (e.NewElement != null)
{
if (Control == null)
{
SetNativeControl(new Windows.UI.Xaml.Controls.WebView());
}
Control.NavigationCompleted += OnWebViewNavigationCompleted;
Control.ScriptNotify += OnWebViewScriptNotify;
Control.Source = new Uri(string.Format("ms-appx-web:///Content//{0}", Element.Uri));
}
}

async void OnWebViewNavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)


{
if (args.IsSuccess)
{
// Inject JS script
await Control.InvokeScriptAsync("eval", new[] { JavaScriptFunction });
}
}

void OnWebViewScriptNotify(object sender, NotifyEventArgs e)


{
Element.InvokeAction(e.Value);
}
}
}

La clase HybridWebViewRenderer carga la página web especificada en la propiedad HybridWebView.Uri en un control


nativo WebView y la función de JavaScript invokeCSharpAction se inserta en la página web, una vez cargada, con el
método WebView.InvokeScriptAsync . Una vez que el usuario escribe su nombre y hace clic en el elemento HTML
button , se ejecuta la función de JavaScript invokeCSharpAction y se llama al método OnWebViewScriptNotify
después de que se reciba una notificación de la página web. A su vez, este método invoca al método
HybridWebView.InvokeAction , que invoca a la acción registrada para mostrar la ventana emergente.

Esta funcionalidad se logra del siguiente modo:


Siempre que el representador personalizado está asociado a un nuevo elemento de Xamarin.Forms:
Siempre que la propiedad Control es null , se efectúan las siguientes operaciones:
Se llama al método SetNativeControl para crear instancias de un nuevo control nativo WebView y
asignar una referencia a él para la propiedad Control .
Se registran controladores de eventos para los eventos NavigationCompleted y ScriptNotify . El evento
NavigationCompleted se desencadena cuando el control nativo WebView ha terminado de cargar el
contenido actual o si se ha producido un error de navegación. El evento ScriptNotify se desencadena
cuando el contenido del control nativo WebView usa JavaScript para pasar una cadena a la aplicación. La
página web desencadena el evento ScriptNotify mediante una llamada a window.external.notify al
pasar un parámetro string .
La propiedad WebView.Source está establecida en el URI del archivo HTML especificado por la propiedad
HybridWebView.Uri . El código da por supuesto que el archivo está almacenado en la carpeta Content del
proyecto. Una vez que se muestra la página web, se desencadena el evento NavigationCompleted y se
invoca al método OnWebViewNavigationCompleted . La función de JavaScript invokeCSharpAction se inserta
en la página web con el método WebView.InvokeScriptAsync , siempre que la navegación se haya realizado
correctamente.
Cuando cambia el elemento al que está asociado el representador:
Se cancela la suscripción a los eventos.

Resumen
En este artículo se ha mostrado cómo crear un representador personalizado para un control personalizado
HybridWebView , lo que mejora los controles web específicos de la plataforma para permitir la invocación de código
de C# desde JavaScript.

Vínculos relacionados
CustomRendererHybridWebView (ejemplo)
Call C# from JavaScript (Llamada a C# desde JavaScript)
Implementación de un reproductor de vídeo
11/07/2019 • 5 minutes to read • Edit Online

Descargar el ejemplo
A veces es conveniente reproducir archivos de vídeo en una aplicación de Xamarin.Forms. En esta serie de
artículos se explica cómo escribir representadores personalizados para iOS, Android y la plataforma Universal de
Windows (UWP ) para una clase de Xamarin.Forms denominada VideoPlayer .
En el ejemplo VideoPlayerDemos, todos los archivos que implementan y admiten VideoPlayer están en
carpetas denominadas FormsVideoLibrary y se identifican con el espacio de nombres FormsVideoLibrary o los
espacios de nombres que empiezan por FormsVideoLibrary . Esta organización y nomenclatura debería hacer que
resulte más fácil copiar los archivos del reproductor de vídeo en su propia solución de Xamarin.Forms.
VideoPlayer puede reproducir archivos de vídeo de tres tipos de orígenes:
Internet mediante una dirección URL
Un recurso insertado en la aplicación de plataforma
La biblioteca de vídeos del dispositivo
Los reproductores de vídeo necesitan controles de transporte, que son botones para reproducir y pausar el vídeo,
y una barra de posición que muestra el progreso a través del vídeo y permite al usuario ir rápidamente a una
ubicación diferente. VideoPlayer puede usar los controles de transporte y la barra de posición proporcionados
por la plataforma (como se muestra más adelante), o puede proporcionar controles de transporte personalizados
y una barra de posición. Este es el programa que se ejecuta en iOS, Android y la Plataforma universal de
Windows:

Por supuesto, puede colocar el teléfono en horizontal para tener una vista más grande.
Un reproductor de vídeo más sofisticado tendría algunas características adicionales, como un control de volumen,
un mecanismo para interrumpir el vídeo cuando entra una llamada telefónica y una manera de mantener la
pantalla activa durante la reproducción.
En los siguientes artículos se muestra progresivamente cómo se compilan los representadores de plataforma y las
clases auxiliares:
Creación de reproductores de vídeo de la plataforma
Cada plataforma necesita una clase VideoPlayerRenderer que crea y mantiene un control de reproductor de vídeo
compatible con la plataforma. En este artículo se muestra la estructura de las clases del representador y cómo se
crean los reproductores.

Reproducir vídeo web


Probablemente el origen más común de vídeos para cualquier reproductor de vídeo es Internet. En este artículo se
describe cómo se puede hacer referencia a un vídeo web y cómo usarlo como origen para el reproductor de vídeo.

Enlazar orígenes de vídeo con el reproductor


En este artículo se usa ListView para presentar una colección de vídeos para reproducirlos. Un programa
muestra cómo el archivo de código subyacente puede establecer el origen de vídeo del reproductor de vídeo, pero
un segundo programa muestra cómo puede usar el enlace de datos entre ListView y el reproductor de vídeo.

Cargar vídeos de recursos de aplicación


Los vídeos se pueden insertar como recursos en los proyectos de plataforma. En este artículo se muestra cómo
almacenar esos recursos y después cargarlos en el programa para reproducirlos en el reproductor de vídeo.

Acceder a la biblioteca de vídeos del dispositivo


Cuando se crea un vídeo con la cámara del dispositivo, el archivo de vídeo se almacena en la biblioteca de
imágenes del dispositivo. En este artículo se muestra cómo acceder al selector de imágenes del dispositivo para
seleccionar el vídeo y, luego, reproducirlo con el reproductor de vídeo.

Controles de transporte de vídeo personalizados


Aunque los reproductores de vídeo en cada plataforma proporcionan sus propios controles de transporte en
forma de botones para reproducir y pausar, puede suprimir la presentación de estos botones y proporcionar los
suyos propios. En este artículo se muestra cómo hacerlo.

Barra de posición de vídeo personalizada


Todos los reproductores de vídeo de la plataforma tienen una barra de posición que muestra el progreso del vídeo
y permite avanzar o retroceder a una posición determinada. En este artículo se muestra cómo sustituir esa barra
de posición por un control personalizado.

Vínculos relacionados
Demostraciones de reproductor de vídeo (ejemplo)
Creación de reproductores de vídeo de plataforma
11/07/2019 • 13 minutes to read • Edit Online

Descargar el ejemplo
La solución VideoPlayerDemos contiene todo el código necesario para implementar un reproductor de vídeo
para Xamarin.Forms. También incluye una serie de páginas en las que se muestra cómo usar el reproductor de
vídeo dentro de una aplicación. Todo el código VideoPlayer y sus representadores de plataforma residen en
carpetas de proyecto denominadas FormsVideoLibrary que además usan el espacio de nombres
FormsVideoLibrary . Esto debería facilitar la copia de los archivos en la propia aplicación y la referencia a las clases.

Reproductor de vídeo
La clase VideoPlayer forma parte de la biblioteca de .NET Standard VideoPlayerDemos compartida entre las
plataformas. Deriva de View :

using System;
using Xamarin.Forms;

namespace FormsVideoLibrary
{
public class VideoPlayer : View, IVideoPlayerController
{
···
}
}

Los miembros de esta clase (y la interfaz IVideoPlayerController ) se describen en los artículos siguientes.
Cada una de las plataformas contiene una clase denominada VideoPlayerRenderer que contiene el código
específico de la plataforma en la que se va a implementar un reproductor de vídeo. La tarea principal de este
representador es crear un reproductor de vídeo para esa plataforma.
Controlador de vistas del reproductor de iOS
Hay varias clases involucradas en la implementación de un reproductor de vídeo en iOS. La aplicación primero
crea un elemento AVPlayerViewController y luego establece la propiedad Player en un objeto de tipo AVPlayer .
Se necesitan otras clases cuando se asigna un origen de vídeo al reproductor.
Como todos los representadores, el elemento VideoPlayerRenderer de iOS contiene un atributo ExportRenderer
que identifica la vista VideoPlayer con el representador:
using System;
using System.ComponentModel;
using System.IO;

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using UIKit;

using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(FormsVideoLibrary.VideoPlayer),
typeof(FormsVideoLibrary.iOS.VideoPlayerRenderer))]

namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
···
}
}

Por lo general, un representador que establece un control de plataforma deriva de la clase


ViewRenderer<View, NativeView> , donde View es el derivado View de Xamarin.Forms (en este caso, VideoPlayer )
y NativeView es un derivado UIView de iOS para la clase de representador. Para este representador, ese
argumento genérico simplemente se establece en UIView , por motivos que va a entender en breve.
Cuando un representador se basa en un derivado UIViewController (como este), la clase debe reemplazar la
propiedad ViewController y devolver el controlador de vistas, en este caso AVPlayerViewController . Ese es el
propósito del campo _playerViewController :
namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
AVPlayer player;
AVPlayerItem playerItem;
AVPlayerViewController _playerViewController; // solely for ViewController property

public override UIViewController ViewController => _playerViewController;

protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)


{
base.OnElementChanged(args);

if (args.NewElement != null)
{
if (Control == null)
{
// Create AVPlayerViewController
_playerViewController = new AVPlayerViewController();

// Set Player property to AVPlayer


player = new AVPlayer();
_playerViewController.Player = player;

// Use the View from the controller as the native control


SetNativeControl(_playerViewController.View);
}
···
}
}
···
}
}

La responsabilidad principal de la invalidación OnElementChanged es comprobar si la propiedad Control es null y,


si es así, crear un control de plataforma y pasarlo al método SetNativeControl . En este caso, ese objeto solo está
disponible desde la propiedad View de AVPlayerViewController . Ese derivado UIView es una clase privada
denominada AVPlayerView , pero dado que es privada, no puede especificarse explícitamente como segundo
argumento genérico de ViewRenderer .
Por lo general, la propiedad Control de la clase de representador hace referencia al elemento UIView usado para
implementar el representador, pero en este caso, la propiedad Control no se usa en ningún otro lugar.
Vista de vídeo de Android
El representador de Android para VideoPlayer se basa en la clase VideoView de Android. Pero, si se usa
VideoView por sí solo para reproducir un vídeo en una aplicación de Xamarin.Forms, el vídeo llena el área
asignada para VideoPlayer sin mantener la relación de aspecto correcta. Por este motivo (como verá en breve),
VideoView se convierte en elemento secundario de un elemento RelativeLayout de Android. Una directiva using
define ARelativeLayout para distinguirlo del elemento RelativeLayout de Xamarin.Forms, y ese es el segundo
argumento genérico de ViewRenderer :
using System;
using System.ComponentModel;
using System.IO;

using Android.Content;
using Android.Media;
using Android.Widget;
using ARelativeLayout = Android.Widget.RelativeLayout;

using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(FormsVideoLibrary.VideoPlayer),
typeof(FormsVideoLibrary.Droid.VideoPlayerRenderer))]

namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
···
public VideoPlayerRenderer(Context context) : base(context)
{
}
···
}
}

A partir de Xamarin.Forms 2.5, los representadores de Android deben incluir un constructor con un argumento
Context .

La invalidación OnElementChanged crea VideoView y RelativeLayout y establece los parámetros de diseño de


VideoView para centrar dentro de RelativeLayout .
namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
VideoView videoView;
MediaController mediaController; // Used to display transport controls
bool isPrepared;
···
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
base.OnElementChanged(args);

if (args.NewElement != null)
{
if (Control == null)
{
// Save the VideoView for future reference
videoView = new VideoView(Context);

// Put the VideoView in a RelativeLayout


ARelativeLayout relativeLayout = new ARelativeLayout(Context);
relativeLayout.AddView(videoView);

// Center the VideoView in the RelativeLayout


ARelativeLayout.LayoutParams layoutParams =
new ARelativeLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent);
layoutParams.AddRule(LayoutRules.CenterInParent);
videoView.LayoutParameters = layoutParams;

// Handle a VideoView event


videoView.Prepared += OnVideoViewPrepared;

// Use the RelativeLayout as the native control


SetNativeControl(relativeLayout);
}
···
}
···
}

protected override void Dispose(bool disposing)


{
if (Control != null && videoView != null)
{
videoView.Prepared -= OnVideoViewPrepared;
}
base.Dispose(disposing);
}

void OnVideoViewPrepared(object sender, EventArgs args)


{
isPrepared = true;
···
}
···
}
}

Un controlador del evento Prepared se asocia en este método y se desasocia en el método Dispose . Este evento
se desencadena cuando VideoView tiene suficiente información para empezar a reproducir un archivo de vídeo.
Elemento multimedia de UWP
En Plataforma universal de Windows (UWP ), el reproductor de vídeo más común es MediaElement . La
documentación de MediaElement indica que se debe usar MediaPlayerElement en su lugar cuando solo sea
necesario admitir versiones de Windows 10 a partir de la compilación 1607.
La invalidación OnElementChanged debe crear un elemento MediaElement , establecer un par de controladores de
eventos y pasar el objeto MediaElement a SetNativeControl :

using System;
using System.ComponentModel;

using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;

[assembly: ExportRenderer(typeof(FormsVideoLibrary.VideoPlayer),
typeof(FormsVideoLibrary.UWP.VideoPlayerRenderer))]

namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
base.OnElementChanged(args);

if (args.NewElement != null)
{
if (Control == null)
{
MediaElement mediaElement = new MediaElement();
SetNativeControl(mediaElement);

mediaElement.MediaOpened += OnMediaElementMediaOpened;
mediaElement.CurrentStateChanged += OnMediaElementCurrentStateChanged;
}
···
}
···
}

protected override void Dispose(bool disposing)


{
if (Control != null)
{
Control.MediaOpened -= OnMediaElementMediaOpened;
Control.CurrentStateChanged -= OnMediaElementCurrentStateChanged;
}

base.Dispose(disposing);
}
···
}
}

Los dos controladores de eventos se desasocian en el evento Dispose del representador.

Mostrar los controles de transporte


Todos los reproductores de vídeo incluidos en las plataformas admiten un conjunto predeterminado de controles
de transporte que incluyen botones para reproducir y detener y una barra para indicar la posición actual dentro del
vídeo, así como para ir a otra posición.
La clase VideoPlayer define una propiedad denominada AreTransportControlsEnabled y establece el valor
predeterminado en true :
namespace FormsVideoLibrary
{
public class VideoPlayer : View, IVideoPlayerController
{
···
// AreTransportControlsEnabled property
public static readonly BindableProperty AreTransportControlsEnabledProperty =
BindableProperty.Create(nameof(AreTransportControlsEnabled), typeof(bool), typeof(VideoPlayer),
true);

public bool AreTransportControlsEnabled


{
set { SetValue(AreTransportControlsEnabledProperty, value); }
get { return (bool)GetValue(AreTransportControlsEnabledProperty); }
}
···
}
}

Aunque esta propiedad tiene descriptores de acceso set y get , el representador solo tiene que controlar los
casos en que la propiedad esté establecida. El descriptor de acceso get simplemente devuelve el valor actual de la
propiedad.
Propiedades como AreTransportControlsEnabled se controlan en los representadores de plataforma de dos
maneras:
La primera es cuando Xamarin.Forms crea un elemento VideoPlayer . Esto se indica en la invalidación
OnElementChanged del representador si la propiedad NewElement no es null . En este momento, el
representador puede establecer su propio reproductor de vídeo de plataforma a partir del valor inicial de la
propiedad, como se ha definido en VideoPlayer .
Si la propiedad de VideoPlayer cambia más adelante, se llama al método OnElementPropertyChanged del
representador. Esto permite al representador actualizar el reproductor de vídeo de plataforma en función del
nuevo valor de la propiedad.
En las secciones siguientes se explica cómo se controla la propiedad AreTransportControlsEnabled en cada
plataforma.
Controles de reproducción de iOS
La propiedad del elemento AVPlayerViewController de iOS que controla la presentación de controles de transporte
es ShowsPlaybackControls . Así es como esa propiedad se establece en el elemento VideoViewRenderer de iOS:
namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
···
AVPlayerViewController _playerViewController; // solely for ViewController property

public override UIViewController ViewController => _playerViewController;

protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)


{
···
if (args.NewElement != null)
{
···
SetAreTransportControlsEnabled();
···
}
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)


{
base.OnElementPropertyChanged(sender, args);

if (args.PropertyName == VideoPlayer.AreTransportControlsEnabledProperty.PropertyName)
{
SetAreTransportControlsEnabled();
}
···
}

void SetAreTransportControlsEnabled()
{
((AVPlayerViewController)ViewController).ShowsPlaybackControls =
Element.AreTransportControlsEnabled;
}
···
}
}

La propiedad Element del representador hace referencia a la clase VideoPlayer .


Controlador de elementos multimedia de Android
En Android, la presentación de los controles de transporte exige crear un objeto MediaController y asociarlo al
objeto VideoView . La mecánica se muestra en el método SetAreTransportControlsEnabled :
namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
VideoView videoView;
MediaController mediaController; // Used to display transport controls
bool isPrepared;

protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)


{
···
if (args.NewElement != null)
{
···
SetAreTransportControlsEnabled();
···
}
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)


{
base.OnElementPropertyChanged(sender, args);

if (args.PropertyName == VideoPlayer.AreTransportControlsEnabledProperty.PropertyName)
{
SetAreTransportControlsEnabled();
}
···
}

void SetAreTransportControlsEnabled()
{
if (Element.AreTransportControlsEnabled)
{
mediaController = new MediaController(Context);
mediaController.SetMediaPlayer(videoView);
videoView.SetMediaController(mediaController);
}
else
{
videoView.SetMediaController(null);

if (mediaController != null)
{
mediaController.SetMediaPlayer(null);
mediaController = null;
}
}
}
···
}
}

Propiedad de controles de transporte de UWP


El elemento MediaElement de UWP define una propiedad denominada AreTransportControlsEnabled , de modo que
esa propiedad se establece desde la propiedad VideoPlayer del mismo nombre:
namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
···
if (args.NewElement != null)
{
···
SetAreTransportControlsEnabled();
···
}
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)


{
base.OnElementPropertyChanged(sender, args);

if (args.PropertyName == VideoPlayer.AreTransportControlsEnabledProperty.PropertyName)
{
SetAreTransportControlsEnabled();
}
···
}

void SetAreTransportControlsEnabled()
{
Control.AreTransportControlsEnabled = Element.AreTransportControlsEnabled;
}
···
}
}

Una propiedad más es necesaria para empezar a reproducir un vídeo: se trata de la propiedad fundamental
Source que hace referencia a un archivo de vídeo. La implementación de la propiedad Source se describe en el
siguiente artículo, Reproducción de un vídeo de web.

Vínculos relacionados
Demostraciones de reproductor de vídeo (ejemplo)
Reproducción de un vídeo web
11/07/2019 • 14 minutes to read • Edit Online

Descargar el ejemplo
La clase VideoPlayer define una propiedad Source que se usa para especificar el origen del archivo de vídeo, así
como una propiedad AutoPlay . El valor predeterminado de AutoPlay es true , lo que significa que el vídeo se
debería comenzar a reproducir de forma automática después de establecer Source :

using System;
using Xamarin.Forms;

namespace FormsVideoLibrary
{
public class VideoPlayer : View, IVideoPlayerController
{
···
// Source property
public static readonly BindableProperty SourceProperty =
BindableProperty.Create(nameof(Source), typeof(VideoSource), typeof(VideoPlayer), null);

[TypeConverter(typeof(VideoSourceConverter))]
public VideoSource Source
{
set { SetValue(SourceProperty, value); }
get { return (VideoSource)GetValue(SourceProperty); }
}

// AutoPlay property
public static readonly BindableProperty AutoPlayProperty =
BindableProperty.Create(nameof(AutoPlay), typeof(bool), typeof(VideoPlayer), true);

public bool AutoPlay


{
set { SetValue(AutoPlayProperty, value); }
get { return (bool)GetValue(AutoPlayProperty); }
}
···
}
}

La propiedad Source es de tipo VideoSource , que se modela a partir de la clase abstracta ImageSource de
Xamarin.Forms y sus tres derivadas UriImageSource , FileImageSource y StreamImageSource . Pero no hay ninguna
opción de hacer streaming disponible para VideoPlayer , porque iOS y Android no admiten la reproducción de un
vídeo desde una secuencia.

Orígenes de vídeo
La clase abstracta VideoSource consta únicamente de tres métodos estáticos que crean las instancias de las tres
clases que se derivan de VideoSource :
namespace FormsVideoLibrary
{
[TypeConverter(typeof(VideoSourceConverter))]
public abstract class VideoSource : Element
{
public static VideoSource FromUri(string uri)
{
return new UriVideoSource() { Uri = uri };
}

public static VideoSource FromFile(string file)


{
return new FileVideoSource() { File = file };
}

public static VideoSource FromResource(string path)


{
return new ResourceVideoSource() { Path = path };
}
}
}

La clase UriVideoSource se usa para especificar un archivo de vídeo descargable con un URI. Define una única
propiedad de tipo string :

namespace FormsVideoLibrary
{
public class UriVideoSource : VideoSource
{
public static readonly BindableProperty UriProperty =
BindableProperty.Create(nameof(Uri), typeof(string), typeof(UriVideoSource));

public string Uri


{
set { SetValue(UriProperty, value); }
get { return (string)GetValue(UriProperty); }
}
}
}

El control de los objetos de tipo UriVideoSource se describe a continuación.


La clase ResourceVideoSource se usa para acceder a los archivos de vídeo que se almacenan como recursos
insertados en la aplicación de plataforma, que también se especifica con una propiedad string :

namespace FormsVideoLibrary
{
public class ResourceVideoSource : VideoSource
{
public static readonly BindableProperty PathProperty =
BindableProperty.Create(nameof(Path), typeof(string), typeof(ResourceVideoSource));

public string Path


{
set { SetValue(PathProperty, value); }
get { return (string)GetValue(PathProperty); }
}
}
}

El control de los objetos de tipo ResourceVideoSource se describe en el artículo Carga de vídeos de recursos de
aplicación. La clase VideoPlayer no tiene ninguna función para cargar un archivo de vídeo almacenado como un
recurso en la biblioteca de .NET Standard.
La clase FileVideoSource se usa para acceder a los archivos de vídeo desde la biblioteca de vídeos del dispositivo.
La única propiedad también es de tipo string :

namespace FormsVideoLibrary
{
public class FileVideoSource : VideoSource
{
public static readonly BindableProperty FileProperty =
BindableProperty.Create(nameof(File), typeof(string), typeof(FileVideoSource));

public string File


{
set { SetValue(FileProperty, value); }
get { return (string)GetValue(FileProperty); }
}
}
}

El control de los objetos de tipo FileVideoSource se describe en el artículo Acceso a la biblioteca de vídeos del
dispositivo.
La clase VideoSource incluye un atributo TypeConverter que hace referencia a VideoSourceConverter :

namespace FormsVideoLibrary
{
[TypeConverter(typeof(VideoSourceConverter))]
public abstract class VideoSource : Element
{
···
}
}

Este convertidor de tipos se invoca cuando la propiedad Source se establece en una cadena en XAML. Esta es la
clase VideoSourceConverter :

namespace FormsVideoLibrary
{
public class VideoSourceConverter : TypeConverter
{
public override object ConvertFromInvariantString(string value)
{
if (!String.IsNullOrWhiteSpace(value))
{
Uri uri;
return Uri.TryCreate(value, UriKind.Absolute, out uri) && uri.Scheme != "file" ?
VideoSource.FromUri(value) : VideoSource.FromResource(value);
}

throw new InvalidOperationException("Cannot convert null or whitespace to ImageSource");


}
}
}

El método ConvertFromInvariantString intenta convertir la cadena en un objeto Uri . Si lo consigue, y el esquema


no es file: , el método devuelve un elemento UriVideoSource . De lo contrario, devuelve un elemento
ResourceVideoSource .
Establecimiento del origen de vídeo
El resto de la lógica que implica orígenes de vídeo se implementa en los representadores de cada plataforma. En
las secciones siguientes se muestra cómo los representadores de plataforma reproducen vídeos cuando la
propiedad Source se establece en un objeto UriVideoSource .
Origen de vídeo de iOS
Dos secciones de VideoPlayerRenderer están implicadas en la configuración del origen de vídeo del reproductor
de vídeo. Cuando Xamarin.Forms crea por primera vez un objeto de tipo VideoPlayer , se llama al método
OnElementChanged con la propiedad NewElement del objeto de argumentos establecida en ese objeto VideoPlayer .
El método OnElementChanged llama a SetSource :

namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
···
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
···
if (args.NewElement != null)
{
···
SetSource();
···
}
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)


{
···
else if (args.PropertyName == VideoPlayer.SourceProperty.PropertyName)
{
SetSource();
}
···
}
···
}
}

Más adelante, cuando se cambia la propiedad Source , se llama al método OnElementPropertyChanged con una
propiedad PropertyName de "Source" (Origen), y se vuelve a llamar a SetSource .
Para reproducir un archivo de vídeo en iOS, primero se crea un objeto de tipo AVAsset para encapsular el archivo
de vídeo, que se usa para crear un elemento AVPlayerItem , que después se pasa al objeto AVPlayer . Esta es la
forma en la que el método SetSource controla la propiedad Source cuando es de tipo UriVideoSource :
namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
AVPlayer player;
AVPlayerItem playerItem;
···
void SetSource()
{
AVAsset asset = null;

if (Element.Source is UriVideoSource)
{
string uri = (Element.Source as UriVideoSource).Uri;

if (!String.IsNullOrWhiteSpace(uri))
{
asset = AVAsset.FromUrl(new NSUrl(uri));
}
}
···
if (asset != null)
{
playerItem = new AVPlayerItem(asset);
}
else
{
playerItem = null;
}

player.ReplaceCurrentItemWithPlayerItem(playerItem);

if (playerItem != null && Element.AutoPlay)


{
player.Play();
}
}
···
}
}

La propiedad AutoPlay no cuenta con ninguna análoga en las clases de vídeo de iOS, por lo que se examina al
final del método SetSource para llamar al método Play en el objeto AVPlayer .
En algunos casos, los vídeos se siguen reproduciendo después de que la página con el elemento VideoPlayer
haya vuelto a la página principal. Para detener el vídeo, ReplaceCurrentItemWithPlayerItem también se establece en
la invalidación de Dispose :
namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
···
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);

if (player != null)
{
player.ReplaceCurrentItemWithPlayerItem(null);
}
}
···
}
}

Origen de vídeo de Android


El objeto VideoPlayerRenderer de Android debe establecer el origen del reproductor de vídeo al crear
VideoPlayer por primera vez y posteriormente cuando cambia la propiedad Source :

namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
···
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
···
if (args.NewElement != null)
{
···
SetSource();
···
}
}
···
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)
{
···
else if (args.PropertyName == VideoPlayer.SourceProperty.PropertyName)
{
SetSource();
}
···
}
···
}
}

El método SetSource controla los objetos de tipo UriVideoSource mediante una llamada a SetVideoUri en
VideoView con un objeto Uri de Android creado a partir de la cadena de URI. Aquí la clase Uri es completa
para distinguirla de la clase Uri de .NET:
namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
···
void SetSource()
{
isPrepared = false;
bool hasSetSource = false;

if (Element.Source is UriVideoSource)
{
string uri = (Element.Source as UriVideoSource).Uri;

if (!String.IsNullOrWhiteSpace(uri))
{
videoView.SetVideoURI(Android.Net.Uri.Parse(uri));
hasSetSource = true;
}
}
···

if (hasSetSource && Element.AutoPlay)


{
videoView.Start();
}
}
···
}
}

El objeto VideoView de Android no tiene una propiedad AutoPlay correspondiente, por lo que se llama al método
Start si se ha establecido un vídeo nuevo.

Hay una diferencia entre el comportamiento de los representadores de iOS y Android si la propiedad Source de
VideoPlayer se establece en null , o bien si la propiedad Uri de UriVideoSource se establece en null o en una
cadena vacía. Si en el reproductor de vídeo de iOS se está reproduciendo un vídeo, y Source está establecido en
null (o la cadena es null o está en blanco), se llama a ReplaceCurrentItemWithPlayerItem con el valor null . Se
reemplaza el vídeo actual y se detiene la reproducción.
Android no admite una función similar. Si la propiedad Source se establece en null , el método SetSource
simplemente la ignora y se sigue reproduciendo el vídeo actual.
Origen de vídeo de UWP
El objeto MediaElement de UWP define una propiedad AutoPlay , que se controla en el representador como
cualquier otra propiedad:
namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
···
if (args.NewElement != null)
{
···
SetSource();
SetAutoPlay();
···
}
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)


{
···
else if (args.PropertyName == VideoPlayer.SourceProperty.PropertyName)
{
SetSource();
}
else if (args.PropertyName == VideoPlayer.AutoPlayProperty.PropertyName)
{
SetAutoPlay();
}
···
}
···
}
}

La propiedad SetSource controla un objeto UriVideoSource mediante el establecimiento de la propiedad Source


de MediaElement en un valor Uri de .NET, o bien en null si la propiedad Source de VideoPlayer se establece
en null :
namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
···
async void SetSource()
{
bool hasSetSource = false;

if (Element.Source is UriVideoSource)
{
string uri = (Element.Source as UriVideoSource).Uri;

if (!String.IsNullOrWhiteSpace(uri))
{
Control.Source = new Uri(uri);
hasSetSource = true;
}
}
···
if (!hasSetSource)
{
Control.Source = null;
}
}

void SetAutoPlay()
{
Control.AutoPlay = Element.AutoPlay;
}
···
}
}

Establecimiento de un origen de dirección URL


Con la implementación de estas propiedades en los tres representadores, es posible reproducir un vídeo desde un
origen de dirección URL. La página Play Web Video (Reproducir vídeo web) del programa VideoPlayDemos
se define con el siguiente archivo XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:video="clr-namespace:FormsVideoLibrary"
x:Class="VideoPlayerDemos.PlayWebVideoPage"
Title="Play Web Video">

<video:VideoPlayer Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4" />

</ContentPage>

La clase VideoSourceConverter convierte la cadena en un elemento UriVideoSource . Cuando se desplaza a la


página Play Web Video, se empieza a cargar el vídeo y se inicia la reproducción cuando se ha descargado y
almacenado en búfer una cantidad de datos suficiente. El vídeo tiene aproximadamente 10 minutos de duración:
En cada una de las plataformas, los controles de transporte se atenúan si no se usan, pero puede pulsar en el
vídeo para restaurarlos y verlos.
Para impedir que el vídeo se inicie de forma automática, establezca la propiedad AutoPlay en false :

<video:VideoPlayer Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"
AutoPlay="false" />

Tendrá que presionar el botón Reproducir para iniciar el vídeo.


De forma similar, puede suprimir la presentación de los controles de transporte si establece la propiedad
AreTransportControlsEnabled en false :

<video:VideoPlayer Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"
AreTransportControlsEnabled="False" />

Si establece las dos propiedades en false , el vídeo no empezará a reproducirse y no habrá ninguna manera de
iniciarlo. Tendría que llamar a Play desde el archivo de código subyacente, o bien crear controles de transporte
propios como se describe en el artículo Implementación de controles de transporte de vídeo personalizados.
En el archivo App.xaml se incluyen recursos para dos vídeos adicionales:
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:video="clr-namespace:FormsVideoLibrary"
x:Class="VideoPlayerDemos.App">
<Application.Resources>
<ResourceDictionary>

<video:UriVideoSource x:Key="ElephantsDream"
Uri="https://archive.org/download/ElephantsDream/ed_hd_512kb.mp4" />

<video:UriVideoSource x:Key="BigBuckBunny"
Uri="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"
/>

<video:UriVideoSource x:Key="Sintel"
Uri="https://archive.org/download/Sintel/sintel-2048-stereo_512kb.mp4" />

</ResourceDictionary>
</Application.Resources>
</Application>

Para hacer referencia a una de estas películas, puede reemplazar la dirección URL explícita en el archivo
PlayWebVideo.xaml con una extensión de marcado StaticResource , en cuyo caso no se necesitará
VideoSourceConverter para crear el objeto UriVideoSource :

<video:VideoPlayer Source="{StaticResource ElephantsDream}" />

Como alternativa, puede establecer la propiedad Source de un archivo de vídeo en un elemento ListView , como
se describe en el artículo siguiente, Enlace de orígenes de vídeo al reproductor.

Vínculos relacionados
Demostraciones de reproductor de vídeo (ejemplo)
Enlazar orígenes de vídeo con el reproductor
11/07/2019 • 4 minutes to read • Edit Online

Descargar el ejemplo
Cuando la propiedad Source de la vista VideoPlayer se establece en un nuevo archivo de vídeo, el vídeo existente
deja de reproducirse y se inicia el nuevo vídeo. Esto se demuestra mediante la página Seleccionar vídeo web del
ejemplo VideoPlayerDemos. En esta página, se incluye un elemento ListView con los títulos de los tres vídeos a
los que se hace referencia en el archivo App.xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:video="clr-namespace:FormsVideoLibrary"
x:Class="VideoPlayerDemos.SelectWebVideoPage"
Title="Select Web Video">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<video:VideoPlayer x:Name="videoPlayer"
Grid.Row="0" />

<ListView Grid.Row="1"
ItemSelected="OnListViewItemSelected">
<ListView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Elephant's Dream</x:String>
<x:String>Big Buck Bunny</x:String>
<x:String>Sintel</x:String>
</x:Array>
</ListView.ItemsSource>
</ListView>
</Grid>
</ContentPage>

Al seleccionar un vídeo, se ejecuta el controlador de eventos ItemSelected del archivo de código subyacente. El
controlador elimina los espacios en blanco y los apóstrofos del título y usa el resultado como una clave para
obtener uno de los recursos definidos en el archivo App.xaml. Después, el objeto UriVideoSource se establece en
la propiedad Source del elemento VideoPlayer .
namespace VideoPlayerDemos
{
public partial class SelectWebVideoPage : ContentPage
{
public SelectWebVideoPage()
{
InitializeComponent();
}

void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs args)


{
if (args.SelectedItem != null)
{
string key = ((string)args.SelectedItem).Replace(" ", "").Replace("'", "");
videoPlayer.Source = (UriVideoSource)Application.Current.Resources[key];
}
}
}
}

Cuando se carga la primera página, no se selecciona ningún elemento en ListView , por lo que tendrá que
seleccionar uno para que el vídeo empiece a reproducirse:

La propiedad Source de VideoPlayer se complementa con una propiedad enlazable, lo que quiere decir que
puede ser el objetivo de un enlace de datos. Esto se demuestra mediante la página Enlazar a VideoPlayer. El
marcado del archivo es compatible con la clase siguiente BindToVideoPlayer.xaml, que encapsula un título para
un vídeo y un objeto correspondiente VideoSource :

namespace VideoPlayerDemos
{
public class VideoInfo
{
public string DisplayName { set; get; }

public VideoSource VideoSource { set; get; }

public override string ToString()


{
return DisplayName;
}
}
}
El elemento ListView del archivo BindToVideoPlayer.xaml contiene una matriz de estos objetos VideoInfo y
cada uno se inicializa con un título del vídeo y el objeto UriVideoSource del diccionario de recursos en App.xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:VideoPlayerDemos"
xmlns:video="clr-namespace:FormsVideoLibrary"
x:Class="VideoPlayerDemos.BindToVideoPlayerPage"
Title="Bind to VideoPlayer">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<video:VideoPlayer x:Name="videoPlayer"
Grid.Row="0"
Source="{Binding Source={x:Reference listView},
Path=SelectedItem.VideoSource}" />

<ListView x:Name="listView"
Grid.Row="1">
<ListView.ItemsSource>
<x:Array Type="{x:Type local:VideoInfo}">
<local:VideoInfo DisplayName="Elephant's Dream"
VideoSource="{StaticResource ElephantsDream}" />

<local:VideoInfo DisplayName="Big Buck Bunny"


VideoSource="{StaticResource BigBuckBunny}" />

<local:VideoInfo DisplayName="Sintel"
VideoSource="{StaticResource Sintel}" />
</x:Array>
</ListView.ItemsSource>
</ListView>
</Grid>
</ContentPage>

La propiedad Source del elemento VideoPlayer se enlaza al elemento ListView . El elemento Path del enlace se
especifica como SelectedItem.VideoSource , que es un trazado compuesto formado por dos propiedades:
SelectedItem es una propiedad de ListView . El elemento seleccionado es del tipo VideoInfo , que tiene una
propiedad VideoSource .
Como con la primera página Seleccionar vídeo web, de manera inicial no se selecciona ningún elemento desde
ListView , por lo que necesita seleccionar uno de los vídeos para que empiece a reproducirse.

Vínculos relacionados
Demostraciones de reproductor de vídeo (ejemplo)
Carga de vídeos de recursos de aplicación
11/07/2019 • 7 minutes to read • Edit Online

Descargar el ejemplo
Los representadores personalizados para la vista VideoPlayer son capaces de reproducir archivos de vídeo que se
han insertado en los proyectos de cada plataforma como recursos de la aplicación. Pero la versión actual de
VideoPlayer no puede acceder a los recursos insertados en una biblioteca de .NET Standard.

Para cargar estos recursos, cree una instancia de ResourceVideoSource estableciendo la propiedad Path en el
nombre de archivo (o la carpeta y el nombre de archivo) del recurso. Como alternativa, puede llamar al método
VideoSource.FromResource estático para hacer referencia al recurso. Después, establezca el objeto
ResourceVideoSource en la propiedad Source de VideoPlayer .

Almacenamiento de los archivos de vídeo


El almacenamiento de un archivo de vídeo en el proyecto de la plataforma es diferente para cada plataforma.
Recursos de vídeo de iOS
En un proyecto de iOS, puede almacenar un vídeo en la carpeta Recursos, o bien en una subcarpeta de la carpeta
Recursos. El archivo de vídeo debe tener un elemento Build Action de tipo BundleResource . Establezca la
propiedad Path de ResourceVideoSource en el nombre de archivo, por ejemplo MiArchivo.mp4 para un archivo
en la carpeta Recursos, o bien MiCarpeta/MiArchivo.mp4, donde MiCarpeta es una subcarpeta de Recursos.
En la solución VideoPlayerDemos, el proyecto VideoPlayerDemos.iOS contiene una subcarpeta de Recursos
denominada Videos que contiene un archivo denominado iOSApiVideo.mp4. Se trata de un breve vídeo en el
que se muestra cómo usar el sitio web de Xamarin para encontrar documentación para la clase
AVPlayerViewController de iOS.

Recursos de vídeo de Android


En un proyecto de Android, los vídeos se deben almacenar en una subcarpeta de Recursos denominada raw. La
carpeta raw no puede contener subcarpetas. Asigne al archivo de vídeo un elemento Build Action de tipo
AndroidResource . Establezca la propiedad Path de ResourceVideoSource en el nombre de archivo, por ejemplo,
MiArchivo.mp4.
El proyecto VideoPlayerDemos.Android contiene una subcarpeta de Recursos denominada raw, que contiene
un archivo denominado AndroidApiVideo.mp4.
Recursos de vídeo de UWP
En un proyecto para Plataforma Universal de Windows, puede almacenar vídeos en cualquier carpeta del
proyecto. Asigne al archivo un elemento Build Action de tipo Content . Establezca la propiedad Path de
ResourceVideoSource en la carpeta y el nombre de archivo, por ejemplo, MiCarpeta/MiVideo.mp4.

El proyecto VideoPlayerDemos.UWP contiene una carpeta denominada Videos con el archivo


UWPApiVideo.mp4.

Carga de los archivos de vídeo


Cada una de las clases de representador de plataforma contiene código en su método SetSource para cargar los
archivos de vídeo almacenados como recursos.
Carga de recursos de iOS
La versión de iOS de VideoPlayerRenderer usa el método GetUrlForResource de NSBundle para cargar el recurso.
La ruta de acceso completa se debe dividir en un nombre de archivo, una extensión y un directorio. En el código se
usa la clase Path del espacio de nombres System.IO de .NET para dividir la ruta de acceso en estos
componentes:

namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
···
void SetSource()
{
AVAsset asset = null;
···
else if (Element.Source is ResourceVideoSource)
{
string path = (Element.Source as ResourceVideoSource).Path;

if (!String.IsNullOrWhiteSpace(path))
{
string directory = Path.GetDirectoryName(path);
string filename = Path.GetFileNameWithoutExtension(path);
string extension = Path.GetExtension(path).Substring(1);
NSUrl url = NSBundle.MainBundle.GetUrlForResource(filename, extension, directory);
asset = AVAsset.FromUrl(url);
}
}
···
}
···
}
}

Carga de recursos de Android


El elemento VideoPlayerRenderer de Android usa el nombre de paquete y el nombre de archivo para construir un
objeto Uri . El nombre del paquete es el nombre de la aplicación, en este caso VideoPlayerDemos.Android,
que se puede obtener de la propiedad estática Context.PackageName . Después, el objeto Uri resultante se pasa al
método SetVideoURI de VideoView :
namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
···
void SetSource()
{
isPrepared = false;
bool hasSetSource = false;
···
else if (Element.Source is ResourceVideoSource)
{
string package = Context.PackageName;
string path = (Element.Source as ResourceVideoSource).Path;

if (!String.IsNullOrWhiteSpace(path))
{
string filename = Path.GetFileNameWithoutExtension(path).ToLowerInvariant();
string uri = "android.resource://" + package + "/raw/" + filename;
videoView.SetVideoURI(Android.Net.Uri.Parse(uri));
hasSetSource = true;
}
}
···
}
···
}
}

Carga de recursos de UWP


El elemento VideoPlayerRenderer de UWP crea un objeto Uri para la ruta de acceso y lo establece en la
propiedad Source de MediaElement :

namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
···
async void SetSource()
{
bool hasSetSource = false;
···
else if (Element.Source is ResourceVideoSource)
{
string path = "ms-appx:///" + (Element.Source as ResourceVideoSource).Path;

if (!String.IsNullOrWhiteSpace(path))
{
Control.Source = new Uri(path);
hasSetSource = true;
}
}
}
···
}
}

Reproducción del archivo de recursos


En la página Reproducir recurso de vídeo de la solución VideoPlayerDemos se usa la clase OnPlatform para
especificar el archivo de vídeo para cada plataforma:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:video="clr-namespace:FormsVideoLibrary"
x:Class="VideoPlayerDemos.PlayVideoResourcePage"
Title="Play Video Resource">
<video:VideoPlayer>
<video:VideoPlayer.Source>
<video:ResourceVideoSource>
<video:ResourceVideoSource.Path>
<OnPlatform x:TypeArguments="x:String">
<On Platform="iOS" Value="Videos/iOSApiVideo.mp4" />
<On Platform="Android" Value="AndroidApiVideo.mp4" />
<On Platform="UWP" Value="Videos/UWPApiVideo.mp4" />
</OnPlatform>
</video:ResourceVideoSource.Path>
</video:ResourceVideoSource>
</video:VideoPlayer.Source>
</video:VideoPlayer>
</ContentPage>

Si el recurso de iOS se almacena en la carpeta Recursos, y si el recurso de UWP se almacena en la carpeta raíz del
proyecto, puede usar el mismo nombre de archivo para cada plataforma. En ese caso, puede establecer ese
nombre directamente en la propiedad Source de VideoPlayer .
Esta es la ejecución de la página:

Ya ha visto cómo cargar vídeos desde un URI web y cómo reproducir recursos insertados. Además, puede cargar
vídeos desde la biblioteca de vídeos del dispositivo.

Vínculos relacionados
Demostraciones de reproductor de vídeo (ejemplo)
Acceder a la biblioteca de vídeos del dispositivo
11/07/2019 • 9 minutes to read • Edit Online

Descargar el ejemplo
Los dispositivos móviles y los equipos de escritorio más modernos tienen la capacidad de grabar vídeos mediante
la cámara que llevan incorporada. Los vídeos que un usuario crea se almacenan como archivos en el dispositivo.
Estos archivos se pueden recuperar de la biblioteca de imágenes, y la clase VideoPlayer puede reproducirlos
igual que cualquier otro vídeo.

El servicio de dependencia del selector de fotos


Cada una de las plataformas incluye una utilidad que permite al usuario seleccionar una foto o un vídeo de la
biblioteca de imágenes del dispositivo. El primer paso para reproducir un vídeo de la biblioteca de imágenes del
dispositivo consiste en crear un servicio de dependencia que invoque al selector de imagen en cada plataforma. El
servicio de dependencia descrito anteriormente es muy similar al que se define en el artículo Seleccionar una
foto de la biblioteca de imágenes, salvo que el selector de vídeo devuelve un nombre de archivo en lugar de
un objeto Stream .
El proyecto de la biblioteca de .NET Standard define una interfaz denominada IVideoPicker para el servicio de
dependencia:

namespace FormsVideoLibrary
{
public interface IVideoPicker
{
Task<string> GetVideoFileAsync();
}
}

Cada una de las plataformas contiene una clase denominada VideoPicker que implementa esta interfaz.
El selector de vídeo de iOS
VideoPicker de iOS utiliza UIImagePickerController para acceder a la biblioteca de imágenes y especifica que
dicho acceso debe restringirse a los vídeos (denominados "películas") de la propiedad MediaType de iOS. Tenga
en cuenta que VideoPicker implementa explícitamente la interfaz de IVideoPicker . Tenga en cuenta también el
atributo Dependency que identifica esta clase como un servicio de dependencia. Estos son los dos requisitos que
permiten a Xamarin.Forms encontrar el servicio de dependencia en el proyecto de la plataforma:
using System;
using System.Threading.Tasks;
using UIKit;
using Xamarin.Forms;

[assembly: Dependency(typeof(FormsVideoLibrary.iOS.VideoPicker))]

namespace FormsVideoLibrary.iOS
{
public class VideoPicker : IVideoPicker
{
TaskCompletionSource<string> taskCompletionSource;
UIImagePickerController imagePicker;

public Task<string> GetVideoFileAsync()


{
// Create and define UIImagePickerController
imagePicker = new UIImagePickerController
{
SourceType = UIImagePickerControllerSourceType.SavedPhotosAlbum,
MediaTypes = new string[] { "public.movie" }
};

// Set event handlers


imagePicker.FinishedPickingMedia += OnImagePickerFinishedPickingMedia;
imagePicker.Canceled += OnImagePickerCancelled;

// Present UIImagePickerController;
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
viewController.PresentModalViewController(imagePicker, true);

// Return Task object


taskCompletionSource = new TaskCompletionSource<string>();
return taskCompletionSource.Task;
}

void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)


{
if (args.MediaType == "public.movie")
{
taskCompletionSource.SetResult(args.MediaUrl.AbsoluteString);
}
else
{
taskCompletionSource.SetResult(null);
}
imagePicker.DismissModalViewController(true);
}

void OnImagePickerCancelled(object sender, EventArgs args)


{
taskCompletionSource.SetResult(null);
imagePicker.DismissModalViewController(true);
}
}
}

El selector de vídeo de Android


La implementación de Android de IVideoPicker requiere un método de devolución de llamada que forma parte
de la actividad de la aplicación. Por ese motivo, la clase MainActivity define dos propiedades, un campo y un
método de devolución de llamada:
namespace VideoPlayerDemos.Droid
{
···
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
Current = this;
···
}

// Field, properties, and method for Video Picker


public static MainActivity Current { private set; get; }

public static readonly int PickImageId = 1000;

public TaskCompletionSource<string> PickImageTaskCompletionSource { set; get; }

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)


{
base.OnActivityResult(requestCode, resultCode, data);

if (requestCode == PickImageId)
{
if ((resultCode == Result.Ok) && (data != null))
{
// Set the filename as the completion of the Task
PickImageTaskCompletionSource.SetResult(data.DataString);
}
else
{
PickImageTaskCompletionSource.SetResult(null);
}
}
}
}
}

El método OnCreate en MainActivity almacena su propia instancia en la propiedad Current estática. Esto
permite a la implementación de IVideoPicker obtener la instancia de MainActivity para iniciar el selector
Seleccione vídeo:
using System;
using System.Threading.Tasks;
using Android.Content;
using Xamarin.Forms;

// Need application's MainActivity


using VideoPlayerDemos.Droid;

[assembly: Dependency(typeof(FormsVideoLibrary.Droid.VideoPicker))]

namespace FormsVideoLibrary.Droid
{
public class VideoPicker : IVideoPicker
{
public Task<string> GetVideoFileAsync()
{
// Define the Intent for getting images
Intent intent = new Intent();
intent.SetType("video/*");
intent.SetAction(Intent.ActionGetContent);

// Get the MainActivity instance


MainActivity activity = MainActivity.Current;

// Start the picture-picker activity (resumes in MainActivity.cs)


activity.StartActivityForResult(
Intent.CreateChooser(intent, "Select Video"),
MainActivity.PickImageId);

// Save the TaskCompletionSource object as a MainActivity property


activity.PickImageTaskCompletionSource = new TaskCompletionSource<string>();

// Return Task object


return activity.PickImageTaskCompletionSource.Task;
}
}
}

Las adiciones al objeto MainActivity son el único código de la solución VideoPlayerDemos en que el código de
aplicación normal debe modificarse para admitir las clases FormsVideoLibrary .
El selector de vídeo de UWP
La implementación de UWP de la interfaz de IVideoPicker utiliza el FileOpenPicker de UWP. Inicia la búsqueda
de archivos con la biblioteca de imágenes y restringe los tipos de archivo a MP4 y WMV (vídeo de Windows
Media):
using System;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Pickers;
using Xamarin.Forms;

[assembly: Dependency(typeof(FormsVideoLibrary.UWP.VideoPicker))]

namespace FormsVideoLibrary.UWP
{
public class VideoPicker : IVideoPicker
{
public async Task<string> GetVideoFileAsync()
{
// Create and initialize the FileOpenPicker
FileOpenPicker openPicker = new FileOpenPicker
{
ViewMode = PickerViewMode.Thumbnail,
SuggestedStartLocation = PickerLocationId.PicturesLibrary
};

openPicker.FileTypeFilter.Add(".wmv");
openPicker.FileTypeFilter.Add(".mp4");

// Get a file and return the path


StorageFile storageFile = await openPicker.PickSingleFileAsync();
return storageFile?.Path;
}
}
}

Invocar el servicio de dependencia


En la página Reproducir vídeo de la biblioteca del programa VideoPlayerDemos se muestra cómo utilizar el
servicio de dependencia del selector de vídeo. El archivo XAML contiene una instancia de VideoPlayer y un
Button con la etiqueta Mostrar la biblioteca de vídeos:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:video="clr-namespace:FormsVideoLibrary"
x:Class="VideoPlayerDemos.PlayLibraryVideoPage"
Title="Play Library Video">
<StackLayout>
<video:VideoPlayer x:Name="videoPlayer"
VerticalOptions="FillAndExpand" />

<Button Text="Show Video Library"


Margin="10"
HorizontalOptions="Center"
Clicked="OnShowVideoLibraryClicked" />
</StackLayout>
</ContentPage>

El archivo de código subyacente contiene el controlador de Clicked para el Button . Para invocar el servicio de
dependencia es necesario llamar a DependencyService.Get para obtener la implementación de una interfaz de
IVideoPicker en el proyecto de plataforma. A continuación, se llama al método GetVideoFileAsync en esa
instancia:
namespace VideoPlayerDemos
{
public partial class PlayLibraryVideoPage : ContentPage
{
public PlayLibraryVideoPage()
{
InitializeComponent();
}

async void OnShowVideoLibraryClicked(object sender, EventArgs args)


{
Button btn = (Button)sender;
btn.IsEnabled = false;

string filename = await DependencyService.Get<IVideoPicker>().GetVideoFileAsync();

if (!String.IsNullOrWhiteSpace(filename))
{
videoPlayer.Source = new FileVideoSource
{
File = filename
};
}

btn.IsEnabled = true;
}
}
}

Posteriormente, el controlador de Clicked utiliza ese nombre de archivo para crear un objeto FileVideoSource y
establecerlo en la propiedad Source del VideoPlayer .
Cada una de las clases VideoPlayerRenderer contiene código en su método SetSource para objetos de tipo
FileVideoSource . Se muestran a continuación:

Control de archivos de iOS


La versión de iOS de VideoPlayerRenderer procesa los objetos FileVideoSource mediante el uso del método
Asset.FromUrl estático con el nombre de archivo. Esto crea un objeto AVAsset que representa el archivo de la
biblioteca de imágenes del dispositivo:
namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
···
void SetSource()
{
AVAsset asset = null;
···
else if (Element.Source is FileVideoSource)
{
string uri = (Element.Source as FileVideoSource).File;

if (!String.IsNullOrWhiteSpace(uri))
{
asset = AVAsset.FromUrl(new NSUrl(uri));
}
}
···
}
···
}
}

Control de archivos de Android


Al procesar los objetos de tipo FileVideoSource , la implementación de Android de VideoPlayerRenderer utiliza el
método SetVideoPath de VideoView para especificar el archivo de biblioteca de imágenes del dispositivo:

namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
···
void SetSource()
{
isPrepared = false;
bool hasSetSource = false;
···
else if (Element.Source is FileVideoSource)
{
string filename = (Element.Source as FileVideoSource).File;

if (!String.IsNullOrWhiteSpace(filename))
{
videoView.SetVideoPath(filename);
hasSetSource = true;
}
}
···
}
···
}
}

Control de archivos de UWP


Al controlar los objetos de tipo FileVideoSource , la implementación de UWP del método SetSource debe crear
un objeto StorageFile , abrir ese archivo para leerlo y pasar el objeto de secuencia al método SetSource del
MediaElement :
namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
···
async void SetSource()
{
bool hasSetSource = false;
···
else if (Element.Source is FileVideoSource)
{
// Code requires Pictures Library in Package.appxmanifest Capabilities to be enabled
string filename = (Element.Source as FileVideoSource).File;

if (!String.IsNullOrWhiteSpace(filename))
{
StorageFile storageFile = await StorageFile.GetFileFromPathAsync(filename);
IRandomAccessStreamWithContentType stream = await storageFile.OpenReadAsync();
Control.SetSource(stream, storageFile.ContentType);
hasSetSource = true;
}
}
···
}
···
}
}

En todas las plataformas, el vídeo empieza a reproducirse casi inmediatamente después de establecer el origen de
vídeo porque el archivo se encuentra en el dispositivo y no debe descargarse.

Vínculos relacionados
Demostraciones de reproductor de vídeo (ejemplo)
Seleccionar una foto de la biblioteca de imágenes
Controles de transporte de vídeo personalizados
11/07/2019 • 17 minutes to read • Edit Online

Descargar el ejemplo
Los controles de transporte de un reproductor de vídeo son los botones que realizan las funciones Reproducir,
Pausa y Detener. Estos botones suelen identificarse con iconos conocidos en lugar de texto, y las funciones
Reproducir y Pausa suelen combinarse en un mismo botón.
De forma predeterminada, el elemento VideoPlayer muestra controles de transporte compatibles con cada
plataforma. Al establecer la propiedad AreTransportControlsEnabled en false , se eliminan estos controles.
Después, puede controlar el elemento VideoPlayer mediante programación, o bien puede eliminar sus propios
controles de transporte.

Métodos Reproducir, Pausa y Detener


La clase VideoPlayer define tres métodos (llamados Play , Pause y Stop ) que se implementan mediante la
activación de eventos:

namespace FormsVideoLibrary
{
public class VideoPlayer : View, IVideoPlayerController
{
···
public event EventHandler PlayRequested;

public void Play()


{
PlayRequested?.Invoke(this, EventArgs.Empty);
}

public event EventHandler PauseRequested;

public void Pause()


{
PauseRequested?.Invoke(this, EventArgs.Empty);
}

public event EventHandler StopRequested;

public void Stop()


{
StopRequested?.Invoke(this, EventArgs.Empty);
}
}
}

Los controladores de eventos para estos eventos se establecen mediante la clase VideoPlayerRenderer en cada
plataforma, como se muestra a continuación:
Implementaciones de transporte de iOS
La versión de iOS de VideoPlayerRenderer usa el método OnElementChanged para establecer controladores para
estos tres eventos cuando la propiedad NewElement no es null y elimina la asociación de los controladores de
eventos cuando OldElement no es null :
namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
AVPlayer player;
···
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
···
if (args.NewElement != null)
{
···
args.NewElement.PlayRequested += OnPlayRequested;
args.NewElement.PauseRequested += OnPauseRequested;
args.NewElement.StopRequested += OnStopRequested;
}

if (args.OldElement != null)
{
···
args.OldElement.PlayRequested -= OnPlayRequested;
args.OldElement.PauseRequested -= OnPauseRequested;
args.OldElement.StopRequested -= OnStopRequested;
}
}
···
// Event handlers to implement methods
void OnPlayRequested(object sender, EventArgs args)
{
player.Play();
}

void OnPauseRequested(object sender, EventArgs args)


{
player.Pause();
}

void OnStopRequested(object sender, EventArgs args)


{
player.Pause();
player.Seek(new CMTime(0, 1));
}
}
}

Para implementar los controladores de eventos, se llama a métodos en el objeto AVPlayer . Como no hay ningún
método Stop para AVPlayer , para simularlo, se pausa el vídeo y se mueve la posición al principio.
Implementaciones de transporte de Android
La implementación de Android es similar a la implementación de iOS. Los controladores para las tres funciones
se establecen cuando NewElement no es null y, cuando OldElement no es null , se elimina la asociación:
namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
VideoView videoView;
···
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
···
if (args.NewElement != null)
{
···
args.NewElement.PlayRequested += OnPlayRequested;
args.NewElement.PauseRequested += OnPauseRequested;
args.NewElement.StopRequested += OnStopRequested;
}

if (args.OldElement != null)
{
···
args.OldElement.PlayRequested -= OnPlayRequested;
args.OldElement.PauseRequested -= OnPauseRequested;
args.OldElement.StopRequested -= OnStopRequested;
}
}
···
void OnPlayRequested(object sender, EventArgs args)
{
videoView.Start();
}

void OnPauseRequested(object sender, EventArgs args)


{
videoView.Pause();
}

void OnStopRequested(object sender, EventArgs args)


{
videoView.StopPlayback();
}
}
}

Estas tres funciones llaman a métodos definidos por VideoView .


Implementaciones de transporte de UWP
La implementación de UWP de las tres funciones de transporte es muy similar a las implementaciones de iOS y
Android:
namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
···
if (args.NewElement != null)
{
···
args.NewElement.PlayRequested += OnPlayRequested;
args.NewElement.PauseRequested += OnPauseRequested;
args.NewElement.StopRequested += OnStopRequested;
}

if (args.OldElement != null)
{
···
args.OldElement.PlayRequested -= OnPlayRequested;
args.OldElement.PauseRequested -= OnPauseRequested;
args.OldElement.StopRequested -= OnStopRequested;
}
}
···
// Event handlers to implement methods
void OnPlayRequested(object sender, EventArgs args)
{
Control.Play();
}

void OnPauseRequested(object sender, EventArgs args)


{
Control.Pause();
}

void OnStopRequested(object sender, EventArgs args)


{
Control.Stop();
}
}
}

Estado del reproductor de vídeo


Implementar las funciones Reproducir, Pausa y Detener no es suficiente para admitir los controles de
transporte. Con frecuencia, los comandos Reproducir y Pausa se implementan con el mismo botón, que cambia
su apariencia para indicar si el vídeo está reproduciéndose o en pausa en ese momento. Además, el botón no tiene
que habilitarse si el vídeo aún no se ha cargado.
Estos requisitos implican que el reproductor de vídeo necesita mostrar un estado actual que indique si está
reproduciéndose o en pausa, o bien si aún no está preparado para reproducir un vídeo. (Cada plataforma también
admite propiedades que indican si el vídeo se puede pausar o se puede mover a una nueva posición, pero estas
propiedades pueden aplicarse para la transmisión por streaming de vídeo, en lugar de aplicarlas en archivos de
vídeo, por lo que no se admiten en el elemento VideoPlayer que se describe aquí).
En el proyecto VideoPlayerDemos, se incluye una enumeración VideoStatus con tres miembros:
namespace FormsVideoLibrary
{
public enum VideoStatus
{
NotReady,
Playing,
Paused
}
}

La clase VideoPlayer define una propiedad enlazable de solo lectura denominada Status del tipo VideoStatus .
Esta propiedad se define como de solo lectura porque únicamente tiene que establecerse desde el representador
de la plataforma:

using System;
using Xamarin.Forms;

namespace FormsVideoLibrary
{
public class VideoPlayer : View, IVideoPlayerController
{
···
// Status read-only property
private static readonly BindablePropertyKey StatusPropertyKey =
BindableProperty.CreateReadOnly(nameof(Status), typeof(VideoStatus), typeof(VideoPlayer),
VideoStatus.NotReady);

public static readonly BindableProperty StatusProperty = StatusPropertyKey.BindableProperty;

public VideoStatus Status


{
get { return (VideoStatus)GetValue(StatusProperty); }
}

VideoStatus IVideoPlayerController.Status
{
set { SetValue(StatusPropertyKey, value); }
get { return Status; }
}
···
}
}

Normalmente, una propiedad enlazable de solo lectura tendría un descriptor de acceso set privado en la
propiedad Status para permitirle establecerlo en la clase. Pero, para un elemento View derivado admitido por
representadores, la propiedad tiene que establecerse desde fuera de la clase, pero solo por el representador de la
plataforma.
Por este motivo, se define otra propiedad con el nombre IVideoPlayerController.Status . Esta es una
implementación de interfaz explícita y es posible mediante la interfaz IVideoPlayerController que implementa la
clase VideoPlayer :
namespace FormsVideoLibrary
{
public interface IVideoPlayerController
{
VideoStatus Status { set; get; }

TimeSpan Duration { set; get; }


}
}

Esto es similar a la forma en que el control WebView usa la interfaz IWebViewController para implementar las
propiedades CanGoBack y CanGoForward . (Para obtener más información, vea el código fuente de WebView y sus
representadores).
Esto permite que una clase externa a VideoPlayer pueda establecer la propiedad Status al hacer referencia a la
interfaz IVideoPlayerController . (Verá el código en breve). La propiedad también se puede establecer desde otras
clases, pero es poco probable que se establezca por error. Aún más importante, la propiedad Status no se puede
establecer mediante un enlace de datos.
Para ayudar a los representadores a mantener actualizada la propiedad Status , la clase VideoPlayer define un
evento UpdateStatus que se desencadena cada décima de segundo:

namespace FormsVideoLibrary
{
public class VideoPlayer : View, IVideoPlayerController
{
public event EventHandler UpdateStatus;

public VideoPlayer()
{
Device.StartTimer(TimeSpan.FromMilliseconds(100), () =>
{
UpdateStatus?.Invoke(this, EventArgs.Empty);
return true;
});
}
···
}
}

Ajuste de estado de iOS


El elemento VideoPlayerRenderer de iOS establece un controlador para el evento UpdateStatus (y anula la
asociación de ese controlador cuando el elemento VideoPlayer subyacente está ausente) y usa el controlador
para establecer la propiedad Status :
namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
···
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
···
if (args.NewElement != null)
{
···
args.NewElement.UpdateStatus += OnUpdateStatus;
···
}

if (args.OldElement != null)
{
args.OldElement.UpdateStatus -= OnUpdateStatus;
···
}
}
···
void OnUpdateStatus(object sender, EventArgs args)
{
VideoStatus videoStatus = VideoStatus.NotReady;

switch (player.Status)
{
case AVPlayerStatus.ReadyToPlay:
switch (player.TimeControlStatus)
{
case AVPlayerTimeControlStatus.Playing:
videoStatus = VideoStatus.Playing;
break;

case AVPlayerTimeControlStatus.Paused:
videoStatus = VideoStatus.Paused;
break;
}
break;
}
}

((IVideoPlayerController)Element).Status = videoStatus;
···
}
···
}
}

Se debe acceder a dos propiedades de AVPlayer : la propiedad Status de tipo AVPlayerStatus y la propiedad
TimeControlStatus de tipo AVPlayerTimeControlStatus . Tenga en cuenta que la propiedad Element (que es el
objeto VideoPlayer ) tiene que transmitir a IVideoPlayerController para establecer la propiedad Status .
Ajuste de estado de Android
La propiedad IsPlaying del elemento VideoView de Android es un operador booleano que solo indica si el vídeo
está reproduciéndose o en pausa. Para determinar si el elemento VideoView aún no puede reproducir ni pausar el
vídeo, es necesario controlar el evento Prepared de VideoView . Estos dos controladores se establecen en el
método OnElementChanged y se anula la asociación durante el reemplazo Dispose :
namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
VideoView videoView;
···
bool isPrepared;

protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)


{
···
if (args.NewElement != null)
{
if (Control == null)
{
···
videoView.Prepared += OnVideoViewPrepared;
···
}
···
args.NewElement.UpdateStatus += OnUpdateStatus;
···
}

if (args.OldElement != null)
{
args.OldElement.UpdateStatus -= OnUpdateStatus;
···
}

protected override void Dispose(bool disposing)


{
if (Control != null && videoView != null)
{
videoView.Prepared -= OnVideoViewPrepared;
}
if (Element != null)
{
Element.UpdateStatus -= OnUpdateStatus;
}

base.Dispose(disposing);
}
···
}
}

El controlador UpdateStatus usa el campo isPrepared (establecido en el controlador Prepared ) y la propiedad


IsPlaying para establecer la propiedad Status :
namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
VideoView videoView;
···
bool isPrepared;
···
void OnVideoViewPrepared(object sender, EventArgs args)
{
isPrepared = true;
···
}
···
void OnUpdateStatus(object sender, EventArgs args)
{
VideoStatus status = VideoStatus.NotReady;

if (isPrepared)
{
status = videoView.IsPlaying ? VideoStatus.Playing : VideoStatus.Paused;
}
···
}
···
}
}

Ajuste de estado de UWP


El elemento VideoPlayerRenderer de UWP usa el evento UpdateStatus , pero no lo necesita para establecer la
propiedad Status . El elemento MediaElement define un evento CurrentStateChanged que permite notificar al
representador si se cambia la propiedad CurrentState . En el reemplazo Dispose , se anula la asociación de la
propiedad:
namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
base.OnElementChanged(args);

if (args.NewElement != null)
{
if (Control == null)
{
···
mediaElement.CurrentStateChanged += OnMediaElementCurrentStateChanged;
};
···
}
···
}

protected override void Dispose(bool disposing)


{
if (Control != null)
{
···
Control.CurrentStateChanged -= OnMediaElementCurrentStateChanged;
}

base.Dispose(disposing);
}
···
}
}

La propiedad CurrentState es el tipo MediaElementState y se asigna fácilmente a VideoStatus :

namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
···
void OnMediaElementCurrentStateChanged(object sender, RoutedEventArgs args)
{
VideoStatus videoStatus = VideoStatus.NotReady;

switch (Control.CurrentState)
{
case MediaElementState.Playing:
videoStatus = VideoStatus.Playing;
break;

case MediaElementState.Paused:
case MediaElementState.Stopped:
videoStatus = VideoStatus.Paused;
break;
}

((IVideoPlayerController)Element).Status = videoStatus;
}
···
}
}
Botones Reproducir, Pausa y Detener
Usar caracteres Unicode para imágenes simbólicas de Reproducir, Pausar y Detener es problemático. En la
sección Aspectos técnicos varios del estándar Unicode, se definen tres caracteres de símbolos que parecen
apropiados para esta finalidad. Estos son:
0x23F5 (triángulo que apunta hacia la derecha de tamaño medio y color negro) o para Reproducir
0x23F8 (barra doble vertical) o para Pausa
0x23F9 (cuadrado negro) o para Detener
Independientemente de cómo se muestren estos símbolos en el explorador (ya que en cada explorador se
controlan de formas distintas), no se muestran de forma coherente en las plataformas admitidas por
Xamarin.Forms. En dispositivos con iOS y UWP, los caracteres Pausa y Detener tienen una apariencia gráfica,
pero con un fondo 3D azul y un primer plano blanco. Esto no ocurre en Android, donde el símbolo es
simplemente azul. Pero el punto de código 0x23F5 de Reproducir no tiene la misma apariencia en UWP y,
además, ni siquiera se admite en iOS y Android.
Por ese motivo, el punto de código 0x23F5 no se puede usar para Reproducir. Un sustituto adecuado es:
0x25B6 (triángulo que apunta hacia la derecha negro) o ▶ para Reproducir
Esto se admite en todas las plataformas, pero se trata de un triángulo negro sin formato que no tiene la apariencia
3D de Pausa y Detener. Una posibilidad es seguir el punto de código 0x25B6 con un código de variante:
0x25B6 seguido de 0xFE0F (variante 16) o ▶ para Reproducir
Esto es lo que se usa en el marcado que se muestra a continuación. En iOS, proporciona al símbolo de
Reproducir la misma apariencia 3D que los botones Pausa y Detener, pero la variante no funciona en Android
ni en UWP.
La página Transporte personalizado establece la propiedad AreTransportControlsEnabled en false e incluye
un elemento ActivityIndicator mostrado cuando el vídeo está cargándose, además de dos botones. Los objetos
DataTrigger se usan para habilitar y deshabilitar el elemento ActivityIndicator y los botones, así como para
cambiar el primer botón entre Reproducir y Pausa:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:video="clr-namespace:FormsVideoLibrary"
x:Class="VideoPlayerDemos.CustomTransportPage"
Title="Custom Transport">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<video:VideoPlayer x:Name="videoPlayer"
Grid.Row="0"
AutoPlay="False"
AreTransportControlsEnabled="False"
Source="{StaticResource BigBuckBunny}" />

<ActivityIndicator Grid.Row="0"
Color="Gray"
IsVisible="False">
<ActivityIndicator.Triggers>
<DataTrigger TargetType="ActivityIndicator"
Binding="{Binding Source={x:Reference videoPlayer},
Path=Status}"
Value="{x:Static video:VideoStatus.NotReady}">
<Setter Property="IsVisible" Value="True" />
<Setter Property="IsRunning" Value="True" />
<Setter Property="IsRunning" Value="True" />
</DataTrigger>
</ActivityIndicator.Triggers>
</ActivityIndicator>

<StackLayout Grid.Row="1"
Orientation="Horizontal"
Margin="0, 10"
BindingContext="{x:Reference videoPlayer}">

<Button Text="&#x25B6;&#xFE0F; Play"


HorizontalOptions="CenterAndExpand"
Clicked="OnPlayPauseButtonClicked">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding Status}"
Value="{x:Static video:VideoStatus.Playing}">
<Setter Property="Text" Value="&#x23F8; Pause" />
</DataTrigger>

<DataTrigger TargetType="Button"
Binding="{Binding Status}"
Value="{x:Static video:VideoStatus.NotReady}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Button.Triggers>
</Button>

<Button Text="&#x23F9; Stop"


HorizontalOptions="CenterAndExpand"
Clicked="OnStopButtonClicked">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding Status}"
Value="{x:Static video:VideoStatus.NotReady}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Button.Triggers>
</Button>
</StackLayout>
</Grid>
</ContentPage>

Los desencadenadores de datos se describen con detalle en el artículo Desencadenadores de datos.


El archivo de código subyacente contiene los controladores para los eventos Clicked del botón:
namespace VideoPlayerDemos
{
public partial class CustomTransportPage : ContentPage
{
public CustomTransportPage()
{
InitializeComponent();
}

void OnPlayPauseButtonClicked(object sender, EventArgs args)


{
if (videoPlayer.Status == VideoStatus.Playing)
{
videoPlayer.Pause();
}
else if (videoPlayer.Status == VideoStatus.Paused)
{
videoPlayer.Play();
}
}

void OnStopButtonClicked(object sender, EventArgs args)


{
videoPlayer.Stop();
}
}
}

Como AutoPlay se establece en false en el archivo CustomTransport.xaml, necesita pulsar el botón


Reproducir cuando este se habilite para iniciar el vídeo. Los botones se definen de forma que los caracteres
Unicode indicados anteriormente se muestren con sus equivalentes de texto. Los botones tienen una apariencia
coherente en todas las plataformas cuando el vídeo está reproduciéndose:

Pero, en Android y UWP, el botón Reproducir tiene una apariencia muy distinta cuando el vídeo está pausado:
En una aplicación de producción, es probable que quiera usar sus propias imágenes de mapa de bits para los
botones con el fin de conseguir una uniformidad visual.

Vínculos relacionados
Demostraciones de reproductor de vídeo (ejemplo)
Barra de posición de vídeo personalizada
11/07/2019 • 18 minutes to read • Edit Online

Descargar el ejemplo
Los controles de transporte que cada plataforma implementa incluyen una barra de posición. Esta barra es similar
a un control deslizante o una barra de desplazamiento y muestra la ubicación actual del vídeo dentro de su
duración total. Además, el usuario puede manipular la barra de posición para avanzar o retroceder a una nueva
posición en el vídeo.
En este artículo se muestra cómo puede implementar su propia barra de posición personalizada.

La propiedad Duration
Un elemento de información que VideoPlayer necesita para admitir una barra de posición personalizada es la
duración del vídeo. VideoPlayer define una propiedad Duration de solo lectura de tipo TimeSpan :

namespace FormsVideoLibrary
{
public class VideoPlayer : View, IVideoPlayerController
{
···
// Duration read-only property
private static readonly BindablePropertyKey DurationPropertyKey =
BindableProperty.CreateReadOnly(nameof(Duration), typeof(TimeSpan), typeof(VideoPlayer), new
TimeSpan(),
propertyChanged: (bindable, oldValue, newValue) => ((VideoPlayer)bindable).SetTimeToEnd());

public static readonly BindableProperty DurationProperty = DurationPropertyKey.BindableProperty;

public TimeSpan Duration


{
get { return (TimeSpan)GetValue(DurationProperty); }
}

TimeSpan IVideoPlayerController.Duration
{
set { SetValue(DurationPropertyKey, value); }
get { return Duration; }
}
···
}
}

Al igual que la propiedad Status descrita en el artículo anterior, esta propiedad Duration es de solo lectura. Se
define con una clave BindablePropertyKey privada y solo se puede establecer mediante una referencia a la interfaz
de IVideoPlayerController , que incluye esta propiedad Duration :
namespace FormsVideoLibrary
{
public interface IVideoPlayerController
{
VideoStatus Status { set; get; }

TimeSpan Duration { set; get; }


}
}

Tenga en cuenta también el controlador de cambio de propiedad que llama a un método denominado
SetTimeToEnd que se describe más adelante en este artículo.

La duración de un vídeo no está disponible inmediatamente después de que se establezca la propiedad Source de
VideoPlayer . El archivo de vídeo debe descargarse parcialmente antes de que el reproductor de vídeo subyacente
pueda determinar su duración.
Aquí le mostramos cómo cada uno de los representadores de la plataforma obtiene la duración del vídeo:
Duración del vídeo en iOS
En iOS, la duración de un vídeo se obtiene a partir de la propiedad Duration de AVPlayerItem , pero no
inmediatamente después de que se haya creado AVPlayerItem . Es posible establecer un observador de iOS para la
propiedad Duration , pero VideoPlayerRenderer obtiene la duración en el método UpdateStatus , al que se llama 10
veces por segundo:

namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
···
void OnUpdateStatus(object sender, EventArgs args)
{
···
if (playerItem != null)
{
((IVideoPlayerController)Element).Duration = ConvertTime(playerItem.Duration);
···
}
}

TimeSpan ConvertTime(CMTime cmTime)


{
return TimeSpan.FromSeconds(Double.IsNaN(cmTime.Seconds) ? 0 : cmTime.Seconds);

}
···
}
}

El método ConvertTime convierte un objeto CMTime en un valor TimeSpan .


Duración del vídeo en Android
La propiedad Duration de la VideoView de Android notifica una duración válida en milisegundos cuando se activa
el evento Prepared de VideoView . La clase VideoPlayerRenderer de Android utiliza ese controlador para obtener la
propiedad Duration :
namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
···
void OnVideoViewPrepared(object sender, EventArgs args)
{
···
((IVideoPlayerController)Element).Duration = TimeSpan.FromMilliseconds(videoView.Duration);
}
···
}
}

Duración del vídeo en UWP


La propiedad NaturalDuration de MediaElement es un valor TimeSpan y entra en vigor cuando MediaElement
activa el evento MediaOpened :

namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
···
void OnMediaElementMediaOpened(object sender, RoutedEventArgs args)
{
((IVideoPlayerController)Element).Duration = Control.NaturalDuration.TimeSpan;
}
···
}
}

La propiedad Position
VideoPlayer también necesita una propiedad Position que aumente de cero a Duration mientras se reproduce
el vídeo. VideoPlayer implementa esta propiedad como la propiedad Position del MediaElement de UWP, que es
una propiedad enlazable normal con descriptores de acceso set y get públicos:

namespace FormsVideoLibrary
{
public class VideoPlayer : View, IVideoPlayerController
{
···
// Position property
public static readonly BindableProperty PositionProperty =
BindableProperty.Create(nameof(Position), typeof(TimeSpan), typeof(VideoPlayer), new TimeSpan(),
propertyChanged: (bindable, oldValue, newValue) => ((VideoPlayer)bindable).SetTimeToEnd());

public TimeSpan Position


{
set { SetValue(PositionProperty, value); }
get { return (TimeSpan)GetValue(PositionProperty); }
}
···
}
}

El descriptor de acceso get devuelve la posición actual del vídeo mientras se reproduce, pero el descriptor de
acceso set está pensado para responder a la manipulación del usuario de la barra de posición para mover la
posición de vídeo hacia adelante o hacia atrás.
En iOS y Android, la propiedad que obtiene la posición actual solo tiene un descriptor de acceso get , y hay
disponible un método Seek para realizar esta segunda tarea. Si se piensa bien, un método Seek por separado
parece ser un enfoque más sensato que una sola propiedad Position . Una sola propiedad Position tiene un
problema inherente: mientras se reproduce el vídeo, la propiedad Position debe actualizarse continuamente para
reflejar la nueva posición. Pero no es recomendable que la mayoría de los cambios en la propiedad Position
hagan que el reproductor de vídeo se mueva a una nueva posición en el vídeo. Si eso ocurriera, el reproductor de
vídeo, como respuesta, buscaría el último valor de la propiedad Position y el vídeo no avanzaría.
A pesar de las dificultades de la implementación de una propiedad Position con los descriptores de acceso set y
get , se ha elegido este enfoque porque es coherente con UWP MediaElement , y tiene una gran ventaja con el
enlace de datos: La propiedad Position de VideoPlayer pueden estar enlazada al control deslizante que se usa
para mostrar la posición y para buscar una nueva posición. Sin embargo, se deben tomar varias precauciones al
implementar esta propiedad Position para evitar bucles de retroalimentación.
Establecer y obtener la posición en iOS
En iOS, la propiedad CurrentTime del objeto AVPlayerItem indica la posición actual del vídeo en reproducción.
VideoPlayerRenderer de iOS establece la propiedad Position en el controlador UpdateStatus al mismo tiempo
que establece la propiedad Duration :

namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
···
void OnUpdateStatus(object sender, EventArgs args)
{
···
if (playerItem != null)
{
···
((IElementController)Element).SetValueFromRenderer(VideoPlayer.PositionProperty,
ConvertTime(playerItem.CurrentTime));
}
}
···
}
}

El representador detecta cuándo el conjunto de propiedades Position de VideoPlayer ha cambiado en la


invalidación de OnElementPropertyChanged y utiliza ese valor nuevo para llamar a un método Seek en el objeto
AVPlayer :
namespace FormsVideoLibrary.iOS
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
···
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)
{
···
else if (args.PropertyName == VideoPlayer.PositionProperty.PropertyName)
{
TimeSpan controlPosition = ConvertTime(player.CurrentTime);

if (Math.Abs((controlPosition - Element.Position).TotalSeconds) > 1)


{
player.Seek(CMTime.FromSeconds(Element.Position.TotalSeconds, 1));
}
}
}
···
}
}

Tenga en cuenta que cada vez que la propiedad Position en VideoPlayer se establece desde el controlador
OnUpdateStatus , la propiedad Position activa un evento PropertyChanged , que se detecta en la invalidación de
OnElementPropertyChanged . En la mayoría de estos cambios, el método OnElementPropertyChanged no debe hacer
nada. En caso contrario, ante cada cambio en la posición del vídeo, se movería a la misma posición a la que ha
llegado.
Para evitar este bucle de comentarios, el método OnElementPropertyChanged solo llama a Seek cuando la diferencia
entre la propiedad Position y la posición actual del AVPlayer es mayor que un segundo.
Establecer y obtener la posición en Android
Al igual que en el representador de iOS, el VideoPlayerRenderer de Android establece un valor nuevo para la
propiedad Position en el controlador OnUpdateStatus . La propiedad CurrentPosition de VideoView contiene la
posición nueva en unidades de milisegundos:

namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
···
void OnUpdateStatus(object sender, EventArgs args)
{
···
TimeSpan timeSpan = TimeSpan.FromMilliseconds(videoView.CurrentPosition);
((IElementController)Element).SetValueFromRenderer(VideoPlayer.PositionProperty, timeSpan);
}
···
}
}

Además, al igual que en el representador de iOS, el representador de Android llama al método SeekTo de
VideoView cuando la propiedad Position ha cambiado, pero solo cuando el cambio y el valor CurrentPosition de
VideoView difieren en más de un segundo:
namespace FormsVideoLibrary.Droid
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
···
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)
{
···
else if (args.PropertyName == VideoPlayer.PositionProperty.PropertyName)
{
if (Math.Abs(videoView.CurrentPosition - Element.Position.TotalMilliseconds) > 1000)
{
videoView.SeekTo((int)Element.Position.TotalMilliseconds);
}
}
}
···
}
}

Establecer y obtener la posición en UWP


VideoPlayerRenderer de UWP controla el valor Position de la misma manera que los representadores de iOS y
Android; sin embargo, dado que la propiedad Position del MediaElement de UWP también es un valor TimeSpan ,
no es necesario realizar ninguna conversión:

namespace FormsVideoLibrary.UWP
{
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, MediaElement>
{
···
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)
{
···
else if (args.PropertyName == VideoPlayer.PositionProperty.PropertyName)
{
if (Math.Abs((Control.Position - Element.Position).TotalSeconds) > 1)
{
Control.Position = Element.Position;
}
}
}
···
void OnUpdateStatus(object sender, EventArgs args)
{
((IElementController)Element).SetValueFromRenderer(VideoPlayer.PositionProperty,
Control.Position);
}
···
}
}

Calcular una propiedad TimeToEnd


A veces, los reproductores de vídeo muestran el tiempo restante en el vídeo. Este valor comienza en la duración del
vídeo cuando el vídeo empieza y disminuye a cero cuando el vídeo finaliza.
VideoPlayer incluye una propiedad TimeToEnd de solo lectura que se controla por completo dentro de la clase
según los cambios en las propiedades Duration y Position :
namespace FormsVideoLibrary
{
public class VideoPlayer : View, IVideoPlayerController
{
···
private static readonly BindablePropertyKey TimeToEndPropertyKey =
BindableProperty.CreateReadOnly(nameof(TimeToEnd), typeof(TimeSpan), typeof(VideoPlayer), new
TimeSpan());

public static readonly BindableProperty TimeToEndProperty = TimeToEndPropertyKey.BindableProperty;

public TimeSpan TimeToEnd


{
private set { SetValue(TimeToEndPropertyKey, value); }
get { return (TimeSpan)GetValue(TimeToEndProperty); }
}

void SetTimeToEnd()
{
TimeToEnd = Duration - Position;
}
···
}
}

El método SetTimeToEnd se llama desde los controladores de cambio de propiedad de Duration y Position .

Un control deslizante personalizado para vídeo


Es posible escribir un control personalizado para una barra de posición o para utilizar el Slider de Xamarin.Forms
o una clase que derive de Slider , como la siguiente clase PositionSlider . La clase define dos propiedades nuevas
denominadas Duration y Position de tipo TimeSpan que están diseñadas para enlazarse a las dos propiedades
del mismo nombre en VideoPlayer . Tenga en cuenta que el modo de enlace predeterminado de la propiedad
Position es bidireccional:
namespace FormsVideoLibrary
{
public class PositionSlider : Slider
{
public static readonly BindableProperty DurationProperty =
BindableProperty.Create(nameof(Duration), typeof(TimeSpan), typeof(PositionSlider), new
TimeSpan(1),
propertyChanged: (bindable, oldValue, newValue) =>
{
double seconds = ((TimeSpan)newValue).TotalSeconds;
((PositionSlider)bindable).Maximum = seconds <= 0 ? 1 : seconds;
});

public TimeSpan Duration


{
set { SetValue(DurationProperty, value); }
get { return (TimeSpan)GetValue(DurationProperty); }
}

public static readonly BindableProperty PositionProperty =


BindableProperty.Create(nameof(Position), typeof(TimeSpan), typeof(PositionSlider), new
TimeSpan(0),
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
double seconds = ((TimeSpan)newValue).TotalSeconds;
((PositionSlider)bindable).Value = seconds;
});

public TimeSpan Position


{
set { SetValue(PositionProperty, value); }
get { return (TimeSpan)GetValue(PositionProperty); }
}

public PositionSlider()
{
PropertyChanged += (sender, args) =>
{
if (args.PropertyName == "Value")
{
TimeSpan newPosition = TimeSpan.FromSeconds(Value);

if (Math.Abs(newPosition.TotalSeconds - Position.TotalSeconds) / Duration.TotalSeconds >


0.01)
{
Position = newPosition;
}
}
};
}
}
}

El controlador de cambio de propiedad para la propiedad Duration establece la propiedad Maximum del Slider
subyacente en la propiedad TotalSeconds del valor TimeSpan . De forma similar, el controlador de cambio de
propiedad para Position establece la propiedad Value del Slider . De esta manera, el Slider subyacente realiza
un seguimiento de la posición del PositionSlider .
PositionSlider se actualiza desde el Slider subyacente en una única instancia: cuando el usuario manipula el
Slider para indicar que el vídeo debe avanzar o retroceder a una posición nueva. Esto se detecta en el controlador
PropertyChanged en el constructor del PositionSlider . El controlador comprueba si hay algún cambio en la
propiedad Value y, si es diferente de la propiedad Position , la propiedad Position se establece desde la
propiedad Value .
En teoría, la instrucción if interna podría escribirse así:

if (newPosition.Seconds != Position.Seconds)
{
Position = newPosition;
}

Sin embargo, la implementación de Slider en Android solo tiene 1000 pasos discretos, independientemente de la
configuración de Minimum y Maximum . Si la longitud de un vídeo fuera mayor que 1000 segundos, dos valores
Position diferentes se corresponderían a la misma configuración de Value del Slider , y esta instrucción if
desencadenaría un falso positivo de una manipulación de usuario del Slider . En su lugar, es más seguro
comprobar que las posiciones nueva y existente son mayores que una centésima de la duración total.

Utilizar el PositionSlider
La documentación del MediaElement de UWP advierte sobre el enlace a la propiedad Position porque la
propiedad se actualiza con frecuencia. La documentación recomienda que se utilice un temporizador para
consultar la propiedad Position .
Esa es una buena recomendación, pero las tres clases VideoPlayerRenderer ya utilizan indirectamente un
temporizador para actualizar la propiedad Position . La propiedad Position se cambia en un controlador para el
evento UpdateStatus , que se activa solo 10 veces por segundo.
Por tanto, la propiedad Position del VideoPlayer puede enlazarse a la propiedad Position del PositionSlider
sin problemas de rendimiento, como se muestra en la página Barra de posición personalizada:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:video="clr-namespace:FormsVideoLibrary"
x:Class="VideoPlayerDemos.CustomPositionBarPage"
Title="Custom Position Bar">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<video:VideoPlayer x:Name="videoPlayer"
Grid.Row="0"
AreTransportControlsEnabled="False"
Source="{StaticResource ElephantsDream}" />

···

<StackLayout Grid.Row="1"
Orientation="Horizontal"
Margin="10, 0"
BindingContext="{x:Reference videoPlayer}">

<Label Text="{Binding Path=Position,


StringFormat='{0:hh\\:mm\\:ss}'}"
VerticalOptions="Center"/>

···

<Label Text="{Binding Path=TimeToEnd,


StringFormat='{0:hh\\:mm\\:ss}'}"
VerticalOptions="Center" />
</StackLayout>

<video:PositionSlider Grid.Row="2"
Margin="10, 0, 10, 10"
BindingContext="{x:Reference videoPlayer}"
Duration="{Binding Duration}"
Position="{Binding Position}">
<video:PositionSlider.Triggers>
<DataTrigger TargetType="video:PositionSlider"
Binding="{Binding Status}"
Value="{x:Static video:VideoStatus.NotReady}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</video:PositionSlider.Triggers>
</video:PositionSlider>
</Grid>
</ContentPage>

Los primeros puntos suspensivos (···) ocultan el ActivityIndicator ; es el mismo que en la página anterior
Transporte personalizado. Tenga en cuenta los dos elementos Label que muestran las propiedades Position y
TimeToEnd . Los puntos suspensivos entre esos dos elementos Label ocultan los dos elementos Button que
aparecen en la página Transporte personalizado para reproducir, hacer pausa y detener. La lógica de código
subyacente también es la misma que la de la página Transporte personalizado.
Con esto concluye la explicación de VideoPlayer .

Vínculos relacionados
Demostraciones de reproductor de vídeo (ejemplo)
Enlace de datos de Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

Descargar el ejemplo
El enlace de datos es la técnica que consiste en vincular las propiedades de dos objetos para que los cambios
en una propiedad se reflejen automáticamente en la otra propiedad. El enlace de datos es una parte integral
de la arquitectura de aplicación Model-View -ViewModel (MVVM ).

El problema de vinculación de datos


Una aplicación de Xamarin.Forms consta de una o varias páginas, cada una de los cuales generalmente
contiene varios objetos de interfaz de usuario denominados vistas. Una de las tareas principales del programa
consiste en mantener estas vistas sincronizadas y realizar un seguimiento de los distintos valores o selecciones
que representan. A menudo las vistas representan valores de un origen de datos subyacente y el usuario
manipula estas vistas para cambiar esos datos. Cuando la vista cambia, los datos subyacentes deben reflejar
ese cambio y, de forma similar, cuando los datos subyacentes cambian, ese cambio debe reflejarse en la vista.
Para controlar este trabajo correctamente, el programa debe recibir una notificación de cambios en estas vistas
o en los datos subyacentes. La solución habitual consiste en definir eventos que indican cuándo se produce un
cambio. Después, se puede instalar un controlador de eventos que recibe la notificación de estos cambios, y
responde transfiriendo datos de un objeto a otro. Pero cuando hay muchas vistas, también es necesario que
haya muchos controladores de eventos y se involucra una gran cantidad de código.

La solución de enlace de datos


El enlace de datos automatiza este trabajo y vuelve innecesarios los controladores de eventos. (pero los
eventos siguen siendo necesarios porque la infraestructura de enlace de datos los usa). Los enlaces de datos se
pueden implementar en el código o en XAML, pero son mucho más comunes en XAML, ya que así es más fácil
reducir el tamaño del archivo de código subyacente. Al reemplazar el código de procedimientos en los
controladores de eventos con código declarativo o marcado, se simplifica y se aclara la aplicación.
Uno de los dos objetos implicados en un enlace de datos es casi siempre un elemento que se deriva de View y
forma parte de la interfaz visual de una página. El otro objeto puede ser:
Otro derivado de View , normalmente en la misma página.
Un objeto en un archivo de código.
En los programas de demo, como los del ejemplo DataBindingDemos, los enlaces de datos entre dos
derivados de View a menudo se muestran con fines de claridad y simplicidad. Pero se pueden aplicar los
mismos principios a los enlaces de datos entre un View y otros objetos. Cuando se compila una aplicación con
la arquitectura Model-View -ViewModel (MVVM ), la clase con los datos subyacentes a menudo se denomina
ViewModel.
En los siguientes artículos se abordan los enlaces de datos:

Enlaces básicos
Conozca la diferencia entre el origen y el destino del enlace de datos y vea enlaces de datos sencillos en código
y en XAML.
Modo de enlace
Descubra cómo el modo de enlace puede controlar el flujo de datos entre los dos objetos.

Formato de cadena
Use un enlace de datos para dar formato y mostrar objetos como cadenas.

Enlace de ruta de acceso


Profundice en la propiedad Path del enlace de datos para acceder a las subpropiedades y los miembros de la
colección.

Enlace de convertidores de valores


Use convertidores de valor de enlace para modificar valores en el enlace de datos.

Conmutación por recuperación de enlaces


Fortalezca los enlaces de datos mediante la definición de valores de reserva para usarlos si se produce un error
en el proceso de enlace.

Interfaz de comandos
Implemente la propiedad Command con los enlaces de datos.

Enlaces compilados
Use enlaces compilados para mejorar el rendimiento del enlace de datos.

Vínculos relacionados
Data Binding Demos (sample) (Demos de enlace de datos [ejemplo])
Capítulo sobre enlace de datos del libro de Xamarin.Forms
Extensiones de marcado XAML
Enlaces básicos de Xamarin.Forms
11/07/2019 • 19 minutes to read • Edit Online

Descargar el ejemplo
Un enlace de datos de Xamarin.Forms enlaza un par de propiedades entre dos objetos, del que al menos uno suele
ser un objeto de interfaz de usuario. Estos dos objetos se denominan el destino y el origen:
El destino es el objeto (y la propiedad) en la que se establece el enlace de datos.
El origen es el objeto (y la propiedad) al que hace referencia el enlace de datos.
Esta distinción a veces puede ser un poco confusa: en el caso más simple, los datos fluyen desde el origen al
destino, lo que significa que el valor de la propiedad de destino se establece en el valor de la propiedad de origen.
Pero en algunos casos, los datos también pueden fluir desde el destino al origen, o en ambas direcciones. Para
evitar confusiones, tenga en cuenta que el destino siempre es el objeto en el que se establece el enlace de datos,
incluso si ofrece datos en lugar de recibirlos.

Enlaces con un contexto de enlace


Aunque los enlaces de datos normalmente se especifican totalmente en XAML, es útil verlos en el código. La
página Enlace básico de código contiene un archivo XAML con un elemento Label y un elemento Slider :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBindingDemos.BasicCodeBindingPage"
Title="Basic Code Binding">
<StackLayout Padding="10, 0">
<Label x:Name="label"
Text="TEXT"
FontSize="48"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

<Slider x:Name="slider"
Maximum="360"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

El elemento Slider se establece para un intervalo de 0 a 360. El propósito de este programa es girar el elemento
Label mediante la manipulación de Slider .

Sin los enlaces de datos, se establecería el evento ValueChanged del elemento Slider en un controlador de
eventos que accede a la propiedad Value del elemento Slider y establece ese valor en la propiedad Rotation
del elemento Label . El enlace de datos automatiza ese trabajo; el controlador de eventos y el código que contiene
ya no son necesarios.
Puede establecer un enlace en una instancia de cualquier clase que se derive de BindableObject , lo que incluye las
derivadas Element , VisualElement , View y View . El enlace siempre se establece en el objeto de destino. El enlace
hace referencia al objeto de origen. Para establecer el enlace de datos, use los dos miembros siguientes de la clase
de destino:
La propiedad BindingContext especifica el objeto de origen.
El método SetBinding especifica la propiedad de destino y la de origen.
En este ejemplo, Label es el destino de enlace y Slider es el origen de enlace. Los cambios en el origen de
Slider afectan a la rotación del destino de Label . Los datos fluyen del origen al destino.

El método SetBinding definido por BindableObject tiene un argumento de tipo BindingBase del que se deriva la
clase Binding , pero existen otros métodos SetBinding definidos por la clase BindableObjectExtensions . En el
archivo de código subyacente del ejemplo Enlace de código básico se usa un método de extensión SetBinding
más sencillo de esta clase.

public partial class BasicCodeBindingPage : ContentPage


{
public BasicCodeBindingPage()
{
InitializeComponent();

label.BindingContext = slider;
label.SetBinding(Label.RotationProperty, "Value");
}
}

El objeto Label es el destino de enlace, por lo que es el objeto en el que se establece esta propiedad y en el que se
llama al método. La propiedad BindingContext indica el origen de enlace, que es el elemento Slider .
El método SetBinding se llama en el destino de enlace, pero especifica tanto la propiedad de destino como la de
origen. La propiedad de destino se especifica como un objeto BindableProperty : Label.RotationProperty . La
propiedad de origen se especifica como una cadena e indica la propiedad Value de Slider .
El método SetBinding revela una de las reglas de enlaces de datos más importantes:
La propiedad de destino debe estar respaldada por una propiedad enlazable.
Esta regla implica que el objeto de destino debe ser una instancia de una clase que se derive de BindableObject .
Vea el artículo Propiedades enlazables para obtener información general sobre los objetos y las propiedades
enlazables.
No hay ninguna regla de este tipo para la propiedad de origen, que se especifica como una cadena. De forma
interna, se usa la reflexión para acceder a la propiedad real. Pero en este caso concreto, la propiedad Value
también está respaldada por una propiedad enlazable.
El código se puede simplificar ligeramente: la propiedad enlazable RotationProperty se define mediante
VisualElement , y también la heredan Label y ContentPage , por lo que no es necesario el nombre de clase en la
llamada a SetBinding :

label.SetBinding(RotationProperty, "Value");

Pero la inclusión del nombre de clase es un buen recordatorio del objeto de destino.
Al manipular el elemento Slider , el elemento Label gira según corresponda:
La página Enlace Xaml básico es idéntica a la página Enlace de código básico, con la excepción de que define
el enlace de datos completo en XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBindingDemos.BasicXamlBindingPage"
Title="Basic XAML Binding">
<StackLayout Padding="10, 0">
<Label Text="TEXT"
FontSize="80"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
BindingContext="{x:Reference Name=slider}"
Rotation="{Binding Path=Value}" />

<Slider x:Name="slider"
Maximum="360"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

Al igual que en el código, el enlace de datos se establece en el objeto de destino, que es el elemento Label .
Intervienen dos extensiones de marcado XAML. Son reconocibles al instante por los delimitadores de llave:
La extensión de marcado x:Reference es necesaria para hacer referencia al objeto de origen, que es el
elemento Slider denominado slider .
La extensión de marcado Binding enlaza la propiedad Rotation de Label a la propiedad Value de Slider .
Vea el artículo Extensiones de marcado XAML para obtener más información sobre las extensiones de marcado
XAML. La extensión de marcado x:Reference es compatible con la clase ReferenceExtension ; Binding es
compatible con la clase BindingExtension . Como indican los prefijos de espacio de nombres XML, x:Reference
forma parte de la especificación 2009 de XAML, mientras que Binding forma parte de Xamarin.Forms. Observe
que no aparecen comillas entre las llaves.
Es fácil olvidar la extensión de marcado x:Reference cuando se establece el valor BindingContext . Un error
habitual es establecer la propiedad directamente en el nombre del origen de enlace, como en este caso:

BindingContext="slider"
Pero eso no es correcto. Ese marcado establece la propiedad BindingContext en un objeto string cuyos
caracteres deletrean "slider".
Observe que la propiedad de origen se especifica con la propiedad Path de BindingExtension , que se
corresponde con la propiedad Path de la clase Binding .
El marcado que se muestra en la página Basic XAML Binding (Enlace XAML básico) se puede simplificar: en las
extensiones de marcado XAML como x:Reference y Binding se pueden definir atributos de propiedad de
contenido, lo que para las extensiones de marcado XAML significa que no es necesario que aparezca el nombre de
propiedad. La propiedad Name es la propiedad de contenido de x:Reference y la propiedad Path es la propiedad
de contenido de Binding , lo que significa que se pueden eliminar de las expresiones:

<Label Text="TEXT"
FontSize="80"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
BindingContext="{x:Reference slider}"
Rotation="{Binding Value}" />

Enlaces sin un contexto de enlace


La propiedad BindingContext es un componente importante de los enlaces de datos, pero no siempre es
necesaria. En su lugar, el objeto de origen se puede especificar en la llamada a SetBinding o en la extensión de
marcado Binding .
Esto se muestra en el ejemplo Enlace de código alternativo. El archivo XAML es similar al ejemplo Enlace de
código básico, salvo que el elemento Slider se define para controlar la propiedad Scale del elemento Label .
Por ese motivo, el elemento Slider se establece para un intervalo de –2 a 2:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBindingDemos.AlternativeCodeBindingPage"
Title="Alternative Code Binding">
<StackLayout Padding="10, 0">
<Label x:Name="label"
Text="TEXT"
FontSize="40"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

<Slider x:Name="slider"
Minimum="-2"
Maximum="2"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

El archivo de código subyacente establece el enlace con el método SetBinding definido por el objeto
BindableObject . El argumento es un constructor para la clase Binding :
public partial class AlternativeCodeBindingPage : ContentPage
{
public AlternativeCodeBindingPage()
{
InitializeComponent();

label.SetBinding(Label.ScaleProperty, new Binding("Value", source: slider));


}
}

El constructor Binding tiene seis parámetros, por lo que el parámetro source se especifica con un argumento con
nombre. El argumento es el objeto slider .
La ejecución de este programa podría ser un poco sorprendente:

En la pantalla de iOS de la izquierda se muestra el aspecto de la pantalla cuando aparece la página por primera
vez. ¿Dónde está el elemento Label ?
El problema es que el elemento Slider tiene un valor inicial de 0. Esto hace que la propiedad Scale de Label
también se establezca en 0, y se reemplace su valor predeterminado de 1. Como resultado, el elemento Label es
invisible inicialmente. Como se muestra en las capturas de pantalla de Android y Plataforma Universal de
Windows (UWP ), el elemento Slider se puede manipular para que Label aparezca de nuevo, pero su
desaparición inicial es desconcertante.
En el artículo siguiente verá cómo evitar este problema si inicializa el elemento Slider a partir del valor
predeterminado de la propiedad Scale .

NOTE
La clase VisualElement también define las propiedades ScaleX y ScaleY , que pueden escalar el elemento
VisualElement de forma diferente en dirección horizontal y vertical.

En la página Enlace XAML alternativo se muestra el enlace equivalente completamente en XAML:


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBindingDemos.AlternativeXamlBindingPage"
Title="Alternative XAML Binding">
<StackLayout Padding="10, 0">
<Label Text="TEXT"
FontSize="40"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Scale="{Binding Source={x:Reference slider},
Path=Value}" />

<Slider x:Name="slider"
Minimum="-2"
Maximum="2"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

Ahora la extensión de marcado Binding tiene dos propiedades establecidas, Source y Path , separadas por una
coma. Si lo prefiere, pueden aparecer en la misma línea:

Scale="{Binding Source={x:Reference slider}, Path=Value}" />

La propiedad Source se establece en una extensión de marcado x:Reference insertada que, en caso contrario,
tiene la misma sintaxis que la configuración de BindingContext . Observe que no aparecen comillas dentro de las
llaves, y que las dos propiedades se deben separar por una coma.
La propiedad de contenido de la extensión de marcado Binding es Path , pero la parte Path= de la extensión de
marcado solo se puede eliminar si es la primera propiedad en la expresión. Para eliminar la parte Path= , es
necesario intercambiar las dos propiedades:

Scale="{Binding Value, Source={x:Reference slider}}" />

Aunque las extensiones de marcado XAML suelen estar delimitadas por llaves, también se pueden expresar como
elementos de objeto:

<Label Text="TEXT"
FontSize="40"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand">
<Label.Scale>
<Binding Source="{x:Reference slider}"
Path="Value" />
</Label.Scale>
</Label>

Ahora las propiedades Source y Path son atributos XAML normales: los valores aparecen entre comillas y los
atributos no están separados por una coma. La extensión de marcado x:Reference también se puede convertir en
un elemento de objeto:
<Label Text="TEXT"
FontSize="40"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand">
<Label.Scale>
<Binding Path="Value">
<Binding.Source>
<x:Reference Name="slider" />
</Binding.Source>
</Binding>
</Label.Scale>
</Label>

Esta sintaxis no es común, pero a veces es necesaria cuando hay objetos complejos implicados.
En los ejemplos mostrados hasta ahora se establecen las propiedades BindingContext y Source de Binding en
una extensión de marcado x:Reference para hacer referencia a otra vista en la página. Estas dos propiedades son
de tipo Object , y se pueden establecer en cualquier objeto que incluya propiedades adecuadas para los orígenes
de enlace.
En los artículos siguientes, descubrirá que puede establecer la propiedad BindingContext o Source en una
extensión de marcado x:Static para hacer referencia al valor de una propiedad estática o un campo, o bien en
una extensión de marcado StaticResource para hacer referencia a un objeto almacenado en un diccionario de
recursos, o directamente en un objeto, que suele ser (pero no siempre) una instancia de una clase ViewModel.
La propiedad BindingContext se puede establecer en un objeto Binding para que las propiedades Source y
Path de Binding definan el contexto de enlace.

Herencia del contexto de enlace


En este artículo, ha visto que puede especificar el objeto de origen mediante las propiedades BindingContext o
Source del objeto Binding . Si se establecen las dos, la propiedad Source de Binding tiene prioridad sobre
BindingContext .

La propiedad BindingContext tiene una característica muy importante:


La configuración de la propiedad BindingContext se hereda a través del árbol visual.
Como verá, esto puede ser muy útil para simplificar las expresiones de enlace y, en algunos casos, especialmente
en escenarios de Model-View -ViewModel (MVVM ), es esencial.
El ejemplo Herencia de contexto de enlace es una simple demostración de la herencia de contexto de enlace:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBindingDemos.BindingContextInheritancePage"
Title="BindingContext Inheritance">
<StackLayout Padding="10">

<StackLayout VerticalOptions="FillAndExpand"
BindingContext="{x:Reference slider}">

<Label Text="TEXT"
FontSize="80"
HorizontalOptions="Center"
VerticalOptions="EndAndExpand"
Rotation="{Binding Value}" />

<BoxView Color="#800000FF"
WidthRequest="180"
HeightRequest="40"
HorizontalOptions="Center"
VerticalOptions="StartAndExpand"
Rotation="{Binding Value}" />
</StackLayout>

<Slider x:Name="slider"
Maximum="360" />

</StackLayout>
</ContentPage>

La propiedad BindingContext de StackLayout se establece en el objeto slider . Label y BoxView heredan este
contexto de enlace, y en los dos sus propiedades Rotation se establecen en la propiedad Value del elemento
Slider :

En el artículo siguiente, verá cómo el modo de enlace puede cambiar el flujo de datos entre los objetos de origen y
destino.

Vínculos relacionados
Data Binding Demos (sample) (Demos de enlace de datos [ejemplo])
Capítulo sobre enlace de datos del libro de Xamarin.Forms
Vídeo relacionado
Encuentre más vídeos de Xamarin en Channel 9 y YouTube.
Modo de enlace de Xamarin.Forms
11/07/2019 • 28 minutes to read • Edit Online

Descargar el ejemplo
En el artículo anterior, las páginas Enlace de código alternativo y Enlace XAML alternativo contenían un
objeto Label con su propiedad Scale enlazada a la propiedad Value de un elemento Slider . Como el valor
inicial de Slider es 0, la propiedad Scale de Label se establece en 0 en lugar de 1, y el elemento Label
desaparece.
En el ejemplo DataBindingDemos, la página Enlace inverso es similar a los programas del artículo anterior,
salvo que el enlace de datos se define en Slider en lugar de Label :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBindingDemos.ReverseBindingPage"
Title="Reverse Binding">
<StackLayout Padding="10, 0">

<Label x:Name="label"
Text="TEXT"
FontSize="80"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

<Slider x:Name="slider"
VerticalOptions="CenterAndExpand"
Value="{Binding Source={x:Reference label},
Path=Opacity}" />
</StackLayout>
</ContentPage>

A primera vista, esto podría parecer al contrario: ahora Label es el origen de enlace de datos y Slider es el
destino. El enlace hace referencia a la propiedad Opacity de Label , que tiene un valor predeterminado de 1.
Como cabría esperar, Slider se inicializa en el valor 1 a partir del valor Opacity inicial de Label . Esto se
muestra en la captura de pantalla de iOS de la izquierda:
Pero es posible que le sorprenda que Slider siga funcionando, como se muestra en las capturas de pantalla de
Android y UWP. Esto parece sugerir que el enlace de datos funciona mejor cuando Slider es el destino de
enlace en lugar de Label , porque la inicialización funciona de la forma esperada.
La diferencia entre el ejemplo Enlace inverso y los ejemplos anteriores implica el modo de enlace.

El modo de enlace predeterminado


El modo de enlace se especifica con un miembro de la enumeración BindingMode :
Default
TwoWay – los datos van en ambas direcciones entre el origen y el destino
OneWay – los datos van del origen al destino
OneWayToSource – los datos van del destino al origen
OneTime – los datos van del origen al destino, pero solo cuando cambia BindingContext (novedad de
Xamarin.Forms 3.0)
Todas las propiedades enlazables tienen un modo de enlace predeterminado que se establece cuando se crea la
propiedad enlazable, y que está disponible en la propiedad DefaultBindingMode del objeto BindableProperty . Este
modo de enlace predeterminado indica el modo en vigor cuando esa propiedad es un destino de enlace de datos.
El modo de enlace predeterminado para la mayoría de las propiedades como Rotation , Scale y Opacity es
OneWay . Cuando estas propiedades son destinos de enlace de datos, la propiedad de destino se establece desde
el origen.
Pero el modo de enlace predeterminado para la propiedad Value de Slider es TwoWay . Esto significa que
cuando la propiedad Value es un destino de enlace de datos, el destino se establece desde el origen (como es
habitual), pero el origen también se establece desde el destino. Esto es lo que permite que Slider se establezca
en el valor Opacity inicial.
Es posible que parezca que este enlace bidireccional crea un bucle infinito, pero no es lo que sucede. Las
propiedades enlazables no señalan un cambio de propiedad a menos que la propiedad cambie realmente. Esto
evita un bucle infinito.
Enlaces bidireccionales
La mayoría de las propiedades enlazables tienen un modo de enlace predeterminado de OneWay , pero las
propiedades siguientes tienen un modo de enlace predeterminado de TwoWay :
Propiedad Date de DatePicker
Propiedad Text de Editor , Entry , SearchBar y EntryCell
Propiedad IsRefreshing de ListView
Propiedad SelectedItem de MultiPage
Propiedades SelectedIndex y SelectedItem de Picker
Propiedad Value de Slider y Stepper
Propiedad IsToggled de Switch
Propiedad On de SwitchCell
Propiedad Time de TimePicker

Estas propiedades concretas se definen como TwoWay por una muy buena razón:
Cuando los enlaces de datos se usan con la arquitectura de aplicación Model-View -ViewModel (MVVM ), la clase
ViewModel es el origen del enlace de datos y la vista, que consta de vistas como Slider , son destinos de enlace
de datos. Los enlaces de MVVM son más similares al ejemplo Enlace inverso que los enlaces de los ejemplos
anteriores. Es muy probable que quiera que cada vista de la página se inicialice con el valor de la propiedad
correspondiente en el modelo de vista, pero los cambios en la vista también deberían afectar a la propiedad
ViewModel.
Las propiedades con modos de enlace predeterminados de TwoWay son las que probablemente se usen más en
escenarios de MVVM.
Enlaces unidireccionales al origen
Las propiedades enlazables de solo lectura tienen un modo de enlace predeterminado de OneWayToSource . Solo
hay una propiedad enlazable de lectura y escritura que tiene un modo de enlace predeterminado de
OneWayToSource :

Propiedad SelectedItem de ListView

La razón es que el resultado de un enlace en la propiedad SelectedItem debe ser el establecimiento del origen de
enlace. En un ejemplo posterior de este artículo se invalida este comportamiento.
Enlaces de un solo uso
Varias propiedades enlazables tienen un modo de enlace predeterminado de OneTime , que incluye la propiedad
IsTextPredictionEnabled de Entry .

Las propiedades de destino con un modo de enlace de OneTime solo se actualizan cuando cambia el contexto de
enlace. Para los enlaces en estas propiedades de destino, esto simplifica la infraestructura de enlace ya que no es
necesario supervisar los cambios en las propiedades de origen.

Modelos de vista y notificaciones de cambio de propiedad


En la página Selector de colores simple se muestra el uso de un modelo de vista simple. Los enlaces de datos
permiten al usuario seleccionar un color mediante tres elementos Slider para el matiz, la saturación y la
luminosidad.
El modelo de vista es el origen del enlace de datos. El modelo de vista no define propiedades enlazables, pero
implementa un mecanismo de notificación que permite que la infraestructura de enlace reciba una notificación
cuando cambia el valor de una propiedad. Este mecanismo de notificación es la interfaz de
INotifyPropertyChanged , que define un único evento denominado PropertyChanged . Una clase que implemente
esta interfaz normalmente desencadena el evento cuando cambia el valor de una de sus propiedades públicas.
No es necesario desencadenar el evento si la propiedad no cambia nunca. (La interfaz INotifyPropertyChanged
también se implementa mediante BindableObject y se desencadena un evento PropertyChanged cada vez que
una propiedad enlazable cambia de valor).
La HslColorViewModel clase define cinco propiedades: las propiedades Hue , Saturation , Luminosity y Color
están interrelacionadas. Cuando cambia el valor de cualquiera de los tres componentes de color, se vuelve a
calcular la propiedad Color y se desencadenan eventos PropertyChanged para las cuatro propiedades:

public class HslColorViewModel : INotifyPropertyChanged


{
Color color;
string name;

public event PropertyChangedEventHandler PropertyChanged;

public double Hue


{
set
{
if (color.Hue != value)
{
Color = Color.FromHsla(value, color.Saturation, color.Luminosity);
}
}
}
get
{
return color.Hue;
}
}

public double Saturation


{
set
{
if (color.Saturation != value)
{
Color = Color.FromHsla(color.Hue, value, color.Luminosity);
}
}
get
{
return color.Saturation;
}
}

public double Luminosity


{
set
{
if (color.Luminosity != value)
{
Color = Color.FromHsla(color.Hue, color.Saturation, value);
}
}
get
{
return color.Luminosity;
}
}

public Color Color


{
set
{
if (color != value)
{
color = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Hue"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Saturation"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Luminosity"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));

Name = NamedColor.GetNearestColorName(color);
}
}
get
{
return color;
}
}

public string Name


{
private set
{
if (name != value)
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
get
{
return name;
}
}
}

Cuando cambia la propiedad Color , el método estático GetNearestColorName de la clase NamedColor (incluido
también en la solución DataBindingDemos)obtiene el color con nombre más cercano y establece la propiedad
Name . Esta propiedad Name tiene un descriptor de acceso set privado, por lo que no se puede establecer desde
fuera de la clase.
Cuando se establece una clase ViewModel como un origen de enlace, la infraestructura de enlace adjunta un
controlador al evento PropertyChanged . De esta manera, el enlace puede recibir una notificación de cambios en
las propiedades y, después, puede establecer las propiedades de destino a partir de los valores cambiados.
Pero cuando una propiedad de destino (o la definición Binding en una propiedad de destino) tiene un elemento
BindingMode de tipo OneTime , no es necesario que la infraestructura de enlace adjunte un controlador al evento
PropertyChanged . La propiedad de destino solo se actualiza cuando cambia BindingContext y no cuando cambia
la propiedad de origen.
El archivo XAML Selector de colores simple crea una instancia de HslColorViewModel en el diccionario de
recursos de la página e inicializa la propiedad Color . La propiedad BindingContext de Grid se establece en una
extensión de enlace StaticResource para hacer referencia a ese recurso:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.SimpleColorSelectorPage">

<ContentPage.Resources>
<ResourceDictionary>
<local:HslColorViewModel x:Key="viewModel"
Color="MediumTurquoise" />

<Style TargetType="Slider">
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<Grid BindingContext="{StaticResource viewModel}">


<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<BoxView Color="{Binding Color}"


Grid.Row="0" />

<StackLayout Grid.Row="1"
Margin="10, 0">

<Label Text="{Binding Name}"


HorizontalTextAlignment="Center" />

<Slider Value="{Binding Hue}" />

<Slider Value="{Binding Saturation}" />

<Slider Value="{Binding Luminosity}" />


</StackLayout>
</Grid>
</ContentPage>

Los elementos BoxView , Label y tres vistas Slider heredan el contexto de enlace del elemento Grid . Todas
estas vistas son destinos de enlace que hacen referencia a las propiedades de origen en el modelo de vista. Para
la propiedad Color de BoxView y la propiedad Text de Label , los enlaces de datos son OneWay : Las
propiedades de la vista se establecen de las propiedades de ViewModel.
Pero la propiedad Value de Slider es TwoWay . Esto permite que cada elemento Slider se establezca a partir
del modelo de vista, y también que el modelo de vista se establezca a partir de cada elemento Slider .
Cuando se ejecuta por primera vez el programa, BoxView , Label y los tres elementos Slider están establecidos
a partir del modelo de vista en función de la propiedad Color inicial establecida cuando se creó la instancia del
modelo de vista. Esto se muestra en la captura de pantalla de iOS de la izquierda:
Al manipular los controles deslizantes, se actualizan BoxView y Label en consecuencia, como se muestra en las
capturas de pantalla de Android y UWP.
Un enfoque común consiste en crear instancias de ViewModel en el diccionario de recursos. También se pueden
crear instancias de ViewModel dentro de etiquetas de elemento de propiedad para la propiedad BindingContext .
En el archivo XAML Selector de colores simple, intente quitar HslColorViewModel del diccionario de recursos y
establézcalo en la propiedad BindingContext de Grid de esta forma:

<Grid>
<Grid.BindingContext>
<local:HslColorViewModel Color="MediumTurquoise" />
</Grid.BindingContext>

···

</Grid>

El contexto de enlace se puede establecer de varias formas. En ocasiones, el archivo de código subyacente crea
una instancia de ViewModel y la establece en la propiedad BindingContext de la página. Todos estos enfoques
son válidos.

Reemplazo del modo de enlace


Si el modo de enlace predeterminado en la propiedad de destino no es adecuado para un enlace de datos
concreto, es posible invalidarlo si se establece la propiedad Mode de Binding (o la propiedad Mode de la
extensión de marcado Binding ) en uno de los miembros de la enumeración BindingMode .
Pero establecer la propiedad Mode en TwoWay no siempre funciona como cabría esperar. Por ejemplo, intente
modificar el archivo XAML Enlace XAML alternativo para incluir TwoWay en la definición del enlace:

<Label Text="TEXT"
FontSize="40"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Scale="{Binding Source={x:Reference slider},
Path=Value,
Mode=TwoWay}" />

Se podría esperar que Slider se inicializara en el valor inicial de la propiedad Scale , que es 1, pero no es lo que
sucede. Cuando se inicializa un enlace TwoWay , primero se establece el destino a partir del origen, lo que significa
que la propiedad Scale se establece en el valor predeterminado de Slider de 0. Cuando se configura el enlace
TwoWay en el elemento Slider , el elemento Slider se establece inicialmente a partir del origen.

Puede establecer el modo de enlace en OneWayToSource en el ejemplo Enlace XAML alternativo:

<Label Text="TEXT"
FontSize="40"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Scale="{Binding Source={x:Reference slider},
Path=Value,
Mode=OneWayToSource}" />

Ahora se inicializa Slider en 1 (el valor predeterminado de Scale ) pero la manipulación de Slider no afecta a
la propiedad Scale , por lo que esto no es muy útil.

NOTE
La clase VisualElement también define las propiedades ScaleX y ScaleY , que pueden escalar el elemento
VisualElement de forma diferente en dirección horizontal y vertical.

Una aplicación muy útil de la invalidación del modo de enlace predeterminado con TwoWay implica la propiedad
SelectedItem de ListView . El modo de enlace predeterminado es OneWayToSource . Cuando se establece un
enlace de datos en la propiedad SelectedItem para hacer referencia a una propiedad de origen en un modelo de
vista, esa propiedad de origen se establece a partir de la selección ListView . Pero en algunas circunstancias, es
posible que le interese que también se inicialice ListView a partir del modelo de vista.
En la página Configuración de ejemplo se muestra esta técnica. Esta página representa una implementación
simple de la configuración de la aplicación, que a menudo se define en un modelo de vista, como en este archivo
SampleSettingsViewModel :

public class SampleSettingsViewModel : INotifyPropertyChanged


{
string name;
DateTime birthDate;
bool codesInCSharp;
double numberOfCopies;
NamedColor backgroundNamedColor;

public event PropertyChangedEventHandler PropertyChanged;

public SampleSettingsViewModel(IDictionary<string, object> dictionary)


{
Name = GetDictionaryEntry<string>(dictionary, "Name");
BirthDate = GetDictionaryEntry(dictionary, "BirthDate", new DateTime(1980, 1, 1));
CodesInCSharp = GetDictionaryEntry<bool>(dictionary, "CodesInCSharp");
NumberOfCopies = GetDictionaryEntry(dictionary, "NumberOfCopies", 1.0);
BackgroundNamedColor = NamedColor.Find(GetDictionaryEntry(dictionary, "BackgroundNamedColor",
"White"));
}

public string Name


{
set { SetProperty(ref name, value); }
get { return name; }
}

public DateTime BirthDate


{
set { SetProperty(ref birthDate, value); }
get { return birthDate; }
}

public bool CodesInCSharp


{
set { SetProperty(ref codesInCSharp, value); }
get { return codesInCSharp; }
}

public double NumberOfCopies


{
set { SetProperty(ref numberOfCopies, value); }
get { return numberOfCopies; }
}

public NamedColor BackgroundNamedColor


{
set
{
if (SetProperty(ref backgroundNamedColor, value))
{
OnPropertyChanged("BackgroundColor");
}
}
get { return backgroundNamedColor; }
}

public Color BackgroundColor


{
get { return BackgroundNamedColor?.Color ?? Color.White; }
}

public void SaveState(IDictionary<string, object> dictionary)


{
dictionary["Name"] = Name;
dictionary["BirthDate"] = BirthDate;
dictionary["CodesInCSharp"] = CodesInCSharp;
dictionary["NumberOfCopies"] = NumberOfCopies;
dictionary["BackgroundNamedColor"] = BackgroundNamedColor.Name;
}

T GetDictionaryEntry<T>(IDictionary<string, object> dictionary, string key, T defaultValue = default(T))


{
return dictionary.ContainsKey(key) ? (T)dictionary[key] : defaultValue;
}

bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)


{
if (Object.Equals(storage, value))
return false;

storage = value;
OnPropertyChanged(propertyName);
return true;
}

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)


{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Cada configuración de la aplicación es una propiedad que se guarda en el diccionario de propiedades de


Xamarin.Forms en un método denominado SaveState y se carga desde ese diccionario al constructor. Hacia la
parte inferior de la clase hay dos métodos que ayudan a simplificar los modelos de vista y hacer que sean menos
propensos a los errores. El método OnPropertyChanged de la parte inferior tiene un parámetro opcional que se
establece en la propiedad que realiza la llamada. Esto evita errores ortográficos al especificar el nombre de la
propiedad como una cadena.
El método SetProperty de la clase hace incluso más: compara el valor que se establece en la propiedad con el
valor almacenado como un campo, y solo llama a OnPropertyChanged cuando los dos valores no son iguales.
En la clase se definen dos propiedades para el color de fondo: la propiedad
SampleSettingsViewModel
BackgroundNamedColores de tipo NamedColor , que es una clase que también se incluye en la solución
DataBindingDemos. La propiedad BackgroundColor es de tipo Color y se obtiene de la propiedad Color del
objeto NamedColor .
La clase NamedColor usa la reflexión de .NET para enumerar todos los campos públicos estáticos en la estructura
de Color de Xamarin.Forms y almacenarlos con sus nombres en una colección que sea accesible desde la
propiedad All estática:

public class NamedColor : IEquatable<NamedColor>, IComparable<NamedColor>


{
// Instance members
private NamedColor()
{
}

public string Name { private set; get; }

public string FriendlyName { private set; get; }

public Color Color { private set; get; }

public string RgbDisplay { private set; get; }

public bool Equals(NamedColor other)


{
return Name.Equals(other.Name);
}

public int CompareTo(NamedColor other)


{
return Name.CompareTo(other.Name);
}

// Static members
static NamedColor()
{
List<NamedColor> all = new List<NamedColor>();
StringBuilder stringBuilder = new StringBuilder();

// Loop through the public static fields of the Color structure.


foreach (FieldInfo fieldInfo in typeof(Color).GetRuntimeFields())
{
if (fieldInfo.IsPublic &&
fieldInfo.IsStatic &&
fieldInfo.FieldType == typeof(Color))
{
// Convert the name to a friendly name.
string name = fieldInfo.Name;
stringBuilder.Clear();
int index = 0;

foreach (char ch in name)


{
if (index != 0 && Char.IsUpper(ch))
{
stringBuilder.Append(' ');
}
}
stringBuilder.Append(ch);
index++;
}

// Instantiate a NamedColor object.


Color color = (Color)fieldInfo.GetValue(null);

NamedColor namedColor = new NamedColor


{
Name = name,
FriendlyName = stringBuilder.ToString(),
Color = color,
RgbDisplay = String.Format("{0:X2}-{1:X2}-{2:X2}",
(int)(255 * color.R),
(int)(255 * color.G),
(int)(255 * color.B))
};

// Add it to the collection.


all.Add(namedColor);
}
}
all.TrimExcess();
all.Sort();
All = all;
}

public static IList<NamedColor> All { private set; get; }

public static NamedColor Find(string name)


{
return ((List<NamedColor>)All).Find(nc => nc.Name == name);
}

public static string GetNearestColorName(Color color)


{
double shortestDistance = 1000;
NamedColor closestColor = null;

foreach (NamedColor namedColor in NamedColor.All)


{
double distance = Math.Sqrt(Math.Pow(color.R - namedColor.Color.R, 2) +
Math.Pow(color.G - namedColor.Color.G, 2) +
Math.Pow(color.B - namedColor.Color.B, 2));

if (distance < shortestDistance)


{
shortestDistance = distance;
closestColor = namedColor;
}
}
return closestColor.Name;
}
}

En la clase del proyecto DataBindingDemos se define una propiedad denominada Settings de tipo
App
SampleSettingsViewModel . Esta propiedad se inicializa cuando se crea una instancia de la clase App y el método
SaveState se llama cuando se llama al método OnSleep :
public partial class App : Application
{
public App()
{
InitializeComponent();

Settings = new SampleSettingsViewModel(Current.Properties);

MainPage = new NavigationPage(new MainPage());


}

public SampleSettingsViewModel Settings { private set; get; }

protected override void OnStart()


{
// Handle when your app starts
}

protected override void OnSleep()


{
// Handle when your app sleeps
Settings.SaveState(Current.Properties);
}

protected override void OnResume()


{
// Handle when your app resumes
}
}

Para obtener más información sobre los métodos de ciclo de vida de aplicación, vea el artículo Ciclo de vida de
aplicación.
Prácticamente todo lo demás se controla en el archivo SampleSettingsPage.xaml. El elemento BindingContext
de la página se establece mediante una extensión de marcado Binding : el origen de enlace es la propiedad
estática Application.Current , que es la instancia de la clase App del proyecto, y Path se establece en la
propiedad Settings , que es el objeto SampleSettingsViewModel :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.SampleSettingsPage"
Title="Sample Settings"
BindingContext="{Binding Source={x:Static Application.Current},
Path=Settings}">

<StackLayout BackgroundColor="{Binding BackgroundColor}"


Padding="10"
Spacing="10">

<StackLayout Orientation="Horizontal">
<Label Text="Name: "
VerticalOptions="Center" />

<Entry Text="{Binding Name}"


Placeholder="your name"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center" />
</StackLayout>

<StackLayout Orientation="Horizontal">
<Label Text="Birth Date: "
VerticalOptions="Center" />

<DatePicker Date="{Binding BirthDate}"


<DatePicker Date="{Binding BirthDate}"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center" />
</StackLayout>

<StackLayout Orientation="Horizontal">
<Label Text="Do you code in C#? "
VerticalOptions="Center" />

<Switch IsToggled="{Binding CodesInCSharp}"


VerticalOptions="Center" />
</StackLayout>

<StackLayout Orientation="Horizontal">
<Label Text="Number of Copies: "
VerticalOptions="Center" />

<Stepper Value="{Binding NumberOfCopies}"


VerticalOptions="Center" />

<Label Text="{Binding NumberOfCopies}"


VerticalOptions="Center" />
</StackLayout>

<Label Text="Background Color:" />

<ListView x:Name="colorListView"
ItemsSource="{x:Static local:NamedColor.All}"
SelectedItem="{Binding BackgroundNamedColor, Mode=TwoWay}"
VerticalOptions="FillAndExpand"
RowHeight="40">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<BoxView Color="{Binding Color}"
HeightRequest="32"
WidthRequest="32"
VerticalOptions="Center" />

<Label Text="{Binding FriendlyName}"


FontSize="24"
VerticalOptions="Center" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>

Todos los elementos secundarios de la página heredan el contexto de enlace. La mayoría de los demás enlaces de
esta página son para las propiedades de SampleSettingsViewModel . La propiedad BackgroundColor se usa para
establecer la propiedad BackgroundColor de StackLayout , y las propiedades Entry , DatePicker , Switch y
Stepper se enlazan a otras propiedades del modelo de vista.

La propiedad ItemsSource de ListView se establece en la propiedad estática NamedColor.All . Esto rellena


ListView con todas las instancias de NamedColor . Para cada elemento de ListView , el contexto de enlace para el
elemento se establece en un objeto NamedColor . Los elementos BoxView y Label de ViewCell están enlazados a
propiedades de NamedColor .
La propiedad SelectedItem de ListView es de tipo NamedColor y se enlaza a la propiedad BackgroundNamedColor
de SampleSettingsViewModel :
SelectedItem="{Binding BackgroundNamedColor, Mode=TwoWay}"

El modo de enlace predeterminado para SelectedItem es OneWayToSource , que establece la propiedad


ViewModel a partir del elemento seleccionado. El modo TwoWay permite que se inicialice SelectedItem a partir
del modelo de vista.
Pero cuando SelectedItem se establece de esta forma, ListView no se desplaza automáticamente para mostrar
el elemento seleccionado. Se necesita un poco de código en el archivo de código subyacente:

public partial class SampleSettingsPage : ContentPage


{
public SampleSettingsPage()
{
InitializeComponent();

if (colorListView.SelectedItem != null)
{
colorListView.ScrollTo(colorListView.SelectedItem,
ScrollToPosition.MakeVisible,
false);
}
}
}

En la captura de pantalla de iOS de la izquierda se muestra el programa al ejecutarlo por primera vez. El
constructor de SampleSettingsViewModel inicializa el color de fondo en blanco, y eso es lo que está seleccionado
en ListView :

La otra captura de pantalla muestra la configuración modificada. Al experimentar con esta página, recuerde
colocar el programa en modo de suspensión o finalícelo en el dispositivo o emulador que se está ejecutando. La
finalización del programa desde el depurador de Visual Studio no provocará que se llame a la invalidación de
OnSleep en la clase App .

En el artículo siguiente verá cómo especificar los formatos de cadena de los enlaces de datos que se establecen
en la propiedad Text de Label .

Vínculos relacionados
Data Binding Demos (sample) (Demos de enlace de datos [ejemplo])
Capítulo sobre enlace de datos del libro de Xamarin.Forms
Formato de cadena de Xamarin.Forms
11/07/2019 • 8 minutes to read • Edit Online

Descargar el ejemplo
A veces es conveniente usar enlaces de datos para mostrar la representación de cadena de un objeto o valor. Por
ejemplo, es posible que quiera utilizar una Label para mostrar el valor actual de un Slider . En este enlace de
datos, Slider es el origen y el destino es la propiedad Text de Label .
Cuando se muestran las cadenas en el código, la herramienta más eficaz es el método String.Format estático. La
cadena de formato incluye códigos específicos para distintos tipos de objetos de formato, y puede incluir otros
textos junto con los valores a los que se va a aplicar formato. Para obtener más información sobre el formato de
cadena, vea Aplicar formato a tipos en .NET.

La propiedad StringFormat
Este recurso se transmite a los enlaces de datos: usted establece la propiedad StringFormat de Binding (o la
propiedad StringFormat de la extensión de marcado Binding ) a una cadena de formato .NET estándar con un
marcador de posición:

<Slider x:Name="slider" />


<Label Text="{Binding Source={x:Reference slider},
Path=Value,
StringFormat='The slider value is {0:F2}'}" />

Tenga en cuenta que la cadena de formato está delimitada por caracteres de comillas simples (apóstrofo) para
ayudar al analizador XAML a evitar que trate las llaves como otra extensión de marcado XAML. En caso contrario,
esa cadena sin el carácter de comillas simples es la misma cadena que utilizaría para mostrar un valor de punto
flotante en una llamada a String.Format . Una especificación de formato de F2 hace que el valor se muestre con
dos posiciones decimales.
La propiedad StringFormat sólo tiene sentido cuando la propiedad de destino es de tipo string , y el modo de
enlace es OneWay o TwoWay . Para los enlaces bidireccionales, el StringFormat solo es aplicable para los valores
que se pasan desde el origen al destino.
Como verá en el artículo Xamarin.Forms Binding Path (Ruta de acceso de Xamarin.Forms), los enlaces de datos
pueden llegar a ser bastante complejos y enrevesados. Al depurar estos enlaces de datos, puede agregar una
Label en el archivo XAML con un StringFormat para mostrar algunos resultados intermedios. Puede resultar
útil incluso si se usa solo para mostrar un tipo de objeto.
La página Formato de cadena muestra varios usos de la propiedad StringFormat :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
x:Class="DataBindingDemos.StringFormattingPage"
Title="String Formatting">

<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>

<Style TargetType="BoxView">
<Setter Property="Color" Value="Blue" />
<Setter Property="HeightRequest" Value="2" />
<Setter Property="Margin" Value="0, 5" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<StackLayout Margin="10">
<Slider x:Name="slider" />
<Label Text="{Binding Source={x:Reference slider},
Path=Value,
StringFormat='The slider value is {0:F2}'}" />

<BoxView />

<TimePicker x:Name="timePicker" />


<Label Text="{Binding Source={x:Reference timePicker},
Path=Time,
StringFormat='The TimeSpan is {0:c}'}" />

<BoxView />

<Entry x:Name="entry" />


<Label Text="{Binding Source={x:Reference entry},
Path=Text,
StringFormat='The Entry text is &quot;{0}&quot;'}" />

<BoxView />

<StackLayout BindingContext="{x:Static sys:DateTime.Now}">


<Label Text="{Binding}" />
<Label Text="{Binding Path=Ticks,
StringFormat='{0:N0} ticks since 1/1/1'}" />
<Label Text="{Binding StringFormat='The {{0:MMMM}} specifier produces {0:MMMM}'}" />
<Label Text="{Binding StringFormat='The long date is {0:D}'}" />
</StackLayout>

<BoxView />

<StackLayout BindingContext="{x:Static sys:Math.PI}">


<Label Text="{Binding}" />
<Label Text="{Binding StringFormat='PI to 4 decimal points = {0:F4}'}" />
<Label Text="{Binding StringFormat='PI in scientific notation = {0:E7}'}" />
</StackLayout>
</StackLayout>
</ContentPage>

Los enlaces en el Slider y el TimePicker muestran el uso de las especificaciones de formato específicas de los
tipos de datos double y TimeSpan . El StringFormat que muestra el texto de la vista Entry muestra cómo
especificar las comillas dobles en la cadena de formato con el uso de la entidad HTML &quot; .
La siguiente sección en el archivo XAML es un StackLayout con un BindingContext establecido en una extensión
de marcado de x:Static que hace referencia a la propiedad DateTime.Now estática. El primer enlace no tiene
propiedades:

<Label Text="{Binding}" />

Esto muestra simplemente el valor DateTime de BindingContext con el formato predeterminado. El segundo
enlace muestra la propiedad Ticks de DateTime , mientras que los otros dos enlaces muestran el propio
DateTime con un formato específico. Observe este StringFormat :

<Label Text="{Binding StringFormat='The {{0:MMMM}} specifier produces {0:MMMM}'}" />

Si necesita mostrar llaves a la izquierda o la derecha en la cadena de formato, simplemente use un par de ellas.
Los conjuntos de la última sección establecen el BindingContext al valor de Math.PI y lo muestran con formato
de forma predeterminada y dos tipos diferentes de formato numérico.
Esta es la ejecución del programa:

ViewModels y formato de cadena


Cuando se usa Label y StringFormat para mostrar el valor de una vista que también es el destino de una clase
ViewModel, puede definir el enlace de la vista para Label o desde el ViewModel a Label . En general, el segundo
enfoque es mejor porque comprueba que funcionan los enlaces entre View y ViewModel.
Este método se muestra en el ejemplo Mejor selector de colores, que usa el mismo ViewModel que el
programa Selector de colores simple que aparece en el artículo Xamarin.Forms Binding Mode (Modo de
enlace de Xamarin.Forms):
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.BetterColorSelectorPage"
Title="Better Color Selector">

<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Slider">
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
</Style>

<Style TargetType="Label">
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<StackLayout>
<StackLayout.BindingContext>
<local:HslColorViewModel Color="Sienna" />
</StackLayout.BindingContext>

<BoxView Color="{Binding Color}"


VerticalOptions="FillAndExpand" />

<StackLayout Margin="10, 0">


<Label Text="{Binding Name}" />

<Slider Value="{Binding Hue}" />


<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />

<Slider Value="{Binding Saturation}" />


<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />

<Slider Value="{Binding Luminosity}" />


<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</StackLayout>
</ContentPage>

Ahora hay tres pares de elementos Slider y Label que están enlazados a la misma propiedad de origen en el
objeto HslColorViewModel . La única diferencia es que Label tiene una propiedad StringFormat para mostrar
cada valor Slider .
Tal vez se pregunte cómo podría mostrar sus valores RGB (rojos, verdes y azules) en formato hexadecimal de dos
dígitos tradicional. Los valores enteros no están directamente disponibles desde la estructura de Color . Una
solución sería calcular valores enteros de los componentes de color dentro de ViewModel y exponerlos como
propiedades. Después puede aplicarles formato utilizando la especificación de formato X2 .
Otro enfoque es más general: puede escribir un convertidor de valores de enlace como se describe en el siguiente
artículo, Convertidor de valores de enlace de Xamarin.Forms.
Empero, el siguiente artículo explora la Ruta de enlace con más detalle y muestra cómo se puede utilizar para
hacer referencia a elementos de las colecciones y subpropiedades.

Vínculos relacionados
Data Binding Demos (sample) (Demos de enlace de datos [ejemplo])
Capítulo sobre enlace de datos del libro de Xamarin.Forms
Ruta de acceso de enlace de Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

Descargar el ejemplo
En todos los ejemplos de enlace de datos anteriores, la propiedad Path de la clase Binding (o la propiedad
Path de la extensión de marcado Binding ) se había establecido en una sola propiedad. En realidad es posible
establecer Path en una subpropiedad (una propiedad de una propiedad), o bien en un miembro de una
colección.
Por ejemplo, suponga que la página contiene un control TimePicker :

<TimePicker x:Name="timePicker">

La propiedad Time de TimePicker es de tipo TimeSpan , pero es posible que quiera crear un enlace de datos que
haga referencia a la propiedad TotalSeconds de ese valor TimeSpan . Este es el enlace de datos:

{Binding Source={x:Reference timePicker},


Path=Time.TotalSeconds}

La propiedad Time es de tipo TimeSpan , que tiene una propiedad TotalSeconds . Las propiedades Time y
TotalSeconds se conectan simplemente con un punto. Los elementos de la cadena Path siempre hacen
referencia a propiedades y no a los tipos de estas propiedades.
Ese y otros muchos ejemplos se muestran en la página Variaciones de la ruta de acceso:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:globe="clr-namespace:System.Globalization;assembly=mscorlib"
x:Class="DataBindingDemos.PathVariationsPage"
Title="Path Variations"
x:Name="page">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="FontSize" Value="Large" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<StackLayout Margin="10, 0">


<TimePicker x:Name="timePicker" />

<Label Text="{Binding Source={x:Reference timePicker},


Path=Time.TotalSeconds,
StringFormat='{0} total seconds'}" />

<Label Text="{Binding Source={x:Reference page},


Path=Content.Children.Count,
StringFormat='There are {0} children in this StackLayout'}" />

<Label Text="{Binding Source={x:Static globe:CultureInfo.CurrentCulture},


Path=DateTimeFormat.DayNames[3],
StringFormat='The middle day of the week is {0}'}" />

<Label>
<Label.Text>
<Binding Path="DateTimeFormat.DayNames[3]"
StringFormat="The middle day of the week in France is {0}">
<Binding.Source>
<globe:CultureInfo>
<x:Arguments>
<x:String>fr-FR</x:String>
</x:Arguments>
</globe:CultureInfo>
</Binding.Source>
</Binding>
</Label.Text>
</Label>

<Label Text="{Binding Source={x:Reference page},


Path=Content.Children[1].Text.Length,
StringFormat='The second Label has {0} characters'}" />
</StackLayout>
</ContentPage>

En el segundo control Label , el origen de enlace es la propia página. La propiedad Content es de tipo
StackLayout , que tiene una propiedad Children de tipo IList<View> , que a su vez tiene una propiedad Count
que indica el número de elementos secundarios.

Rutas de acceso con indizadores


El enlace en el tercer control Label de las páginas Variaciones de la ruta de acceso hace referencia a la clase
CultureInfo del espacio de nombres System.Globalization :
<Label Text="{Binding Source={x:Static globe:CultureInfo.CurrentCulture},
Path=DateTimeFormat.DayNames[3],
StringFormat='The middle day of the week is {0}'}" />

El origen se establece en la propiedad estática CultureInfo.CurrentCulture , que es un objeto de tipo CultureInfo .


Esa clase define una propiedad denominada DateTimeFormat de tipo DateTimeFormatInfo que contiene una
colección DayNames . El índice selecciona el cuarto elemento.
El cuarto control Label hace algo similar pero para la referencia cultural asociada con Francia. La propiedad
Source del enlace se establece en el objeto CultureInfo con un constructor:

<Label>
<Label.Text>
<Binding Path="DateTimeFormat.DayNames[3]"
StringFormat="The middle day of the week in France is {0}">
<Binding.Source>
<globe:CultureInfo>
<x:Arguments>
<x:String>fr-FR</x:String>
</x:Arguments>
</globe:CultureInfo>
</Binding.Source>
</Binding>
</Label.Text>
</Label>

Vea Pasar argumentos de constructor para obtener más información sobre cómo especificar argumentos de
constructor en XAML.
El último ejemplo es similar al segundo, salvo que hace referencia a uno de los elementos secundarios del
elemento StackLayout :

<Label Text="{Binding Source={x:Reference page},


Path=Content.Children[1].Text.Length,
StringFormat='The first Label has {0} characters'}" />

Ese elemento secundario es un control Label , que tiene una propiedad Text de tipo String , que a su vez tiene
una propiedad Length . El primer objeto Label notifica el valor TimeSpan establecido en el elemento TimePicker ,
de modo que cuando se cambia el texto, el último objeto Label también cambia.
Esta es la ejecución del programa:
Depuración de rutas de acceso complejas
Las definiciones de ruta de acceso complejas pueden ser difíciles de construir: necesita saber el tipo de cada
subpropiedad o el tipo de los elementos de la colección para agregar correctamente la subpropiedad siguiente,
pero los propios tipos no aparecen en la ruta de acceso. Una técnica adecuada consiste en crear la ruta de acceso
de forma incremental y observar los resultados intermedios. Para ese último ejemplo, se podría empezar sin
ninguna definición de Path :

<Label Text="{Binding Source={x:Reference page},


StringFormat='{0}'}" />

Eso muestra el tipo del origen de enlace, o DataBindingDemos.PathVariationsPage . Ya sabe que PathVariationsPage
se deriva de ContentPage , por lo que tiene una propiedad Content :

<Label Text="{Binding Source={x:Reference page},


Path=Content,
StringFormat='{0}'}" />

Ahora, se revela que el tipo de la propiedad Content es Xamarin.Forms.StackLayout . Agregue la propiedad


Children a Path y el tipo es Xamarin.Forms.ElementCollection'1[Xamarin.Forms.View] , que es una clase interna
para Xamarin.Forms, pero obviamente un tipo de colección. Agregue un índice a lo anterior y el tipo será
Xamarin.Forms.Label . Continúe de esta manera.

A medida que Xamarin.Forms procesa la ruta de acceso de enlace, instala un controlador PropertyChanged en
todos los objetos de la ruta de acceso que implementen la interfaz INotifyPropertyChanged . Por ejemplo, el enlace
final reacciona ante un cambio en el primer objeto Label porque cambia la propiedad Text .
Si una propiedad en la ruta de acceso de enlace no implementa INotifyPropertyChanged , se omitirán todos los
cambios a esa propiedad. Algunos cambios podrían invalidar completamente la ruta de acceso de enlace, por lo
que solo debe usar esta técnica cuando la cadena de propiedades y subpropiedades nunca sea no válida.

Vínculos relacionados
Data Binding Demos (sample) (Demos de enlace de datos [ejemplo])
Capítulo sobre enlace de datos del libro de Xamarin.Forms
Convertidor de valores de enlace de Xamarin.Forms
11/07/2019 • 18 minutes to read • Edit Online

Descargar el ejemplo
Los enlaces de datos normalmente transfieren datos desde una propiedad de origen a una propiedad de destino y,
en algunos casos, desde la propiedad de destino a la propiedad de origen. Esta transferencia es sencilla cuando las
propiedades de origen y destino son del mismo tipo, o cuando un tipo se puede convertir al otro mediante una
conversión implícita. Cuando no es así, debe realizarse una conversión de tipos.
En el artículo String Formatting (Formato de cadena), vio cómo puede usar la propiedad StringFormat de un
enlace de datos para convertir cualquier tipo en una cadena. Para otros tipos de conversiones, deberá escribir
código especializado en una clase que implementa la interfaz de IValueConverter . (La Plataforma universal de
Windows contiene una clase similar denominada IValueConverter en el espacio de nombres
Windows.UI.Xaml.Data , pero este IValueConverter está en el espacio de nombres Xamarin.Forms .) Las clases que
implementan IValueConverter se denominan convertidores de valores, pero también se denominan a menudo
convertidores de enlaces o convertidores de valores de enlace.

La interfaz de IValueConverter
Suponga que desea definir un enlace de datos cuya propiedad de origen es del tipo int pero la propiedad de
destino es un bool . Desea que este enlace de datos genere un valor false cuando el origen del entero es igual a
0 y true en caso contrario.
Puede hacerlo con una clase que implementa la interfaz IValueConverter :

public class IntToBoolConverter : IValueConverter


{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value != 0;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? 1 : 0;
}
}

Establezca una instancia de esta clase en la propiedad Converter de la clase Binding o en la propiedad
Converter de la extensión de marcado Binding . Esta clase se convierte en parte del enlace de datos.

Se llama al método Convert cuando los datos se mueven desde el origen al destino en los enlaces OneWay o
TwoWay . El parámetro value es el objeto o el valor del origen de enlace de datos. El método debe devolver un
valor del tipo del destino de enlace de datos. El método que se muestra aquí convierte el parámetro value a un
int y después lo compara con 0 para un valor devuelto bool .

Se llama al método ConvertBack cuando los datos se mueven desde el destino al origen en los enlaces TwoWay o
OneWayToSource . ConvertBack realiza la conversión opuesta: supone que el parámetro value es un bool desde el
destino y lo convierte en un valor devuelto int para el origen.
Si el enlace de datos también incluye una configuración StringFormat , se invoca el convertidor de valores antes
de que se le dé formato de cadena al resultado.
La página Enable Buttons (Habilitar botones) en el ejemplo de Data Binding Demos (Demostraciones de
enlace de datos) muestra cómo utilizar este convertidor de valores en un enlace de datos. Se crea una instancia de
IntToBoolConverter en el diccionario de recursos de la página. Después se le hace referencia con una extensión de
marcado StaticResource para establecer la propiedad Converter en dos enlaces de datos. Es muy común
compartir los convertidores de tipos de datos entre varios enlaces de datos en la página:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.EnableButtonsPage"
Title="Enable Buttons">
<ContentPage.Resources>
<ResourceDictionary>
<local:IntToBoolConverter x:Key="intToBool" />
</ResourceDictionary>
</ContentPage.Resources>

<StackLayout Padding="10, 0">


<Entry x:Name="entry1"
Text=""
Placeholder="enter search term"
VerticalOptions="CenterAndExpand" />

<Button Text="Search"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
IsEnabled="{Binding Source={x:Reference entry1},
Path=Text.Length,
Converter={StaticResource intToBool}}" />

<Entry x:Name="entry2"
Text=""
Placeholder="enter destination"
VerticalOptions="CenterAndExpand" />

<Button Text="Submit"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
IsEnabled="{Binding Source={x:Reference entry2},
Path=Text.Length,
Converter={StaticResource intToBool}}" />
</StackLayout>
</ContentPage>

Si se usa un convertidor de valores en varias páginas de la aplicación, puede crear una instancia de él en el
diccionario de recursos en el archivo App.xaml.
La página Enable Buttons (Habilitar botones) muestra una necesidad común cuando un Button realiza una
operación basada en texto que el usuario escribe en un vista Entry . Si no se ha escrito nada en el Entry , el
Button debe deshabilitarse. Cada Button contiene un enlace de datos en su propiedad IsEnabled . El origen de
enlace de datos es la propiedad Length de la propiedad Text de la Entry correspondiente. Si esa propiedad
Length no es 0, el convertidor de valores devuelve true y se habilita el Button :
Tenga en cuenta que la propiedad Text en cada Entry se inicializa en una cadena vacía. La propiedad Text es
null de forma predeterminada, y los datos de enlace no funcionarán en ese caso.

Algunos convertidores de valores se escriben específicamente para determinadas aplicaciones, mientras que otros
están generalizados. Si sabe que un convertidor de valores solo se usará en los enlaces OneWay , el método
ConvertBack puede devolver simplemente null .

El método Convert mostrado anteriormente supone implícitamente que el argumento value es de tipo int y el
valor devuelto debe ser de tipo bool . De forma similar, el método ConvertBack supone que el argumento value
es de tipo bool y el valor devuelto es int . Si no es así, se producirá una excepción en tiempo de ejecución.
Puede escribir los convertidores de valores para que sean más generalizados y acepten diferentes tipos de datos.
Los métodos Convert y ConvertBack pueden usar los operadores as o is con el parámetro value , o pueden
llamar a GetType en ese parámetro para determinar su tipo y después realizar algo adecuado. El tipo esperado del
valor devuelto de cada método viene dado por el parámetro targetType . A veces, los convertidores de valores se
utilizan con los enlaces de datos de diferentes tipos de destino; el convertidor de valores puede usar el argumento
targetType para realizar una conversión del tipo correcto.

Si la conversión que se realiza es diferente para distintas referencias culturales, utilice el parámetro culture para
este propósito. El argumento parameter para Convert y ConvertBack se explica más adelante en este artículo.

Propiedades de convertidor de tipos de enlace


Las clases de convertidor de valores pueden tener propiedades y parámetros genéricos. Este convertidor de
valores determinado convierte un bool desde el origen a un objeto de tipo T para el destino:
public class BoolToObjectConverter<T> : IValueConverter
{
public T TrueObject { set; get; }

public T FalseObject { set; get; }

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? TrueObject : FalseObject;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((T)value).Equals(TrueObject);
}
}

La página Switch Indicators (Indicadores de conmutador) muestra cómo puede usarse para mostrar el valor de
una vista Switch . Aunque es común crear instancias de los convertidores de valores como recursos en un
diccionario de recursos, esta página muestra una alternativa: se crea una instancia de cada convertidor de valores
entre etiquetas de elemento de propiedad Binding.Converter . El x:TypeArguments indica el argumento genérico, y
TrueObject y FalseObject se establecen en objetos de ese tipo:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.SwitchIndicatorsPage"
Title="Switch Indicators">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="FontSize" Value="18" />
<Setter Property="VerticalOptions" Value="Center" />
</Style>

<Style TargetType="Switch">
<Setter Property="VerticalOptions" Value="Center" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<StackLayout Padding="10, 0">


<StackLayout Orientation="Horizontal"
VerticalOptions="CenterAndExpand">
<Label Text="Subscribe?" />
<Switch x:Name="switch1" />
<Label>
<Label.Text>
<Binding Source="{x:Reference switch1}"
Path="IsToggled">
<Binding.Converter>
<local:BoolToObjectConverter x:TypeArguments="x:String"
TrueObject="Of course!"
FalseObject="No way!" />
</Binding.Converter>
</Binding>
</Label.Text>
</Label>
</StackLayout>

<StackLayout Orientation="Horizontal"
VerticalOptions="CenterAndExpand">
<Label Text="Allow popups?" />
<Switch x:Name="switch2" />
<Label>
<Label>
<Label.Text>
<Binding Source="{x:Reference switch2}"
Path="IsToggled">
<Binding.Converter>
<local:BoolToObjectConverter x:TypeArguments="x:String"
TrueObject="Yes"
FalseObject="No" />
</Binding.Converter>
</Binding>
</Label.Text>
<Label.TextColor>
<Binding Source="{x:Reference switch2}"
Path="IsToggled">
<Binding.Converter>
<local:BoolToObjectConverter x:TypeArguments="Color"
TrueObject="Green"
FalseObject="Red" />
</Binding.Converter>
</Binding>
</Label.TextColor>
</Label>
</StackLayout>

<StackLayout Orientation="Horizontal"
VerticalOptions="CenterAndExpand">
<Label Text="Learn more?" />
<Switch x:Name="switch3" />
<Label FontSize="18"
VerticalOptions="Center">
<Label.Style>
<Binding Source="{x:Reference switch3}"
Path="IsToggled">
<Binding.Converter>
<local:BoolToObjectConverter x:TypeArguments="Style">
<local:BoolToObjectConverter.TrueObject>
<Style TargetType="Label">
<Setter Property="Text" Value="Indubitably!" />
<Setter Property="FontAttributes" Value="Italic, Bold" />
<Setter Property="TextColor" Value="Green" />
</Style>
</local:BoolToObjectConverter.TrueObject>

<local:BoolToObjectConverter.FalseObject>
<Style TargetType="Label">
<Setter Property="Text" Value="Maybe later" />
<Setter Property="FontAttributes" Value="None" />
<Setter Property="TextColor" Value="Red" />
</Style>
</local:BoolToObjectConverter.FalseObject>
</local:BoolToObjectConverter>
</Binding.Converter>
</Binding>
</Label.Style>
</Label>
</StackLayout>
</StackLayout>
</ContentPage>

En el último de los tres pares Switch y Label , el argumento genérico se establece en Style y se proporcionan
objetos Style completos para los valores de TrueObject y FalseObject . Esto ignora el estilo implícito para
Label que se establece en el diccionario de recursos, por lo que las propiedades de ese estilo están asignadas
explícitamente a la Label . Activar o desactivar el Switch hace que el correspondiente Label refleje el cambio:
También es posible usar Triggers para implementar cambios similares en la interfaz de usuario en función de
otras vistas.

Parámetros de convertidor de tipos de enlace


La clase Binding define una propiedad ConverterParameter y la extensión de marcado Binding también define
una propiedad ConverterParameter . Si se establece esta propiedad, el valor se pasa a los métodos Convert y
ConvertBack como el argumento parameter . Incluso si la instancia del convertidor se comparte entre varios
enlaces de datos, el ConverterParameter puede ser diferente para realizar conversiones algo diferentes.
El uso de ConverterParameter se muestra con un programa de selección de color. En este caso, el
RgbColorViewModel tiene tres propiedades de tipo double denominadas Red , Green y Blue que usa para
construir un valor Color :

public class RgbColorViewModel : INotifyPropertyChanged


{
Color color;
string name;

public event PropertyChangedEventHandler PropertyChanged;

public double Red


{
set
{
if (color.R != value)
{
Color = new Color(value, color.G, color.B);
}
}
get
{
return color.R;
}
}

public double Green


{
set
{
if (color.G != value)
{
Color = new Color(color.R, value, color.B);
}
}
get
{
return color.G;
}
}

public double Blue


{
set
{
if (color.B != value)
{
Color = new Color(color.R, color.G, value);
}
}
get
{
return color.B;
}
}

public Color Color


{
set
{
if (color != value)
{
color = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Red"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Green"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Blue"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));

Name = NamedColor.GetNearestColorName(color);
}
}
get
{
return color;
}
}

public string Name


{
private set
{
if (name != value)
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
get
{
return name;
}
}
}

Las propiedades Red , Green y Blue oscilan entre 0 y 1. Con todo, es preferible que los componentes se
muestren como valores hexadecimales de dos dígitos.
Para mostrar estos elementos como valores hexadecimales en XAML, deben multiplicarse por 255, convertirse en
un entero y después debe aplicárseles formato con la especificación de "X2" en la propiedad StringFormat . Las
dos primeras tareas (multiplicar por 255 y convertir en un entero) pueden controlarse mediante el convertidor de
valores. Para hacer el convertidor de valores tan generalizado como sea posible, puede especificarse el factor de
multiplicación con la propiedad ConverterParameter , lo que significa que introduce los métodos Convert y
ConvertBack como el argumento parameter :

public class DoubleToIntConverter : IValueConverter


{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)Math.Round((double)value * GetParameter(parameter));
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value / GetParameter(parameter);
}

double GetParameter(object parameter)


{
if (parameter is double)
return (double)parameter;

else if (parameter is int)


return (int)parameter;

else if (parameter is string)


return double.Parse((string)parameter);

return 1;
}
}

El Convert convierte de un double a int al multiplicar por el valor parameter ; el ConvertBack divide el
argumento value entero entre parameter y devuelve un resultado double . (En el programa que se muestra
debajo, el convertidor de valores se usa solo en relación con el formato de cadenas, por lo que ConvertBack no se
usa.)
Es probable que el tipo del argumento parameter sea diferente en función de si se ha definido el enlace de datos
en el código o en XAML. Si la propiedad ConverterParameter de Binding se establece en código, es probable que
se establezca en un valor numérico:

binding.ConverterParameter = 255;

La propiedad ConverterParameter es de tipo Object , por lo que el compilador C# interpreta el literal 255 como un
entero y establece la propiedad en ese valor.
Pero en XAML es probable que el ConverterParameter se establezca de este modo:

<Label Text="{Binding Red,


Converter={StaticResource doubleToInt},
ConverterParameter=255,
StringFormat='Red = {0:X2}'}" />

El 255 parece un número, pero dado que ConverterParameter es de tipo Object , el analizador XAML trata el 255
como una cadena.
Por ese motivo, el convertidor de valores mostrado anteriormente incluye un método GetParameter
independiente que controla los casos para parameter de tipo double , int o string .
La página RGB Color Selector (Selector de colores RGB ) crea una instancia de la página DoubleToIntConverter
en su diccionario de recursos siguiendo la definición de dos estilos implícitos:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.RgbColorSelectorPage"
Title="RGB Color Selector">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Slider">
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
</Style>

<Style TargetType="Label">
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>

<local:DoubleToIntConverter x:Key="doubleToInt" />


</ResourceDictionary>
</ContentPage.Resources>

<StackLayout>
<StackLayout.BindingContext>
<local:RgbColorViewModel Color="Gray" />
</StackLayout.BindingContext>

<BoxView Color="{Binding Color}"


VerticalOptions="FillAndExpand" />

<StackLayout Margin="10, 0">


<Label Text="{Binding Name}" />

<Slider Value="{Binding Red}" />


<Label Text="{Binding Red,
Converter={StaticResource doubleToInt},
ConverterParameter=255,
StringFormat='Red = {0:X2}'}" />

<Slider Value="{Binding Green}" />


<Label Text="{Binding Green,
Converter={StaticResource doubleToInt},
ConverterParameter=255,
StringFormat='Green = {0:X2}'}" />

<Slider Value="{Binding Blue}" />


<Label>
<Label.Text>
<Binding Path="Blue"
StringFormat="Blue = {0:X2}"
Converter="{StaticResource doubleToInt}">
<Binding.ConverterParameter>
<x:Double>255</x:Double>
</Binding.ConverterParameter>
</Binding>
</Label.Text>
</Label>
</StackLayout>
</StackLayout>
</ContentPage>

Los valores de las propiedades Red y Green se muestran con una extensión de marcado Binding . Empero, la
propiedad Blue crea una instancia de la clase Binding para demostrar cómo un valor double explícito puede
establecerse en la propiedad ConverterParameter .
Este es el resultado:

Vínculos relacionados
Data Binding Demos (sample) (Demos de enlace de datos [ejemplo])
Capítulo sobre enlace de datos del libro de Xamarin.Forms
Reservas de enlace de Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

Descargar el ejemplo
En ocasiones, los enlaces de datos producen errores, porque no se puede resolver el origen de enlace o porque el
enlace se realiza correctamente pero devuelve un valor null . Si bien estos escenarios se pueden controlar con los
convertidores de valor, u otro código adicional, los enlaces de datos pueden hacerse más sólidos mediante la
definición de valores de reserva para su uso si se produce un error en el proceso de enlace. Esto puede realizarse
mediante la definición de las propiedades FallbackValue y TargetNullValue en una expresión de enlace. Dado que
estas propiedades residen en la clase BindingBase , pueden usarse con enlaces, con enlaces compilados y con la
extensión de marcado Binding .

NOTE
El uso de las propiedades FallbackValue y TargetNullValue en una expresión de enlace es opcional.

Definición de un valor de reserva


La propiedad FallbackValue permite que se pueda definir un valor de reserva que se usará cuando el origen del
enlace no se pueda resolver. Un escenario común para establecer esta propiedad es cuando se realiza el enlace con
propiedades de origen que podrían no existir en todos los objetos en una colección enlazada de tipos
heterogéneos.
La página MonkeyDetail ilustra el establecimiento de la propiedad FallbackValue :

<Label Text="{Binding Population, FallbackValue='Population size unknown'}"


... />

El enlace en Label define un valor de FallbackValue que se establecerá en el destino si no se puede resolver el
origen del enlace. Por lo tanto, el valor definido por la propiedad FallbackValue se mostrará si la propiedad
Population no existe en el objeto enlazado. Tenga en cuenta que aquí, el valor de la propiedad FallbackValue está
delimitado por caracteres de comillas simples (apóstrofo).
En lugar de definir los valores de la propiedad FallbackValue en línea, se recomienda definirlos como recursos en
ResourceDictionary . La ventaja de este enfoque es que estos valores se definen una vez en una sola ubicación y
son localizables más fácilmente. Posteriormente, se pueden recuperar los recursos mediante la extensión de
marcado StaticResource :

<Label Text="{Binding Population, FallbackValue={StaticResource populationUnknown}}"


... />

NOTE
No es posible establecer la propiedad FallbackValue con una expresión de enlace.

Esta es la ejecución del programa:


Cuando el FallbackValue propiedad no está establecida en una expresión de enlace y la ruta de acceso del enlace
o parte de la ruta de acceso no se resuelve, BindableProperty.DefaultValue se establece en el destino. Sin embargo,
cuando la propiedad FallbackValue está establecida y la ruta de acceso del enlace o parte de la ruta de acceso no
se resuelve, el valor de la propiedad del valor FallbackValue se establece en el destino. Por lo tanto, en la página
MonkeyDetail, Label muestra el mensaje "Population size unknown" ("Tamaño de la población desconocido"),
porque al elemento enlazado le falta una propiedad Population .

IMPORTANT
Un convertidor de valores definido no se ejecuta en una expresión de enlace cuando la propiedad FallbackValue está
establecida.

Definición de un valor de reemplazo nulo


La propiedad TargetNullValue permite que se pueda definir un valor de reemplazo que se usará cuando el origen
del enlace se resuelva pero el valor sea null . Un escenario común para establecer esta propiedad es cuando se
realiza el enlace con propiedades de origen que podrían ser null en una colección enlazada.
La página Monkeys ilustra el establecimiento de la propiedad TargetNullValue :

<ListView ItemsSource="{Binding Monkeys}"


...>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
...
<Image Source="{Binding ImageUrl,
TargetNullValue='https://upload.wikimedia.org/wikipedia/commons/2/20/Point_d_interrogation.jpg'}"
... />
...
<Label Text="{Binding Location, TargetNullValue='Location unknown'}"
... />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Los enlaces en Image y Label definen valores TargetNullValue que se aplicará si la ruta de acceso de enlace
devuelve null . Por lo tanto, los valores definidos por las propiedades TargetNullValue se mostrarán para los
objetos de la colección donde las propiedades ImageUrl y Location no estén definidas. Tenga en cuenta que aquí,
los valores de la propiedad TargetNullValue están delimitados por caracteres de comillas simples (apóstrofo).
En lugar de definir los valores de la propiedad TargetNullValue en línea, se recomienda definirlos como recursos
en ResourceDictionary . La ventaja de este enfoque es que estos valores se definen una vez en una sola ubicación y
son localizables más fácilmente. Posteriormente, se pueden recuperar los recursos mediante la extensión de
marcado StaticResource :
<Image Source="{Binding ImageUrl, TargetNullValue={StaticResource fallbackImageUrl}}"
... />
<Label Text="{Binding Location, TargetNullValue={StaticResource locationUnknown}}"
... />

NOTE
No es posible establecer la propiedad TargetNullValue con una expresión de enlace.

Esta es la ejecución del programa:

Cuando la propiedad TargetNullValue no está establecida en una expresión de enlace, un valor de origen de null
se convertirá si se define un convertidor de valores, se le aplicará formato si se define StringFormat y, a
continuación, se establecerá el resultado en el destino. Sin embargo, cuando la propiedad TargetNullValue está
establecida, un valor de origen de null se convertirá si se ha definido un convertidor de valores, y si sigue siendo
null después de la conversión, el valor de la propiedad TargetNullValue está establecido en el destino.

IMPORTANT
No se aplicará formato a la cadena en una expresión de enlace cuando la propiedad TargetNullValue esté establecida.

Vínculos relacionados
Demos de enlace de datos (ejemplo)
La interfaz de comandos de Xamarin.Forms
11/07/2019 • 31 minutes to read • Edit Online

Descargar el ejemplo
En la arquitectura Model-View -ViewModel (MVVM ), los enlaces de datos se definen entre las propiedades de
ViewModel, que suele ser una clase que se deriva de INotifyPropertyChanged , y las propiedades en la vista, que
suele ser el archivo XAML. A veces, una aplicación tiene necesidades que van más allá de estos enlaces de
propiedad y solicita al usuario que inicie comandos que influyen en el modelo de vista. Por lo general, estos
comandos se señalizan mediante clics de botón o pulsaciones con el dedo, y tradicionalmente se procesan en el
archivo de código subyacente en un controlador para el evento Clicked del elemento Button o el evento
Tapped de un elemento TapGestureRecognizer .

La interfaz de comandos proporciona un enfoque alternativo para implementar comandos que se adapta mucho
mejor a la arquitectura MVVM. El propio modelo de vista puede contener comandos, que son métodos que se
ejecutan en respuesta a una actividad específica en la vista como un clic de Button . Los enlaces de datos se
definen entre estos comandos y el objeto Button .
Para permitir un enlace de datos entre un objeto Button y un ViewModel, el objeto Button define dos
propiedades:
Command de tipo System.Windows.Input.ICommand
CommandParameter de tipo Object

Para usar la interfaz de comandos, defina un enlace de datos que tenga como destino la propiedad Command del
objeto Button donde el origen sea una propiedad en el modelo de vista de tipo ICommand . El modelo de vista
contiene código asociado con la propiedad ICommand que se ejecuta cuando se hace clic en el botón. Puede
establecer CommandParameter en datos arbitrarios para distinguir entre varios botones si todos se enlazan a la
misma propiedad ICommand en el modelo de vista.
Las propiedades Command y CommandParameter también se definen mediante las clases siguientes:
MenuItem y, por tanto, ToolbarItem , que se deriva de MenuItem
TextCell y, por tanto, ImageCell , que se deriva de TextCell
TapGestureRecognizer

SearchBar define una propiedad SearchCommand de tipo ICommand y una propiedad SearchCommandParameter . La
propiedad RefreshCommand de ListView también es de tipo ICommand .
Todos estos comandos se pueden controlar en un modelo de vista de forma que no dependa del objeto de
interfaz de usuario concreto de la vista.

La interfaz ICommand
La interfaz System.Windows.Input.ICommand no forma parte de Xamarin.Forms. En su lugar, se define en el espacio
de nombres System.Windows.Input y consta de dos métodos y un evento:
public interface ICommand
{
public void Execute (Object parameter);

public bool CanExecute (Object parameter);

public event EventHandler CanExecuteChanged;


}

Para usar la interfaz de comandos, el modelo de vista contiene propiedades de tipo ICommand :

public ICommand MyCommand { private set; get; }

El modelo de vista también debe hacer referencia a una clase que implemente la interfaz ICommand . Esta clase se
describirá en breve. En la vista, la propiedad Command de un objeto Button está enlazada a esa propiedad:

<Button Text="Execute command"


Command="{Binding MyCommand}" />

Cuando el usuario presiona el elemento Button , Button llama al método Execute del objeto ICommand
enlazado a su propiedad Command . Es la parte más sencilla de la interfaz de comandos.
El método CanExecute es más complejo. Cuando se define por primera vez el enlace en la propiedad Command del
objeto Button , y cuando cambia de algún modo el enlace de datos, el objeto Button llama al método
CanExecute del objeto ICommand . Si CanExecute devuelve false , el objeto Button se deshabilita a sí mismo.
Esto indica que el comando concreto no está disponible actualmente o no es válido.
El objeto Button también adjunta un controlador al evento CanExecuteChanged de ICommand . El evento se
desencadena desde dentro del modelo de vista. Cuando se desencadena ese evento, el objeto Button vuelve a
llamar a CanExecute . El objeto Button se habilita a sí mismo si CanExecute devuelve true y se deshabilita si
CanExecute devuelve false .

IMPORTANT
No use la propiedad IsEnabled de Button si usa la interfaz de comandos.

La clase Command
Cuando en el modelo de vista se define una propiedad de tipo ICommand , el modelo de vista también debe
contener o hacer referencia a una clase que implemente la interfaz ICommand . Esta clase debe contener o hacer
referencia a los métodos Execute y CanExecute , y desencadenar el evento CanExecuteChanged cada vez que el
método CanExecute pueda devolver otro valor.
Puede escribir este tipo de clase, o bien puede usar una escrita por otra persona. Como ICommand forma parte de
Microsoft Windows, se ha usado durante años con las aplicaciones MVVM de Windows. El uso de una clase de
Windows que implementa ICommand permite compartir los modelos de vista entre las aplicaciones de Windows y
las de Xamarin.Forms.
Si el uso compartido de modelos de vista entre Windows y Xamarin.Forms no constituye un problema, puede
usar la clase Command o Command<T> incluida en Xamarin.Forms para implementar la interfaz ICommand . Estas
clases permiten especificar los cuerpos de los métodos Execute y CanExecute en los constructores de clase. Use
Command<T> cuando use la propiedad CommandParameter para distinguir entre varias vistas enlazadas a la misma
propiedad ICommand , y la clase Command más sencilla cuando no sea un requisito.
Comandos básicos
En la página Entrada de personas del programa Demostraciones de enlace de datos se muestran algunos
comandos sencillos implementados en un modelo de vista.
PersonViewModel define tres propiedades denominadas Name , Age y Skills que definen una persona. Esta clase
no contiene ninguna propiedad ICommand :

public class PersonViewModel : INotifyPropertyChanged


{
string name;
double age;
string skills;

public event PropertyChangedEventHandler PropertyChanged;

public string Name


{
set { SetProperty(ref name, value); }
get { return name; }
}

public double Age


{
set { SetProperty(ref age, value); }
get { return age; }
}

public string Skills


{
set { SetProperty(ref skills, value); }
get { return skills; }
}

public override string ToString()


{
return Name + ", age " + Age;
}

bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)


{
if (Object.Equals(storage, value))
return false;

storage = value;
OnPropertyChanged(propertyName);
return true;
}

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)


{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

El elemento PersonCollectionViewModel que se muestra a continuación crea objetos de tipo PersonViewModel y


permite al usuario rellenar los datos. Para ello, la clase define las propiedades IsEditing de tipo bool y
PersonEdit de tipo PersonViewModel . Además, la clase define tres propiedades de tipo ICommand y una propiedad
denominada Persons de tipo IList<PersonViewModel> :
public class PersonCollectionViewModel : INotifyPropertyChanged
{
PersonViewModel personEdit;
bool isEditing;

public event PropertyChangedEventHandler PropertyChanged;

···

public bool IsEditing


{
private set { SetProperty(ref isEditing, value); }
get { return isEditing; }
}

public PersonViewModel PersonEdit


{
set { SetProperty(ref personEdit, value); }
get { return personEdit; }
}

public ICommand NewCommand { private set; get; }

public ICommand SubmitCommand { private set; get; }

public ICommand CancelCommand { private set; get; }

public IList<PersonViewModel> Persons { get; } = new ObservableCollection<PersonViewModel>();

bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)


{
if (Object.Equals(storage, value))
return false;

storage = value;
OnPropertyChanged(propertyName);
return true;
}

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)


{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Esta lista abreviada no incluye el constructor de la clase, que es donde se definen las tres propiedades de tipo
ICommand , que se mostrará en breve. Tenga en cuenta que los cambios en las tres propiedades de tipo ICommand y
la propiedad Persons no hacen que se desencadenen eventos PropertyChanged . Estas propiedades se establecen
al crear la clase por primera vez y no cambian a partir de entonces.
Antes de examinar el constructor de la clase PersonCollectionViewModel , veremos los archivos XAML para el
programa Entrada de personas. Este archivo contiene un elemento Grid con su propiedad BindingContext
establecida en el elemento PersonCollectionViewModel . El objeto Grid contiene un objeto Button con el texto
New con su propiedad Command enlazada a la propiedad NewCommand del modelo de vista, un formulario de
entrada con propiedades enlazadas a la propiedad IsEditing , así como las propiedades de PersonViewModel , y
dos botones más enlazados a las propiedades SubmitCommand y CancelCommand de la clase ViewModel. El objeto
ListView final muestra la colección de las personas que ya se han introducido:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.PersonEntryPage"
Title="Person Entry">
Title="Person Entry">
<Grid Margin="10">
<Grid.BindingContext>
<local:PersonCollectionViewModel />
</Grid.BindingContext>

<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<!-- New Button -->


<Button Text="New"
Grid.Row="0"
Command="{Binding NewCommand}"
HorizontalOptions="Start" />

<!-- Entry Form -->


<Grid Grid.Row="1"
IsEnabled="{Binding IsEditing}">

<Grid BindingContext="{Binding PersonEdit}">


<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Label Text="Name: " Grid.Row="0" Grid.Column="0" />


<Entry Text="{Binding Name}"
Grid.Row="0" Grid.Column="1" />

<Label Text="Age: " Grid.Row="1" Grid.Column="0" />


<StackLayout Orientation="Horizontal"
Grid.Row="1" Grid.Column="1">
<Stepper Value="{Binding Age}"
Maximum="100" />
<Label Text="{Binding Age, StringFormat='{0} years old'}"
VerticalOptions="Center" />
</StackLayout>

<Label Text="Skills: " Grid.Row="2" Grid.Column="0" />


<Entry Text="{Binding Skills}"
Grid.Row="2" Grid.Column="1" />

</Grid>
</Grid>

<!-- Submit and Cancel Buttons -->


<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Button Text="Submit"
Grid.Column="0"
Command="{Binding SubmitCommand}"
VerticalOptions="CenterAndExpand" />

<Button Text="Cancel"
Grid.Column="1"
Command="{Binding CancelCommand}"
Command="{Binding CancelCommand}"
VerticalOptions="CenterAndExpand" />
</Grid>

<!-- List of Persons -->


<ListView Grid.Row="3"
ItemsSource="{Binding Persons}" />
</Grid>
</ContentPage>

Este es el funcionamiento: el usuario presiona primero el botón New (Nuevo). Esto habilita el formulario de
entrada, pero deshabilita el botón New. Después, el usuario escribe un nombre, la edad y las habilidades. En
cualquier momento durante la edición, el usuario puede presionar el botón Cancel (Cancelar) para volver a
empezar. El botón Submit (Enviar) solo se habilita cuando se ha escrito un nombre y una edad válida. Al
presionar este botón Submit, se transfiere a la persona a la colección mostrada por ListView . Después de
presionar el botón Cancel o Submit, se borra el formulario de entrada y se vuelve a habilitar el botón New.
En la pantalla de iOS de la izquierda se muestra el diseño antes de escribir una vigencia válida. En las pantallas de
Android y UWP se muestra el botón Submit habilitado después de haber establecido una edad:

El programa no tiene ninguna función para editar las entradas existentes y no guarda las entradas al salir de la
página.
Toda la lógica para los botones New, Submit y Cancel se controla en PersonCollectionViewModel a través de
definiciones de las propiedades NewCommand , SubmitCommand y CancelCommand . El constructor de
PersonCollectionViewModel establece estas tres propiedades en objetos de tipo Command .

Un constructor de la clase Command permite pasar argumentos de tipo Action y Func<bool> correspondientes a
los métodos Execute y CanExecute . Es más fácil definir estas acciones y funciones como funciones lambda
directamente en el constructor de Command . Esta es la definición del objeto Command para la propiedad
NewCommand :
public class PersonCollectionViewModel : INotifyPropertyChanged
{

···

public PersonCollectionViewModel()
{
NewCommand = new Command(
execute: () =>
{
PersonEdit = new PersonViewModel();
PersonEdit.PropertyChanged += OnPersonEditPropertyChanged;
IsEditing = true;
RefreshCanExecutes();
},
canExecute: () =>
{
return !IsEditing;
});

···

void OnPersonEditPropertyChanged(object sender, PropertyChangedEventArgs args)


{
(SubmitCommand as Command).ChangeCanExecute();
}

void RefreshCanExecutes()
{
(NewCommand as Command).ChangeCanExecute();
(SubmitCommand as Command).ChangeCanExecute();
(CancelCommand as Command).ChangeCanExecute();
}

···

Cuando el usuario hace clic en el botón New, se ejecuta la función execute pasada al constructor de Command .
Esto crea un objeto PersonViewModel , establece un controlador en el evento PropertyChanged de ese objeto,
establece IsEditing en true , y llama al método RefreshCanExecutes definido después del constructor.
Además de implementar la interfaz ICommand , la clase Command también define un método denominado
ChangeCanExecute . El modelo de vista debe llamar a ChangeCanExecute para una propiedad ICommand cada vez
que suceda algo que pueda cambiar el valor devuelto del método CanExecute . Una llamada a ChangeCanExecute
hace que la clase Command desencadene el método CanExecuteChanged . El objeto Button ha adjuntado un
controlador para ese evento y responde mediante una nueva llamada a CanExecute y, después, se habilita a sí
mismo en función del valor devuelto de ese método.
Cuando el método execute de NewCommand llama a RefreshCanExecutes , la propiedad NewCommand recibe una
llamada a ChangeCanExecute , y Button llama al método canExecute , que ahora devuelve false porque la
propiedad IsEditing es true .
El controlador para el nuevo objeto PersonViewModel llama al método
PropertyChanged ChangeCanExecute de
SubmitCommand . Aquí se muestra la implementación de esa propiedad de comando:
public class PersonCollectionViewModel : INotifyPropertyChanged
{

···

public PersonCollectionViewModel()
{

···

SubmitCommand = new Command(


execute: () =>
{
Persons.Add(PersonEdit);
PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;
PersonEdit = null;
IsEditing = false;
RefreshCanExecutes();
},
canExecute: () =>
{
return PersonEdit != null &&
PersonEdit.Name != null &&
PersonEdit.Name.Length > 1 &&
PersonEdit.Age > 0;
});

···
}

···

La función canExecute para SubmitCommand se llama cada vez que cambia una propiedad en el objeto
PersonViewModel que se está editando. Solo devuelve true cuando la propiedad Name tiene al menos un
carácter de longitud, y Age es mayor que 0. En ese momento, se habilita el botón Submit.
La función execute para Submit quita el controlador de cambio de propiedad de PersonViewModel , agrega el
objeto a la colección Persons y devuelve todo a las condiciones iniciales.
La función execute para el botón Cancel hace lo mismo que el botón Submit excepto agregar el objeto a la
colección:
public class PersonCollectionViewModel : INotifyPropertyChanged
{

···

public PersonCollectionViewModel()
{

···

CancelCommand = new Command(


execute: () =>
{
PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;
PersonEdit = null;
IsEditing = false;
RefreshCanExecutes();
},
canExecute: () =>
{
return IsEditing;
});
}

···

El método canExecute devuelve true en cualquier momento que se modifique un elemento PersonViewModel .
Estas técnicas se podrían adaptar para escenarios más complejos: una propiedad de PersonCollectionViewModel
se podría enlazar a la propiedad SelectedItem del objeto ListView para editar los elementos existentes, y se
podría agregar un botón Delete (Eliminar) para eliminar esos elementos.
No es necesario definir los métodos execute y canExecute como funciones lambda. Puede escribirlos como
métodos privados estándar en el modelo de vista y hacer referencia a ellos en constructores de Command . Pero
este enfoque tiende a crear una gran cantidad de métodos a los que solo se hace referencia una vez en el modelo
de vista.

Uso de parámetros de comando


A veces es conveniente que uno o varios botones (u otros objetos de interfaz de usuario) compartan la misma
propiedad ICommand en el modelo de vista. En este caso, se usa la propiedad CommandParameter para distinguir los
botones.
Se puede seguir usando la clase Command para estas propiedades ICommand compartidas. La clase define un
constructor alternativo que acepta métodos execute y canExecute con parámetros de tipo Object . Esta es la
forma de pasar CommandParameter a estos métodos.
Pero cuando se usa CommandParameter , resulta más fácil utilizar la clase Command<T> genérica para especificar el
tipo del objeto establecido en CommandParameter . Los métodos execute y canExecute que especifique tienen
parámetros de ese tipo.
En la página Teclado decimal se ilustra esta técnica y se muestra cómo implementar un teclado numérico para
escribir números decimales. El elemento BindingContext para el objeto Grid es un elemento
DecimalKeypadViewModel . La propiedad Entry de este modelo de vista se enlaza a la propiedad Text de un
elemento Label . Todos los objetos Button están enlazados a varios comandos del modelo de vista:
ClearCommand , BackspaceCommand y DigitCommand :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.DecimalKeypadPage"
Title="Decimal Keyboard">

<Grid WidthRequest="240"
HeightRequest="480"
ColumnSpacing="2"
RowSpacing="2"
HorizontalOptions="Center"
VerticalOptions="Center">

<Grid.BindingContext>
<local:DecimalKeypadViewModel />
</Grid.BindingContext>

<Grid.Resources>
<ResourceDictionary>
<Style TargetType="Button">
<Setter Property="FontSize" Value="32" />
<Setter Property="BorderWidth" Value="1" />
<Setter Property="BorderColor" Value="Black" />
</Style>
</ResourceDictionary>
</Grid.Resources>

<Label Text="{Binding Entry}"


Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"
FontSize="32"
LineBreakMode="HeadTruncation"
VerticalTextAlignment="Center"
HorizontalTextAlignment="End" />

<Button Text="CLEAR"
Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
Command="{Binding ClearCommand}" />

<Button Text="&#x21E6;"
Grid.Row="1" Grid.Column="2"
Command="{Binding BackspaceCommand}" />

<Button Text="7"
Grid.Row="2" Grid.Column="0"
Command="{Binding DigitCommand}"
CommandParameter="7" />

<Button Text="8"
Grid.Row="2" Grid.Column="1"
Command="{Binding DigitCommand}"
CommandParameter="8" />

<Button Text="9"
Grid.Row="2" Grid.Column="2"
Command="{Binding DigitCommand}"
CommandParameter="9" />

<Button Text="4"
Grid.Row="3" Grid.Column="0"
Command="{Binding DigitCommand}"
CommandParameter="4" />

<Button Text="5"
Grid.Row="3" Grid.Column="1"
Command="{Binding DigitCommand}"
CommandParameter="5" />

<Button Text="6"
Grid.Row="3" Grid.Column="2"
Command="{Binding DigitCommand}"
CommandParameter="6" />

<Button Text="1"
Grid.Row="4" Grid.Column="0"
Command="{Binding DigitCommand}"
CommandParameter="1" />

<Button Text="2"
Grid.Row="4" Grid.Column="1"
Command="{Binding DigitCommand}"
CommandParameter="2" />

<Button Text="3"
Grid.Row="4" Grid.Column="2"
Command="{Binding DigitCommand}"
CommandParameter="3" />

<Button Text="0"
Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2"
Command="{Binding DigitCommand}"
CommandParameter="0" />

<Button Text="&#x00B7;"
Grid.Row="5" Grid.Column="2"
Command="{Binding DigitCommand}"
CommandParameter="." />
</Grid>
</ContentPage>

Los 11 botones para los 10 dígitos y el separador decimal comparten un enlace a DigitCommand .
CommandParameter distingue entre estos botones. El valor establecido en CommandParameter suele ser el mismo que
el texto que se muestra en el botón, excepto por el separador decimal que, para una mayor claridad, se muestra
con un carácter de punto central.
Este es el programa en acción:

Observe que el botón para el separador decimal en las tres capturas de pantalla está deshabilitado porque el
número introducido ya contiene un separador decimal.
DecimalKeypadViewModel define una propiedad Entry de tipo string (que es la única que desencadena un
evento PropertyChanged ) y tres propiedades de tipo ICommand :
public class DecimalKeypadViewModel : INotifyPropertyChanged
{
string entry = "0";

public event PropertyChangedEventHandler PropertyChanged;

···

public string Entry


{
private set
{
if (entry != value)
{
entry = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Entry"));
}
}
get
{
return entry;
}
}

public ICommand ClearCommand { private set; get; }

public ICommand BackspaceCommand { private set; get; }

public ICommand DigitCommand { private set; get; }


}

El botón correspondiente a ClearCommand siempre está habilitado y simplemente establece la entrada en "0":

public class DecimalKeypadViewModel : INotifyPropertyChanged


{

···

public DecimalKeypadViewModel()
{
ClearCommand = new Command(
execute: () =>
{
Entry = "0";
RefreshCanExecutes();
});

···

void RefreshCanExecutes()
{
((Command)BackspaceCommand).ChangeCanExecute();
((Command)DigitCommand).ChangeCanExecute();
}

···

Como el botón siempre está habilitado, no es necesario especificar un argumento canExecute en el constructor
de Command .
La lógica para escribir números y el retroceso es un poco complicada, porque si no se ha especificado ningún
dígito, la propiedad Entry es la cadena "0". Si el usuario escribe más ceros, Entry todavía contiene un solo cero.
Si el usuario escribe cualquier otro dígito, ese dígito reemplaza al cero. Pero si el usuario escribe un separador
decimal antes de cualquier otro dígito, Entry es la cadena "0.".
El botón Backspace (Retroceso) solo se habilita cuando la longitud de la entrada es mayor que 1, o bien si Entry
no es igual a la cadena "0":

public class DecimalKeypadViewModel : INotifyPropertyChanged


{

···

public DecimalKeypadViewModel()
{

···

BackspaceCommand = new Command(


execute: () =>
{
Entry = Entry.Substring(0, Entry.Length - 1);
if (Entry == "")
{
Entry = "0";
}
RefreshCanExecutes();
},
canExecute: () =>
{
return Entry.Length > 1 || Entry != "0";
});

···

···

La lógica para la función execute para el botón Backspace garantiza que Entry es al menos una cadena de "0".
La propiedad está enlazada a 11 botones, que se identifican a sí mismos con la propiedad
DigitCommand
CommandParameter de forma individual. Se podría establecer DigitCommand en una instancia de la clase Command
normal, pero es más fácil usar la clase Command<T> genérica. Cuando se usa la interfaz de comandos con XAML,
las propiedades CommandParameter suelen ser cadenas, y ese es el tipo del argumento genérico. Las funciones
execute y canExecute tienen argumentos de tipo string :
public class DecimalKeypadViewModel : INotifyPropertyChanged
{

···

public DecimalKeypadViewModel()
{

···

DigitCommand = new Command<string>(


execute: (string arg) =>
{
Entry += arg;
if (Entry.StartsWith("0") && !Entry.StartsWith("0."))
{
Entry = Entry.Substring(1);
}
RefreshCanExecutes();
},
canExecute: (string arg) =>
{
return !(arg == "." && Entry.Contains("."));
});
}

···

El método execute anexa el argumento de cadena a la propiedad Entry . Pero si el resultado comienza con un
cero (pero no un cero y un separador decimal), ese cero inicial se debe quitar mediante la función Substring .
El método canExecute devuelve false solo si el argumento es el separador decimal (lo que indica que se
presiona el separador decimal) y Entry ya contiene un separador decimal.
Todos los métodos execute llaman a RefreshCanExecutes , que llama a ChangeCanExecute para DigitCommand y
ClearCommand . Esto garantiza que los botones de retroceso y separador decimal se habilitan o deshabilitan en
función de la secuencia actual de dígitos especificados.

Adición de comandos a las vistas existentes


Si quiere usar la interfaz de comandos con vistas que no la admiten, puede utilizar un comportamiento de
Xamarin.Forms que convierte un evento en un comando. Esto se describe en el artículo
EventToCommandBehavior reutilizable.

Comandos asincrónicos para menús de navegación


Los comandos son útiles para la implementación de menús de navegación, como los del propio programa
Demostraciones de enlace de datos. Este es un fragmento de MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.MainPage"
Title="Data Binding Demos"
Padding="10">
<TableView Intent="Menu">
<TableRoot>
<TableSection Title="Basic Bindings">

<TextCell Text="Basic Code Binding"


Detail="Define a data-binding in code"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:BasicCodeBindingPage}" />

<TextCell Text="Basic XAML Binding"


Detail="Define a data-binding in XAML"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:BasicXamlBindingPage}" />

<TextCell Text="Alternative Code Binding"


Detail="Define a data-binding in code without a BindingContext"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:AlternativeCodeBindingPage}" />

···

</TableSection>
</TableRoot>
</TableView>
</ContentPage>

Al usar comandos con XAML, las propiedades CommandParameter normalmente se establecen en cadenas. Pero en
este caso, se usa una extensión de marcado XAML para que CommandParameter sea de tipo System.Type .
Cada propiedad Command se enlaza a una propiedad denominada NavigateCommand . Esa propiedad se define en el
archivo de código subyacente, MainPage.xaml.cs:

public partial class MainPage : ContentPage


{
public MainPage()
{
InitializeComponent();

NavigateCommand = new Command<Type>(


async (Type pageType) =>
{
Page page = (Page)Activator.CreateInstance(pageType);
await Navigation.PushAsync(page);
});

BindingContext = this;
}

public ICommand NavigateCommand { private set; get; }


}

El constructor establece la propiedad NavigateCommand en un método execute que crea una instancia del
parámetro System.Type y, después, navega hasta ella. Como la llamada a PushAsync requiere un operador await
, el método execute debe estar marcado como asincrónico. Esto se consigue con la palabra clave async por
delante de la lista de parámetros.
El constructor establece también el elemento BindingContext de la página en sí mismo para que los enlaces
hagan referencia a NavigateCommand en esta clase.
El orden del código de este constructor establece una diferencia: la llamada a InitializeComponent hace que se
analice el código XAML, pero en ese momento, el enlace a una propiedad denominada NavigateCommand no se
puede resolver porque BindingContext está establecido en null . Si se establece BindingContext en el
constructor antes de establecer NavigateCommand , el enlace se puede resolver cuando se establezca
BindingContext , pero en ese momento, NavigateCommand sigue siendo null . Establecer NavigateCommand
después de BindingContext no tendrá ningún efecto en el enlace porque un cambio en NavigateCommand no
desencadena un evento PropertyChanged y el enlace no sabe que NavigateCommand ahora es válido.
El establecimiento de y BindingContext (en cualquier orden) antes de llamar a
NavigateCommand
InitializeComponent funcionará porque los dos componentes del enlace se establecen cuando el analizador
XAML encuentra la definición de enlace.
En ocasiones, los enlaces de datos pueden resultar complicados, pero como ha visto en esta serie de artículos, son
eficaces y versátiles, y ayudan considerablemente a organizar el código mediante la separación de la lógica
subyacente de la interfaz de usuario.

Vínculos relacionados
Data Binding Demos (sample) (Demos de enlace de datos [ejemplo])
Capítulo sobre enlace de datos del libro de Xamarin.Forms
Enlaces compilados de Xamarin.Forms
11/07/2019 • 14 minutes to read • Edit Online

Descargar el ejemplo
Los enlaces compilados se resuelven más rápidamente que los enlaces clásicos, lo cual mejora el rendimiento del
enlace de datos en las aplicaciones de Xamarin.Forms.
Los enlaces de datos tienen dos problemas principales:
1. No hay ninguna validación de las expresiones de enlace en tiempo de compilación. Alternativamente, los
enlaces se resuelven en tiempo de ejecución. Por lo tanto, los enlaces no válidos no se detectan hasta el tiempo
de ejecución, cuando la aplicación no se comporta según lo esperado o aparecen mensajes de error.
2. No son rentables. Los enlaces se resuelven en tiempo de ejecución mediante la inspección de objetos de uso
general (reflejo); el trabajo adicional que supone llevarlo a cabo varía en función de la plataforma.
Los enlaces compilados mejoran el rendimiento de enlace de datos en las aplicaciones de Xamarin.Forms
mediante la resolución de expresiones de enlace en tiempo de compilación en lugar de en tiempo de ejecución.
Además, esta validación en tiempo de compilación de expresiones de enlace permite una mejor experiencia de
solución de problemas, porque los enlaces no válidos se notifican como errores de compilación.
El proceso para usar enlaces compilados es el siguiente:
1. Habilite la compilación XAML. Para obtener más información acerca de la compilación XAML, consulte XAML
Compilation (Compilación XAML ).
2. Establezca un atributo x:DataType de un elemento VisualElement para el tipo del objeto al cual
VisualElement y sus elementos secundarios se enlazará. Tenga en cuenta que este atributo puede volver a
definirse en cualquier ubicación en una jerarquía de vistas.

NOTE
Se recomienda establecer el atributo x:DataType en el mismo nivel de la jerarquía de vistas en que está establecido
BindingContext .

En tiempo de compilación XAML, las expresiones de enlace no válidas se notificarán como errores de
compilación. Sin embargo, el compilador XAML solo notificará un error de compilación para la primera expresión
de enlace no válida que encuentre. Las expresiones de enlace válidas que se definen en VisualElement o en sus
elementos secundarios se compilarán, independientemente de si BindingContext está establecido en XAML o en
código. La compilación de una expresión de enlace genera un código compilado que obtendrá un valor de una
propiedad en el origen y lo establecerá en la propiedad en el destino que se especifica en el marcado. Además,
dependiendo de la expresión de enlace, el código generado puede observar cambios en el valor de la propiedad
de origen y actualizar la propiedad de destino, y puede insertar los cambios desde el destino de nuevo al origen.

IMPORTANT
Los enlaces compilados actualmente están deshabilitados para las expresiones de enlace que definen la propiedad Source .
Esto es así porque la propiedad Source siempre se establece mediante la extensión de marcado x:Reference , que no se
puede resolver en tiempo de compilación.
Uso de enlaces compilados
En la página Compiled Color Selector (Selector de colores compilados) se muestra el uso de enlaces
compilados entre las vistas de Xamarin.Forms y las propiedades de ViewModel:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.CompiledColorSelectorPage"
Title="Compiled Color Selector">
...
<StackLayout x:DataType="local:HslColorViewModel">
<StackLayout.BindingContext>
<local:HslColorViewModel Color="Sienna" />
</StackLayout.BindingContext>
<BoxView Color="{Binding Color}"
... />
<StackLayout Margin="10, 0">
<Label Text="{Binding Name}" />
<Slider Value="{Binding Hue}" />
<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
<Slider Value="{Binding Saturation}" />
<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
<Slider Value="{Binding Luminosity}" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</StackLayout>
</ContentPage>

El elemento StackLayout raíz crea una instancia de HslColorViewModel e inicializa la propiedad Color dentro de
las etiquetas de elemento de propiedad para la propiedad BindingContext . El elemento StackLayout raíz también
define el atributo x:DataType como el tipo ViewModel, lo cual indica que todas las expresiones de enlace en la
jerarquía de vistas StackLayout raíz se compilarán. Esto se puede comprobar cambiando cualquiera de las
expresiones de enlace para enlazar a una propiedad ViewModel inexistente, lo cual generará a un error de
compilación.

IMPORTANT
El atributo x:DataType puede volver a definirse en cualquier punto de una jerarquía de vistas.

Los elementos BoxView , Label y las vistas Slider heredan el contexto de enlace del elemento StackLayout .
Todas estas vistas son destinos de enlace que hacen referencia a las propiedades de origen en ViewModel. Para la
propiedad BoxView.Color y la propiedad Label.Text , los enlaces de datos son OneWay ; las propiedades de las
vista se establecen a partir de las propiedades en ViewModel. Sin embargo, la propiedad Slider.Value utiliza un
enlace TwoWay . Esto permite que cada Slider se establezca a partir de ViewModel, y que ViewModel se
establezca a partir de cada Slider .
Cuando la aplicación se ejecuta por primera vez, los elementos BoxView , Label , y los elementos Slider están
establecidos a partir de ViewModel en base a la propiedad Color inicial establecida cuando se creó una instancia
de ViewModel. Esto se muestra en la captura de pantalla siguiente:
A medida que se manipulan los controles deslizantes, los elementos BoxView y Label se actualizan del modo
correspondiente.
Para obtener más información acerca de este selector de colores, consulte ViewModels and Property-Change
Notifications (ViewModels y las notificaciones de cambio de propiedad).

Uso de enlaces compilados en DataTemplate


Los enlaces en DataTemplate se interpretan en el contexto del objeto del cual se crea la plantilla. Por lo tanto,
cuando utilice enlaces de compilación en DataTemplate , DataTemplate debe declarar el tipo de su objeto de datos
mediante el atributo x:DataType .
En la página Compiled Color List (Lista de colores compilados) se muestra el uso de enlaces compilados en
DataTemplate :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.CompiledColorListPage"
Title="Compiled Color List">
<Grid>
...
<ListView x:Name="colorListView"
ItemsSource="{x:Static local:NamedColor.All}"
... >
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:NamedColor">
<ViewCell>
<StackLayout Orientation="Horizontal">
<BoxView Color="{Binding Color}"
... />
<Label Text="{Binding FriendlyName}"
... />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- The BoxView doesn't use compiled bindings -->
<BoxView Color="{Binding Source={x:Reference colorListView}, Path=SelectedItem.Color}"
... />
</Grid>
</ContentPage>
La propiedad ListView.ItemsSource está establecida en la propiedad NamedColor.All estática. La clase
NamedColor usa la fijación de .NET para enumerar todos los campos públicos estáticos en la estructura de Color
y almacenarlos con sus nombres en una colección que sea accesible desde la propiedad All estática. Por lo
tanto, ListView se rellena con todas las instancias de NamedColor . Para cada elemento de ListView , el contexto
de enlace para el elemento está establecido en un objeto NamedColor . Los elementos BoxView y Label de
ViewCell están enlazados a propiedades NamedColor .

Tenga en cuenta que x:DataType``NamedColor define el atributo DataTemplate para ser del tipo DataTemplate , lo
cual indica que todas las expresiones de enlace en la jerarquía de vistas se compilarán. Esto se puede comprobar
cambiando cualquiera de las expresiones de enlace para enlazar a una propiedad NamedColor inexistente, lo cual
generará a un error de compilación.
Cuando la aplicación se ejecuta por primera vez, ListView se rellena con instancias de NamedColor . Cuando un
elemento de ListView está seleccionado, la propiedad BoxView.Color se establece en el color del elemento
seleccionado en ListView :

Al seleccionar otros elementos de ListView se actualiza el color de BoxView .

Combinación de enlaces compilados con enlaces clásicos


Las expresiones de enlace solo se compilan para la jerarquía de vistas en la cual está definido el atributo
x:DataType . Por contra, todas las vistas en una jerarquía en la cual el atributo x:DataType no esté definido
utilizarán enlaces clásicos. Por lo tanto, es posible combinar enlaces compilados y enlaces clásicos en una página.
Por ejemplo, en la sección anterior, las vistas dentro de DataTemplate utilizan enlaces compilados, mientras que el
elemento BoxView que se establece en el color seleccionado en ListView , no lo hace.
Una estructuración cuidadosa de atributos x:DataType , por lo tanto, puede conseguir que una página utilice
enlaces compilados y clásicos. De forma alternativa, el atributo x:DataType se puede volver a definir en cualquier
punto en una jerarquía de vistas para null utilizando la extensión de marcado x:Null . Esto indica que las
expresiones de enlace dentro de la jerarquía de vistas utilizarán enlaces clásicos. La página Mixed Bindings
(Enlaces mixtos) muestra este enfoque:
<StackLayout x:DataType="local:HslColorViewModel">
<StackLayout.BindingContext>
<local:HslColorViewModel Color="Sienna" />
</StackLayout.BindingContext>
<BoxView Color="{Binding Color}"
VerticalOptions="FillAndExpand" />
<StackLayout x:DataType="{x:Null}"
Margin="10, 0">
<Label Text="{Binding Name}" />
<Slider Value="{Binding Hue}" />
<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
<Slider Value="{Binding Saturation}" />
<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
<Slider Value="{Binding Luminosity}" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</StackLayout>

El elemento StackLayout raíz establece el atributo x:DataType para ser del tipo HslColorViewModel , lo cual indica
que todas las expresiones de enlace en la jerarquía de vistas StackLayout se compilarán. Sin embargo, el
StackLayout interno redefine el atributo x:DataType en null con la expresión de marcado x:Null . Por lo tanto,
las expresiones de enlace dentro del StackLayout interno usan enlaces clásicos. Solo BoxView , dentro de la
jerarquía de vistas StackLayout raíz, utiliza enlaces compilados.
Para obtener más información acerca de la expresión de marcado x:Null , consulte x:Null Markup Extension
(Extensión de marcado x:Null).

Rendimiento
Los enlaces compilados mejoran el rendimiento del enlace de datos, con unas ventajas de rendimiento variables.
Las pruebas de unidades muestran lo siguiente:
Un enlace compilado que utiliza la notificación de cambio de propiedad (es decir, un enlace OneWay ,
OneWayToSource o TwoWay ) se resuelve aproximadamente 8 veces más rápidamente que un enlace clásico.
Un enlace compilado que no utiliza la notificación de cambio de propiedad (es decir, un enlace OneTime ) se
resuelve aproximadamente 20 veces más rápidamente que un enlace clásico.
El establecimiento de BindingContext en un enlace compilado que utiliza la notificación de cambio de
propiedad (es decir, un enlace OneWay , OneWayToSource o TwoWay ) se resuelve aproximadamente 5 veces más
rápidamente que el establecimiento de BindingContext en un enlace clásico.
El establecimiento de BindingContext en un enlace compilado que no utiliza la notificación de cambio de
propiedad (es decir, un enlace OneTime ) se resuelve aproximadamente 7 veces más rápidamente que el
establecimiento de BindingContext en un enlace clásico.

Estas diferencias de rendimiento se pueden ampliar en dispositivos móviles, dependiendo de la plataforma que se
utilice, la versión del sistema operativo que se utilice y el dispositivo en el que se ejecute la aplicación.

Vínculos relacionados
Demos de enlace de datos (ejemplo)
Xamarin.Forms DependencyService
17/07/2019 • 2 minutes to read • Edit Online

Introducción
La clase DependencyService es un localizador de servicios que habilita las aplicaciones de Xamarin.Forms para
invocar la funcionalidad nativa de la plataforma desde código compartido.

Registro y resolución
Las implementaciones de la plataforma deben registrarse con DependencyService y, después, deben resolverse
desde código compartido para poder invocarse.

Selección de una foto de la biblioteca


En este artículo, se explica cómo usar la clase DependencyService de Xamarin.Forms para seleccionar una foto de
la biblioteca de imágenes del teléfono.
Introducción a DependencyService de Xamarin.Forms
17/07/2019 • 5 minutes to read • Edit Online

Descargar el ejemplo
La clase DependencyService es un localizador de servicios que habilita las aplicaciones de Xamarin.Forms para
invocar la funcionalidad nativa de la plataforma desde código compartido.
El proceso para usar DependencyService para invocar la funcionalidad nativa de la plataforma es el siguiente:
1. Cree una interfaz para la funcionalidad de la plataforma nativa en el código compartido. Para más información,
vea Creación de una interfaz.
2. Implemente la interfaz en los proyectos de la plataforma requeridos. Para obtener más información, vea
Implementación de la interfaz en cada plataforma.
3. Registro de las implementaciones de la plataforma con DependencyService . Esto permite que Xamarin.Forms
localice las implementaciones de la plataforma en tiempo de ejecución. Para obtener más información, vea
Registro de las implementaciones de la plataforma.
4. Resuelva las implementaciones de la plataforma desde el código compartido e invóquelas. Para obtener más
información, vea Resolución de las implementaciones de la plataforma.
En el siguiente diagrama, se muestra cómo se invoca la funcionalidad nativa de la plataforma en una aplicación de
Xamarin.Forms:

Creación de una interfaz


El primer paso para poder invocar la funcionalidad nativa de la plataforma desde código compartido es crear una
interfaz que defina la API para interactuar con la funcionalidad nativa de la plataforma. Esta interfaz debe colocarse
en su proyecto de código compartido.
En el ejemplo siguiente, se muestra una interfaz para una API que puede usarse para recuperar la orientación de
un dispositivo:

public interface IDeviceOrientationService


{
DeviceOrientation GetOrientation();
}

Implementación de la interfaz en cada plataforma


Después de crear la interfaz que define la API para interactuar con la funcionalidad nativa de la plataforma, la
interfaz deberá implementarse en cada proyecto de la plataforma.
iOS
En el siguiente ejemplo de código, se muestra la implementación de la interfaz de IDeviceOrientationService en
iOS:

namespace DependencyServiceDemos.iOS
{
public class DeviceOrientationService : IDeviceOrientationService
{
public DeviceOrientation GetOrientation()
{
UIInterfaceOrientation orientation = UIApplication.SharedApplication.StatusBarOrientation;

bool isPortrait = orientation == UIInterfaceOrientation.Portrait ||


orientation == UIInterfaceOrientation.PortraitUpsideDown;
return isPortrait ? DeviceOrientation.Portrait : DeviceOrientation.Landscape;
}
}
}

Android
En el siguiente ejemplo de código, se muestra la implementación de la interfaz de IDeviceOrientationService en
Android:

namespace DependencyServiceDemos.Droid
{
public class DeviceOrientationService : IDeviceOrientationService
{
public DeviceOrientation GetOrientation()
{
IWindowManager windowManager =
Android.App.Application.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();

SurfaceOrientation orientation = windowManager.DefaultDisplay.Rotation;


bool isLandscape = orientation == SurfaceOrientation.Rotation90 ||
orientation == SurfaceOrientation.Rotation270;
return isLandscape ? DeviceOrientation.Landscape : DeviceOrientation.Portrait;
}
}
}

Plataforma universal de Windows


En el siguiente ejemplo de código, se muestra la implementación de la interfaz de IDeviceOrientationService en la
Plataforma universal de Windows (UWP ):

namespace DependencyServiceDemos.UWP
{
public class DeviceOrientationService : IDeviceOrientationService
{
public DeviceOrientation GetOrientation()
{
ApplicationViewOrientation orientation = ApplicationView.GetForCurrentView().Orientation;
return orientation == ApplicationViewOrientation.Landscape ? DeviceOrientation.Landscape :
DeviceOrientation.Portrait;
}
}
}
Registro de las implementaciones de la plataforma
Después de implementar la interfaz en cada proyecto de la plataforma, las implementaciones de la plataforma
deberán registrarse con DependencyService para que Xamarin.Forms pueda ubicarlas en tiempo de ejecución.
Normalmente, esto se realiza con DependencyAttribute , lo que indica que el tipo especificado proporciona una
implementación de la interfaz.
En el siguiente ejemplo se muestra el uso de DependencyAttribute para registrar la implementación en iOS de la
interfaz de IDeviceOrientationService :

using Xamarin.Forms;

[assembly: Dependency(typeof(DeviceOrientationService))]
namespace DependencyServiceDemos.iOS
{
public class DeviceOrientationService : IDeviceOrientationService
{
public DeviceOrientation GetOrientation()
{
...
}
}
}

En este ejemplo, DependencyAttribute registra DeviceOrientationService con DependencyService . De forma similar,


las implementaciones de la interfaz de IDeviceOrientationService en otras plataformas se debe registrar con
DependencyAttribute .

Para obtener más información sobre el registro de implementaciones de plataforma con DependencyService , vea
Registro y resolución de DependencyService de Xamarin.Forms.

Resolución de las implementaciones de la plataforma


Tras el registro de las implementaciones de la plataforma con DependencyService , las implementaciones deberán
resolverse antes de invocarse. Normalmente, esto se realiza en código compartido mediante el método
DependencyService.Get<T> .

En el código siguiente, se muestra un ejemplo de llamada al método Get<T> para resolver la interfaz de
IDeviceOrientationService y, después, invocar su método GetOrientation :

IDeviceOrientationService service = DependencyService.Get<IDeviceOrientationService>();


DeviceOrientation orientation = service.GetOrientation();

Como alternativa, este código se puede comprimir en una sola línea:

DeviceOrientation orientation = DependencyService.Get<IDeviceOrientationService>().GetOrientation();

Para obtener más información sobre la resolución de las implementaciones de la plataforma con
DependencyService , vea Registro y resolución de DependencyService de Xamarin.Forms.

Vínculos relacionados
Demostraciones de DependencyService (ejemplo)
Registro y resolución de DependencyService de Xamarin.Forms
Registro y resolución de DependencyService de
Xamarin.Forms
17/07/2019 • 11 minutes to read • Edit Online

Descargar el ejemplo
Al usar DependencyService de Xamarin.Forms para invocar la funcionalidad de la plataforma nativa, las
implementaciones de la plataforma deben estar registradas con DependencyService y, a continuación, resolverse
desde código compartido para poder invocarse.

Registro de las implementaciones de la plataforma


Las implementaciones de la plataforma deben estar registradas con DependencyService para que Xamarin.Forms
pueda localizarlas en tiempo de ejecución.
El registro se puede realizar con DependencyAttribute o con los métodos Register .

IMPORTANT
Las compilaciones de versión de los proyectos de UWP que usan la compilación nativa de .NET deben registrar las
implementaciones de plataforma con los métodos Register .

Registro por atributo


DependencyAttribute se puede usar para registrar una implementación de la plataforma con DependencyService .
El atributo indica que el tipo especificado proporciona una implementación concreta de la interfaz.
En el siguiente ejemplo se muestra el uso de DependencyAttribute para registrar la implementación en iOS de la
interfaz de IDeviceOrientationService :

using Xamarin.Forms;

[assembly: Dependency(typeof(DeviceOrientationService))]
namespace DependencyServiceDemos.iOS
{
public class DeviceOrientationService : IDeviceOrientationService
{
public DeviceOrientation GetOrientation()
{
...
}
}
}

En este ejemplo, DependencyAttribute registra DeviceOrientationService con DependencyService . Esto da como


resultado el tipo concreto que se va a registrar en la interfaz que implementará.
De forma similar, las implementaciones de la interfaz de IDeviceOrientationService en otras plataformas se debe
registrar con DependencyAttribute .
NOTE
El registro con DependencyAttribute se realiza en el nivel de espacio de nombres.

Registro por método


Los métodos DependencyService.Register se pueden usar para registrar una implementación de la plataforma
con DependencyService .
En el siguiente ejemplo se muestra el uso del método Register para registrar la implementación en iOS de la
interfaz de IDeviceOrientationService :

[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
DependencyService.Register<IDeviceOrientationService, DeviceOrientationService>();
return base.FinishedLaunching(app, options);
}
}

En este ejemplo, el método Register registra el tipo concreto, DeviceOrientationService , en la interfaz de


IDeviceOrientationService . De forma alternativa, se puede usar una sobrecarga del método Register para
registrar una implementación de plataforma con DependencyService :

DependencyService.Register<DeviceOrientationService>();

En este ejemplo, el método Register registra DeviceOrientationService con DependencyService . Esto da como
resultado el tipo concreto que se va a registrar en la interfaz que implementará.
De forma similar, las implementaciones de la interfaz IDeviceOrientationService en otras plataformas se puede
registrar con los métodos Register .

IMPORTANT
El registro con los métodos Register se debe realizar en los proyectos de la plataforma antes de que se invoque la
funcionalidad que ha proporcionado la implementación de la plataforma mediante el código compartido.

Resolución de las implementaciones de la plataforma


Las implementaciones de la plataforma deben resolverse antes de invocarse. Normalmente, esto se realiza en
código compartido mediante el método DependencyService.Get<T> . Sin embargo, también se puede lograr con el
método DependencyService.Resolve<T> .
De forma predeterminada, DependencyService solo resuelve implementaciones de plataforma con constructores
sin parámetros. Pero se puede incorporar a Xamarin.Forms un método de resolución de dependencias que use un
contenedor de inserción de dependencias o métodos de generador para resolver implementaciones de
plataforma. Este enfoque puede usarse para resolver las implementaciones de plataforma que tienen
constructores con parámetros. Para obtener más información, vea Resolución de dependencias en
Xamarin.Forms.
IMPORTANT
La invocación de una implementación de la plataforma que no se haya registrado con DependencyService tendrá como
resultado la generación de NullReferenceException .

Resolución mediante el método Get<T>


El método Get<T> recupera la implementación de la plataforma de la interfaz de T en tiempo de ejecución y
crea una instancia de ella como singleton. Esta instancia estará activa durante la vigencia de la aplicación, y las
llamadas subsiguientes para resolver la misma implementación de la plataforma recuperarán la misma instancia.
En el código siguiente, se muestra un ejemplo de llamada al método Get<T> para resolver la interfaz de
IDeviceOrientationService y, después, invocar su método GetOrientation :

IDeviceOrientationService service = DependencyService.Get<IDeviceOrientationService>();


DeviceOrientation orientation = service.GetOrientation();

Como alternativa, este código se puede comprimir en una sola línea:

DeviceOrientation orientation = DependencyService.Get<IDeviceOrientationService>().GetOrientation();

NOTE
El método Get<T> crea una instancia de la implementación de la plataforma de la interfaz de T como singleton de forma
predeterminada. No obstante, este comportamiento se puede modificar. Para obtener más información, vea Administración
de la vigencia de los objetos resueltos.

Resolución mediante el método Resolve<T>


El método Resolve<T> recupera la implementación de la plataforma de la interfaz de T en tiempo de ejecución
mediante un método de resolución de dependencias que se ha insertado en Xamarin.Forms con la clase
DependencyResolver . Si un método de resolución de dependencias no se ha insertado en Xamarin.Forms, el
método Resolve<T> llamará al método Get<T> como solución alternativa para recuperar la implementación de la
plataforma. Para obtener más información sobre la inserción de métodos de resolución de dependencias en
Xamarin.Forms, vea Resolución de dependencias en Xamarin.Forms.
En el código siguiente, se muestra un ejemplo de llamada al método Resolve<T> para resolver la interfaz de
IDeviceOrientationService y, después, invocar su método GetOrientation :

IDeviceOrientationService service = DependencyService.Resolve<IDeviceOrientationService>();


DeviceOrientation orientation = service.GetOrientation();

Como alternativa, este código se puede comprimir en una sola línea:

DeviceOrientation orientation = DependencyService.Resolve<IDeviceOrientationService>().GetOrientation();


NOTE
Cuando el método Resolve<T> llama al método Get<T> como solución alternativa, crea una instancia de la
implementación de la plataforma de la interfaz de T como singleton de forma predeterminada. No obstante, este
comportamiento se puede modificar. Para obtener más información, vea Administración de la vigencia de los objetos
resueltos.

Administración de la vigencia de los objetos resueltos


El comportamiento predeterminado de la clase DependencyService es resolver las implementaciones de la
plataforma como elementos singleton. Por lo tanto, las implementaciones de la plataforma permanecerán activas
durante la vigencia de la aplicación en cuestión.
Este comportamiento se especifica con el argumento opcional DependencyFetchTarget en los métodos Get<T> y
Resolve<T> . La enumeración DependencyFetchTarget define dos miembros:

GlobalInstance , que devuelve la implementación de la plataforma como singleton.


NewInstance , que devuelve una nueva instancia de la implementación de la plataforma. La aplicación es
responsable de administrar la vigencia de la instancia de la implementación de la plataforma.
Los métodos Get<T> y establecen sus argumentos opcionales en
Resolve<T>
DependencyFetchTarget.GlobalInstance , por lo que las implementaciones de la plataforma siempre se resuelven
como elementos singleton. Este comportamiento se puede cambiar especificando
DependencyFetchTarget.NewInstance como argumentos para los métodos Get<T> y Resolve<T> con el fin de que
crear las instancias de las implementaciones de la plataforma:

ITextToSpeechService service = DependencyService.Get<ITextToSpeechService>


(DependencyFetchTarget.NewInstance);

En este ejemplo, DependencyService crea una instancia de la implementación de la plataforma para la interfaz de
ITextToSpeechService . Las llamadas subsiguientes para resolver ITextToSpeechService también crearán nuevas
instancias.
La consecuencia de crear siempre una nueva instancia de una implementación de la plataforma es que la
aplicación adquiere la responsabilidad de administrar la vigencia de las instancias. Esto significa que, si se
suscribe a un evento definido en una implementación de la plataforma, deberá cancelar la suscripción al evento
cuando ya no se requiera la implementación de la plataforma. Además, significa que puede ser necesario que las
implementaciones de la plataforma implementen IDisposable y realicen una limpieza de sus recursos en los
métodos Dispose . La aplicación de ejemplo muestra este escenario en sus implementaciones de la plataforma de
TextToSpeechService .

Cuando una aplicación termina de utilizar una implementación de la plataforma que implementa IDisposable ,
debe llamar a la implementación del objeto Dispose . Una manera de realizar esta acción es usar una instrucción
using :

ITextToSpeechService service = DependencyService.Get<ITextToSpeechService>


(DependencyFetchTarget.NewInstance);
using (service as IDisposable)
{
await service.SpeakAsync("Hello world");
}

En este ejemplo, una vez que se ha invocado el método SpeakAsync , la instrucción using desecha
automáticamente el objeto de la implementación de la plataforma. Esto da como resultado la invocación del
método Dispose del objeto, que realiza la limpieza requerida.
Para obtener más información sobre cómo llamar al método Dispose de un objeto, vea Uso de objetos que
implementan IDisposable.

Vínculos relacionados
Demostraciones de DependencyService (ejemplo)
Resolución de dependencias en Xamarin.Forms
Seleccionar una foto de la biblioteca de imágenes
17/07/2019 • 10 minutes to read • Edit Online

Descargar el ejemplo
En este artículo, se explica cómo crear una aplicación que permita al usuario seleccionar una foto de la biblioteca
de imágenes del teléfono. Como en Xamarin.Forms no se incluye esta función, es necesario usar
DependencyService para acceder a las API nativas en cada plataforma.

Creación de la interfaz
Primero, cree una interfaz en el código compartido que exprese la función deseada. En el caso de una aplicación
de selección de fotos, solo se necesita un método. Esto se define en la interfaz IPicturePicker de la biblioteca de
.NET Standard del código de ejemplo:

namespace DependencyServiceSample
{
public interface IPicturePicker
{
Task<Stream> GetImageStreamAsync();
}
}

El método GetImageStreamAsync se define como asincrónico porque tiene que devolver resultados rápidamente,
pero no puede devolver un objeto Stream para la foto seleccionada hasta que el usuario haya abierto la
biblioteca de imágenes y haya seleccionado una.
Esta interfaz se implementa en todas las plataformas que usan código específico de plataforma.

Implementación en iOS
La implementación de iOS de la interfaz IPicturePicker usa el objeto UIImagePickerController , como se
describe en la receta Seleccionar una foto de la galería y en el código de ejemplo.
La implementación de iOS se contiene en la clase PicturePickerImplementation del proyecto de iOS del código de
ejemplo. Para que el administrador DependencyService pueda ver esta clase, tiene que identificarse con un
atributo [ assembly ] del tipo Dependency y, además, tiene que ser pública e implementar explícitamente la interfaz
IPicturePicker :
[assembly: Dependency (typeof (PicturePickerImplementation))]

namespace DependencyServiceSample.iOS
{
public class PicturePickerImplementation : IPicturePicker
{
TaskCompletionSource<Stream> taskCompletionSource;
UIImagePickerController imagePicker;

public Task<Stream> GetImageStreamAsync()


{
// Create and define UIImagePickerController
imagePicker = new UIImagePickerController
{
SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
MediaTypes =
UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary)
};

// Set event handlers


imagePicker.FinishedPickingMedia += OnImagePickerFinishedPickingMedia;
imagePicker.Canceled += OnImagePickerCancelled;

// Present UIImagePickerController;
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
viewController.PresentModalViewController(imagePicker, true);

// Return Task object


taskCompletionSource = new TaskCompletionSource<Stream>();
return taskCompletionSource.Task;
}
...
}
}

El método GetImageStreamAsync crea un elemento UIImagePickerController y lo inicializa para seleccionar


imágenes desde la biblioteca de fotos. Se necesitan dos controladores de eventos: uno para cuando el usuario
seleccione una foto y otro para cuando cancele la visualización de la biblioteca de fotos. Después,
PresentModalViewController muestra la biblioteca de fotos al usuario.

En este momento, el método GetImageStreamAsync tiene que devolver un objeto Task<Stream> al código que
realiza la llamada. Esta tarea solo se completa cuando el usuario termina de interactuar con la biblioteca de fotos
y se llama a uno de los controladores de eventos. Para situaciones como esta, la clase TaskCompletionSource es
esencial. La clase proporciona un objeto Task al tipo genérico adecuado para devolver desde el método
GetImageStreamAsync y, después, se puede enviar una señal a la clase cuando se complete la tarea.

Se llama al controlador de eventos FinishedPickingMedia cuando el usuario ha seleccionado una imagen. Pero el
controlador proporciona un objeto UIImage y el elemento Task tiene que devolver un objeto Stream de .NET.
Esto se realiza en dos pasos: primero, el objeto UIImage se convierte a un archivo JPEG en memoria almacenado
en un objeto NSData y, después, el objeto NSData se convierte a un objeto Stream de .NET. Una llamada al
método SetResult del objeto TaskCompletionSource completa la tarea al proporcionar el objeto Stream :
namespace DependencyServiceSample.iOS
{
public class PicturePickerImplementation : IPicturePicker
{
TaskCompletionSource<Stream> taskCompletionSource;
UIImagePickerController imagePicker;
...
void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)
{
UIImage image = args.EditedImage ?? args.OriginalImage;

if (image != null)
{
// Convert UIImage to .NET Stream object
NSData data = image.AsJPEG(1);
Stream stream = data.AsStream();

UnregisterEventHandlers();

// Set the Stream as the completion of the Task


taskCompletionSource.SetResult(stream);
}
else
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
}
imagePicker.DismissModalViewController(true);
}

void OnImagePickerCancelled(object sender, EventArgs args)


{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
imagePicker.DismissModalViewController(true);
}

void UnregisterEventHandlers()
{
imagePicker.FinishedPickingMedia -= OnImagePickerFinishedPickingMedia;
imagePicker.Canceled -= OnImagePickerCancelled;
}
}
}

Una aplicación de iOS necesita el permiso del usuario para acceder a la biblioteca de fotos del teléfono. Agregue
el código siguiente a la sección dict del archivo Info.plist:

<key>NSPhotoLibraryUsageDescription</key>
<string>Picture Picker uses photo library</string>

Implementación en Android
La implementación de Android usa la técnica descrita en la receta Seleccionar una imagen y en el código de
ejemplo. Pero el método al que se llama cuando el usuario ha seleccionado una imagen desde la biblioteca de
imágenes es un reemplazo de OnActivityResult en una clase derivada de Activity . Por este motivo, la clase
MainActivity normal del proyecto de Android se ha complementado con un campo, una propiedad y un
reemplazo del método OnActivityResult :
public class MainActivity : FormsAppCompatActivity
{
...
// Field, property, and method for Picture Picker
public static readonly int PickImageId = 1000;

public TaskCompletionSource<Stream> PickImageTaskCompletionSource { set; get; }

protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)


{
base.OnActivityResult(requestCode, resultCode, intent);

if (requestCode == PickImageId)
{
if ((resultCode == Result.Ok) && (intent != null))
{
Android.Net.Uri uri = intent.Data;
Stream stream = ContentResolver.OpenInputStream(uri);

// Set the Stream as the completion of the Task


PickImageTaskCompletionSource.SetResult(stream);
}
else
{
PickImageTaskCompletionSource.SetResult(null);
}
}
}
}

El reemplazo OnActivityResult indica el archivo de imagen seleccionado con un objeto Uri de Android, pero
esto se puede convertir a un objeto Stream de .NET si se llama al método OpenInputStream del objeto
ContentResolver obtenido desde la propiedad ContentResolver de la actividad.

Al igual que la implementación de iOS, en la implementación de Android se usa un elemento


TaskCompletionSource para indicar que se ha completado la tarea. Este objeto TaskCompletionSource se define
como una propiedad pública de la clase MainActivity . Esto permite hace referencia a la propiedad en la clase
PicturePickerImplementation del proyecto de Android. Esta es la clase con el método GetImageStreamAsync :
[assembly: Dependency(typeof(PicturePickerImplementation))]

namespace DependencyServiceSample.Droid
{
public class PicturePickerImplementation : IPicturePicker
{
public Task<Stream> GetImageStreamAsync()
{
// Define the Intent for getting images
Intent intent = new Intent();
intent.SetType("image/*");
intent.SetAction(Intent.ActionGetContent);

// Start the picture-picker activity (resumes in MainActivity.cs)


MainActivity.Instance.StartActivityForResult(
Intent.CreateChooser(intent, "Select Picture"),
MainActivity.PickImageId);

// Save the TaskCompletionSource object as a MainActivity property


MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<Stream>();

// Return Task object


return MainActivity.Instance.PickImageTaskCompletionSource.Task;
}
}
}

Este método accede a la clase MainActivity por varios motivos: para la propiedad Instance , para el campo
PickImageId , para la propiedad TaskCompletionSource y para llamar a StartActivityForResult . Este método se
define mediante la clase FormsAppCompatActivity , que es la clase base de MainActivity .

Implementación en UWP
Al contrario que en las implementaciones de iOS y Android, la implementación del selector de fotos para la
Plataforma universal de Windows no necesita la clase TaskCompletionSource . La clase
PicturePickerImplementation usa la clase FileOpenPicker para acceder a la biblioteca de fotos. Como el método
PickSingleFileAsync de FileOpenPicker es en sí asincrónico, el método GetImageStreamAsync puede simplemente
usar await con ese método (y otros métodos asincrónicos) y devolver un objeto Stream :
[assembly: Dependency(typeof(PicturePickerImplementation))]

namespace DependencyServiceSample.UWP
{
public class PicturePickerImplementation : IPicturePicker
{
public async Task<Stream> GetImageStreamAsync()
{
// Create and initialize the FileOpenPicker
FileOpenPicker openPicker = new FileOpenPicker
{
ViewMode = PickerViewMode.Thumbnail,
SuggestedStartLocation = PickerLocationId.PicturesLibrary,
};

openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");

// Get a file and return a Stream


StorageFile storageFile = await openPicker.PickSingleFileAsync();

if (storageFile == null)
{
return null;
}

IRandomAccessStreamWithContentType raStream = await storageFile.OpenReadAsync();


return raStream.AsStreamForRead();
}
}
}

Implementación en código compartido


Después de implementar la interfaz para cada plataforma, la aplicación de la biblioteca de .NET Standard puede
usarla.
La clase App crea un elemento Button para seleccionar una foto:

Button pickPictureButton = new Button


{
Text = "Pick Photo",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand
};
stack.Children.Add(pickPictureButton);

El controlador Clicked usa la clase DependencyService para llamar a GetImageStreamAsync . Como resultado, se
produce una llamada en el proyecto de la plataforma. Si el método devuelve un objeto Stream , el controlador
crea un elemento Image para esa imagen con un elemento TapGestureRecognizer y reemplaza el elemento
StackLayout en la página por ese elemento Image :
pickPictureButton.Clicked += async (sender, e) =>
{
pickPictureButton.IsEnabled = false;
Stream stream = await DependencyService.Get<IPicturePicker>().GetImageStreamAsync();

if (stream != null)
{
Image image = new Image
{
Source = ImageSource.FromStream(() => stream),
BackgroundColor = Color.Gray
};

TapGestureRecognizer recognizer = new TapGestureRecognizer();


recognizer.Tapped += (sender2, args) =>
{
(MainPage as ContentPage).Content = stack;
pickPictureButton.IsEnabled = true;
};
image.GestureRecognizers.Add(recognizer);

(MainPage as ContentPage).Content = image;


}
else
{
pickPictureButton.IsEnabled = true;
}
};

Al pulsar Image en el elemento, la página vuelve al estado normal.

Vínculos relacionados
Seleccionar una foto de la galería (iOS )
Seleccionar una imagen (Android)
DependencyService (ejemplo)
Efectos de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Las interfaces de usuario de Xamarin.Forms se representan mediante los controles nativos de la plataforma de
destino, lo que permite que las aplicaciones de Xamarin.Forms conserven la apariencia adecuada para cada
plataforma. Con los efectos se pueden personalizar los controles nativos de cada plataforma sin tener que
recurrir a la implementación de un representador personalizado.

Introducción a los efectos


Con los efectos se pueden personalizar los controles nativos de cada plataforma y normalmente se usan para
pequeños cambios de estilo. En este artículo se proporciona una introducción a los efectos, se describe el límite
entre los efectos y los representadores personalizados y se describe la clase PlatformEffect .

Crear un efecto
Los efectos simplifican la personalización de un control. En este artículo se muestra cómo crear un efecto que
cambia el color de fondo del control Entry cuando el control recibe el foco.

Pasar parámetros a un efecto


La creación de un efecto que se configura a través de parámetros permite que el efecto se pueda volver a usar.
En estos artículos se muestra cómo usar las propiedades para pasar parámetros a un efecto y cambiar un
parámetro en tiempo de ejecución.

Invocación de eventos desde un efecto


Los efectos pueden invocar eventos. En este artículo se muestra cómo crear un evento que implementa el
seguimiento de dedo multitoque de bajo nivel y señala una aplicación para presiones de toque, movimientos y
liberaciones.
Introducción a los efectos
11/07/2019 • 6 minutes to read • Edit Online

Con los efectos se pueden personalizar los controles nativos de cada plataforma y normalmente se usan para
pequeños cambios de estilo. En este artículo se proporciona una introducción a los efectos, se describe el límite
entre los efectos y los representadores personalizados, y se describe la clase PlatformEffect.
En Páginas, diseños y controles de Xamarin.Forms se presenta una API común para describir interfaces de usuario
móviles multiplataforma. Cada página, diseño y control se representan de forma diferente en cada plataforma
mediante una clase Renderer que, a su vez, crea un control nativo (correspondiente a la representación de
Xamarin.Forms), lo organiza en la pantalla y agrega el comportamiento especificado en el código compartido.
Los desarrolladores pueden implementar sus propias clases Renderer personalizadas para personalizar la
apariencia o el comportamiento de un control. Pero la implementación de una clase de representador
personalizado para llevar a cabo una personalización de controles simples suele ser una respuesta compleja. Los
efectos simplifican este proceso y permiten que los controles nativos de cada plataforma se puedan personalizar
más fácilmente.
Los efectos se crean en proyectos específicos de la plataforma mediante la creación de subclases del control
PlatformEffect , y después se consumen adjuntándolos a un control adecuado en una biblioteca de .NET Standard
de Xamarin.Forms o un proyecto de biblioteca compartida.

¿Por qué usar un efecto en lugar de un representador personalizado?


Los efectos simplifican la personalización de un control, son reutilizables y se pueden parametrizar para aumentar
todavía más la reutilización.
Todo lo que se puede lograr con un efecto también se puede lograr con un representador personalizado. Pero los
representadores personalizados ofrecen más flexibilidad y personalización que los efectos. En las instrucciones
siguientes se enumeran las circunstancias bajo las que elegir un efecto en lugar de un representador
personalizado:
Un efecto se recomienda cuando el cambio de las propiedades de un control específico de la plataforma
proporcionará el resultado deseado.
Un representador personalizado es necesario cuando hay que invalidar los métodos de un control específico de
la plataforma.
Un representador personalizado es necesario cuando hay que reemplazar el control específico de la plataforma
que implementa un control de Xamarin.Forms.

Creación de subclases de la clase PlatformEffect


En la tabla siguiente se muestra el espacio de nombres para la clase PlatformEffect en cada plataforma y los tipos
de sus propiedades:

PLATAFORMA ESPACIO DE NOMBRES CONTENEDOR CONTROL

iOS Xamarin.Forms.Platform.iOS UIView UIView

Android Xamarin.Forms.Platform.And ViewGroup Ver


roid
PLATAFORMA ESPACIO DE NOMBRES CONTENEDOR CONTROL

Plataforma universal de Xamarin.Forms.Platform.UW FrameworkElement FrameworkElement


Windows (UWP) P

Cada una de las clases PlatformEffect específicas de la plataforma expone las propiedades siguientes:
Container : hace referencia al control específico de la plataforma que se usa para implementar el diseño.
Control : hace referencia al control específico de la plataforma que se usa para implementar el control de
Xamarin.Forms.
Element : hace referencia al control de Xamarin.Forms que se va a representar.

Los efectos no tienen información de tipo sobre el contenedor, el control o el elemento al que se adjuntan, ya que
se pueden adjuntar a cualquier elemento. Por tanto, cuando se adjunta un efecto a un elemento que no es
compatible, se debe degradar correctamente o iniciar una excepción. Pero las propiedades Container , Control y
Element se pueden convertir a su tipo de implementación. Para obtener más información sobre estos tipos, vea
Clases base y controles nativos del representador.
Cada clase PlatformEffect específica de la plataforma expone los métodos siguientes, que se deben invalidar para
implementar un efecto:
OnAttached : se llama cuando se adjunta un efecto a un control de Xamarin.Forms. Una versión invalidada de
este método, en cada clase de efecto específica de la plataforma, es el lugar para realizar la personalización del
control, junto con el control de excepciones en caso de que no se pueda aplicar el efecto al control de
Xamarin.Forms especificado.
OnDetached : se llama cuando se desasocia un efecto de un control de Xamarin.Forms. Una versión invalidada
de este método, en cada clase de efecto específica de la plataforma, es el lugar para realizar cualquier limpieza
de efectos, como anular el registro de un controlador de eventos.
Además, PlatformEffect expone el método OnElementPropertyChanged , que también se puede invalidar. Este
método se llama cuando ha cambiado una propiedad del elemento. Una versión invalidada de este método, en
cada clase de efecto específica de la plataforma, es el lugar para responder a los cambios de propiedad enlazable
en el control de Xamarin.Forms. Siempre se debe realizar una comprobación de la propiedad que ha modificado,
ya que esta invalidación se puede llamar varias veces.

Vínculos relacionados
Representadores personalizados
Creación de un efecto
11/07/2019 • 12 minutes to read • Edit Online

Descargar el ejemplo
Los efectos simplifican la personalización de un control. En este artículo se muestra cómo crear un efecto que
cambia el color de fondo del control Entry cuando recibe el foco.
El proceso para crear un efecto de cada proyecto específico de la plataforma es el siguiente:
1. Se crea una subclase de la clase PlatformEffect .
2. Se invalida el método OnAttached y se escribe lógica para personalizar el control.
3. Se invalida el método OnDetached y se escribe lógica para limpiar la personalización del control, si es
necesario.
4. Se agrega un atributo ResolutionGroupName a la clase de efecto. Este atributo establece un espacio de nombres
para los efectos para toda la empresa, lo que evita conflictos con otros efectos con el mismo nombre. Tenga en
cuenta que este atributo solo se puede aplicar una vez por proyecto.
5. Agregue un atributo ExportEffect a la clase de efecto. Este atributo registra el efecto con un identificador
único que Xamarin.Forms usa junto con el nombre del grupo para buscar el efecto antes de aplicarlo a un
control. El atributo toma dos parámetros: el nombre de tipo del efecto y una cadena única que se usará para
buscar el efecto antes de aplicarlo a un control.
Después, el efecto se puede consumir si se adjunta al control adecuado.

NOTE
Proporcionar un efecto en cada proyecto de la plataforma es un paso opcional. Al intentar usar un efecto cuando no se ha
registrado uno, se devolverá un valor distinto de NULL que no hace nada.

En la aplicación de ejemplo se muestra un elemento FocusEffect que cambia el color de fondo de un control
cuando recibe el foco. En el diagrama siguiente se ilustran las responsabilidades de cada proyecto en la aplicación
de ejemplo, junto con las relaciones entre ellos:

La clase FocusEffect personaliza un control Entry en el elemento HomePage en cada proyecto específico de la
plataforma. Cada clase FocusEffect se deriva de la clase PlatformEffect para cada plataforma. Como resultado,
se representa el control Entry con un color de fondo específico de la plataforma, que cambia cuando el control
recibe el foco, como se muestra en las capturas de pantalla siguientes:
Creación del efecto en cada plataforma
En las secciones siguientes se describe la implementación específica de la plataforma de la clase FocusEffect .

Proyecto de iOS
En el ejemplo de código siguiente se muestra la implementación FocusEffect para el proyecto de iOS:
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(FocusEffect), nameof(FocusEffect))]
namespace EffectsDemo.iOS
{
public class FocusEffect : PlatformEffect
{
UIColor backgroundColor;

protected override void OnAttached ()


{
try {
Control.BackgroundColor = backgroundColor = UIColor.FromRGB (204, 153, 255);
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}

protected override void OnDetached ()


{
}

protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)


{
base.OnElementPropertyChanged (args);

try {
if (args.PropertyName == "IsFocused") {
if (Control.BackgroundColor == backgroundColor) {
Control.BackgroundColor = UIColor.White;
} else {
Control.BackgroundColor = backgroundColor;
}
}
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}
}
}

El método establece la propiedad BackgroundColor del control en color morado claro con el método
OnAttached
UIColor.FromRGB y también almacena este color en un campo. Esta funcionalidad se encapsula en un bloque try /
catch en caso de que el control al que está asociado el efecto no tenga una propiedad BackgroundColor . El
método OnDetached no proporciona ninguna implementación porque no se necesita limpieza.
La invalidación de OnElementPropertyChanged responde a los cambios de propiedad enlazable en el control de
Xamarin.Forms. Cuando cambia la propiedad IsFocused , la propiedad BackgroundColor del control se cambia a
color blanco si el control tiene el foco; en caso contrario, se cambia a color morado claro. Esta funcionalidad se
encapsula en un bloque try / catch en caso de que el control al que está asociado el efecto no tenga una
propiedad BackgroundColor .

Proyecto de Android
En el ejemplo de código siguiente se muestra la implementación FocusEffect para el proyecto de Android:
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect(typeof(FocusEffect), nameof(FocusEffect))]
namespace EffectsDemo.Droid
{
public class FocusEffect : PlatformEffect
{
Android.Graphics.Color backgroundColor;

protected override void OnAttached ()


{
try {
backgroundColor = Android.Graphics.Color.LightGreen;
Control.SetBackgroundColor (backgroundColor);

} catch (Exception ex) {


Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}

protected override void OnDetached ()


{
}

protected override void OnElementPropertyChanged (System.ComponentModel.PropertyChangedEventArgs args)


{
base.OnElementPropertyChanged (args);
try {
if (args.PropertyName == "IsFocused") {
if (((Android.Graphics.Drawables.ColorDrawable)Control.Background).Color ==
backgroundColor) {
Control.SetBackgroundColor (Android.Graphics.Color.Black);
} else {
Control.SetBackgroundColor (backgroundColor);
}
}
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}
}
}

El método OnAttached llama al método SetBackgroundColor para establecer el color de fondo del control en verde
claro y también almacena este color en un campo. Esta funcionalidad se encapsula en un bloque try / catch en
caso de que el control al que está asociado el efecto no tenga una propiedad SetBackgroundColor . El método
OnDetached no proporciona ninguna implementación porque no se necesita limpieza.

La invalidación de OnElementPropertyChanged responde a los cambios de propiedad enlazable en el control de


Xamarin.Forms. Cuando cambia la propiedad IsFocused , el color de fondo del control se cambia a color blanco si
el control tiene el foco; en caso contrario, se cambia a color verde claro. Esta funcionalidad se encapsula en un
bloque try / catch en caso de que el control al que está asociado el efecto no tenga una propiedad
BackgroundColor .

Proyectos de la Plataforma universal de Windows


En el ejemplo de código siguiente se muestra la implementación FocusEffect para los proyectos de la Plataforma
universal de Windows (UWP ):
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;

[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(FocusEffect), nameof(FocusEffect))]
namespace EffectsDemo.UWP
{
public class FocusEffect : PlatformEffect
{
protected override void OnAttached()
{
try
{
(Control as Windows.UI.Xaml.Controls.Control).Background = new SolidColorBrush(Colors.Cyan);
(Control as FormsTextBox).BackgroundFocusBrush = new SolidColorBrush(Colors.White);
}
catch (Exception ex)
{
Debug.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
}
}

protected override void OnDetached()


{
}
}
}

El método OnAttached establece la propiedad Background del control en cian y la propiedad


BackgroundFocusBrush en blanco. Esta funcionalidad se encapsula en un bloque try / catch en caso de que el
control al que está asociado el efecto carezca de estas propiedades. El método OnDetached no proporciona
ninguna implementación porque no se necesita limpieza.

Consumo del efecto


El proceso para consumir un efecto desde un proyecto de biblioteca de .NET Standard o de biblioteca compartida
de Xamarin.Forms es el siguiente:
1. Se declara un control que el efecto va a personalizar.
2. Se adjunta el efecto al control agregándolo a la colección Effects del control.

NOTE
Una instancia de efecto solo se puede adjuntar a un único control. Por tanto, un efecto se debe resolver dos veces para
usarlo en dos controles.

Consumo del efecto en XAML


En el ejemplo de código XAML siguiente se muestra un control Entry al que se adjunta el elemento FocusEffect :

<Entry Text="Effect attached to an Entry" ...>


<Entry.Effects>
<local:FocusEffect />
</Entry.Effects>
...
</Entry>

La clase FocusEffect de la biblioteca de .NET Standard admite el consumo de efectos en XAML, y se muestra en
el ejemplo de código siguiente:

public class FocusEffect : RoutingEffect


{
public FocusEffect () : base ($"MyCompany.{nameof(FocusEffect)}")
{
}
}

La clase FocusEffect crea subclases de la clase RoutingEffect , que representa un efecto independiente de la
plataforma que encapsula un efecto interno que suele ser específico de la plataforma. La clase FocusEffect llama
al constructor de clase base, y se pasa un parámetro que consiste en la concatenación del nombre del grupo de
resolución (que se especifica con el atributo ResolutionGroupName en la clase de efecto), y el identificador único
que se ha especificado con el atributo ExportEffect en la clase de efecto. Por tanto, cuando se inicializa Entry en
tiempo de ejecución, se agrega una nueva instancia de MyCompany.FocusEffect a la colección Effects del control.
Los efectos también se pueden adjuntar a los controles mediante un comportamiento, o bien mediante
propiedades adjuntas. Para obtener más información sobre cómo adjuntar un efecto a un control mediante un
comportamiento, vea EffectBehavior reutilizable. Para obtener más información sobre cómo adjuntar un efecto a
un control mediante propiedades adjuntas, vea Pasar parámetros a un efecto.

Consumo del efecto en C#


El control Entry equivalente en C# se muestra en el ejemplo de código siguiente:

var entry = new Entry {


Text = "Effect attached to an Entry",
...
};

FocusEffectse adjunta a la instancia de Entry mediante la adición del efecto a la colección Effects del control,
como se muestra en el ejemplo de código siguiente:

public HomePageCS ()
{
...
entry.Effects.Add (Effect.Resolve ($"MyCompany.{nameof(FocusEffect)}"));
...
}

Effect.Resolve devuelve un elemento Effect para el nombre especificado, que es una concatenación del
nombre del grupo de resolución (que se especifica con el atributo ResolutionGroupName en la clase de efecto), y el
identificador único que se ha especificado con el atributo ExportEffect en la clase de efecto. Si una plataforma no
proporciona el efecto, el método Effect.Resolve devolverá un valor que no es null .

Resumen
En este artículo se ha mostrado cómo crear un efecto que cambia el color de fondo del control Entry cuando el
control recibe el foco.

Vínculos relacionados
Representadores personalizados
Efecto
PlatformEffect
Efecto de color de fondo (ejemplo)
Efecto de enfoque (ejemplo)
Pasar parámetros a un efecto
11/07/2019 • 2 minutes to read • Edit Online

Parámetros de efecto pueden definirse mediante propiedades, habilitar el efecto reutilizar. Parámetros, a
continuación, pueden pasarse al efecto especificando valores para cada propiedad al crear una instancia del
efecto.

Pasar parámetros de efecto como propiedades de tiempo de ejecución


de lenguaje común
Propiedades de Common Language Runtime (CLR ) pueden utilizarse para definir los parámetros del efecto que
no responden a los cambios de propiedad en tiempo de ejecución. Este artículo muestra cómo utilizar
propiedades CLR para pasar parámetros a un efecto.

Pasar parámetros de efecto como propiedades adjuntas


Propiedades adjuntas pueden utilizarse para definir los parámetros del efecto que responden a los cambios de
propiedad en tiempo de ejecución. Este artículo se muestra el uso de propiedades para pasar parámetros a un
efecto y el cambio de un parámetro en tiempo de ejecución asociadas.
Pasar parámetros de efecto como propiedades de
Common Language Runtime
11/07/2019 • 10 minutes to read • Edit Online

Descargar el ejemplo
Las propiedades de Common Language Runtime (CLR ) se pueden usar para definir parámetros de efecto que no
responden a los cambios de propiedades en tiempo de ejecución. En este artículo se muestra cómo usar
propiedades CLR para pasar parámetros a un efecto.
El proceso para crear parámetros de efecto que no respondan a los cambios de propiedades en tiempo de
ejecución es el siguiente:
1. Cree una clase public que genere subclases de la clase RoutingEffect . La clase RoutingEffect representa un
efecto independiente de la plataforma que encapsula un efecto interno, que suele ser específico de la
plataforma.
2. Cree un constructor que llame al constructor de clase base, y pase una concatenación del nombre del grupo de
resolución y el identificador único que se ha especificado en cada clase de efecto específica de la plataforma.
3. Agregue propiedades a la clase para cada parámetro que se va a pasar al efecto.
Después, se pueden pasar parámetros al efecto mediante la especificación de valores para cada propiedad al crear
una instancia del efecto.
En la aplicación de ejemplo se muestra un elemento ShadowEffect que agrega una sombra al texto mostrado por
un control Label . En el diagrama siguiente se ilustran las responsabilidades de cada proyecto en la aplicación de
ejemplo, junto con las relaciones entre ellos:

LabelShadowEffect personaliza un control Label en el elemento HomePage en cada proyecto específico de la


plataforma. Los parámetros se pasan a cada elemento LabelShadowEffect a través de las propiedades de la clase
ShadowEffect . Cada clase LabelShadowEffect se deriva de la clase PlatformEffect para cada plataforma. Como
resultado, se agrega una sombra al texto mostrado por el control Label , como se muestra en las capturas de
pantalla siguientes:
Creación de parámetros de efecto
Se debe crear una clase public que genere subclases de la clase RoutingEffect para representar los parámetros
efecto, como se muestra en el ejemplo de código siguiente:

public class ShadowEffect : RoutingEffect


{
public float Radius { get; set; }

public Color Color { get; set; }

public float DistanceX { get; set; }

public float DistanceY { get; set; }

public ShadowEffect () : base ("MyCompany.LabelShadowEffect")


{
}
}

El elemento ShadowEffect contiene cuatro propiedades que representan los parámetros que se van a pasar a cada
elemento LabelShadowEffect específico de la plataforma. El constructor de la clase llama al constructor de clase
base, y se pasa un parámetro que consiste en la concatenación del nombre del grupo de resolución y el
identificador único que se ha especificado en cada clase de efecto específica de la plataforma. Por tanto, cuando se
crea una instancia de ShadowEffect , se agrega una nueva instancia de MyCompany.LabelShadowEffect a la colección
Effects de un control.

Consumo del efecto


En el ejemplo de código XAML siguiente se muestra un control Label al que se adjunta el elemento ShadowEffect
:

<Label Text="Label Shadow Effect" ...>


<Label.Effects>
<local:ShadowEffect Radius="5" DistanceX="5" DistanceY="5">
<local:ShadowEffect.Color>
<OnPlatform x:TypeArguments="Color">
<On Platform="iOS" Value="Black" />
<On Platform="Android" Value="White" />
<On Platform="UWP" Value="Red" />
</OnPlatform>
</local:ShadowEffect.Color>
</local:ShadowEffect>
</Label.Effects>
</Label>

El control Label equivalente en C# se muestra en el ejemplo de código siguiente:


var label = new Label {
Text = "Label Shadow Effect",
...
};

Color color = Color.Default;


switch (Device.RuntimePlatform)
{
case Device.iOS:
color = Color.Black;
break;
case Device.Android:
color = Color.White;
break;
case Device.UWP:
color = Color.Red;
break;
}

label.Effects.Add (new ShadowEffect {


Radius = 5,
Color = color,
DistanceX = 5,
DistanceY = 5
});

En ambos ejemplos de código, se crea una instancia de la clase ShadowEffect con valores especificados para cada
propiedad, antes de agregarse a la colección Effects del control. Observe que la propiedad ShadowEffect.Color
usa valores de color específicos de la plataforma. Para obtener más información, vea Clase Device.

Creación del efecto en cada plataforma


En las secciones siguientes se describe la implementación específica de la plataforma de la clase
LabelShadowEffect .

Proyecto de iOS
En el ejemplo de código siguiente se muestra la implementación LabelShadowEffect para el proyecto de iOS:
[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.iOS
{
public class LabelShadowEffect : PlatformEffect
{
protected override void OnAttached ()
{
try {
var effect = (ShadowEffect)Element.Effects.FirstOrDefault (e => e is ShadowEffect);
if (effect != null) {
Control.Layer.CornerRadius = effect.Radius;
Control.Layer.ShadowColor = effect.Color.ToCGColor ();
Control.Layer.ShadowOffset = new CGSize (effect.DistanceX, effect.DistanceY);
Control.Layer.ShadowOpacity = 1.0f;
}
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}

protected override void OnDetached ()


{
}
}
}

El método OnAttached recupera la instancia de ShadowEffect y, después, establece las propiedades Control.Layer
en los valores de propiedad especificados para crear la sombra. Esta funcionalidad se encapsula en un bloque try
/ catch en caso de que el control al que está asociado el efecto no tenga las propiedades Control.Layer . El
método OnDetached no proporciona ninguna implementación porque no se necesita limpieza.
Proyecto de Android
En el ejemplo de código siguiente se muestra la implementación LabelShadowEffect para el proyecto de Android:

[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.Droid
{
public class LabelShadowEffect : PlatformEffect
{
protected override void OnAttached ()
{
try {
var control = Control as Android.Widget.TextView;
var effect = (ShadowEffect)Element.Effects.FirstOrDefault (e => e is ShadowEffect);
if (effect != null) {
float radius = effect.Radius;
float distanceX = effect.DistanceX;
float distanceY = effect.DistanceY;
Android.Graphics.Color color = effect.Color.ToAndroid ();
control.SetShadowLayer (radius, distanceX, distanceY, color);
}
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}

protected override void OnDetached ()


{
}
}
}
El método OnAttached recupera la instancia de ShadowEffect y llama al método TextView.SetShadowLayer para
crear una sombra con los valores de propiedad especificados. Esta funcionalidad se encapsula en un bloque try /
catch en caso de que el control al que está asociado el efecto no tenga las propiedades Control.Layer . El método
OnDetached no proporciona ninguna implementación porque no se necesita limpieza.

Proyecto de la Plataforma universal de Windows


En el ejemplo de código siguiente se muestra la implementación LabelShadowEffect para el proyecto de
Plataforma universal de Windows (UWP ):

[assembly: ResolutionGroupName ("Xamarin")]


[assembly: ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.UWP
{
public class LabelShadowEffect : PlatformEffect
{
bool shadowAdded = false;

protected override void OnAttached ()


{
try {
if (!shadowAdded) {
var effect = (ShadowEffect)Element.Effects.FirstOrDefault (e => e is ShadowEffect);
if (effect != null) {
var textBlock = Control as Windows.UI.Xaml.Controls.TextBlock;
var shadowLabel = new Label ();
shadowLabel.Text = textBlock.Text;
shadowLabel.FontAttributes = FontAttributes.Bold;
shadowLabel.HorizontalOptions = LayoutOptions.Center;
shadowLabel.VerticalOptions = LayoutOptions.CenterAndExpand;
shadowLabel.TextColor = effect.Color;
shadowLabel.TranslationX = effect.DistanceX;
shadowLabel.TranslationY = effect.DistanceY;

((Grid)Element.Parent).Children.Insert (0, shadowLabel);


shadowAdded = true;
}
}
} catch (Exception ex) {
Debug.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}

protected override void OnDetached ()


{
}
}
}

La Plataforma Universal de Windows no proporciona un efecto de sombra, por lo que la implementación de


LabelShadowEffect en ambas plataformas simula una mediante la adición de un segundo control Label de
desplazamiento detrás del control Label principal. El método OnAttached recupera la instancia de ShadowEffect ,
crea el objeto Label y establece algunas propiedades de diseño en el objeto Label . Después, para crear la
sombra, se establecen las propiedades TextColor , TranslationX y TranslationY para controlar el color y la
ubicación del objeto Label . Después, el elemento shadowLabel se inserta mediante desplazamiento detrás del
elemento Label principal. Esta funcionalidad se encapsula en un bloque try / catch en caso de que el control al
que está asociado el efecto no tenga las propiedades Control.Layer . El método OnDetached no proporciona
ninguna implementación porque no se necesita limpieza.

Resumen
En este artículo se ha mostrado cómo usar propiedades CLR para pasar parámetros a un efecto. Las propiedades
CLR se pueden usar para definir parámetros de efecto que no responden a los cambios de propiedades en tiempo
de ejecución.

Vínculos relacionados
Representadores personalizados
Efecto
PlatformEffect
RoutingEffect
Efecto de sombra (ejemplo)
Pasar parámetros de efecto como propiedades
adjuntas
11/07/2019 • 19 minutes to read • Edit Online

Descargar el ejemplo
Las propiedades adjuntas se pueden usar para definir los parámetros de efecto que responden a los cambios de
propiedades en tiempo de ejecución. En este artículo se muestra cómo usar las propiedades adjuntas para pasar
parámetros a un efecto y cambiar un parámetro en tiempo de ejecución.
El proceso para crear parámetros de efecto que respondan a los cambios de propiedades en tiempo de ejecución
es el siguiente:
1. Se crea una clase static que contiene una propiedad adjunta para cada parámetro que se va a pasar al efecto.
2. Se agrega una propiedad adjunta adicional a la clase que se va a usar para controlar la adición o eliminación
del efecto del control al que se va a conectar la clase. Se asegura que esta propiedad adjunta registra un
delegado propertyChanged que se ejecutará cuando cambie el valor de la propiedad.
3. Se crean captadores y establecedores static para cada propiedad adjunta.
4. Se implementa la lógica en el delegado propertyChanged para agregar y quitar el efecto.
5. Se implementa una clase anidada dentro de la clase static , con el nombre del efecto, que crea subclases de la
clase RoutingEffect . Para el constructor, se llama al constructor de clase base, y se pasa una concatenación del
nombre del grupo de resolución y el identificador único que se ha especificado en cada clase de efecto
específica de la plataforma.
Después, se pueden pasar parámetros al efecto mediante la adición de las propiedades adjuntas y los valores de
propiedad al control adecuado. Además, los parámetros se pueden cambiar en tiempo de ejecución mediante la
especificación de un nuevo valor de propiedad adjunta.

NOTE
Una propiedad adjunta es un tipo especial de propiedad enlazable, definida en una clase, pero adjunta a otros objetos, y
reconocible en XAML como atributos que contienen una clase y un nombre de propiedad separados por un punto. Para
obtener más información, vea Propiedades adjuntas.

En la aplicación de ejemplo se muestra un elemento ShadowEffect que agrega una sombra al texto mostrado por
un control Label . Además, el color de la sombra se puede cambiar en tiempo de ejecución. En el diagrama
siguiente se ilustran las responsabilidades de cada proyecto en la aplicación de ejemplo, junto con las relaciones
entre ellos:
LabelShadowEffect personaliza un control Label en el elemento HomePage en cada proyecto específico de la
plataforma. Los parámetros se pasan a cada elemento LabelShadowEffect a través de las propiedades adjuntas de
la clase ShadowEffect . Cada clase LabelShadowEffect se deriva de la clase PlatformEffect para cada plataforma.
Como resultado, se agrega una sombra al texto mostrado por el control Label , como se muestra en las capturas
de pantalla siguientes:

Creación de parámetros de efecto


Se debe crear una clase static para representar los parámetros efecto, como se muestra en el ejemplo de código
siguiente:
public static class ShadowEffect
{
public static readonly BindableProperty HasShadowProperty =
BindableProperty.CreateAttached ("HasShadow", typeof(bool), typeof(ShadowEffect), false, propertyChanged:
OnHasShadowChanged);
public static readonly BindableProperty ColorProperty =
BindableProperty.CreateAttached ("Color", typeof(Color), typeof(ShadowEffect), Color.Default);
public static readonly BindableProperty RadiusProperty =
BindableProperty.CreateAttached ("Radius", typeof(double), typeof(ShadowEffect), 1.0);
public static readonly BindableProperty DistanceXProperty =
BindableProperty.CreateAttached ("DistanceX", typeof(double), typeof(ShadowEffect), 0.0);
public static readonly BindableProperty DistanceYProperty =
BindableProperty.CreateAttached ("DistanceY", typeof(double), typeof(ShadowEffect), 0.0);

public static bool GetHasShadow (BindableObject view)


{
return (bool)view.GetValue (HasShadowProperty);
}

public static void SetHasShadow (BindableObject view, bool value)


{
view.SetValue (HasShadowProperty, value);
}
...

static void OnHasShadowChanged (BindableObject bindable, object oldValue, object newValue)


{
var view = bindable as View;
if (view == null) {
return;
}

bool hasShadow = (bool)newValue;


if (hasShadow) {
view.Effects.Add (new LabelShadowEffect ());
} else {
var toRemove = view.Effects.FirstOrDefault (e => e is LabelShadowEffect);
if (toRemove != null) {
view.Effects.Remove (toRemove);
}
}
}

class LabelShadowEffect : RoutingEffect


{
public LabelShadowEffect () : base ("MyCompany.LabelShadowEffect")
{
}
}
}

ShadowEffect contiene cinco propiedades adjuntas, con captadores y establecedores static para cada propiedad
adjunta. Cuatro de estas propiedades representan los parámetros que se van a pasar a cada elemento
LabelShadowEffect específico de la plataforma. La clase ShadowEffect también define una propiedad adjunta
HasShadow que se usa para controlar la adición o eliminación del efecto del control al que se conecta la clase
ShadowEffect . Esta propiedad adjunta registra el método OnHasShadowChanged que se ejecutará cuando cambie el
valor de la propiedad. Este método agrega o quita el efecto en función del valor de la propiedad adjunta
HasShadow .

La clase LabelShadowEffect anidada, que crea subclases de la clase RoutingEffect , admite la adición y eliminación
de efectos. La clase RoutingEffect representa un efecto independiente de la plataforma que encapsula un efecto
interno, que suele ser específico de la plataforma. Esto simplifica el proceso de eliminación del efecto, ya que no
hay ningún acceso en tiempo de compilación a la información de tipo para un efecto específico de la plataforma. El
constructor LabelShadowEffect llama al constructor de clase base, y se pasa un parámetro que consiste en la
concatenación del nombre del grupo de resolución y el identificador único que se ha especificado en cada clase de
efecto específica de la plataforma. Esto habilita la adición y eliminación del efecto en el método
OnHasShadowChanged , como se indica a continuación:

Adición de efectos: se agrega una nueva instancia de LabelShadowEffect a la colección Effects del control.
Esto reemplaza al uso del método Effect.Resolve para agregar el efecto.
Eliminación de efectos: se recupera y se quita la primera instancia de LabelShadowEffect en la colección
Effects del control.

Consumo del efecto


Cada elemento LabelShadowEffect específico de la plataforma se puede usar mediante la adición de las
propiedades adjuntas a un control Label , como se muestra en el ejemplo de código XAML siguiente:

<Label Text="Label Shadow Effect" ...


local:ShadowEffect.HasShadow="true" local:ShadowEffect.Radius="5"
local:ShadowEffect.DistanceX="5" local:ShadowEffect.DistanceY="5">
<local:ShadowEffect.Color>
<OnPlatform x:TypeArguments="Color">
<On Platform="iOS" Value="Black" />
<On Platform="Android" Value="White" />
<On Platform="UWP" Value="Red" />
</OnPlatform>
</local:ShadowEffect.Color>
</Label>

El control Label equivalente en C# se muestra en el ejemplo de código siguiente:

var label = new Label {


Text = "Label Shadow Effect",
...
};

Color color = Color.Default;


switch (Device.RuntimePlatform)
{
case Device.iOS:
color = Color.Black;
break;
case Device.Android:
color = Color.White;
break;
case Device.UWP:
color = Color.Red;
break;
}

ShadowEffect.SetHasShadow (label, true);


ShadowEffect.SetRadius (label, 5);
ShadowEffect.SetDistanceX (label, 5);
ShadowEffect.SetDistanceY (label, 5);
ShadowEffect.SetColor (label, color));

Al establecer la propiedad adjunta ShadowEffect.HasShadow en true se ejecuta el método


ShadowEffect.OnHasShadowChanged que agrega o quita LabelShadowEffect del control Label . En ambos ejemplos de
código, la propiedad adjunta ShadowEffect.Color proporciona valores de color específicos de la plataforma. Para
obtener más información, vea Clase Device.
Además, Button permite que se pueda cambiar el color de la sombra en tiempo de ejecución. Cuando se hace clic
en Button , el código siguiente cambia el color de la sombra estableciendo la propiedad adjunta
ShadowEffect.Color :

ShadowEffect.SetColor (label, Color.Teal);

Consumo del efecto con un estilo


Los efectos que se pueden consumir mediante la adición de propiedades adjuntas a un control también se pueden
consumir con un estilo. En el ejemplo de código XAML siguiente se muestra un estilo explícito para el efecto de
sombra, que se puede aplicar a controles Label :

<Style x:Key="ShadowEffectStyle" TargetType="Label">


<Style.Setters>
<Setter Property="local:ShadowEffect.HasShadow" Value="True" />
<Setter Property="local:ShadowEffect.Radius" Value="5" />
<Setter Property="local:ShadowEffect.DistanceX" Value="5" />
<Setter Property="local:ShadowEffect.DistanceY" Value="5" />
</Style.Setters>
</Style>

Style se puede aplicar a un control Label si se establece su propiedad Style en la instancia de Style mediante
la extensión de marcado StaticResource , como se muestra en el ejemplo de código siguiente:

<Label Text="Label Shadow Effect" ... Style="{StaticResource ShadowEffectStyle}" />

Para obtener más información sobre los estilos, vea Estilos.

Creación del efecto en cada plataforma


En las secciones siguientes se describe la implementación específica de la plataforma de la clase
LabelShadowEffect .

Proyecto de iOS
En el ejemplo de código siguiente se muestra la implementación LabelShadowEffect para el proyecto de iOS:
[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.iOS
{
public class LabelShadowEffect : PlatformEffect
{
protected override void OnAttached ()
{
try {
UpdateRadius ();
UpdateColor ();
UpdateOffset ();
Control.Layer.ShadowOpacity = 1.0f;
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}

protected override void OnDetached ()


{
}
...

void UpdateRadius ()
{
Control.Layer.CornerRadius = (nfloat)ShadowEffect.GetRadius (Element);
}

void UpdateColor ()
{
Control.Layer.ShadowColor = ShadowEffect.GetColor (Element).ToCGColor ();
}

void UpdateOffset ()
{
Control.Layer.ShadowOffset = new CGSize (
(double)ShadowEffect.GetDistanceX (Element),
(double)ShadowEffect.GetDistanceY (Element));
}
}

El método OnAttached llama a métodos que recuperan los valores de propiedad adjunta mediante los captadores
ShadowEffect , y que establecen propiedades Control.Layer en los valores de propiedad para crear la sombra. Esta
funcionalidad se encapsula en un bloque try / catch en caso de que el control al que está asociado el efecto no
tenga las propiedades Control.Layer . El método OnDetached no proporciona ninguna implementación porque no
se necesita limpieza.
Respuesta a los cambios de propiedad
Si alguno de los valores de las propiedades adjuntas ShadowEffect cambia en tiempo de ejecución, el efecto debe
responder mostrando los cambios. Una versión invalidada del método OnElementPropertyChanged en la clase de
efecto específica de la plataforma es el lugar para responder a los cambios de propiedad enlazable, como se
muestra en el ejemplo de código siguiente:
public class LabelShadowEffect : PlatformEffect
{
...
protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)
{
if (args.PropertyName == ShadowEffect.RadiusProperty.PropertyName) {
UpdateRadius ();
} else if (args.PropertyName == ShadowEffect.ColorProperty.PropertyName) {
UpdateColor ();
} else if (args.PropertyName == ShadowEffect.DistanceXProperty.PropertyName ||
args.PropertyName == ShadowEffect.DistanceYProperty.PropertyName) {
UpdateOffset ();
}
}
...
}

El método OnElementPropertyChanged actualiza el radio, el color o el desplazamiento de la sombra, siempre que


haya cambiado el valor de la propiedad adjunta ShadowEffect correspondiente. Siempre se debe realizar una
comprobación de la propiedad que ha modificado, ya que esta invalidación se puede llamar varias veces.
Proyecto de Android
En el ejemplo de código siguiente se muestra la implementación LabelShadowEffect para el proyecto de Android:
[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.Droid
{
public class LabelShadowEffect : PlatformEffect
{
Android.Widget.TextView control;
Android.Graphics.Color color;
float radius, distanceX, distanceY;

protected override void OnAttached ()


{
try {
control = Control as Android.Widget.TextView;
UpdateRadius ();
UpdateColor ();
UpdateOffset ();
UpdateControl ();
} catch (Exception ex) {
Console.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}

protected override void OnDetached ()


{
}
...

void UpdateControl ()
{
if (control != null) {
control.SetShadowLayer (radius, distanceX, distanceY, color);
}
}

void UpdateRadius ()
{
radius = (float)ShadowEffect.GetRadius (Element);
}

void UpdateColor ()
{
color = ShadowEffect.GetColor (Element).ToAndroid ();
}

void UpdateOffset ()
{
distanceX = (float)ShadowEffect.GetDistanceX (Element);
distanceY = (float)ShadowEffect.GetDistanceY (Element);
}
}

El método OnAttached llama a métodos que recuperan los valores de propiedad adjunta mediante los captadores
ShadowEffect , y llama a un método que llama al método TextView.SetShadowLayer para crear una sombra con los
valores de propiedad. Esta funcionalidad se encapsula en un bloque try / catch en caso de que el control al que
está asociado el efecto no tenga las propiedades Control.Layer . El método OnDetached no proporciona ninguna
implementación porque no se necesita limpieza.
Respuesta a los cambios de propiedad
Si alguno de los valores de las propiedades adjuntas ShadowEffect cambia en tiempo de ejecución, el efecto debe
responder mostrando los cambios. Una versión invalidada del método OnElementPropertyChanged en la clase de
efecto específica de la plataforma es el lugar para responder a los cambios de propiedad enlazable, como se
muestra en el ejemplo de código siguiente:
public class LabelShadowEffect : PlatformEffect
{
...
protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)
{
if (args.PropertyName == ShadowEffect.RadiusProperty.PropertyName) {
UpdateRadius ();
UpdateControl ();
} else if (args.PropertyName == ShadowEffect.ColorProperty.PropertyName) {
UpdateColor ();
UpdateControl ();
} else if (args.PropertyName == ShadowEffect.DistanceXProperty.PropertyName ||
args.PropertyName == ShadowEffect.DistanceYProperty.PropertyName) {
UpdateOffset ();
UpdateControl ();
}
}
...
}

El método OnElementPropertyChanged actualiza el radio, el color o el desplazamiento de la sombra, siempre que


haya cambiado el valor de la propiedad adjunta ShadowEffect correspondiente. Siempre se debe realizar una
comprobación de la propiedad que ha modificado, ya que esta invalidación se puede llamar varias veces.
Proyecto de la Plataforma universal de Windows
En el ejemplo de código siguiente se muestra la implementación LabelShadowEffect para el proyecto de
Plataforma universal de Windows (UWP ):
[assembly: ResolutionGroupName ("MyCompany")]
[assembly: ExportEffect (typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace EffectsDemo.UWP
{
public class LabelShadowEffect : PlatformEffect
{
Label shadowLabel;
bool shadowAdded = false;

protected override void OnAttached ()


{
try {
if (!shadowAdded) {
var textBlock = Control as Windows.UI.Xaml.Controls.TextBlock;

shadowLabel = new Label ();


shadowLabel.Text = textBlock.Text;
shadowLabel.FontAttributes = FontAttributes.Bold;
shadowLabel.HorizontalOptions = LayoutOptions.Center;
shadowLabel.VerticalOptions = LayoutOptions.CenterAndExpand;

UpdateColor ();
UpdateOffset ();

((Grid)Element.Parent).Children.Insert (0, shadowLabel);


shadowAdded = true;
}
} catch (Exception ex) {
Debug.WriteLine ("Cannot set property on attached control. Error: ", ex.Message);
}
}

protected override void OnDetached ()


{
}
...

void UpdateColor ()
{
shadowLabel.TextColor = ShadowEffect.GetColor (Element);
}

void UpdateOffset ()
{
shadowLabel.TranslationX = ShadowEffect.GetDistanceX (Element);
shadowLabel.TranslationY = ShadowEffect.GetDistanceY (Element);
}
}
}

La Plataforma Universal de Windows no proporciona un efecto de sombra, por lo que la implementación de


LabelShadowEffect en ambas plataformas simula una mediante la adición de un segundo control Label de
desplazamiento detrás del control Label principal. El método OnAttached crea el objeto Label y establece
algunas propiedades de diseño en el objeto Label . Después, llama a métodos que recuperan los valores de
propiedad adjunta mediante los captadores ShadowEffect , y crea la sombra mediante el establecimiento de las
propiedades TextColor , TranslationX y TranslationY para controlar el color y la ubicación de Label . Después, el
elemento shadowLabel se inserta mediante desplazamiento detrás del elemento Label principal. Esta
funcionalidad se encapsula en un bloque try / catch en caso de que el control al que está asociado el efecto no
tenga las propiedades Control.Layer . El método OnDetached no proporciona ninguna implementación porque no
se necesita limpieza.
Respuesta a los cambios de propiedad
Si alguno de los valores de las propiedades adjuntas ShadowEffect cambia en tiempo de ejecución, el efecto debe
responder mostrando los cambios. Una versión invalidada del método OnElementPropertyChanged en la clase de
efecto específica de la plataforma es el lugar para responder a los cambios de propiedad enlazable, como se
muestra en el ejemplo de código siguiente:

public class LabelShadowEffect : PlatformEffect


{
...
protected override void OnElementPropertyChanged (PropertyChangedEventArgs args)
{
if (args.PropertyName == ShadowEffect.ColorProperty.PropertyName) {
UpdateColor ();
} else if (args.PropertyName == ShadowEffect.DistanceXProperty.PropertyName ||
args.PropertyName == ShadowEffect.DistanceYProperty.PropertyName) {
UpdateOffset ();
}
}
...
}

El método OnElementPropertyChanged actualiza el color o el desplazamiento de la sombra, siempre que haya


cambiado el valor de la propiedad adjunta ShadowEffect correspondiente. Siempre se debe realizar una
comprobación de la propiedad que ha modificado, ya que esta invalidación se puede llamar varias veces.

Resumen
En este artículo se ha mostrado cómo usar propiedades adjuntas para pasar parámetros a un efecto y cambiar un
parámetro en tiempo de ejecución. Las propiedades adjuntas se pueden usar para definir los parámetros de efecto
que responden a los cambios de propiedades en tiempo de ejecución.

Vínculos relacionados
Representadores personalizados
Efecto
PlatformEffect
RoutingEffect
Efecto de sombra (ejemplo)
Invocación de eventos desde efectos
11/07/2019 • 38 minutes to read • Edit Online

Descargar el ejemplo
Un efecto puede definir e invocar un evento, señalando cambios en la vista nativa subyacente. En este artículo se
muestra cómo implementar seguimiento multitáctil de bajo nivel y cómo se generan eventos que indican
actividad táctil.
El efecto que se describe en este artículo proporciona acceso a eventos de función táctil de bajo nivel. Estos
eventos de bajo nivel no están disponibles a través de las clases GestureRecognizer existentes, pero son vitales
para algunos tipos de aplicaciones. Por ejemplo, una aplicación de dibujo táctil necesita realizar un seguimiento de
los dedos individuales cuando se mueven en la pantalla. Un teclado musical debe detectar cuándo se pulsan y
sueltan teclas individuales, así como un deslizamiento de dedos de una clave a otra en un glissando.
Un efecto es ideal para el seguimiento multitáctil, ya que puede asociarse a cualquier elemento de Xamarin.Forms.

Eventos táctiles de plataforma


iOS, Android y la Plataforma universal de Windows incluyen una API de bajo nivel que permite a las aplicaciones
detectar la actividad táctil. Todas estas plataformas distinguen entre tres tipos básicos de eventos táctiles:
Presionado, cuando un dedo toca la pantalla
Movido, cuando se mueve un dedo tocando la pantalla
Soltado, cuando se separa el dedo de la pantalla
En un entorno multitáctil, varios dedos pueden tocar la pantalla al mismo tiempo. Las diferentes plataformas
incluyen un número de identificación (ID ) que las aplicaciones pueden usar para distinguir entre varios dedos.
En iOS, la clase UIView define tres métodos reemplazables, TouchesBegan , TouchesMoved y TouchesEnded
correspondientes a estos tres eventos básicos. En el artículo Multi-Touch Finger Tracking (Seguimiento multitáctil)
se describe cómo usar estos métodos. Sin embargo, un programa de iOS no necesita invalidar una clase que
derive de UIView para usar estos métodos. UIGestureRecognizer de iOS también define estos mismos tres
métodos y se puede adjuntar a una instancia de una clase que deriva de UIGestureRecognizer a cualquier objeto
UIView .

En Android, la clase View define un método reemplazable denominado OnTouchEvent para procesar toda la
actividad táctil. El tipo de la actividad táctil se define por miembros de la enumeración Down , PointerDown , Move ,
Up y PointerUp tal como se describe en el artículo Multi-Touch Finger Tracking ( Seguimiento multitáctil). View
de Android también define un evento denominado Touch que permite que un controlador de eventos se adjunte
a cualquier objeto View .
En la Plataforma universal de Windows (UWP ), la clase UIElement define eventos denominados PointerPressed ,
PointerMoved y PointerReleased . Estos se describen en el artículo Handle pointer input ( Controlar la entrada del
puntero) de MSDN y en la documentación de API para la clase UIElement .
La API Pointer en la Plataforma universal de Windows está diseñada para unificar la entrada de mouse, táctil y
manuscrita. Por ese motivo, el evento PointerMoved se invoca cuando el mouse se mueve a través de un elemento,
incluso cuando no se presionó un botón del mouse. El objeto PointerRoutedEventArgs que acompaña a estos
eventos tiene una propiedad denominada Pointer , que tiene una propiedad denominada IsInContact , que indica
si se presiona un botón del mouse o un dedo está en contacto con la pantalla.
Además, la UWP define dos eventos más denominados PointerEntered y PointerExited . Estos indican si un dedo
o el mouse se mueven de un elemento a otro. Por ejemplo, imagine dos elementos adyacentes denominados A y
B. Ambos elementos tienen controladores instalados para los eventos de puntero. Cuando se presiona un dedo en
A, el evento PointerPressed se invoca. Cuando se mueve el dedo, A invoca eventos PointerMoved . Si el dedo se
mueve de A a B, A invoca un evento PointerExited y B invoca un evento PointerEntered . Si después se suelta el
dedo, B invoca un evento PointerReleased .
Las plataformas iOS y Android son diferentes de UWP: la vista que obtiene primero la llamada a TouchesBegan o
OnTouchEvent cuando un dedo toca la vista continúa obteniendo toda la actividad de interacción, incluso si se
mueve el dedo a distintas vistas. UWP puede comportarse de forma similar si la aplicación captura el puntero: en
el controlador de eventos PointerEntered , el elemento llama a CapturePointer y después obtiene toda la actividad
táctil de ese dedo.
El enfoque de UWP resulta muy útil para algunos tipos de aplicaciones, por ejemplo, un teclado musical. Cada
clave puede controlar los eventos táctiles para esa clave y detectar cuando un dedo se deslizó de una tecla a otra
mediante los eventos PointerEntered y PointerExited .
Por ese motivo, el efecto de seguimiento táctil que se describe en este artículo implementa el enfoque de UWP.

La API de efecto de seguimiento táctil


El ejemplo de Touch Tracking Effect Demos (Demostraciones de efecto de seguimiento táctil) contiene las
clases (y una enumeración) que implementan el seguimiento táctil de bajo nivel. Estos tipos pertenecen al espacio
de nombres TouchTracking y comienzan con la palabra Touch . El proyecto de biblioteca de .NET Standard
TouchTrackingEffectDemos incluye la enumeración TouchActionType para el tipo de eventos táctiles:

public enum TouchActionType


{
Entered,
Pressed,
Moved,
Released,
Exited,
Cancelled
}

Todas las plataformas incluyen también un evento que indica que se ha cancelado el evento táctil.
La clase TouchEffect de la biblioteca .NET Standard deriva de RoutingEffect y define un evento denominado
TouchAction y un método denominado OnTouchAction que invoca al evento TouchAction :

public class TouchEffect : RoutingEffect


{
public event TouchActionEventHandler TouchAction;

public TouchEffect() : base("XamarinDocs.TouchEffect")


{
}

public bool Capture { set; get; }

public void OnTouchAction(Element element, TouchActionEventArgs args)


{
TouchAction?.Invoke(element, args);
}
}

Observe también la propiedad Capture . Para capturar eventos táctiles, una aplicación debe establecer esta
propiedad en true antes de un evento Pressed . En caso contrario, los eventos táctiles se comportan como los de
la Plataforma universal de Windows.
La clase TouchActionEventArgs en la biblioteca de .NET Standard contiene toda la información que acompaña a
cada evento:

public class TouchActionEventArgs : EventArgs


{
public TouchActionEventArgs(long id, TouchActionType type, Point location, bool isInContact)
{
Id = id;
Type = type;
Location = location;
IsInContact = isInContact;
}

public long Id { private set; get; }

public TouchActionType Type { private set; get; }

public Point Location { private set; get; }

public bool IsInContact { private set; get; }


}

Una aplicación puede utilizar la propiedad Id para el seguimiento de dedos individuales. Observe la propiedad
IsInContact . Esta propiedad es siempre true para eventos Pressed y false para eventos Released . También
es siempre true para eventos Moved en iOS y Android. La propiedad IsInContact podría ser false para Moved
eventos en la Plataforma universal de Windows cuando se ejecuta el programa en el escritorio y se mueve el
puntero del mouse sin un botón presionado.
Puede usar la clase TouchEffect en sus propias aplicaciones mediante la inclusión del archivo en el proyecto de
biblioteca de .NET Standard de la solución y mediante la adición de una instancia a la colección de Effects de
cualquier elemento de Xamarin.Forms. Adjunte un controlador al evento TouchAction para obtener los eventos
táctiles.
Para usar TouchEffect en su propia aplicación, también necesitará las implementaciones de plataforma incluidas
en la solución TouchTrackingEffectDemos.

Las implementaciones de efecto de seguimiento táctil


Las implementaciones de iOS, Android y UWP de TouchEffect se describen aquí comenzando con la
implementación más sencilla (UWP ) y terminando con la implementación de iOS porque es estructuralmente más
compleja que las demás.
La implementación de UWP
La implementación de UWP de TouchEffect es la más sencilla. Como de costumbre, la clase se deriva de
PlatformEffect e incluye dos atributos de ensamblado:
[assembly: ResolutionGroupName("XamarinDocs")]
[assembly: ExportEffect(typeof(TouchTracking.UWP.TouchEffect), "TouchEffect")]

namespace TouchTracking.UWP
{
public class TouchEffect : PlatformEffect
{
...
}
}

La invalidación OnAttached guarda información, como los campos, y adjunta los controladores a todos los eventos
de puntero:

public class TouchEffect : PlatformEffect


{
FrameworkElement frameworkElement;
TouchTracking.TouchEffect effect;
Action<Element, TouchActionEventArgs> onTouchAction;

protected override void OnAttached()


{
// Get the Windows FrameworkElement corresponding to the Element that the effect is attached to
frameworkElement = Control == null ? Container : Control;

// Get access to the TouchEffect class in the .NET Standard library


effect = (TouchTracking.TouchEffect)Element.Effects.
FirstOrDefault(e => e is TouchTracking.TouchEffect);

if (effect != null && frameworkElement != null)


{
// Save the method to call on touch events
onTouchAction = effect.OnTouchAction;

// Set event handlers on FrameworkElement


frameworkElement.PointerEntered += OnPointerEntered;
frameworkElement.PointerPressed += OnPointerPressed;
frameworkElement.PointerMoved += OnPointerMoved;
frameworkElement.PointerReleased += OnPointerReleased;
frameworkElement.PointerExited += OnPointerExited;
frameworkElement.PointerCanceled += OnPointerCancelled;
}
}
...
}

El controlador OnPointerPressed invoca el evento de efecto llamando al campo onTouchAction en el método


CommonHandler :
public class TouchEffect : PlatformEffect
{
...
void OnPointerPressed(object sender, PointerRoutedEventArgs args)
{
CommonHandler(sender, TouchActionType.Pressed, args);

// Check setting of Capture property


if (effect.Capture)
{
(sender as FrameworkElement).CapturePointer(args.Pointer);
}
}
...
void CommonHandler(object sender, TouchActionType touchActionType, PointerRoutedEventArgs args)
{
PointerPoint pointerPoint = args.GetCurrentPoint(sender as UIElement);
Windows.Foundation.Point windowsPoint = pointerPoint.Position;

onTouchAction(Element, new TouchActionEventArgs(args.Pointer.PointerId,


touchActionType,
new Point(windowsPoint.X, windowsPoint.Y),
args.Pointer.IsInContact));
}
}

OnPointerPressed también comprueba el valor de la propiedad Capture en la clase efecto en la biblioteca de .NET
Standard y llama a CapturePointer si es true .
Los otros controladores de eventos de UWP son incluso más sencillos:

public class TouchEffect : PlatformEffect


{
...
void OnPointerEntered(object sender, PointerRoutedEventArgs args)
{
CommonHandler(sender, TouchActionType.Entered, args);
}
...
}

La implementación de Android
Las implementaciones de Android y iOS son necesariamente más complejas porque deben implementar los
eventos Exited y Entered cuando un dedo se mueve de un elemento a otro. Ambas implementaciones tienen
una estructura similar.
La clase TouchEffect de Android instala un controlador para el evento Touch :

view = Control == null ? Container : Control;


...
view.Touch += OnTouch;

La clase también define dos diccionarios estáticos:


public class TouchEffect : PlatformEffect
{
...
static Dictionary<Android.Views.View, TouchEffect> viewDictionary =
new Dictionary<Android.Views.View, TouchEffect>();

static Dictionary<int, TouchEffect> idToEffectDictionary =


new Dictionary<int, TouchEffect>();
...

viewDictionary obtiene una nueva entrada cada vez que se llama a la invalidación OnAttached :

viewDictionary.Add(view, this);

La entrada se quita del diccionario en OnDetached . Todas las instancias de TouchEffect están asociadas a una vista
concreta a la que está conectada el efecto. El diccionario estático permite cualquier instancia de TouchEffect para
enumerar todas las demás vistas y sus correspondientes instancias de TouchEffect . Esto es necesario para
permitir la transferencia de los eventos de una vista a otra.
Android asigna un código de identificador a los eventos táctiles que permite que una aplicación realice un
seguimiento de los dedos individuales. El idToEffectDictionary asocia este código de identificador con una
instancia de TouchEffect . Se agrega un elemento a este diccionario cuando se llama al controlador Touch para
una pulsación de dedo:

void OnTouch(object sender, Android.Views.View.TouchEventArgs args)


{
...
switch (args.Event.ActionMasked)
{
case MotionEventActions.Down:
case MotionEventActions.PointerDown:
FireEvent(this, id, TouchActionType.Pressed, screenPointerCoords, true);

idToEffectDictionary.Add(id, this);

capture = libTouchEffect.Capture;
break;

El elemento se quita de idToEffectDictionary cuando se separa el dedo de la pantalla. El método FireEvent


simplemente acumula toda la información necesaria para llamar al método OnTouchAction :

void FireEvent(TouchEffect touchEffect, int id, TouchActionType actionType, Point pointerLocation, bool
isInContact)
{
// Get the method to call for firing events
Action<Element, TouchActionEventArgs> onTouchAction = touchEffect.libTouchEffect.OnTouchAction;

// Get the location of the pointer within the view


touchEffect.view.GetLocationOnScreen(twoIntArray);
double x = pointerLocation.X - twoIntArray[0];
double y = pointerLocation.Y - twoIntArray[1];
Point point = new Point(fromPixels(x), fromPixels(y));

// Call the method


onTouchAction(touchEffect.formsElement,
new TouchActionEventArgs(id, actionType, point, isInContact));
}
Se procesan todos los demás tipos de interacción de dos maneras diferentes: si la propiedad Capture es true , el
evento táctil es una traducción bastante simple para la información de TouchEffect . Se complica más cuando
Capture es false , porque los eventos táctiles quizás tengan que moverse de una vista a otra. Esta es la
responsabilidad del método CheckForBoundaryHop , al que se llama durante los eventos de movimiento. Este
método usa ambos diccionarios estáticos. Enumera a través de viewDictionary para determinar la vista que
actualmente esté tocando el dedo y usa idToEffectDictionary para almacenar la instancia de TouchEffect actual
(y por lo tanto, la vista actual) asociada con un identificador determinado:

void CheckForBoundaryHop(int id, Point pointerLocation)


{
TouchEffect touchEffectHit = null;

foreach (Android.Views.View view in viewDictionary.Keys)


{
// Get the view rectangle
try
{
view.GetLocationOnScreen(twoIntArray);
}
catch // System.ObjectDisposedException: Cannot access a disposed object.
{
continue;
}
Rectangle viewRect = new Rectangle(twoIntArray[0], twoIntArray[1], view.Width, view.Height);

if (viewRect.Contains(pointerLocation))
{
touchEffectHit = viewDictionary[view];
}
}

if (touchEffectHit != idToEffectDictionary[id])
{
if (idToEffectDictionary[id] != null)
{
FireEvent(idToEffectDictionary[id], id, TouchActionType.Exited, pointerLocation, true);
}
if (touchEffectHit != null)
{
FireEvent(touchEffectHit, id, TouchActionType.Entered, pointerLocation, true);
}
idToEffectDictionary[id] = touchEffectHit;
}
}

Si ha habido un cambio en el idToEffectDictionary , el método potencialmente llama a FireEvent para Exited y


Entered para transferir de una vista a otra. Con todo, es posible que el dedo se moviese a un área ocupada por
una vista sin un TouchEffect adjunto o desde esa área a una vista con el efecto adjunto.
Tenga en cuenta el bloqueo try y catch cuando se accede a la vista. En una página a la que se navega que
después navega a la página principal, no se llama al método OnDetached y los elementos permanecen en el
viewDictionary pero Android los considera eliminados.

La implementación de iOS
La implementación de iOS es similar a la implementación de Android, salvo que la clase TouchEffect de iOS debe
crear una instancia de un derivado de UIGestureRecognizer . Se trata de una clase en el proyecto de iOS
denominado TouchRecognizer . Esta clase mantiene dos diccionarios estáticos que almacenan instancias de
TouchRecognizer :
static Dictionary<UIView, TouchRecognizer> viewDictionary =
new Dictionary<UIView, TouchRecognizer>();

static Dictionary<long, TouchRecognizer> idToTouchDictionary =


new Dictionary<long, TouchRecognizer>();

Gran parte de la estructura de esta clase TouchRecognizer es similar a la de la clase TouchEffect de Android.

IMPORTANT
Muchas de las vistas de UIKit no tienen la funcionalidad táctil habilitada de forma predeterminada. La funcionalidad táctil
se puede habilitar agregando view.UserInteractionEnabled = true; a la invalidación OnAttached en la clase
TouchEffect en el proyecto de iOS. Esto debe ocurrir después obtener UIView , que se corresponde con el elemento al
que está asociado el efecto.

Poner el efecto táctil en funcionamiento


El programa TouchTrackingEffectDemos contiene cinco páginas que prueban el efecto de seguimiento táctil
para tareas comunes.
La página Arrastre de BoxView le permite agregar elementos BoxView a un AbsoluteLayout y después
arrastrarlos en la pantalla. El archivo XAML crea instancias de dos vistas de Button para agregar elementos
BoxView a la AbsoluteLayout y borrar el AbsoluteLayout .

El método en el archivo de código subyacente que agrega un nuevo BoxView a AbsoluteLayout también agrega
un objeto TouchEffect para BoxView y adjunta un controlador de eventos para el efecto:

void AddBoxViewToLayout()
{
BoxView boxView = new BoxView
{
WidthRequest = 100,
HeightRequest = 100,
Color = new Color(random.NextDouble(),
random.NextDouble(),
random.NextDouble())
};

TouchEffect touchEffect = new TouchEffect();


touchEffect.TouchAction += OnTouchEffectAction;
boxView.Effects.Add(touchEffect);
absoluteLayout.Children.Add(boxView);
}

El controlador de eventos TouchAction procesa todos los eventos táctiles para todos los elementos de BoxView ,
pero debe usarse con cuidado: no puede permitir dos dedos en una sola BoxView porque el programa solo
implementa el arrastre y los dos dedos podrían interferir entre sí. Por este motivo, la página define una clase
incrustada para cada dedo del que está realizando el seguimiento:
class DragInfo
{
public DragInfo(long id, Point pressPoint)
{
Id = id;
PressPoint = pressPoint;
}

public long Id { private set; get; }

public Point PressPoint { private set; get; }


}

Dictionary<BoxView, DragInfo> dragDictionary = new Dictionary<BoxView, DragInfo>();

El dragDictionary contiene una entrada para cada BoxView que se arrastra actualmente.
La acción táctil Pressed agrega un elemento a este diccionario y la acción Released lo quita. La lógica Pressed
debe comprobar si ya hay un elemento en el diccionario para esa BoxView . Si es así, BoxView ya se está
arrastrando y el nuevo evento es un segundo dedo en esa misma BoxView . Para las acciones Moved y Released ,
el controlador de eventos debe comprobar si el diccionario tiene una entrada para esa BoxView y que la propiedad
Id táctil para la BoxView arrastrada coincide con el que aparece en la entrada del diccionario:

void OnTouchEffectAction(object sender, TouchActionEventArgs args)


{
BoxView boxView = sender as BoxView;

switch (args.Type)
{
case TouchActionType.Pressed:
// Don't allow a second touch on an already touched BoxView
if (!dragDictionary.ContainsKey(boxView))
{
dragDictionary.Add(boxView, new DragInfo(args.Id, args.Location));

// Set Capture property to true


TouchEffect touchEffect = (TouchEffect)boxView.Effects.FirstOrDefault(e => e is TouchEffect);
touchEffect.Capture = true;
}
break;

case TouchActionType.Moved:
if (dragDictionary.ContainsKey(boxView) && dragDictionary[boxView].Id == args.Id)
{
Rectangle rect = AbsoluteLayout.GetLayoutBounds(boxView);
Point initialLocation = dragDictionary[boxView].PressPoint;
rect.X += args.Location.X - initialLocation.X;
rect.Y += args.Location.Y - initialLocation.Y;
AbsoluteLayout.SetLayoutBounds(boxView, rect);
}
break;

case TouchActionType.Released:
if (dragDictionary.ContainsKey(boxView) && dragDictionary[boxView].Id == args.Id)
{
dragDictionary.Remove(boxView);
}
break;
}
}

La lógica Pressed establece la propiedad Capture del objeto TouchEffect en true . Esto tiene el efecto de
entregar todos los eventos subsiguientes para ese dedo en el mismo controlador de eventos.
La lógica Moved mueve la BoxView modificando la propiedad adjunta LayoutBounds . La propiedad Location de
los argumentos de evento siempre es relativa a la BoxView que se está arrastrando y si el BoxView se está
arrastrando a una velocidad constante, las propiedades Location de los eventos consecutivos serán
aproximadamente las mismas. Por ejemplo, si un dedo presiona el BoxView en su centro, la acción Pressed
almacena una propiedad PressPoint de (50, 50), que sigue siendo la misma para los eventos posteriores. Si la
BoxView se arrastra en diagonal a una velocidad constante, las propiedades Location subsiguientes durante la
acción Moved podrían ser valores de (55, 55), en cuyo caso la lógica Moved agrega 5 a la posición horizontal y
vertical de la BoxView . Esto mueve la BoxView de forma que su centro está de nuevo directamente bajo el dedo.
Puede mover varios elementos BoxView al mismo tiempo usando diferentes dedos.

Creación de subclases de la vista


A menudo, resulta más fácil para un elemento de Xamarin.Forms controlar sus propios eventos táctiles. La página
Arrastre de BoxView arrastrable funciona igual que la página Arrastre de BoxView, pero los elementos que el
usuario arrastra son instancias de una clase DraggableBoxView que se deriva de BoxView :
class DraggableBoxView : BoxView
{
bool isBeingDragged;
long touchId;
Point pressPoint;

public DraggableBoxView()
{
TouchEffect touchEffect = new TouchEffect
{
Capture = true
};
touchEffect.TouchAction += OnTouchEffectAction;
Effects.Add(touchEffect);
}

void OnTouchEffectAction(object sender, TouchActionEventArgs args)


{
switch (args.Type)
{
case TouchActionType.Pressed:
if (!isBeingDragged)
{
isBeingDragged = true;
touchId = args.Id;
pressPoint = args.Location;
}
break;

case TouchActionType.Moved:
if (isBeingDragged && touchId == args.Id)
{
TranslationX += args.Location.X - pressPoint.X;
TranslationY += args.Location.Y - pressPoint.Y;
}
break;

case TouchActionType.Released:
if (isBeingDragged && touchId == args.Id)
{
isBeingDragged = false;
}
break;
}
}
}

El constructor crea y adjunta el TouchEffect y establece la propiedad Capture cuando se crea una instancia de ese
objeto por primera vez. No se necesita ningún diccionario porque la propia clase almacena valores
isBeingDragged , pressPoint y touchId asociados con cada dedo. El control Moved modifica las propiedades
TranslationX y TranslationY , por lo que la lógica funcionará incluso si el elemento primario de la
DraggableBoxView no es un AbsoluteLayout .

Integración con SkiaSharp


Las siguientes dos demostraciones requieren gráficos y usan SkiaSharp para este propósito. Es posible que desee
obtener información acerca de Using SkiaSharp in Xamarin.Forms (Usar SkiaSharp en Xamarin.Forms) antes de
estudiar estos ejemplos. Los dos primeros artículos ("Conceptos básicos de dibujo de SkiaSharp" y "Rutas y líneas
de acceso de SkiaSharp") incluyen todo lo que necesitará aquí.
La página Ellipse Drawing (Dibujo de elipse) le permite dibujar una elipse deslizando el dedo en la pantalla.
Dependiendo de cómo mueva el dedo, puede dibujar la elipse desde la esquina superior izquierda a la esquina
inferior derecha, o desde cualquier otra esquina hasta la esquina opuesta. La elipse se dibuja con un color aleatorio
y una opacidad.

Si después toca uno de los puntos suspensivos, puede arrastrarla a otra ubicación. Esto requiere una técnica
conocida como "prueba de posicionamiento," lo que implica buscar el objeto gráfico en un momento determinado.
Los puntos suspensivos de SkiaSharp no son elementos de Xamarin.Forms, por lo que no pueden realizar su
propio procesamiento de TouchEffect . El TouchEffect debe aplicarse a todo el objeto SKCanvasView .
El archivo EllipseDrawPage.xaml crea una instancia de SKCanvasView en un Grid de una sola celda. El objeto
TouchEffect se asocia a ese Grid :

<Grid x:Name="canvasViewGrid"
Grid.Row="1"
BackgroundColor="White">

<skia:SKCanvasView x:Name="canvasView"
PaintSurface="OnCanvasViewPaintSurface" />
<Grid.Effects>
<tt:TouchEffect Capture="True"
TouchAction="OnTouchEffectAction" />
</Grid.Effects>
</Grid>

En la Plataforma universal de Windows y en Android el TouchEffect se puede conectar directamente a la


SKCanvasView , pero en iOS no es posible. Tenga en cuenta que la propiedad Capture está establecida en true .

Cada elipse que representa SkiaSharp se representa mediante un objeto de tipo EllipseDrawingFigure :
class EllipseDrawingFigure
{
SKPoint pt1, pt2;

public EllipseDrawingFigure()
{
}

public SKColor Color { set; get; }

public SKPoint StartPoint


{
set
{
pt1 = value;
MakeRectangle();
}
}

public SKPoint EndPoint


{
set
{
pt2 = value;
MakeRectangle();
}
}

void MakeRectangle()
{
Rectangle = new SKRect(pt1.X, pt1.Y, pt2.X, pt2.Y).Standardized;
}

public SKRect Rectangle { set; get; }

// For dragging operations


public Point LastFingerLocation { set; get; }

// For the dragging hit-test


public bool IsInEllipse(SKPoint pt)
{
SKRect rect = Rectangle;

return (Math.Pow(pt.X - rect.MidX, 2) / Math.Pow(rect.Width / 2, 2) +


Math.Pow(pt.Y - rect.MidY, 2) / Math.Pow(rect.Height / 2, 2)) < 1;
}
}

Las propiedades StartPoint y EndPoint se utilizan cuando el programa está procesando la entrada táctil; la
propiedad Rectangle se utiliza para dibujar la elipse. La propiedad LastFingerLocation entra en juego cuando se
arrastra la elipse y el método IsInEllipse ayuda en la prueba de posicionamiento. El método devuelve true si el
punto está dentro de la elipse.
El archivo de código subyacente mantiene tres colecciones:

Dictionary<long, EllipseDrawingFigure> inProgressFigures = new Dictionary<long, EllipseDrawingFigure>();


List<EllipseDrawingFigure> completedFigures = new List<EllipseDrawingFigure>();
Dictionary<long, EllipseDrawingFigure> draggingFigures = new Dictionary<long, EllipseDrawingFigure>();

El diccionario draggingFigure contiene un subconjunto de la colección de completedFigures . El controlador de


eventos PaintSurface de SkiaSharp representa simplemente los objetos en las colecciones completedFigures y
inProgressFigures :
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Fill
};
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKCanvas canvas = args.Surface.Canvas;
canvas.Clear();

foreach (EllipseDrawingFigure figure in completedFigures)


{
paint.Color = figure.Color;
canvas.DrawOval(figure.Rectangle, paint);
}
foreach (EllipseDrawingFigure figure in inProgressFigures.Values)
{
paint.Color = figure.Color;
canvas.DrawOval(figure.Rectangle, paint);
}
}

La parte más complicada del procesamiento táctil es el control Pressed . Aquí es donde se realiza la prueba de
posicionamiento, pero si el código detecta una elipse bajo el dedo del usuario, dicha elipse solo se puede arrastrar
si actualmente no la está arrastrando otro dedo. Si no hay ninguna elipse bajo el dedo del usuario, el código
comienza el proceso de dibujar una elipse nueva:
case TouchActionType.Pressed:
bool isDragOperation = false;

// Loop through the completed figures


foreach (EllipseDrawingFigure fig in completedFigures.Reverse<EllipseDrawingFigure>())
{
// Check if the finger is touching one of the ellipses
if (fig.IsInEllipse(ConvertToPixel(args.Location)))
{
// Tentatively assume this is a dragging operation
isDragOperation = true;

// Loop through all the figures currently being dragged


foreach (EllipseDrawingFigure draggedFigure in draggingFigures.Values)
{
// If there's a match, we'll need to dig deeper
if (fig == draggedFigure)
{
isDragOperation = false;
break;
}
}

if (isDragOperation)
{
fig.LastFingerLocation = args.Location;
draggingFigures.Add(args.Id, fig);
break;
}
}
}

if (isDragOperation)
{
// Move the dragged ellipse to the end of completedFigures so it's drawn on top
EllipseDrawingFigure fig = draggingFigures[args.Id];
completedFigures.Remove(fig);
completedFigures.Add(fig);
}
else // start making a new ellipse
{
// Random bytes for random color
byte[] buffer = new byte[4];
random.NextBytes(buffer);

EllipseDrawingFigure figure = new EllipseDrawingFigure


{
Color = new SKColor(buffer[0], buffer[1], buffer[2], buffer[3]),
StartPoint = ConvertToPixel(args.Location),
EndPoint = ConvertToPixel(args.Location)
};
inProgressFigures.Add(args.Id, figure);
}
canvasView.InvalidateSurface();
break;

Otro ejemplo de SkiaSharp es la página Finger Paint (Dibujo con los dedos). Puede seleccionar un color de trazo
y el ancho del trazo desde dos vistas Picker y después dibujar con uno o más dedos:
Este ejemplo también requiere una clase independiente para representar cada línea dibujada en la pantalla:

class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new SKPath();
}

public SKPath Path { set; get; }

public Color StrokeColor { set; get; }

public float StrokeWidth { set; get; }


}

Un objeto SKPath se usa para representar cada línea. El archivo FingerPaint.xaml.cs mantiene dos colecciones de
estos objetos, una para aquellas polilíneas que se están dibujando actualmente y otra para las polilíneas
completadas:

Dictionary<long, FingerPaintPolyline> inProgressPolylines = new Dictionary<long, FingerPaintPolyline>();


List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();

El procesamiento de Pressed crea una nueva FingerPaintPolyline , llama a MoveTo en el objeto de ruta de acceso
para almacenar el punto inicial y agrega ese objeto al diccionario de inProgressPolylines . El procesamiento de
Moved llama a LineTo en el objeto de ruta con la nueva posición del dedo y el procesamiento de Released
transfiere la polilínea completada desde inProgressPolylines a completedPolylines . Una vez más, el código de
dibujo de SkiaSharp real es relativamente sencillo:
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
StrokeCap = SKStrokeCap.Round,
StrokeJoin = SKStrokeJoin.Round
};
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKCanvas canvas = args.Surface.Canvas;
canvas.Clear();

foreach (FingerPaintPolyline polyline in completedPolylines)


{
paint.Color = polyline.StrokeColor.ToSKColor();
paint.StrokeWidth = polyline.StrokeWidth;
canvas.DrawPath(polyline.Path, paint);
}

foreach (FingerPaintPolyline polyline in inProgressPolylines.Values)


{
paint.Color = polyline.StrokeColor.ToSKColor();
paint.StrokeWidth = polyline.StrokeWidth;
canvas.DrawPath(polyline.Path, paint);
}
}

Seguimiento táctil de vista a vista


Todos los ejemplos anteriores han establecido la propiedad Capture del TouchEffect en true , ya sea cuando el
TouchEffect se creó o cuando se produjo el evento Pressed . Esto garantiza que el mismo elemento recibe todos
los eventos asociados con el dedo que presionó la vista primero. El ejemplo final no establece Capture en true .
Esto provoca un comportamiento diferente cuando se mueve un dedo en contacto con la pantalla de un elemento
a otro. El elemento desde el que se mueve el dedo recibe un evento con una propiedad Type establecida en
TouchActionType.Exited y el segundo elemento recibe un evento con una configuración Type de
TouchActionType.Entered .

Este tipo de procesamiento táctil es muy útil para un teclado de música. Una tecla debe ser capaz de detectar
cuándo se pulsa, pero también cuándo un dedo pasa de una tecla a otra.
La página Silent Keyboard teclado silencioso define clases WhiteKey y BlackKey pequeñas que derivan de Key ,
que se deriva de BoxView .
La clase Keyestá lista para usarse en un programa de música real. Define propiedades públicas denominadas
IsPressed y KeyNumber , que están pensadas para establecerse en el código de teclas que establece el estándar
MIDI. La clase Key también define un evento denominado StatusChanged , que se invoca cuando la propiedad
IsPressed cambia.

Se permiten varios dedos en cada tecla. Por este motivo, la clase Key mantiene una List de los números de Id.
táctil de todos los dedos que tocan actualmente esa tecla:

List<long> ids = new List<long>();

El controlador de eventos TouchAction agrega un identificador para la lista ids para un tipo de evento Pressed y
un tipo Entered , pero solo cuando la propiedad IsInContact es true para el evento Entered . Se quita el
identificador de la List para un evento Released o Exited :
void OnTouchEffectAction(object sender, TouchActionEventArgs args)
{
switch (args.Type)
{
case TouchActionType.Pressed:
AddToList(args.Id);
break;

case TouchActionType.Entered:
if (args.IsInContact)
{
AddToList(args.Id);
}
break;

case TouchActionType.Moved:
break;

case TouchActionType.Released:
case TouchActionType.Exited:
RemoveFromList(args.Id);
break;
}
}

Los métodos AddToList y RemoveFromList comprueban si la List ha cambiado entre vacía y no vacía y si es así,
invocan el evento StatusChanged .
Los distintos elementos WhiteKey y BlackKey se organizan en el archivo XAML de la página, que tiene mejor
aspecto cuando se mantiene el teléfono en un modo horizontal:

Si pasa los dedos por las teclas, podrá ver por los pequeños cambios en el color que los eventos táctiles se
transfieren de una tecla a otra.

Resumen
En este artículo se mostró cómo invocar eventos en un efecto, y cómo escribir y usar un efecto que implementa el
procesamiento multitáctil de bajo nivel.

Vínculos relacionados
Multi-Touch Finger Tracking in iOS (Seguimiento de dedos multitáctil en iOS )
Multi-Touch Finger Tracking in Android (Seguimiento de dedos multitáctil en Android)
Touch Tracking Effect (sample) (Efecto de seguimiento táctil [ejemplo])
Gestos de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Reconocedores de gestos que se usan para detectar la interacción del usuario con las vistas en una aplicación de
Xamarin.Forms.
La clase GestureRecognizer de Xamarin.Forms admite los gestos de pulsar, reducir, desplazar lateralmente y
deslizar rápidamente en instancias de View .

Agregar un reconocedor de gesto de pulsar


Se usa un gesto de pulsar para la detección de pulsación y se reconoce con la clase TapGestureRecognizer .

Agregar un reconocedor de gesto de reducir


Se usa un gesto de reducir para realizar un zoom interactivo y se reconoce con la clase PinchGestureRecognizer .

Agregar un reconocedor de gesto de desplazar lateralmente


Se usa un gesto de desplazar lateralmente para detectar el movimiento de los dedos alrededor de la pantalla y
aplicar ese movimiento al contenido; se reconoce con la clase PanGestureRecognizer .

Agregar un reconocedor de gesto de deslizar rápidamente


Un gesto de deslizar rápidamente se produce cuando un dedo se mueve a través de la pantalla en dirección
horizontal o vertical y, a menudo, se usa para iniciar la navegación a través del contenido. Los gestos de deslizar
rápidamente se reconocen con la clase SwipeGestureRecognizer .
Adición de un reconocedor de gesto de pulsar
11/07/2019 • 3 minutes to read • Edit Online

Descargar el ejemplo
El gesto de pulsar se usa para la detección de pulsaciones y se implementa con la clase TapGestureRecognizer.
Para que se pueda hacer clic en un elemento de interfaz de usuario con el gesto de pulsar, cree una instancia de
TapGestureRecognizer , controle el evento Tapped , y agregue el nuevo reconocedor de gestos a la colección
GestureRecognizers en el elemento de interfaz de usuario. En el ejemplo de código siguiente se muestra un
elemento TapGestureRecognizer adjunto a un elemento Image :

var tapGestureRecognizer = new TapGestureRecognizer();


tapGestureRecognizer.Tapped += (s, e) => {
// handle the tap
};
image.GestureRecognizers.Add(tapGestureRecognizer);

De forma predeterminada, la imagen responderá a pulsaciones simples. Establezca la propiedad


NumberOfTapsRequired para que espere una pulsación doble (o más pulsaciones si es necesario).

tapGestureRecognizer.NumberOfTapsRequired = 2; // double-tap

Cuando se establece NumberOfTapsRequired por encima de uno, el controlador de eventos solo se ejecuta si las
pulsaciones se producen dentro de un período de tiempo concreto (que no se puede configurar). Si la segunda
pulsación (o las posteriores) no se producen dentro de ese período, se omiten y se reinicia el "recuento de
pulsaciones".

Uso de Xaml
Un reconocedor de gestos se puede agregar a un control en Xaml mediante propiedades adjuntas. La sintaxis para
agregar un elemento TapGestureRecognizer a una imagen se muestra a continuación (en este caso se define un
evento de pulsación doble):

<Image Source="tapped.jpg">
<Image.GestureRecognizers>
<TapGestureRecognizer
Tapped="OnTapGestureRecognizerTapped"
NumberOfTapsRequired="2" />
</Image.GestureRecognizers>
</Image>

El código para el controlador de eventos (en el ejemplo) incrementa un contador y cambia la imagen de color a
blanco y negro.
void OnTapGestureRecognizerTapped(object sender, EventArgs args)
{
tapCount++;
var imageSender = (Image)sender;
// watch the monkey go from color to black&white!
if (tapCount % 2 == 0) {
imageSender.Source = "tapped.jpg";
} else {
imageSender.Source = "tapped_bw.jpg";
}
}

Uso de ICommand
Las aplicaciones que usan el patrón Model-View -ViewModel (MVVM ) suelen usar ICommand en lugar de conectar
controladores de eventos directamente. El elemento TapGestureRecognizer puede admitir fácilmente ICommand
estableciendo el enlace en el código:

var tapGestureRecognizer = new TapGestureRecognizer();


tapGestureRecognizer.SetBinding (TapGestureRecognizer.CommandProperty, "TapCommand");
image.GestureRecognizers.Add(tapGestureRecognizer);

o con Xaml:

<Image Source="tapped.jpg">
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="Image1" />
</Image.GestureRecognizers>
</Image>

El código completo para este modelo de vista se puede encontrar en el ejemplo. A continuación se muestran los
detalles de implementación de Command relevantes:

public class TapViewModel : INotifyPropertyChanged


{
int taps = 0;
ICommand tapCommand;
public TapViewModel () {
// configure the TapCommand with a method
tapCommand = new Command (OnTapped);
}
public ICommand TapCommand {
get { return tapCommand; }
}
void OnTapped (object s) {
taps++;
Debug.WriteLine ("parameter: " + s);
}
//region INotifyPropertyChanged code omitted
}

Vínculos relacionados
TapGesture (ejemplo)
GestureRecognizer
TapGestureRecognizer
Agregar un reconocedor de gestos de reducir
11/07/2019 • 5 minutes to read • Edit Online

Descargar el ejemplo
El gesto de reducir se usa para realizar un zoom interactivo y se implementa con la clase PinchGestureRecognizer.
Un escenario común para el gesto de reducir es realizar un zoom interactivo de una imagen en la ubicación donde
se realice el gesto. Esto se logra al escalar el contenido de la ventanilla, como se demuestra en este artículo.
Para que un elemento de interfaz de usuario se pueda mover con el gesto de reducir, cree una instancia de
PinchGestureRecognizer , controle el evento PinchUpdated y agregue el nuevo reconocedor de gestos a la colección
GestureRecognizers en el elemento de interfaz de usuario. En el siguiente ejemplo de código, se muestra un
elemento PinchGestureRecognizer asociado a un elemento Image :

var pinchGesture = new PinchGestureRecognizer();


pinchGesture.PinchUpdated += (s, e) => {
// Handle the pinch
};
image.GestureRecognizers.Add(pinchGesture);

Esto también se puede lograr en XAML, como se muestra en el ejemplo de código siguiente:

<Image Source="waterfront.jpg">
<Image.GestureRecognizers>
<PinchGestureRecognizer PinchUpdated="OnPinchUpdated" />
</Image.GestureRecognizers>
</Image>

Después, se agrega el código del controlador de eventos OnPinchUpdated al archivo de código subyacente:

void OnPinchUpdated (object sender, PinchGestureUpdatedEventArgs e)


{
// Handle the pinch
}

Crear un contenedor de PinchToZoom


Para controlar el gesto de reducir y realizar esta operación de zoom, se necesita un cálculo matemático para
transformar la interfaz de usuario. Esta sección contiene una clase auxiliar generalizada para realizar la operación
matemática, que puede usarse para realizar un zoom interactivo en cualquier elemento de la interfaz de usuario. En
el ejemplo de código siguiente se muestra la clase PinchToZoomContainer :
public class PinchToZoomContainer : ContentView
{
...

public PinchToZoomContainer ()
{
var pinchGesture = new PinchGestureRecognizer ();
pinchGesture.PinchUpdated += OnPinchUpdated;
GestureRecognizers.Add (pinchGesture);
}

void OnPinchUpdated (object sender, PinchGestureUpdatedEventArgs e)


{
...
}
}

Esta clase se puede encapsular en un elemento de interfaz de usuario para que el gesto de reducir amplíe o
reduzca el elemento de interfaz de usuario encapsulado. En el siguiente ejemplo de código de XAML, se muestra
cómo encapsular PinchToZoomContainer en un elemento Image :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:PinchGesture;assembly=PinchGesture"
x:Class="PinchGesture.HomePage">
<ContentPage.Content>
<Grid Padding="20">
<local:PinchToZoomContainer>
<local:PinchToZoomContainer.Content>
<Image Source="waterfront.jpg" />
</local:PinchToZoomContainer.Content>
</local:PinchToZoomContainer>
</Grid>
</ContentPage.Content>
</ContentPage>

En el siguiente ejemplo de código, se muestra cómo el elemento PinchToZoomContainer encapsula un elemento


Image en una página de C#:

public class HomePageCS : ContentPage


{
public HomePageCS ()
{
Content = new Grid {
Padding = new Thickness (20),
Children = {
new PinchToZoomContainer {
Content = new Image { Source = ImageSource.FromFile ("waterfront.jpg") }
}
}
};
}
}

Cuando el elemento Image recibe un gesto de reducir, la imagen mostrada se ampliará o reducirá. La acción de
zoom se realiza mediante el método PinchZoomContainer.OnPinchUpdated , que se muestra en el siguiente ejemplo de
código:
void OnPinchUpdated (object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started) {
// Store the current scale factor applied to the wrapped user interface element,
// and zero the components for the center point of the translate transform.
startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
}
if (e.Status == GestureStatus.Running) {
// Calculate the scale factor to be applied.
currentScale += (e.Scale - 1) * startScale;
currentScale = Math.Max (1, currentScale);

// The ScaleOrigin is in relative coordinates to the wrapped user interface element,


// so get the X pixel coordinate.
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;

// The ScaleOrigin is in relative coordinates to the wrapped user interface element,


// so get the Y pixel coordinate.
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;

// Calculate the transformed element pixel coordinates.


double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);

// Apply translation based on the change in origin.


Content.TranslationX = targetX.Clamp (-Content.Width * (currentScale - 1), 0);
Content.TranslationY = targetY.Clamp (-Content.Height * (currentScale - 1), 0);

// Apply scale factor.


Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed) {
// Store the translation delta's of the wrapped user interface element.
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
}
}

Este método actualiza el nivel de zoom del elemento de la interfaz de usuario encapsulada basándose en el gesto
de reducir del usuario. Para lograr esto, se usan los valores de las propiedades Scale , ScaleOrigin y Status de la
instancia de PinchGestureUpdatedEventArgs para calcular el factor de escala que se aplicará en el origen del gesto de
reducir. El elemento del usuario encapsulado se amplía o reduce en el origen del gesto de reducir al establecer las
propiedades TranslationX , TranslationY y Scale en los valores calculados.

Vínculos relacionados
PinchGesture (ejemplo)
GestureRecognizer
PinchGestureRecognizer
Adición de un reconocedor de gesto de
desplazamiento lateral
11/07/2019 • 6 minutes to read • Edit Online

Descargar el ejemplo
El gesto de desplazamiento lateral se usa para detectar el movimiento de los dedos alrededor de la pantalla y
aplicar ese movimiento al contenido, y se implementa con la clase PanGestureRecognizer . Un escenario habitual
para el gesto de desplazamiento lateral consiste en desplazar una imagen de forma horizontal y vertical, para
poder ver todo el contenido de imagen cuando se muestre en una ventanilla más pequeña que las dimensiones de
la imagen. Esto se logra moviendo la imagen dentro de la ventanilla, y se muestra en este artículo.
Para que un elemento de interfaz de usuario se pueda mover con el gesto de desplazamiento lateral, cree una
instancia de PanGestureRecognizer , controle el evento PanUpdated y agregue el nuevo reconocedor de gestos a la
colección GestureRecognizers en el elemento de interfaz de usuario. En el ejemplo de código siguiente se muestra
un elemento PanGestureRecognizer adjunto a un elemento Image :

var panGesture = new PanGestureRecognizer();


panGesture.PanUpdated += (s, e) => {
// Handle the pan
};
image.GestureRecognizers.Add(panGesture);

Esto también se puede lograr en XAML, como se muestra en el ejemplo de código siguiente:

<Image Source="MonoMonkey.jpg">
<Image.GestureRecognizers>
<PanGestureRecognizer PanUpdated="OnPanUpdated" />
</Image.GestureRecognizers>
</Image>

Después, se agrega el código para el controlador de eventos OnPanUpdated al archivo de código subyacente:

void OnPanUpdated (object sender, PanUpdatedEventArgs e)


{
// Handle the pan
}

NOTE
El desplazamiento lateral correcto en Android requiere el paquete NuGet Xamarin.Forms 2.1.0-pre1 como mínimo.

Creación de un contenedor de desplazamiento lateral


Esta sección contiene una clase auxiliar generalizada que realiza el desplazamiento lateral de forma libre, que
normalmente es adecuado para navegar dentro de imágenes o mapas. El control del gesto de desplazamiento
lateral para realizar esta operación requiere un cálculo matemático para transformar la interfaz de usuario. Este
cálculo matemático se usa para el desplazamiento lateral solo dentro de los límites del elemento de interfaz de
usuario ajustado. En el ejemplo de código siguiente se muestra la clase PanContainer :
public class PanContainer : ContentView
{
double x, y;

public PanContainer ()
{
// Set PanGestureRecognizer.TouchPoints to control the
// number of touch points needed to pan
var panGesture = new PanGestureRecognizer ();
panGesture.PanUpdated += OnPanUpdated;
GestureRecognizers.Add (panGesture);
}

void OnPanUpdated (object sender, PanUpdatedEventArgs e)


{
...
}
}

Esta clase se puede encapsular en un elemento de interfaz de usuario para que el gesto desplace el elemento de
interfaz de usuario encapsulado. En el ejemplo de código XAML siguiente se muestra la encapsulación de
PanContainer en un elemento Image :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:PanGesture"
x:Class="PanGesture.HomePage">
<ContentPage.Content>
<AbsoluteLayout>
<local:PanContainer>
<Image Source="MonoMonkey.jpg" WidthRequest="1024" HeightRequest="768" />
</local:PanContainer>
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>

En el ejemplo de código siguiente se muestra cómo el elemento PanContainer encapsula un elemento Image en
una página de C#:

public class HomePageCS : ContentPage


{
public HomePageCS ()
{
Content = new AbsoluteLayout {
Padding = new Thickness (20),
Children = {
new PanContainer {
Content = new Image {
Source = ImageSource.FromFile ("MonoMonkey.jpg"),
WidthRequest = 1024,
HeightRequest = 768
}
}
}
};
}
}

En los dos ejemplos, las propiedades WidthRequest y HeightRequest se establecen en los valores de alto y ancho
de la imagen que se va a mostrar.
Cuando el elemento Image recibe un gesto de desplazamiento lateral, se desplaza lateralmente la imagen
mostrada. El desplazamiento lateral se realiza mediante el método PanContainer.OnPanUpdated , que se muestra en
el ejemplo de código siguiente:

void OnPanUpdated (object sender, PanUpdatedEventArgs e)


{
switch (e.StatusType) {
case GestureStatus.Running:
// Translate and ensure we don't pan beyond the wrapped user interface element bounds.
Content.TranslationX =
Math.Max (Math.Min (0, x + e.TotalX), -Math.Abs (Content.Width - App.ScreenWidth));
Content.TranslationY =
Math.Max (Math.Min (0, y + e.TotalY), -Math.Abs (Content.Height - App.ScreenHeight));
break;

case GestureStatus.Completed:
// Store the translation applied during the pan
x = Content.TranslationX;
y = Content.TranslationY;
break;
}
}

Este método actualiza el contenido visible del elemento de interfaz de usuario encapsulado, en función del gesto de
desplazamiento lateral del usuario. Esto se logra mediante el uso de los valores de las propiedades TotalX y
TotalY de la instancia de PanUpdatedEventArgs para calcular la dirección y la distancia del desplazamiento lateral.
Las propiedades App.ScreenWidth y App.ScreenHeight proporcionan el alto y ancho de la ventanilla, y se
establecen en los valores de ancho y alto de la pantalla del dispositivo por los proyectos específicos de la
plataforma correspondiente. Después, se realiza el desplazamiento lateral del elemento de usuario encapsulado,
mediante el establecimiento de sus propiedades TranslationX y TranslationY en los valores calculados.
Al desplazar lateralmente el contenido de un elemento que no ocupa toda la pantalla, el alto y ancho de la
ventanilla se pueden obtener de las propiedades Height y Width del elemento.

NOTE
Mostrar imágenes de alta resolución puede aumentar considerablemente la superficie de memoria de una aplicación. Por
tanto, solo se deberían crear cuando sea necesario y deberían liberarse en cuanto la aplicación ya no las necesite. Para más
información, vea Optimizar los recursos de imagen.

Vínculos relacionados
PanGesture (ejemplo)
GestureRecognizer
PanGestureRecognizer
Agregar un reconocedor de gesto de deslizar
rápidamente
11/07/2019 • 9 minutes to read • Edit Online

Descargar el ejemplo
Un gesto de deslizar rápidamente se produce cuando un dedo se mueve a través de la pantalla en dirección
horizontal o vertical y, a menudo, se usa para iniciar la navegación a través del contenido. Los ejemplos de código
en este artículo están sacados del ejemplo Swipe Gesture (Gesto de deslizar rápidamente).
Para hacer que View reconozca un gesto de deslizar rápidamente, cree una instancia de SwipeGestureRecognizer ,
establezca la propiedad Direction en un valor de enumeración de SwipeDirection ( Left , Right , Up o Down ),
opcionalmente establezca la propiedad Threshold , controle el evento Swiped y agregue el reconocedor de gestos
nuevo a la colección GestureRecognizers de la vista. En el ejemplo de código siguiente se muestra un elemento
SwipeGestureRecognizer adjunto a BoxView :

<BoxView Color="Teal" ...>


<BoxView.GestureRecognizers>
<SwipeGestureRecognizer Direction="Left" Swiped="OnSwiped"/>
</BoxView.GestureRecognizers>
</BoxView>

El código equivalente en C# es el siguiente:

var boxView = new BoxView { Color = Color.Teal, ... };


var leftSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Left };
leftSwipeGesture.Swiped += OnSwiped;

boxView.GestureRecognizers.Add(leftSwipeGesture);

La clase SwipeGestureRecognizer también incluye una propiedad Threshold , que opcionalmente se puede
establecer en un valor uint que representa la distancia mínima que debe deslizarse un dedo para que se
reconozca el deslizamiento, en unidades independientes del dispositivo. El valor predeterminado de esta propiedad
es 100, lo cual significa que cualquier deslizamiento por debajo de 100 unidades independientes del dispositivo se
ignorará.

Reconocimiento de la dirección del deslizamiento


En los ejemplos anteriores, la propiedad Direction está establecida en un valor único de la enumeración de
SwipeDirection . Sin embargo, también es posible establecer esta propiedad en varios valores desde la
enumeración de SwipeDirection , por lo que el evento Swiped se desencadena en respuesta a un deslizamiento en
más de una dirección. Sin embargo, la restricción es que un único elemento SwipeGestureRecognizer solo puede
reconocer los deslizamientos que se produzcan en el mismo eje. Por lo tanto, se pueden reconocer los
deslizamientos que se producen en el eje horizontal estableciendo la propiedad Direction en Left y Right :

<SwipeGestureRecognizer Direction="Left,Right" Swiped="OnSwiped"/>

De forma similar, se pueden reconocer los deslizamientos que se producen en el eje horizontal estableciendo la
propiedad Direction en Up y Down :
var swipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Up | SwipeDirection.Down };

Como alternativa, puede crearse un SwipeGestureRecognizer para cada dirección de deslizamiento para que
reconozca los deslizamientos en cada dirección:

<BoxView Color="Teal" ...>


<BoxView.GestureRecognizers>
<SwipeGestureRecognizer Direction="Left" Swiped="OnSwiped"/>
<SwipeGestureRecognizer Direction="Right" Swiped="OnSwiped"/>
<SwipeGestureRecognizer Direction="Up" Swiped="OnSwiped"/>
<SwipeGestureRecognizer Direction="Down" Swiped="OnSwiped"/>
</BoxView.GestureRecognizers>
</BoxView>

El código equivalente en C# es el siguiente:

var boxView = new BoxView { Color = Color.Teal, ... };


var leftSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Left };
leftSwipeGesture.Swiped += OnSwiped;
var rightSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Right };
rightSwipeGesture.Swiped += OnSwiped;
var upSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Up };
upSwipeGesture.Swiped += OnSwiped;
var downSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Down };
downSwipeGesture.Swiped += OnSwiped;

boxView.GestureRecognizers.Add(leftSwipeGesture);
boxView.GestureRecognizers.Add(rightSwipeGesture);
boxView.GestureRecognizers.Add(upSwipeGesture);
boxView.GestureRecognizers.Add(downSwipeGesture);

NOTE
En los ejemplos anteriores, el mismo controlador de eventos responde a la activación del evento Swiped . Sin embargo, cada
instancia de SwipeGestureRecognizer puede usar un controlador de eventos distinto, si es necesario.

Respuesta al deslizamiento
En el ejemplo siguiente, se muestra un controlador de eventos para el evento Swiped :
void OnSwiped(object sender, SwipedEventArgs e)
{
switch (e.Direction)
{
case SwipeDirection.Left:
// Handle the swipe
break;
case SwipeDirection.Right:
// Handle the swipe
break;
case SwipeDirection.Up:
// Handle the swipe
break;
case SwipeDirection.Down:
// Handle the swipe
break;
}
}

SwipedEventArgs puede examinarse para determinar la dirección del deslizamiento, con lógica personalizada como
respuesta al deslizamiento, según sea necesario. Se puede obtener la dirección del deslizamiento desde la
propiedad Direction de los argumentos de evento, que se establecerán en uno de los valores de la enumeración
de SwipeDirection . Además, los argumentos de evento también tienen una propiedad Parameter que se
establecerá en el valor de la propiedad CommandParameter , si se ha definido.

Uso de comandos
La clase SwipeGestureRecognizer también incluye las propiedades Command y CommandParameter . Estas propiedades
se utilizan normalmente en aplicaciones que usan el patrón Model-View -ViewModel (MVVM ). La propiedad
Command define el elemento ICommand que se invocará cuando se reconoce un gesto de deslizar rápidamente, con
la propiedad CommandParameter que define un objeto que se pasará al elemento ICommand. . El ejemplo de código
siguiente muestra cómo enlazar la propiedad Command a un elemento ICommand definido en el modelo de vista
cuya instancia se ha establecido como la página BindingContext :

var boxView = new BoxView { Color = Color.Teal, ... };


var leftSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Left, CommandParameter = "Left"
};
leftSwipeGesture.SetBinding(SwipeGestureRecognizer.CommandProperty, "SwipeCommand");
boxView.GestureRecognizers.Add(leftSwipeGesture);

El código XAML equivalente es:

<BoxView Color="Teal" ...>


<BoxView.GestureRecognizers>
<SwipeGestureRecognizer Direction="Left" Command="{Binding SwipeCommand}" CommandParameter="Left" />
</BoxView.GestureRecognizers>
</BoxView>

SwipeCommand es una propiedad de tipo ICommand definida en la instancia de modelo de vista que se establece
como la página BindingContext . Cuando se reconoce un gesto de deslizar rápidamente, se ejecuta el método
Execute del objeto SwipeCommand . El argumento para el método Execute es el valor de la propiedad
CommandParameter . Para obtener más información sobre los comandos, consulte The Command Interface ( La
interfaz de comandos).

Creación de un contenedor de deslizamiento


La clase SwipeContainer , que se muestra en el siguiente ejemplo de código, es una clase de reconocimiento de
deslizamiento que se ha ajustado alrededor de un elemento View para realizar el reconocimiento de gestos de
deslizar rápidamente:

public class SwipeContainer : ContentView


{
public event EventHandler<SwipedEventArgs> Swipe;

public SwipeContainer()
{
GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Left));
GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Right));
GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Up));
GestureRecognizers.Add(GetSwipeGestureRecognizer(SwipeDirection.Down));
}

SwipeGestureRecognizer GetSwipeGestureRecognizer(SwipeDirection direction)


{
var swipe = new SwipeGestureRecognizer { Direction = direction };
swipe.Swiped += (sender, e) => Swipe?.Invoke(this, e);
return swipe;
}
}

La clase crea objetos SwipeGestureRecognizer para las cuatro direcciones de deslizamiento y


SwipeContainer
adjunta Swipe controladores de eventos. Estos controladores de eventos invocan el evento Swipe definido por
SwipeContainer .

En el ejemplo de código XAML siguiente se muestra la clase SwipeContainer que realiza el ajuste de BoxView :

<ContentPage ...>
<StackLayout>
<local:SwipeContainer Swipe="OnSwiped" ...>
<BoxView Color="Teal" ... />
</local:SwipeContainer>
</StackLayout>
</ContentPage>

En el ejemplo de código siguiente se muestra cómo SwipeContainer realiza el ajuste de BoxView en una página de
C#:

public class SwipeContainerPageCS : ContentPage


{
public SwipeContainerPageCS()
{
var boxView = new BoxView { Color = Color.Teal, ... };
var swipeContainer = new SwipeContainer { Content = boxView, ... };
swipeContainer.Swipe += (sender, e) =>
{
// Handle the swipe
};

Content = new StackLayout


{
Children = { swipeContainer }
};
}
}

Cuando BoxView recibe un gesto de deslizar rápidamente, el evento Swiped en SwipeGestureRecognizer se activa.
Esto se controla mediante la clase SwipeContainer , que activa su propio evento Swipe . Este evento Swipe se
controla en la página. Posteriormente, SwipedEventArgs puede examinarse para determinar la dirección del
deslizamiento, con lógica personalizada como respuesta al deslizamiento, según sea necesario.

Vínculos relacionados
Gesto de deslizar rápidamente (ejemplo)
GestureRecognizer
SwipeGestureRecognizer
Localización de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

El marco de trabajo de localización integrado de .NET puede usarse para generar aplicaciones multilingües
multiplataforma con Xamarin.Forms.

Localización de cadenas e imágenes


El mecanismo integrado para la localización de aplicaciones .NET usa archivos RESX y las clases en los espacios de
nombres System.Resources y System.Globalization . Los archivos RESX que contienen las cadenas traducidas se
insertan en el ensamblado de Xamarin.Forms, junto con una clase generada por el compilador que proporciona
acceso fuertemente tipado a las traducciones. Después, se puede recuperar el texto traducido en el código.

Localización de derecha a izquierda


La dirección de flujo es la dirección en la que el ojo humano lee los elementos de la interfaz de usuario en la
página. La localización de derecha a izquierda agrega compatibilidad para la dirección de flujo de derecha a
izquierda para las aplicaciones de Xamarin.Forms.
Localización
11/07/2019 • 45 minutes to read • Edit Online

Descargar el ejemplo
Las aplicaciones de Xamarin.Forms se pueden localizar con archivos de recursos. NET.

Información general
El mecanismo integrado para la localización de aplicaciones .NET usa archivos RESX y las clases de los espacios de
nombres System.Resources y System.Globalization . Los archivos RESX que contienen las cadenas traducidas se
insertan en el ensamblado de Xamarin.Forms, junto con una clase generada por el compilador que proporciona
acceso fuertemente tipado a las traducciones. Después, se puede recuperar el texto traducido en el código.
Código de ejemplo
Hay dos ejemplos relacionados con este documento:
UsingResxLocalization es una demostración muy simple de los conceptos explicados. Los fragmentos de código
que se muestran a continuación son todos de este ejemplo.
TodoLocalized es una aplicación básica de trabajo que usa estas técnicas de localización.
No se recomiendan los proyectos compartidos
El ejemplo TodoLocalized incluye una demostración del proyecto compartido; sin embargo, debido a las
limitaciones del sistema de compilación, los archivos de recursos no obtienen un archivo .designer.cs generado, lo
cual interrumpe la capacidad de tener acceso a cadenas traducidas fuertemente tipadas en el código.
El resto de este documento se relaciona con los proyectos que utilizan la plantilla de biblioteca .NET Standard de
Xamarin.Forms.

Globalización del código de Xamarin.Forms


La globalización de una aplicación es el proceso de universalizarla. Esto significa escribir código que pueda
mostrar idiomas distintos.
Una de las partes principales de la globalización de una aplicación consiste en compilar la interfaz de usuario para
que no haya texto codificado de forma rígida. Alternativamente, nada de lo que se muestra al usuario debe
recuperarse de un conjunto de cadenas que se han traducido a su idioma elegido.
En este documento, examinaremos cómo usar los archivos RESX para almacenar estas cadenas y recuperarlas
para su visualización según la preferencia del usuario.
Los ejemplos de destino son los idiomas inglés, francés, español, alemán, chino, japonés, ruso y portugués (Brasil).
Las aplicaciones se pueden traducir a tantos idiomas como sea necesario.

NOTE
En la Plataforma universal de Windows, los archivos RESW deben usarse para la localización de notificaciones push, en lugar
de los archivos RESX. Para obtener más información, consulte Localización de UWP.

Adición de recursos
El primer paso para la globalización de una aplicación de biblioteca .NET Standard de Xamarin.Forms es agregar
los archivos de recursos RESX que se usarán para almacenar todo el texto que se usa en la aplicación. Necesitamos
agregar un archivo RESX que contiene el texto predeterminado y, a continuación, agregar los archivos RESX
adicionales para cada idioma que desee admitir.
Recurso de idioma base
El archivo de recursos base (RESX) contendrá las cadenas del idioma predeterminado (en los ejemplos se asume
que el idioma predeterminado es el inglés). Agregue el archivo al proyecto de código común de Xamarin.Forms,
haciendo clic con el botón derecho en el proyecto y eligiendo Agregar > Nuevo archivo...
Elija un nombre descriptivo, como AppResources, y pulse Aceptar.

Se agregarán dos archivos al proyecto:


El archivo AppResources.resx, en el cual se almacenan cadenas traducibles en formato XML.
El archivo AppResources.designer.cs, que declara una clase parcial para contener referencias a todos los
elementos creados en el archivo XML RESX.
El árbol de la solución mostrará los archivos según su relación. El archivo RESX debe editarse para agregar nuevas
cadenas traducibles; el archivo .designer.cs no se debe editar.

Vi si b i l i d a d d e l a c a d e n a

De forma predeterminada, cuando se generan referencias fuertemente tipadas para las cadenas, serán de tipo
internal para el ensamblado. Esto es así porque la herramienta de compilación predeterminada para los archivos
RESX genera el archivo .designer.cs con propiedades de tipo internal .
Seleccione el archivo AppResources.resx y observe el panel Propiedades para ver dónde está configurada esta
herramienta de compilación. La captura de pantalla siguiente muestra Herramienta personalizada:
ResXFileCodeGenerator.
Visual Studio
Visual Studio para Mac
Para hacer que las propiedades de la cadena fuertemente tipada sean public , debe cambiar manualmente la
configuración a Herramienta personalizada: PublicResXFileCodeGenerator, tal y como se muestra en la
captura de pantalla siguiente:
Visual Studio
Visual Studio para Mac

Este cambio es opcional y solo es necesario si desea hacer referencia a las cadenas localizadas en ensamblados
diferentes (por ejemplo, si coloca los archivos RESX en un ensamblado diferente en el código). El ejemplo de este
tema deja las cadenas de tipo internal porque ya están definidas en el mismo ensamblado de biblioteca estándar
.NET Standard de Xamarin.Forms en que se usan.
Basta con establecer la herramienta personalizada en el archivo RESX base tal como se muestra anteriormente; no
es necesario establecer ninguna herramienta de compilación en los archivos RESX específicos del idioma que se
describen en las secciones siguientes.
Ed i c i ó n d e l a r c h i v o R E SX

Por desgracia, no hay ningún editor de RESX integrado en Visual Studio para Mac. La adición de nuevas cadenas
traducibles requiere la adición de un elemento data XML nuevo para cada cadena. Cada elemento data puede
contener lo siguiente:
El atributo name (obligatorio) es la clave para esta cadena traducible. Debe ser un nombre de propiedad C#
válido, por lo que no se permiten espacios ni caracteres especiales.
El elemento value (obligatorio), que es la cadena real que se muestra en la aplicación.
El elemento comment (opcional) puede contener instrucciones para el traductor, con explicaciones sobre cómo
se utiliza esta cadena.
El atributo xml:space (opcional) para controlar cómo se conserva el espaciado en la cadena.

Algunos elementos data de ejemplo se muestran aquí:

<data name="NotesLabel" xml:space="preserve">


<value>Notes:</value>
<comment>label for input field</comment>
</data>
<data name="NotesPlaceholder" xml:space="preserve">
<value>eg. buy milk</value>
<comment>example input for notes field</comment>
</data>
<data name="AddButton" xml:space="preserve">
<value>Add new item</value>
</data>

A medida que se escribe la aplicación, cada fragmento de texto que se muestra al usuario debe agregarse al
archivo de recursos RESX base en un nuevo elemento data . Se recomienda incluir elementos comment , tantos
como sea posible, para garantizar una traducción de alta calidad.
NOTE
Visual Studio (incluida la edición gratuita Community) contiene un editor de RESX básico. Si tiene acceso a un equipo
Windows, esta puede ser una manera cómoda de agregar y editar las cadenas en archivos RESX.

Recursos específicos de idioma


Normalmente, la traducción real de las cadenas de texto predeterminadas no se llevará a cabo hasta que se hayan
escrito grandes fragmentos de la aplicación (en cuyo caso el archivo RESX predeterminado contendrá una gran
cantidad de cadenas). Sigue siendo una buena idea agregar los recursos específicos de idioma al principio del ciclo
de desarrollo, rellenándolos opcionalmente con texto traducido automáticamente para ayudar a probar el código
de localización.
Se agrega un archivo RESX adicional para cada idioma que se quiera admitir. Los archivos de recursos específicos
de idioma deben seguir una convención de nomenclatura específica: deben usar el mismo nombre de archivo que
los archivos de recursos base (por ejemplo, AppResources) seguido por un punto (.) y, a continuación, el código
de idioma. Estos son algunos ejemplos sencillos:
AppResources.fr.resx: traducciones en francés.
AppResources.es.resx: traducciones en español.
AppResources.de.resx: traducciones en alemán.
AppResources.ja.resx: traducciones en japonés.
AppResources.zh Hans.resx - traducciones en chino (simplificado).
AppResources.zh Hant.resx - traducciones en chino (tradicional).
AppResources.pt.resx: traducciones en portugués.
AppResources.pt-BR.resx: traducciones en portugués (de Brasil).
El patrón general consiste en usar códigos de idioma de dos letras, pero hay algunos ejemplos (por ejemplo, chino)
donde se usa un formato diferente, y otros ejemplos (por ejemplo, portugués de Brasil) donde se requiere un
identificador de configuración regional de cuatro caracteres.
Estos archivos de recursos específicos de idioma no requieren una clase parcial . designer.cs para poder agregarse
como archivos XML normales, con Acción de compilación: EmbeddedResource establecida. Esta captura de
pantalla muestra una solución que contiene los archivos de recursos específicos de idioma:

A medida que se desarrolla una aplicación y el archivo RESX base tiene texto agregado, debe enviarla a los
traductores, que traducirán cada elemento data y devolverán un archivo de recursos específicos de idioma (con la
convención de nomenclatura que se muestra) para incluir en la aplicación. A continuación, se muestran algunos
ejemplos "traducidos automáticamente":
AppResources.es.resx (español)

<data name="AddButton" xml:space="preserve">


<value>Escribir un artículo</value>
<comment>this string appears on a button to add a new item to the list</comment>
</data>
AppResources.ja.resx ( japonés)

<data name="AddButton" xml:space="preserve">


<value>新しい項目を追加</value>
<comment>this string appears on a button to add a new item to the list</comment>
</data>

AppResources.pt-BR.resx (portugués de Brasil)

<data name="AddButton" xml:space="preserve">


<value>adicionar novo item</value>
<comment>this string appears on a button to add a new item to the list</comment>
</data>

El traductor solo debe actualizar el elemento value ; el elemento comment no está pensado para traducirse.
Recuerde: al editar archivos XML, deben aplicarse secuencias de escape a los caracteres reservados, como < , > ,
& con &lt; , &gt; y &amp; si aparecen en value o comment .

Uso de recursos en el código


Las cadenas de los archivos de recursos RESX estarán disponibles para su uso en el código de la interfaz de
usuario utilizando la clase AppResources . El elemento name asignado a cada cadena en el archivo RESX pasa a ser
una propiedad de esa clase a la cual se puede hacer referencia en el código de Xamarin.Forms, tal como se muestra
a continuación:

var myLabel = new Label ();


var myEntry = new Entry ();
var myButton = new Button ();
// populate UI with translated text values from resources
myLabel.Text = AppResources.NotesLabel;
myEntry.Placeholder = AppResources.NotesPlaceholder;
myButton.Text = AppResources.AddButton;

La interfaz de usuario en iOS, Android y la Plataforma universal de Windows (UWP ) se representa del modo
esperado, salvo que ahora es posible traducir la aplicación en varios idiomas porque el texto que se carga desde un
recurso en vez de estar codificado de forma rígida. La siguiente captura de pantalla muestra la interfaz de usuario
en cada plataforma antes de la traducción:

Solución de problemas
Prueba de un lenguaje específico
Puede resultar complicado cambiar el simulador o un dispositivo a idiomas distintos, especialmente durante el
desarrollo, cuando se quieren probar rápidamente las distintas referencias culturales.
Puede forzar la carga de un idioma específico estableciendo el elemento Culture tal como se muestra en este
fragmento de código:
// force a specific culture, useful for quick testing
AppResources.Culture = new CultureInfo("fr-FR");

Este enfoque, establecer la referencia cultural directamente en la clase AppResources , también se puede usar para
implementar un selector de idioma dentro de la aplicación (en lugar de utilizar la configuración regional del
dispositivo).
Carga de recursos incrustados
El siguiente fragmento de código es útil cuando se intenta depurar problemas con los recursos incrustados (por
ejemplo, los archivos RESX). Agregue este código a la aplicación (al principio del ciclo de vida de aplicación) y
obtendrá una lista de todos los recursos incrustados en el ensamblado, que muestra el identificador del recurso
completo:

using System.Reflection;
// ...
// NOTE: use for debugging, not in released app code!
var assembly = typeof(EmbeddedImages).GetTypeInfo().Assembly; // "EmbeddedImages" should be a class in your
app
foreach (var res in assembly.GetManifestResourceNames())
{
System.Diagnostics.Debug.WriteLine("found resource: " + res);
}

En el archivo AppResources.Designer.cs (hacia la línea 33), se especifica el nombre del administrador de


recursos completo (por ejemplo, "UsingResxLocalization.Resx.AppResources" ) de forma similar al código siguiente:

System.Resources.ResourceManager temp =
new System.Resources.ResourceManager(
"UsingResxLocalization.Resx.AppResources",
typeof(AppResources).GetTypeInfo().Assembly);

Compruebe el resultado de la aplicación para los resultados del código de depuración que se muestra arriba,
para confirmar que se indican los recursos correctos (es decir, "UsingResxLocalization.Resx.AppResources" ).
Si no es así, la clase AppResources no podrá cargar sus recursos. Compruebe lo siguiente para resolver los
problemas que surgen cuando no se pueden encontrar los recursos:
El espacio de nombres predeterminado para el proyecto coincide con el espacio de nombres raíz en el archivo
AppResources.Designer.cs.
Si el archivo AppResources.resx se encuentra en un subdirectorio, el nombre del subdirectorio debe formar
parte del espacio de nombres y debe formar parte del identificador de recursos.
El archivo AppResources.resx tiene la Acción de compilación: EmbeddedResource.
La opción Project Options > Source Code > .NET Naming Policies > Use Visual Studio-style resources
names (Opciones del proyecto > Código fuente > Directivas de nomenclatura de .NET > Usar nombres de
recursos de estilo Visual Studio) está activada. Puede desactivarla, si lo prefiere, pero los espacios de nombres
que se usan al hacer referencia a los recursos RESX tendrán que actualizarse en toda la aplicación.
No funciona en modo de DEPURACIÓN (solo Android)
Si las cadenas traducidas funcionan en las compilaciones de Android PUBLICADAS pero no durante la
depuración, haga clic con el botón derecho en Android Project (Proyecto de Android), seleccione Options >
Build > Android Build (Opciones > Compilar > Compilación de Android ) y asegúrese de que la opción Fast
assembly deployment (Implementación de ensamblado rápido) NO esté activada. Esta opción provoca
problemas con la carga de recursos y no debe usarse si se están probando aplicaciones localizadas.
Visualización del idioma correcto
Hasta ahora hemos examinado cómo escribir código para que se puedan proporcionar traducciones, pero no para
hacer que aparezcan en realidad. El código de Xamarin.Forms puede aprovechar recursos de .NET para cargar las
traducciones en el idioma correcto, pero es necesario consultar el sistema operativo en cada plataforma para
determinar el idioma que ha seleccionado el usuario.
Dado que se requiere algún código específico de la plataforma para obtener la preferencia de idioma del usuario,
utilice un servicio de dependencia para exponer esta información en la aplicación de Xamarin.Forms e
implementarla para cada plataforma.
En primer lugar, defina una interfaz para exponer la referencia cultural preferida del usuario, de forma similar al
código siguiente:

public interface ILocalize


{
CultureInfo GetCurrentCultureInfo ();
void SetLocale (CultureInfo ci);
}

En segundo lugar, utilice DependencyService en la clase App de Xamarin.Forms para llamar a la interfaz y
establecer la referencia cultural de nuestros recursos RESX en el valor correcto. Tenga en cuenta que no es
necesario establecer manualmente este valor para la Plataforma universal de Windows, ya que el marco de trabajo
de los recursos reconoce automáticamente el idioma seleccionado en estas plataformas.

if (Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.Android)


{
var ci = DependencyService.Get<ILocalize>().GetCurrentCultureInfo();
Resx.AppResources.Culture = ci; // set the RESX for resource localization
DependencyService.Get<ILocalize>().SetLocale(ci); // set the Thread for locale-aware methods
}

El recurso Culture debe establecerse cuando la aplicación se carga por primera vez, para que se utilicen las
cadenas de idioma correctas. Opcionalmente, puede actualizar este valor conforme a los eventos específicos de la
plataforma que se generen en iOS o Android, si el usuario actualiza sus preferencias de idioma mientras se ejecuta
la aplicación.
Las implementaciones para la interfaz ILocalize se muestran en la sección siguiente, Código específico de la
plataforma. Estas implementaciones aprovechan esta clase auxiliar PlatformCulture :
public class PlatformCulture
{
public PlatformCulture (string platformCultureString)
{
if (String.IsNullOrEmpty(platformCultureString))
{
throw new ArgumentException("Expected culture identifier", "platformCultureString"); // in C# 6
use nameof(platformCultureString)
}
PlatformString = platformCultureString.Replace("_", "-"); // .NET expects dash, not underscore
var dashIndex = PlatformString.IndexOf("-", StringComparison.Ordinal);
if (dashIndex > 0)
{
var parts = PlatformString.Split('-');
LanguageCode = parts[0];
LocaleCode = parts[1];
}
else
{
LanguageCode = PlatformString;
LocaleCode = "";
}
}
public string PlatformString { get; private set; }
public string LanguageCode { get; private set; }
public string LocaleCode { get; private set; }
public override string ToString()
{
return PlatformString;
}
}

Código específico de la plataforma


El código para detectar el idioma que se mostrará debe ser específico de la plataforma, porque iOS, Android y
UWP exponen esta información de forma ligeramente distinta. El código para el servicio de dependencia
ILocalize se proporciona a continuación para cada plataforma, junto con los requisitos específicos de la
plataforma adicionales, para garantizar que el texto localizado se representa correctamente.
El código específico de la plataforma también debe controlar los casos donde el sistema operativo permite al
usuario configurar un identificador de configuración regional que no es compatible con clase CultureInfo de
.NET. En estos casos, se debe escribir código personalizado para detectar configuraciones regionales no
compatibles y sustituir la mejor configuración regional compatible con .NET.
Proyecto de aplicación de iOS
Los usuarios de iOS seleccionan su idioma preferido independientemente de la referencia cultural que da formato
a la fecha y hora. Para cargar los recursos correctos para localizar una aplicación de Xamarin.Forms, basta con
consultar la matriz NSLocale.PreferredLanguages para el primer elemento.
La siguiente implementación del servicio de dependencia ILocalize debe colocarse en el proyecto de aplicación
de iOS. Dado que iOS usa caracteres de subrayado en lugar de guiones (que es la representación estándar de
.NET) el código reemplaza el carácter de subrayado antes de crear instancias de la clase CultureInfo :

[assembly:Dependency(typeof(UsingResxLocalization.iOS.Localize))]

namespace UsingResxLocalization.iOS
{
public class Localize : UsingResxLocalization.ILocalize
{
public void SetLocale (CultureInfo ci)
{
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
}

public CultureInfo GetCurrentCultureInfo ()


{
var netLanguage = "en";
if (NSLocale.PreferredLanguages.Length > 0)
{
var pref = NSLocale.PreferredLanguages [0];
netLanguage = iOSToDotnetLanguage(pref);
}
// this gets called a lot - try/catch can be expensive so consider caching or something
System.Globalization.CultureInfo ci = null;
try
{
ci = new System.Globalization.CultureInfo(netLanguage);
}
catch (CultureNotFoundException e1)
{
// iOS locale not valid .NET culture (eg. "en-ES" : English in Spain)
// fallback to first characters, in this case "en"
try
{
var fallback = ToDotnetFallbackLanguage(new PlatformCulture(netLanguage));
ci = new System.Globalization.CultureInfo(fallback);
}
catch (CultureNotFoundException e2)
{
// iOS language not valid .NET culture, falling back to English
ci = new System.Globalization.CultureInfo("en");
}
}
return ci;
}

string iOSToDotnetLanguage(string iOSLanguage)


{
// .NET cultures don't support underscores
string netLanguage = iOSLanguage.Replace("_", "-");

//certain languages need to be converted to CultureInfo equivalent


switch (iOSLanguage)
{
case "ms-MY": // "Malaysian (Malaysia)" not supported .NET culture
case "ms-SG": // "Malaysian (Singapore)" not supported .NET culture
netLanguage = "ms"; // closest supported
break;
case "gsw-CH": // "Schwiizertüütsch (Swiss German)" not supported .NET culture
netLanguage = "de-CH"; // closest supported
break;
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
}
return netLanguage;
}

string ToDotnetFallbackLanguage (PlatformCulture platCulture)


{
var netLanguage = platCulture.LanguageCode; // use the first part of the identifier (two chars,
usually);
switch (platCulture.LanguageCode)
{
case "pt":
netLanguage = "pt-PT"; // fallback to Portuguese (Portugal)
break;
case "gsw":
netLanguage = "de-CH"; // equivalent to German (Switzerland) for this app
break;
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
// ONLY use cultures that have been tested and known to work
}
return netLanguage;
}
}
}

NOTE
Los bloques try/catch en el método GetCurrentCultureInfo imitan el comportamiento de reserva que se utiliza
normalmente con los especificadores de configuración regional: si no se encuentra la coincidencia exacta, debe buscarse una
coincidencia próxima basándose solo en el idioma (primer bloque de caracteres en la configuración regional).
En el caso de Xamarin.Forms, algunas configuraciones regionales son válidas en iOS, pero no corresponden a una
CultureInfo válida en .NET; el código anterior intenta controlar esta cuestión.

Por ejemplo, la pantalla de iOS Ajustes > Idioma general & Región le permite establecer el Idioma del teléfono como
inglés pero la Región como España, lo cual da como resultado una cadena de configuración regional "en-ES" . Cuando
falla la creación de CultureInfo , el código vuelve a utilizar solo las dos primeras letras para seleccionar el idioma que se
mostrará.
Los desarrolladores pueden modificar los métodos iOSToDotnetLanguage y ToDotnetFallbackLanguage para controlar los
casos específicos necesarios para sus idiomas admitidos.

Algunos elementos de la interfaz de usuario definidos por el sistema los traduce automáticamente iOS, como el
botón Listo del control Picker . Para obligar a iOS a traducir estos elementos, se debe indicar qué idiomas se
admiten en el archivo Info.plist. Puede agregar estos valores a través de Info.plist > Origen, como se muestra
aquí:

De forma alternativa, abra el archivo Info.plist en un editor XML y edite los valores directamente:

<key>CFBundleLocalizations</key>
<array>
<string>de</string>
<string>es</string>
<string>fr</string>
<string>ja</string>
<string>pt</string> <!-- Brazil -->
<string>pt-PT</string> <!-- Portugal -->
<string>ru</string>
<string>zh-Hans</string>
<string>zh-Hant</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
Una vez que haya implementado el servicio de dependencia y haya actualizado Info.plist, la aplicación de iOS
podrá mostrar el texto traducido.

NOTE
Tenga en cuenta que Apple trata el idioma portugués de un modo ligeramente distinto de lo que cabría esperar. En su
documentación podemos leer una información relacionada, que indica la necesidad de utilizar pt como identificador de
idioma para el portugués que se utiliza en Brasil, y pt-PT como identificador de idioma para el portugués que se utiliza en
Portugal. Esto significa que cuando se elige el portugués en una configuración regional no estándar, el idioma de reserva
será el portugués de Brasil en iOS, a menos que se escriba código para cambiar este comportamiento (como
ToDotnetFallbackLanguage en el ejemplo anterior).

Para obtener más información sobre la localización de iOS, consulte Localización de iOS.
Proyecto de aplicación de Android
Android expone la configuración regional seleccionada actualmente a través de Java.Util.Locale.Default y
también usa un separador de carácter de subrayado en lugar de un guion (que será sustituido por el código
siguiente). Agregue esta implementación de servicio de dependencia al proyecto de aplicación de Android:

[assembly:Dependency(typeof(UsingResxLocalization.Android.Localize))]

namespace UsingResxLocalization.Android
{
public class Localize : UsingResxLocalization.ILocalize
{
public void SetLocale(CultureInfo ci)
{
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
}
public CultureInfo GetCurrentCultureInfo()
{
var netLanguage = "en";
var androidLocale = Java.Util.Locale.Default;
netLanguage = AndroidToDotnetLanguage(androidLocale.ToString().Replace("_", "-"));
// this gets called a lot - try/catch can be expensive so consider caching or something
System.Globalization.CultureInfo ci = null;
try
{
ci = new System.Globalization.CultureInfo(netLanguage);
}
catch (CultureNotFoundException e1)
{
// iOS locale not valid .NET culture (eg. "en-ES" : English in Spain)
// fallback to first characters, in this case "en"
try
{
var fallback = ToDotnetFallbackLanguage(new PlatformCulture(netLanguage));
ci = new System.Globalization.CultureInfo(fallback);
}
catch (CultureNotFoundException e2)
{
// iOS language not valid .NET culture, falling back to English
ci = new System.Globalization.CultureInfo("en");
}
}
return ci;
}
string AndroidToDotnetLanguage(string androidLanguage)
{
var netLanguage = androidLanguage;
//certain languages need to be converted to CultureInfo equivalent
switch (androidLanguage)
{
{
case "ms-BN": // "Malaysian (Brunei)" not supported .NET culture
case "ms-MY": // "Malaysian (Malaysia)" not supported .NET culture
case "ms-SG": // "Malaysian (Singapore)" not supported .NET culture
netLanguage = "ms"; // closest supported
break;
case "in-ID": // "Indonesian (Indonesia)" has different code in .NET
netLanguage = "id-ID"; // correct code for .NET
break;
case "gsw-CH": // "Schwiizertüütsch (Swiss German)" not supported .NET culture
netLanguage = "de-CH"; // closest supported
break;
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
}
return netLanguage;
}
string ToDotnetFallbackLanguage(PlatformCulture platCulture)
{
var netLanguage = platCulture.LanguageCode; // use the first part of the identifier (two chars,
usually);
switch (platCulture.LanguageCode)
{
case "gsw":
netLanguage = "de-CH"; // equivalent to German (Switzerland) for this app
break;
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
}
return netLanguage;
}
}
}

NOTE
Los bloques try/catch en el método GetCurrentCultureInfo imitan el comportamiento de reserva que se utiliza
normalmente con los especificadores de configuración regional: si no se encuentra la coincidencia exacta, debe buscarse una
coincidencia próxima basándose solo en el idioma (primer bloque de caracteres en la configuración regional).
En el caso de Xamarin.Forms, algunas configuraciones regionales son válidas en Android, pero no corresponden a una
CultureInfo válida en .NET; el código anterior intenta controlar esta cuestión.

Los desarrolladores pueden modificar los métodos iOSToDotnetLanguage y ToDotnetFallbackLanguage para controlar los
casos específicos necesarios para sus idiomas admitidos.

Una vez que este código se haya agregado al proyecto de aplicación de Android, podrá mostrar automáticamente
las cadenas traducidas.

NOTE
ADVERTENCIA: Si las cadenas traducidas funcionan en las compilaciones de Android PUBLICADAS pero no durante la
depuración, haga clic con el botón derecho en Android Project (Proyecto de Android), seleccione Options > Build >
Android Build (Opciones > Compilar > Compilación de Android ) y asegúrese de que la opción Fast assembly
deployment (Implementación de ensamblado rápido) NO esté activada. Esta opción provoca problemas con la carga de
recursos y no debe usarse si se están probando aplicaciones localizadas.

Para obtener más información sobre la localización de Android, consulte Localización de Android.
Plataforma universal de Windows
Los proyectos de la Plataforma universal de Windows (UWP ) no requieren el servicio de dependencia. En su lugar,
esta plataforma establece automáticamente la referencia cultural del recurso correctamente.
A sse m b l y I n fo .c s

Expanda el nodo Propiedades en el proyecto de biblioteca de .NET Standard y haga doble clic en el archivo
AssemblyInfo.cs. Agregue la siguiente línea al archivo para establecer el lenguaje de ensamblado de recursos
independiente del idioma como inglés:

[assembly: NeutralResourcesLanguage("en")]

Esto informa al administrador de recursos acerca de la referencia cultural predeterminada de la aplicación y, por lo
tanto, garantiza que las cadenas definidas en el archivo RESX independiente de idioma (AppResources.resx) se
mostrarán cuando la aplicación se ejecute en una de las configuraciones regionales del inglés.
Ejemplo
Después de actualizar los proyectos específicos de plataforma, como se muestra anteriormente, y volver a compilar
la aplicación con archivos RESX traducidos, las traducciones actualizadas estarán disponibles en cada aplicación.
Esta es una captura de pantalla del código de ejemplo traducido a chino simplificado:

Para obtener más información sobre la localización de la plataforma universal de Windows, consulte Localización
de UWP.

Localización de XAML
Al compilar una interfaz de usuario de Xamarin.Forms en XAML, el marcado tendrá un aspecto similar al siguiente,
con cadenas insertadas directamente en el código XML:

<Label Text="Notes:" />


<Entry Placeholder="eg. buy milk" />
<Button Text="Add to list" />

El escenario ideal sería poder traducir los controles de interfaz de usuario directamente en XAML, lo cual puede
hacerse mediante la creación de una extensión de marcado. El código para una extensión de marcado que expone
los recursos RESX en XAML se muestra a continuación. Esta clase se debe agregar al código común de
Xamarin.Forms (junto con las páginas XAML ):
using System;
using System.Globalization;
using System.Reflection;
using System.Resources;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace UsingResxLocalization
{
// You exclude the 'Extension' suffix when using in XAML
[ContentProperty("Text")]
public class TranslateExtension : IMarkupExtension
{
readonly CultureInfo ci = null;
const string ResourceId = "UsingResxLocalization.Resx.AppResources";

static readonly Lazy<ResourceManager> ResMgr = new Lazy<ResourceManager>(


() => new ResourceManager(ResourceId,
IntrospectionExtensions.GetTypeInfo(typeof(TranslateExtension)).Assembly));

public string Text { get; set; }

public TranslateExtension()
{
if (Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.Android)
{
ci = DependencyService.Get<ILocalize>().GetCurrentCultureInfo();
}
}

public object ProvideValue(IServiceProvider serviceProvider)


{
if (Text == null)
return string.Empty;

var translation = ResMgr.Value.GetString(Text, ci);


if (translation == null)
{
#if DEBUG
throw new ArgumentException(
string.Format("Key '{0}' was not found in resources '{1}' for culture '{2}'.", Text,
ResourceId, ci.Name),
"Text");
#else
translation = Text; // HACK: returns the key, which GETS DISPLAYED TO THE USER
#endif
}
return translation;
}
}
}

Las viñetas siguientes explican los elementos importantes en el código anterior:


La clase se denomina TranslateExtension pero, por convención, podemos referirnos a ella como Translate en
nuestro marcado.
La clase implementa IMarkupExtension , que Xamarin.Forms requiere para que funcione.
"UsingResxLocalization.Resx.AppResources" es el identificador de recurso para nuestros recursos RESX. Consta
de nuestro espacio de nombres predeterminado, la carpeta donde se encuentran los archivos de recursos y el
nombre del archivo RESX predeterminado.
La clase ResourceManager se crea mediante
IntrospectionExtensions.GetTypeInfo(typeof(TranslateExtension)).Assembly) , para determinar el ensamblado
actual desde el cual se cargan los recursos, y se almacena en memoria caché en el campo ResMgr estático. Se
crea como tipo Lazy , por lo que su creación se aplaza hasta que se usa en primer lugar en el método
ProvideValue .
ci usa el servicio de dependencia para obtener el idioma elegido del usuario desde el sistema operativo
nativo.
GetString es el método que recupera la cadena traducida real de los archivos de recursos. En la Plataforma
universal de Windows, ci será un valor nulo porque la interfaz ILocalize no está implementada en estas
plataformas. Esto equivale a llamar al método GetString solo con el primer parámetro. De forma alternativa, el
marco de trabajo de los recursos reconocerá automáticamente la configuración regional y recuperará la cadena
traducida desde el archivo RESX correspondiente.
Se ha incluido el control de errores para ayudar a depurar los recursos que faltan iniciando una excepción (solo
en modo DEBUG ).

El fragmento de código XAML siguiente muestra cómo usar la extensión de marcado. Su funcionamiento requiere
dos pasos:
1. Declarar el espacio de nombres xmlns:i18n personalizado en el nodo raíz. namespace y assembly deben tener
exactamente la misma configuración del proyecto; en este ejemplo es idéntica, pero podría ser diferente en su
proyecto específico.
2. Usar la sintaxis {Binding} en los atributos que normalmente deben contener el texto para llamar a la extensión
de marcado Translate . La clave de recurso es el único parámetro necesario.

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="UsingResxLocalization.FirstPageXaml"
xmlns:i18n="clr-namespace:UsingResxLocalization;assembly=UsingResxLocalization">
<StackLayout Padding="0, 20, 0, 0">
<Label Text="{i18n:Translate NotesLabel}" />
<Entry Placeholder="{i18n:Translate NotesPlaceholder}" />
<Button Text="{i18n:Translate AddButton}" />
</StackLayout>
</ContentPage>

La siguiente sintaxis más detallada también es válida para la extensión de marcado:

<Button Text="{i18n:TranslateExtension Text=AddButton}" />

Localización de elementos específicos de plataforma


Aunque podemos gestionar la traducción de la interfaz de usuario en el código de Xamarin.Forms, algunos
elementos deben realizarse en cada proyecto específico de plataforma. Esta sección explica cómo localizar los
elementos siguientes:
Nombre de la aplicación
Imágenes
El proyecto de ejemplo incluye una imagen localizada denominada flag.png, a la cual se hace referencia en C# del
modo siguiente:
var flag = new Image();
switch (Device.RuntimePlatform)
{
case Device.iOS:
case Device.Android:
flag.Source = ImageSource.FromFile("flag.png");
break;
case Device.UWP:
flag.Source = ImageSource.FromFile("Assets/Images/flag.png");
break;
}

También se hace referencia a la imagen de la bandera en el XAML, de este modo:

<Image>
<Image.Source>
<OnPlatform x:TypeArguments="ImageSource">
<On Platform="iOS, Android" Value="flag.png" />
<On Platform="UWP" Value="Assets/Images/flag.png" />
</OnPlatform>
</Image.Source>
</Image>

Todas las plataformas resolverán automáticamente las referencias de imágenes como éstas en las versiones
localizadas de las imágenes, siempre y cuando se implementen las estructuras de proyecto que se explican a
continuación.
Proyecto de aplicación de iOS
iOS utiliza una denominación estándar, Proyectos de localización, o directorios .lproj que contienen recursos de
cadena e imagen. Estos directorios pueden contener las versiones localizadas de imágenes que se usan en la
aplicación y también el archivo InfoPlist.strings que se puede usar para localizar el nombre de la aplicación. Para
obtener más información sobre la localización de iOS, consulte Localización de iOS.
Imágenes
Esta captura de pantalla muestra la aplicación de ejemplo de iOS con directorios .lproj específicos del idioma. El
directorio del idioma español, denominado es.lproj, contiene las versiones localizadas de la imagen
predeterminada, así como flag.png:

Cada directorio de idioma contiene una copia de flag.png, localizada para dicho idioma. Si no se proporciona
ninguna imagen, el sistema operativo será la imagen del directorio de idioma predeterminado. Para obtener
compatibilidad total con la pantalla Retina, debe proporcionar copias @2x y @3x de cada imagen.
Nombre de la aplicación
El contenido de InfoPlist.strings es simplemente un único par clave-valor para configurar el nombre de la
aplicación:

"CFBundleDisplayName" = "ResxEspañol";

Cuando se ejecuta la aplicación, aparecen localizados el nombre de la aplicación y la imagen:

Proyecto de aplicación de Android


Android sigue un esquema diferente para almacenar imágenes localizadas, con directorios drawable y archivos
Strings distintos con un sufijo de código de idioma. Cuando se requiere un código de configuración regional de
cuatro letras (por ejemplo, zh-TW o pt-BR ), tenga en cuenta que Android requiere una letra r adicional después del
guion que precede al código de configuración regional (por ejemplo, zh-rTW o pt-rBR ). Para obtener más
información sobre la localización de Android, consulte Localización de Android.
Imágenes
Esta captura de pantalla muestra el ejemplo de Android localizado con algunos directorios drawable y archivos
Strings localizados:

Tenga en cuenta que Android no usa los códigos zh-Hans y zh-Hant para chino simplificado y tradicional; en su
lugar, solo admite los códigos específicos de país zh-CN y zh-TW.
Para admitir imágenes de una resolución distinta para las pantallas de alta densidad, cree carpetas de idioma
adicionales con sufijos -*dpi , como drawables-es-mdpi, drawables-es-xdpi, drawables-es-xxdpi, y así
sucesivamente. Consulte Providing Alternative Android Resources (Proporcionar recursos de Android alternativos)
para obtener más información.
Nombre de la aplicación
El contenido de Strings.xml es simplemente un único par clave-valor para configurar el nombre de la aplicación:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ResxEspañol</string>
</resources>

Actualice MainActivity.cs en el proyecto de aplicación de Android para que Label haga referencia a las cadenas
XML.

[Activity (Label = "@string/app_name", MainLauncher = true,


ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]

La aplicación ahora localiza la imagen y el nombre de la aplicación. Esta es una captura de pantalla del resultado
(en español):

Proyectos de aplicación de la Plataforma universal de Windows


La Plataforma universal de Windows posee una infraestructura de recursos que simplifica la localización de las
imágenes y el nombre de la aplicación. Para obtener más información sobre la localización de la plataforma
universal de Windows, consulte Localización de UWP.
Imágenes
Las imágenes se pueden localizar colocándolas en una carpeta específica de recursos, como se muestra en la
captura de pantalla siguiente:

En tiempo de ejecución, la infraestructura de recursos de Windows seleccionará la imagen adecuada según la


configuración regional del usuario.
Resumen
Las aplicaciones de Xamarin.Forms pueden localizarse con clases de globalización de .NET y archivos RESX.
Además de requerirse una pequeña cantidad de código específico de plataforma para detectar el idioma que
prefiere el usuario, la mayoría de los esfuerzos de localización se centran en el código común.
Por lo general, las imágenes se tratan específicamente para la plataforma, de cara a aprovechar la compatibilidad
con varias resoluciones proporcionada en iOS y Android.

Vínculos relacionados
Ejemplo de localización de RESX
Aplicación de ejemplo TodoLocalized
Localización multiplataforma
Localización de iOS
Localización de Android
Localización de UWP
Utilizar la clase CultureInfo (MSDN )
Buscar y utilizar recursos para una referencia cultural específica (MSDN )
Localización de derecha a izquierda
11/07/2019 • 9 minutes to read • Edit Online

Descargar el ejemplo
La localización de derecha a izquierda agrega compatibilidad para la dirección de flujo de derecha a izquierda
para las aplicaciones de Xamarin.Forms.

NOTE
La localización de derecha a izquierda requiere el uso de iOS 9 o versiones posteriores; en Android, la API 17 o versiones
posteriores.

La dirección de flujo es la dirección en la que el ojo humano lee los elementos de la interfaz de usuario en la
página. Algunos lenguajes, como el árabe y hebreo, requieren que los elementos de interfaz de usuario se
distribuyan en una dirección de flujo de derecha a izquierda. Esto también puede lograrse estableciendo la
propiedad VisualElement.FlowDirection . Esta propiedad obtiene o establece la dirección en que fluyen los
elementos de interfaz de usuario dentro de cualquier elemento primario que controle su diseño, y debe
establecerse en uno de los valores de enumeración de FlowDirection :
LeftToRight
RightToLeft
MatchParent

Al establecer la propiedad FlowDirection como RightToLeft en un elemento, generalmente se establece la


alineación a la derecha, el orden de lectura de derecha a izquierda y el diseño del control para que fluya de
derecha a izquierda:

TIP
Solo debe establecerse la propiedad FlowDirection en el diseño inicial. El cambio de este valor en tiempo de ejecución
hace que el proceso de diseño sea difícil y que ello afecte al rendimiento.
El valor de la propiedad FlowDirection predeterminado para un elemento sin un elemento primario es
LeftToRight , mientras que el valor predeterminado de FlowDirection para un elemento con un elemento
primario es MatchParent . Por lo tanto, un elemento hereda el valor de la propiedad FlowDirection de su elemento
primario en el árbol visual, y cualquier elemento puede invalidar el valor que obtiene de su elemento primario.

TIP
Al localizar una aplicación para idiomas con flujo de derecha a izquierda, establezca la propiedad FlowDirection en una
página o un diseño raíz. Esto hace que todos los elementos contenidos dentro de la página, o el diseño raíz, respondan
adecuadamente a la dirección del flujo.

Respetar la dirección del flujo del dispositivo


Respetar la dirección del flujo del dispositivo según el idioma y la región seleccionados es una opción explícita del
desarrollador y no se realiza automáticamente. Se consigue estableciendo la propiedad FlowDirection de una
página, o diseño raíz, en el valor static Device.FlowDirection :

<ContentPage ... FlowDirection="{x:Static Device.FlowDirection}"> />

this.FlowDirection = Device.FlowDirection;

Posteriormente, todos los elementos secundarios de la página, o el diseño raíz, de forma predeterminada
heredarán el valor Device.FlowDirection .

Configuración de la plataforma
Se requiere una configuración de la plataforma específica para habilitar las configuraciones regionales de derecha
a izquierda.
iOS
La configuración regional de derecha a izquierda necesaria debe agregarse como un idioma compatible con los
elementos de matriz para la clave CFBundleLocalizations Info.plist. En el ejemplo siguiente se muestra el idioma
árabe agregado a la matriz para la clave CFBundleLocalizations :

<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>ar</string>
</array>

Para obtener más información, consulte Localization Basics in iOS (Introducción a la localización en iOS ).
A continuación, se puede probar la localización de derecha a izquierda cambiando el idioma y la región del
dispositivo o el simulador a una configuración regional de derecha a izquierda que se haya especificado en
Info.plist.
WARNING
Tenga en cuenta que al cambiar el idioma y la región para una configuración regional de derecha a izquierda en iOS, todas
las vistas DatePicker emitirán una excepción si no se incluyen los recursos necesarios para la configuración regional. Por
ejemplo, al probar una aplicación en árabe que tenga un DatePicker , asegúrese de que mideast (Oriente Medio) esté
seleccionado en la sección Internationalization (Internacionalización) del panel iOS Build (Compilación de iOS).

Android
El archivo AndroidManifest.xml de la aplicación debe actualizarse para que el nodo <uses-sdk> establezca el
atributo android:minSdkVersion en 17 y el nodo <application> establezca el atributo android:supportsRtl en
true :

<?xml version="1.0" encoding="utf-8"?>


<manifest ... >
<uses-sdk android:minSdkVersion="17" ... />
<application ... android:supportsRtl="true">
</application>
</manifest>

A continuación, se puede probar la localización de derecha a izquierda cambiando el dispositivo o el emulador


para que use el idioma de derecha a izquierda o habilitando la opción Forzar dirección diseño RTL en Ajustes >
Opciones del desarrollador.
Plataforma universal de Windows (UWP)
Se deben especificar los recursos de idioma necesarios en el nodo <Resources> del archivo
Package.appxmanifest. En el ejemplo siguiente se muestra el idioma árabe agregado al nodo <Resources> :

<Resources>
<Resource Language="x-generate"/>
<Resource Language="en" />
<Resource Language="ar" />
</Resources>

Además, UWP requiere que la referencia cultural predeterminada de la aplicación se defina explícitamente en la
biblioteca .NET Standard. Esto puede realizarse estableciendo el atributo NeutralResourcesLanguage en
AssemblyInfo.cs , o en otra clase, en la referencia cultural predeterminada:

using System.Resources;

[assembly: NeutralResourcesLanguage("en")]

A continuación, se puede probar la localización de derecha a izquierda cambiando el idioma y la región del
dispositivo a la configuración regional de derecha a izquierda adecuada.

Limitaciones
La localización de derecha a izquierda de Xamarin.Forms actualmente tiene una serie de limitaciones:
El control de la ubicación del botón NavigationPage , la ubicación de elementos de barra de herramientas y la
animación de transición lo lleva a cabo la configuración regional del dispositivo, en lugar de la propiedad
FlowDirection .
La dirección de deslizamiento de CarouselPage no realiza la acción.
El contenido visual de Image no se invierte.
El control de la orientación de DisplayAlert y DisplayActionSheet lo lleva a cabo la configuración regional del
dispositivo, en lugar de la propiedad FlowDirection .
El contenido de WebView no respeta la propiedad FlowDirection .
Debe agregarse una propiedad TextDirection , para controlar la alineación del texto.
iOS
El control de la orientación de Stepper lo lleva a cabo la configuración regional del dispositivo, en lugar de la
propiedad FlowDirection .
El control de la alineación del texto de EntryCell lo lleva a cabo la configuración regional del dispositivo, en
lugar de la propiedad FlowDirection .
La alineación y los gestos de ContextActions no se invierten.
Android
El control de la orientación de SearchBar lo lleva a cabo la configuración regional del dispositivo, en lugar de la
propiedad FlowDirection .
El control de la colocación de ContextActions lo lleva a cabo la configuración regional del dispositivo, en lugar
de la propiedad FlowDirection .
UWP
El control de la alineación del texto de Editor lo lleva a cabo la configuración regional del dispositivo, en lugar
de la propiedad FlowDirection .
Los elementos secundarios MasterDetailPage no heredan la propiedad FlowDirection .
El control de la alineación del texto de ContextActions lo lleva a cabo la configuración regional del dispositivo,
en lugar de la propiedad FlowDirection .

Vídeo de Xamarin.University sobre la compatibilidad de idiomas de


derecha a izquierda
Vídeo de compatibilidad de derecha a izquierda de Xamarin.Forms 3.0

Vínculos relacionados
Aplicación de ejemplo TodoLocalizedRTL
MessagingCenter de Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

Descargar el ejemplo
Xamarin.Forms incluye un servicio de mensajería simple para enviar y recibir mensajes.

Información general
MessagingCenter de Xamarin.Forms permite que los modelos de vista y otros componentes se comuniquen sin
tener que saber nada sobre los demás, salvo un sencillo contrato de mensajería.

Funcionamiento de MessagingCenter
MessagingCenter tiene dos partes:
Subscribe: escucha mensajes con una determinada firma y realiza alguna acción cuando se reciben. Varios
suscriptores pueden estar escuchando el mismo mensaje.
Send: publica un mensaje para que los agentes de escucha actúen en consecuencia. Si no hay ningún agente
de escucha suscrito, se omite el mensaje.
MessagingCenter es una clase estática con métodos Subscribe y Send que se usan en toda la solución.
Los mensajes tienen un parámetro de cadena message que se usa como forma de dirigir mensajes. Los métodos
Subscribe y Send usan parámetros genéricos para un mayor control sobre la entrega de los mensajes: dos
mensajes con el mismo texto message pero argumentos de tipo genérico diferentes no se entregan al mismo
suscriptor.
La API de MessagingCenter es simple:
Subscribe<TSender> (object subscriber, string message, Action<TSender> callback, TSender source = null)
Subscribe<TSender, TArgs> (object subscriber, string message, Action<TSender, TArgs> callback, TSender
source = null)
Send<TSender> (TSender sender, string message)
Send<TSender, TArgs> (TSender sender, string message, TArgs args)
Unsubscribe<TSender, TArgs> (object subscriber, string message)
Unsubscribe<TSender> (object subscriber, string message)

Estos métodos se explican a continuación.

Uso de MessagingCenter
Los mensajes se pueden enviar como resultado de la interacción del usuario (por ejemplo, un clic en un botón), un
evento del sistema (por ejemplo, controles que cambian el estado) o algún otro incidente (como la finalización de
una descarga asincrónica). Los suscriptores pueden escuchar para cambiar la apariencia de la interfaz de usuario,
guardar datos o desencadenar alguna otra operación.
Para obtener más información sobre el uso de la clase MessagingCenter , vea Comunicación entre componentes
débilmente acoplados.
Mensaje de cadena simple
El mensaje más simple contiene solo una cadena en el parámetro message . A continuación se muestra un método
Subscribe que escucha un mensaje de cadena simple: observe el tipo genérico que especifica que se espera que
el remitente sea de tipo MainPage . Las clases de la solución pueden suscribirse al mensaje con esta sintaxis:

MessagingCenter.Subscribe<MainPage> (this, "Hi", (sender) => {


// do something whenever the "Hi" message is sent
});

En la clase MainPage , el siguiente código envía el mensaje. El parámetro this es una instancia de MainPage .

MessagingCenter.Send<MainPage> (this, "Hi");

La cadena no cambia: indica el tipo de mensaje y se usa para determinar a qué suscriptores se va a notificar. Este
tipo de mensaje se usa para indicar que se ha producido un evento, como "carga completada", donde no se
requiere ninguna información adicional.
Pasar un argumento
Para pasar un argumento con el mensaje, especifique el argumento Type en los argumentos genéricos Subscribe
y en la firma Action.

MessagingCenter.Subscribe<MainPage, string> (this, "Hi", (sender, arg) => {


// do something whenever the "Hi" message is sent
// using the 'arg' parameter which is a string
});

Para enviar el mensaje con argumento, incluya el parámetro genérico Type y el valor del argumento en la llamada
al método Send .

MessagingCenter.Send<MainPage, string> (this, "Hi", "John");

Este ejemplo sencillo usa un argumento string , pero se puede pasar cualquier objeto de C#.
Cancelar suscripción
Un objeto puede cancelar la suscripción de una firma de mensaje para que no se entreguen los mensajes futuros.
La sintaxis del método Unsubscribe debe reflejar la firma del mensaje (así que puede ser necesario incluir el
parámetro genérico Type para el argumento del mensaje).

MessagingCenter.Unsubscribe<MainPage> (this, "Hi");


MessagingCenter.Unsubscribe<MainPage, string> (this, "Hi");

Resumen
MessagingCenter es una manera sencilla de reducir el acoplamiento, especialmente entre modelos de vista. Se
puede usar para enviar y recibir mensajes sencillos o pasar un argumento entre clases. Las clases deben cancelar
la suscripción a mensajes que ya no quieran recibir.

Vínculos relacionados
Ejemplo de MessagingCenter
Xamarin.Forms Samples (Ejemplos de Xamarin.Forms)
Comunicación entre componentes débilmente acoplados
Navegación por Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Xamarin.Forms proporciona una serie de experiencias de navegación de páginas diferente, en función del tipo de
página que se use.

Como alternativa, las aplicaciones de Xamarin.Forms Shell usan una experiencia de navegación basada en URI que
no obliga a una jerarquía de navegación del conjunto. Para más información, consulte Navegación en
Xamarin.Forms Shell.

Navegación jerárquica
La clase NavigationPage proporciona una experiencia de navegación jerárquica en la que el usuario puede navegar
por las páginas hacia delante y hacia atrás, si quiere. La clase implementa la navegación como una pila de objetos
Page en la que el último en entrar es el primero en salir ( LIFO ).

TabbedPage
TabbedPage de Xamarin.Forms consta de una lista de pestañas y un área de detalles mayor. Cada pestaña carga
contenido en el área de detalles.

CarouselPage
CarouselPage de Xamarin.Forms es una página que los usuarios pueden deslizar de lado a lado para navegar por
páginas de contenido, como una galería.

MasterDetailPage
MasterDetailPage de Xamarin.Forms es una página que administra dos páginas de información relacionada: una
página maestra que presenta los elementos y una página de detalles que muestra los detalles sobre los elementos
de la página maestra.

Páginas modales
Xamarin.Forms también es compatible con las páginas modales. Una página modal anima a los usuarios a
completar una tarea autocontenida que no se puede abandonar mientras no se complete o se cancele la tarea.
Navegación jerárquica
11/07/2019 • 20 minutes to read • Edit Online

Descargar el ejemplo
La clase NavigationPage proporciona una experiencia de navegación jerárquica en la que el usuario puede
navegar por las páginas hacia adelante y hacia atrás, como quiera. La clase implementa la navegación como
una pila de objetos de página en la que el último en entrar es el primero en salir (LIFO ). En este artículo se
muestra cómo utilizar la clase NavigationPage para realizar la navegación en una pila de páginas.
Para pasar de una página a otra, una aplicación insertará una página nueva en la pila de navegación, donde se
convertirá en la página activa, tal como se muestra en el diagrama siguiente.

Para volver a la página anterior, la aplicación mostrará la página actual de la pila de navegación y la nueva página
de nivel superior se convertirá en la página activa, tal como se muestra en el diagrama siguiente:

La propiedad Navigation expone los métodos de navegación en cualquiera de los tipos derivados de Page . Estos
métodos proporcionan la capacidad para insertar páginas en la pila de navegación, sacar páginas de la pila de
navegación y manipular la pila.

Realizar la navegación
En la navegación jerárquica, se usa la clase NavigationPage para navegar por una pila de objetos ContentPage . En
las siguientes capturas de pantalla, se muestran los componentes principales de la NavigationPage en cada
plataforma:

El diseño de una NavigationPage depende de la plataforma:


En iOS, en la parte superior de la página, hay una barra de navegación que muestra un título y presenta un
botón Atrás que vuelve a la página anterior.
En Android, en la parte superior de la página, hay una barra de navegación que muestra un título, un icono y
un botón Atrás que vuelve a la página anterior. El icono se define en el atributo [Activity] que decora la clase
MainActivity en el proyecto específico de la plataforma Android.
En la Plataforma universal de Windows, en la parte superior de la página, hay una barra de navegación que
muestra un título.
En todas las plataformas, el valor de la propiedad Page.Title se mostrará como el título de la página.

NOTE
Se recomienda que una NavigationPage debe rellenarse únicamente con instancias de ContentPage .

Creación de la página raíz


La primera página que se agrega a una pila de navegación se denomina página raíz de la aplicación. En el ejemplo
de código siguiente se muestra cómo se consigue:

public App ()
{
MainPage = new NavigationPage (new Page1Xaml ());
}

Esto hace que la instancia de Page1Xaml ContentPage se inserte en la pila de navegación, donde se convertirá en
la página activa y en la página raíz de la aplicación. Esto se muestra en las capturas de pantalla siguientes:

NOTE
La propiedad RootPage de una instancia de NavigationPage permite acceder a la primera página de la pila de
navegación.

Inserción de páginas en la pila de navegación


Para navegar a Page2Xaml , es necesario invocar el método PushAsync en la propiedad Navigation de la página
actual, como se muestra en el ejemplo de código siguiente:
async void OnNextPageButtonClicked (object sender, EventArgs e)
{
await Navigation.PushAsync (new Page2Xaml ());
}

Esto hace que la instancia de Page2Xaml se inserte en la pila de navegación, donde se convertirá en la página
activa. Esto se muestra en las capturas de pantalla siguientes:

Al invocar el método PushAsync , ocurre lo siguiente:


Se invoca la invalidación de OnDisappearing de la página que llama a PushAsync .
Se invoca la invalidación de OnAppearing de la página a la que se ha navegado.
La tarea PushAsync finaliza.

Sin embargo, el orden exacto en el que se producen estos eventos depende de la plataforma. Para obtener más
información, consulte el capítulo 24 del libro sobre Xamarin.Forms de Charles Petzold.

NOTE
Las llamadas a OnDisappearing y las invalidaciones de OnAppearing no se pueden tratar como indicaciones garantizadas
de navegación de páginas. Por ejemplo, en iOS, la invalidación de OnDisappearing se llama en la página activa cuando la
aplicación finaliza.

Sacar páginas de la pila de navegación


La página activa se puede extraer de la pila de navegación. Para ello, pulse el botón Atrás del dispositivo,
independientemente de si se trata de un botón físico en el dispositivo o de un botón en la pantalla.
Para volver mediante programación a la página original, la instancia Page2Xaml debe invocar el método PopAsync
, como se muestra en el ejemplo de código siguiente:

async void OnPreviousPageButtonClicked (object sender, EventArgs e)


{
await Navigation.PopAsync ();
}

Esto hace que la instancia de Page2Xaml se quite de la pila de navegación y que la nueva página de nivel superior
se convierta en la página activa. Al invocar el método PopAsync , ocurre lo siguiente:
Se invoca la invalidación de OnDisappearing de la página que llama a PopAsync .
Se invoca la invalidación de OnAppearing de la página a la que se ha regresado.
La tarea PopAsync vuelve.

Sin embargo, el orden exacto en el que se producen estos eventos depende de la plataforma. Para obtener más
información, consulte el capítulo 24 del libro sobre Xamarin.Forms de Charles Petzold.
Al igual que los métodos PushAsync y PopAsync , la propiedad Navigation de cada página también proporciona
un método PopToRootAsync , que se muestra en el ejemplo de código siguiente:

async void OnRootPageButtonClicked (object sender, EventArgs e)


{
await Navigation.PopToRootAsync ();
}

Este método saca todas las páginas de la pila de navegación, excepto la Page raíz, de manera que la página raíz
de la aplicación se convierte en la página activa.
Animación de transiciones de página
La propiedad Navigation de cada página también proporciona métodos de inserción y extracción invalidados que
incluyen un parámetro boolean que controla si se debe mostrar una animación de página durante la navegación,
como se muestra en el ejemplo de código siguiente:

async void OnNextPageButtonClicked (object sender, EventArgs e)


{
// Page appearance not animated
await Navigation.PushAsync (new Page2Xaml (), false);
}

async void OnPreviousPageButtonClicked (object sender, EventArgs e)


{
// Page appearance not animated
await Navigation.PopAsync (false);
}

async void OnRootPageButtonClicked (object sender, EventArgs e)


{
// Page appearance not animated
await Navigation.PopToRootAsync (false);
}

Al establecer el parámetro boolean en false , la animación de transición de página se deshabilita, mientras que
al establecer el parámetro en true , la animación de transición de página se habilita, siempre que la plataforma
subyacente lo admita. Sin embargo, los métodos de inserción y extracción que carecen de este parámetro
habilitan la animación de manera predeterminada.

Pasar datos al navegar


A veces es necesario que una página pase datos a otra durante la navegación. Dos técnicas para llevar a cabo esto
son pasar datos a través de un constructor de página y establecer el BindingContext de la página nueva en los
datos. A continuación, se explicarán de uno en uno.
Pasar datos a través de un constructor de página
La técnica más sencilla para pasar datos a otra página durante la navegación es hacerlo a través de un parámetro
de constructor de página, que se muestra en el ejemplo de código siguiente:
public App ()
{
MainPage = new NavigationPage (new MainPage (DateTime.Now.ToString ("u")));
}

Este código crea una instancia de MainPage y pasa la fecha y la hora actuales en formato ISO8601, que se
encapsula en una instancia de NavigationPage .
La instancia de MainPage recibe los datos a través de un parámetro de constructor, tal como se muestra en el
ejemplo de código siguiente:

public MainPage (string date)


{
InitializeComponent ();
dateLabel.Text = date;
}

A continuación, se establece la propiedad Label.Text para que los datos se muestren en la página, como se
muestra en las capturas de pantalla siguientes:

Pasar datos a través de un objeto BindingContext


Un enfoque alternativo para pasar datos a otra página durante la navegación es establecer el BindingContext de
la página nueva en los datos, como se muestra en el siguiente ejemplo de código:

async void OnNavigateButtonClicked (object sender, EventArgs e)


{
var contact = new Contact {
Name = "Jane Doe",
Age = 30,
Occupation = "Developer",
Country = "USA"
};

var secondPage = new SecondPage ();


secondPage.BindingContext = contact;
await Navigation.PushAsync (secondPage);
}

Este código establece el BindingContext de la instancia de SecondPage en la instancia de Contact y después


navega a la SecondPage .
Después, la SecondPage utiliza el enlace de datos para mostrar los datos de la instancia de Contact , como se
muestra en el ejemplo de código XAML siguiente:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PassingData.SecondPage"
Title="Second Page">
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<StackLayout Orientation="Horizontal">
<Label Text="Name:" HorizontalOptions="FillAndExpand" />
<Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" />
</StackLayout>
...
<Button x:Name="navigateButton" Text="Previous Page" Clicked="OnNavigateButtonClicked" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

En el ejemplo de código siguiente se muestra cómo se puede realizar el enlace de datos en C#:

public class SecondPageCS : ContentPage


{
public SecondPageCS ()
{
var nameLabel = new Label {
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
FontAttributes = FontAttributes.Bold
};
nameLabel.SetBinding (Label.TextProperty, "Name");
...
var navigateButton = new Button { Text = "Previous Page" };
navigateButton.Clicked += OnNavigateButtonClicked;

Content = new StackLayout {


HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Children = {
new StackLayout {
Orientation = StackOrientation.Horizontal,
Children = {
new Label{ Text = "Name:", FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
HorizontalOptions = LayoutOptions.FillAndExpand },
nameLabel
}
},
...
navigateButton
}
};
}

async void OnNavigateButtonClicked (object sender, EventArgs e)


{
await Navigation.PopAsync ();
}
}

A continuación, una serie de controles Label muestran los datos en la página, como se muestra en las capturas
de pantalla siguientes:
Para más información sobre el enlace de datos, consulte Data Binding Basics (Aspectos básicos del enlace de
datos).

Manipulación de la pila de navegación


La propiedad Navigation expone una propiedad NavigationStack desde la que se pueden obtener las páginas de
la pila de navegación. Mientras que Xamarin.Forms mantiene el acceso a la pila de navegación, la propiedad
Navigation proporciona los métodos InsertPageBefore y RemovePage para manipular la pila mediante la
inserción o la eliminación de páginas.
El método InsertPageBefore inserta una página especificada en la pila de navegación antes de una página
existente especificada, como se muestra en el diagrama siguiente:

El método RemovePage quita la página especificada de la pila de navegación, como se muestra en el diagrama
siguiente:

Estos métodos permiten una experiencia de navegación personalizada, como sustituir una página de inicio de
sesión por una página nueva, después de iniciar sesión correctamente. El ejemplo de código siguiente muestra
esta situación:

async void OnLoginButtonClicked (object sender, EventArgs e)


{
...
var isValid = AreCredentialsCorrect (user);
if (isValid) {
App.IsUserLoggedIn = true;
Navigation.InsertPageBefore (new MainPage (), this);
await Navigation.PopAsync ();
} else {
// Login failed
}
}
Siempre que las credenciales del usuario sean correctas, la instancia de MainPage se inserta en la pila de
navegación antes de la página actual. A continuación, el método PopAsync quita la página actual de la pila de
navegación y la instancia de MainPage se convierte en la página activa.

Mostrar vistas en la barra de navegación


Cualquier View de Xamarin.Forms se puede mostrar en la barra de navegación de una NavigationPage . Para ello,
establezca la propiedad adjunta NavigationPage.TitleView en una View . Esta propiedad adjunta se puede
establecer en cualquier Page y, cuando la Page se inserte en una NavigationPage , la NavigationPage respetará el
valor de la propiedad.
En el ejemplo siguiente, tomado del Ejemplo de vista de título, se muestra cómo establecer la propiedad adjunta
NavigationPage.TitleView de XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NavigationPageTitleView.TitleViewPage">
<NavigationPage.TitleView>
<Slider HeightRequest="44" WidthRequest="300" />
</NavigationPage.TitleView>
...
</ContentPage>

El código equivalente en C# es el siguiente:

public class TitleViewPage : ContentPage


{
public TitleViewPage()
{
var titleView = new Slider { HeightRequest = 44, WidthRequest = 300 };
NavigationPage.SetTitleView(this, titleView);
...
}
}

Esto da como resultado un Slider que se muestra en la barra de navegación en la NavigationPage :

IMPORTANT
Muchas vistas no aparecen en la barra de navegación, salvo que el tamaño de la vista se especifique con las propiedades
WidthRequest y HeightRequest . Como alternativa, la vista puede encapsularse en un StackLayout con las propiedades
HorizontalOptions y VerticalOptions establecidas en los valores adecuados.

Tenga en cuenta que, dado que la clase Layout deriva de la clase View , la propiedad adjunta TitleView se puede
establecer para mostrar una clase de diseño que contiene varias vistas. En iOS y la Plataforma universal de
Windows (UWP ), la altura de la barra de navegación no se puede cambiar y, por tanto, se recortará si la vista que
se muestra en la barra de navegación es mayor que el tamaño predeterminado de la barra de navegación. Sin
embargo, en Android, la altura de la barra de navegación se puede cambiar estableciendo la propiedad enlazable
NavigationPage.BarHeight en un double que represente la altura nueva. Para obtener más información, consulte
Establecer la altura de la barra de navegación en una NavigationPage.
Como alternativa, para sugerir una barra de exploración extendida, se puede colocar una parte del contenido en la
barra de navegación y otra en una vista en la parte superior del contenido de la página que coincida con el color
de la barra de navegación. Además, en iOS, la línea y la sombra del separador que se encuentran en la parte
inferior de la barra de navegación se pueden quitar estableciendo la propiedad enlazable
NavigationPage.HideNavigationBarSeparator en true . Para obtener más información, consulte Ocultar el
separador de la barra de navegación en una NavigationPage.

NOTE
Las propiedades BackButtonTitle , Title , TitleIcon y TitleView pueden definir valores que ocupan espacio en la
barra de navegación. Mientras que el tamaño de la barra de navegación varía según la plataforma y el tamaño de pantalla,
al establecer todas estas propiedades se producirán conflictos a causa del espacio limitado disponible. En lugar de intentar
utilizar una combinación de estas propiedades, es posible que, si solo establece la propiedad TitleView , pueda elaborar
mejor el diseño de la barra de navegación deseado.

Limitaciones
Al mostrar una View en la barra de navegación de una NavigationPage , hay una serie de limitaciones que debe
conocer:
En iOS, las vistas colocadas en la barra de navegación de una NavigationPage se muestran en una posición
diferente según si están habilitados los títulos de gran tamaño. Para obtener más información sobre cómo
habilitar títulos grandes, consulte Mostrar títulos grandes.
En Android, colocar las vistas en la barra de navegación de una NavigationPage solo puede realizarse en las
aplicaciones que utilizan la compatibilidad de aplicaciones.
No se recomienda colocar vistas grandes y complejas, como ListView y TableView , en la barra de navegación
de una NavigationPage .

Vínculos relacionados
Navegación de páginas
Jerárquica (ejemplo)
PassingData (ejemplo)
LoginFlow (ejemplo)
TitleView (ejemplo)
Vídeo sobre cómo crear un flujo de pantalla de inicio de sesión en Xamarin.Forms
NavigationPage
TabbedPage de Xamarin.Forms
11/07/2019 • 12 minutes to read • Edit Online

Descargar el ejemplo
TabbedPage de Xamarin.Forms consta de una lista de pestañas y un área de detalles mayor. Cada pestaña carga
contenido en el área de detalles. En este artículo se muestra cómo usar una instancia de TabbedPage para
navegar por una colección de páginas.

Información general
En las capturas de pantalla siguientes se muestra un elemento TabbedPage en cada plataforma:

Las capturas de pantalla siguientes se centran en el formato de pestaña en cada plataforma:

El diseño de una instancia de TabbedPage y sus pestañas depende de la plataforma:


En iOS, la lista de pestañas aparece en la parte inferior de la pantalla y el área de detalles está arriba. Cada
pestaña además tiene una imagen de icono que debe tener el formato PNG 30 x 30 con transparencia para
la resolución normal, 60 x 60 para alta resolución y 90 x 90 para resolución de iPhone 6 Plus. Si hay más de
cinco pestañas, aparece una pestaña Más que puede usarse para acceder a las demás pestañas. Para
obtener más información sobre cómo cargar imágenes en una aplicación de Xamarin.Forms, vea Imágenes
en Xamarin.Forms. Para obtener más información sobre los requisitos de los iconos, vea Barras de pestañas
y controladores de la barra de pestañas de Xamarin.iOS.

NOTE
Observe que el elemento TabbedRenderer para iOS tiene un método reemplazable GetIcon que se puede usar
para cargar iconos de pestaña desde un origen especificado. Esta invalidación permite usar imágenes SVG como
iconos en un elemento TabbedPage . Además, se pueden proporcionar versiones seleccionadas y sin seleccionar de
un icono.

En Android, la lista de pestañas aparece en la parte superior de la pantalla de forma predeterminada y el


área de detalles está debajo. Pero se puede mover la lista de pestañas a la parte inferior de la pantalla con
una plataforma específica. Para obtener más información, vea Establecer ubicación de la barra de
herramientas TabbedPage y Color.

NOTE
Tenga en cuenta que al usar AppCompat en Android, cada pestaña también muestra un icono. Además, el elemento
TabbedPageRenderer para Android AppCompat tiene un método reemplazable GetIconDrawable que se puede
usar para cargar iconos de pestaña desde un elemento Drawable personalizado. Esta invalidación permite usar
imágenes SVG como iconos en un elemento TabbedPage y funciona con barras de pestañas superiores e inferiores.
También se puede usar el método reemplazable SetTabIcon para cargar iconos de pestaña desde un elemento
Drawable personalizado para barras de pestañas superiores.

En los factores de forma de tableta de Windows, las pestañas no siempre están visibles y los usuarios
tienen que deslizar hacia abajo (o hacer clic con el botón derecho, si tienen un mouse asociado) para ver las
pestañas en un elemento TabbedPage (como se muestra a continuación).

Creación de TabbedPage
TabbedPage define las siguientes propiedades:
BarBackgroundColor de tipo Color , el color de fondo de la barra de pestañas.
BarTextColor de tipo Color , el color del texto en la barra de pestañas.
SelectedTabColor de tipo Color , el color de la pestaña cuando está seleccionada.
UnselectedTabColor de tipo Color , el color de la pestaña cuando no está seleccionada.

Todas estas propiedades están respaldadas por objetos BindableProperty , lo que significa que se les pueden
aplicar estilos y que las propiedades pueden ser los destinos de los enlaces de datos.
Para crear una instancia de TabbedPage se pueden usar dos métodos:
Rellenar la instancia de TabbedPage con una colección de objetos secundarios Page , como una colección de
instancias de ContentPage .
Asignar una colección a la propiedad ItemsSource y asignar un elemento DataTemplate a la propiedad
ItemTemplate para devolver páginas de objetos de la colección.

Con ambos métodos, TabbedPage muestra cada página cuando el usuario selecciona cada pestaña.

NOTE
Se recomienda que una instancia de TabbedPage se rellene únicamente con instancias de NavigationPage y
ContentPage . Esto ayuda a garantizar una experiencia de usuario coherente en todas las plataformas.

Rellenar TabbedPage con una colección de páginas


El ejemplo de código XAML siguiente muestra un elemento TabbedPage construido al rellenarse con una
colección de objetos secundarios Page :
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TabbedPageWithNavigationPage;assembly=TabbedPageWithNavigationPage"
x:Class="TabbedPageWithNavigationPage.MainPage">
<local:TodayPage />
<NavigationPage Title="Schedule" IconImageSource="schedule.png">
<x:Arguments>
<local:SchedulePage />
</x:Arguments>
</NavigationPage>
</TabbedPage>

En el ejemplo de código siguiente se muestra la instancia de TabbedPage equivalente creada en C#:

public class MainPageCS : TabbedPage


{
public MainPageCS ()
{
var navigationPage = new NavigationPage (new SchedulePageCS ());
navigationPage.IconImageSource = "schedule.png";
navigationPage.Title = "Schedule";

Children.Add (new TodayPageCS ());


Children.Add (navigationPage);
}
}

TabbedPage se rellena con dos objetos secundarios Page . El primer elemento secundario es una instancia de
ContentPage y la segunda pestaña es un elemento NavigationPage que contiene una instancia de ContentPage .

NOTE
La instancia de TabbedPage no admite la virtualización de la interfaz de usuario. Por lo tanto, el rendimiento puede verse
afectado si TabbedPage contiene demasiados elementos secundarios.

Las capturas de pantalla siguientes muestran el elemento TodayPage de la instancia ContentPage , que se muestra
en la pestaña Today:

Al seleccionar la pestaña Schedule, se muestra el elemento SchedulePage de la instancia de ContentPage , que se


incluye en una instancia de NavigationPage y se muestra en la captura de pantalla siguiente:
Para obtener información sobre el diseño de un elemento NavigationPage , vea Realizar la navegación.

NOTE
Aunque es aceptable colocar un elemento NavigationPage en una instancia de TabbedPage , no se recomienda colocar un
elemento TabbedPage en NavigationPage . Esto se debe a que, en iOS, un elemento UITabBarController siempre actúa
como contenedor de UINavigationController . Para obtener más información, vea Combined View Controller Interfaces
(Interfaces combinadas del controlador de vistas) en la biblioteca para desarrolladores de iOS.

Navegación dentro de una pestaña


La navegación se puede realizar desde la segunda pestaña si se invoca al método PushAsync en la propiedad
Navigation de la instancia de ContentPage , como se muestra en el ejemplo de código siguiente:

async void OnUpcomingAppointmentsButtonClicked (object sender, EventArgs e)


{
await Navigation.PushAsync (new UpcomingAppointmentsPage ());
}

Esto hace que la instancia de UpcomingAppointmentsPage se inserte en la pila de navegación, donde se convertirá en
la página activa. Esto se muestra en las capturas de pantalla siguientes:
Para obtener más información sobre la navegación mediante la clase NavigationPage , vea Navegación jerárquica.
Rellenar TabbedPage con una plantilla
El ejemplo de código XAML siguiente muestra una instancia de TabbedPage construida mediante la asignación de
DataTemplate a la propiedad ItemTemplate para devolver páginas para objetos de la colección:

<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabbedPageDemo;assembly=TabbedPageDemo"
x:Class="TabbedPageDemo.TabbedPageDemoPage">
<TabbedPage.Resources>
<ResourceDictionary>
<local:NonNullToBooleanConverter x:Key="booleanConverter" />
</ResourceDictionary>
</TabbedPage.Resources>
<TabbedPage.ItemTemplate>
<DataTemplate>
<ContentPage Title="{Binding Name}" IconImageSource="monkeyicon.png">
<StackLayout Padding="5, 25">
<Label Text="{Binding Name}" Font="Bold,Large" HorizontalOptions="Center" />
<Image Source="{Binding PhotoUrl}" WidthRequest="200" HeightRequest="200" />
<StackLayout Padding="50, 10">
<StackLayout Orientation="Horizontal">
<Label Text="Family:" HorizontalOptions="FillAndExpand" />
<Label Text="{Binding Family}" Font="Bold,Medium" />
</StackLayout>
...
</StackLayout>
</StackLayout>
</ContentPage>
</DataTemplate>
</TabbedPage.ItemTemplate>
</TabbedPage>

TabbedPage se rellena con datos al establecer la propiedad ItemsSource en el constructor para el archivo de
código subyacente:

public TabbedPageDemoPage ()
{
...
ItemsSource = MonkeyDataModel.All;
}

En el ejemplo de código siguiente se muestra la instancia de TabbedPage equivalente creada en C#:


public class TabbedPageDemoPageCS : TabbedPage
{
public TabbedPageDemoPageCS ()
{
var booleanConverter = new NonNullToBooleanConverter ();

ItemTemplate = new DataTemplate (() => {


var nameLabel = new Label {
FontSize = Device.GetNamedSize (NamedSize.Large, typeof(Label)),
FontAttributes = FontAttributes.Bold,
HorizontalOptions = LayoutOptions.Center
};
nameLabel.SetBinding (Label.TextProperty, "Name");

var image = new Image { WidthRequest = 200, HeightRequest = 200 };


image.SetBinding (Image.SourceProperty, "PhotoUrl");

var familyLabel = new Label {


FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
FontAttributes = FontAttributes.Bold
};
familyLabel.SetBinding (Label.TextProperty, "Family");
...

var contentPage = new ContentPage {


IconImageSource = "monkeyicon.png",
Content = new StackLayout {
Padding = new Thickness (5, 25),
Children = {
nameLabel,
image,
new StackLayout {
Padding = new Thickness (50, 10),
Children = {
new StackLayout {
Orientation = StackOrientation.Horizontal,
Children = {
new Label { Text = "Family:", HorizontalOptions = LayoutOptions.FillAndExpand },
familyLabel
}
},
...
}
}
}
}
};
contentPage.SetBinding (TitleProperty, "Name");
return contentPage;
});
ItemsSource = MonkeyDataModel.All;
}
}

Cada pestaña muestra un elemento ContentPage que usa una serie de instancias de StackLayout y Label para
mostrar datos para la pestaña. Las capturas de pantalla siguientes muestran el contenido de la pestaña Tamarin:
Al seleccionar otra pestaña, se muestra el contenido de esa pestaña.

NOTE
La instancia de TabbedPage no admite la virtualización de la interfaz de usuario. Por lo tanto, el rendimiento puede verse
afectado si TabbedPage contiene demasiados elementos secundarios.

Para obtener más información sobre la instancia de TabbedPage , vea el capítulo 25 del libro sobre Xamarin.Forms
de Charles Petzold.

Resumen
En este artículo se ha explicado cómo usar una instancia de TabbedPage para navegar por una colección de
páginas. TabbedPage de Xamarin.Forms consta de una lista de pestañas y un área de detalles mayor. Cada pestaña
carga contenido en el área de detalles.

Vínculos relacionados
Páginas de Xamarin.Forms
TabbedPageWithNavigationPage (ejemplo)
TabbedPage (ejemplo)
TabbedPage
CarouselPage de Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

Descargar el ejemplo
CarouselPage de Xamarin.Forms es una página que los usuarios pueden deslizar de lado a lado para navegar por
páginas de contenido, como una galería. En este artículo se muestra cómo usar CarouselPage para navegar por
una colección de páginas.

Información general
En las capturas de pantalla siguientes se muestra un elemento CarouselPage en cada plataforma:

El diseño de un elemento CarouselPage es idéntico en todas las plataformas. Para navegar por las páginas, hay
que deslizar de derecha a izquierda para avanzar en la colección y de izquierda a derecha para retroceder en la
colección. Las capturas de pantalla siguientes muestran la primera página de una instancia de CarouselPage :

Al deslizar de derecha a izquierda se pasa a la segunda página, como se muestra en las capturas de pantalla
siguientes:
Al volver a deslizar de derecha a izquierda se pasa a la tercera página, mientras que al deslizar de izquierda a
derecha se vuelve a la página anterior.

Creación de CarouselPage
Para crear una instancia de CarouselPage se pueden usar dos métodos:
Rellenar la instancia de CarouselPage con una colección de instancias secundarias de ContentPage .
Asignar una colección a la propiedad ItemsSource y asignar un elemento DataTemplate a la propiedad
ItemTemplate para devolver instancias de ContentPage para objetos de la colección.

Con ambos métodos, CarouselPage muestra a su vez cada página, con una interacción de deslizamiento que pasa
a la página siguiente que se va a mostrar.

NOTE
Una instancia de CarouselPage solo se puede rellenar con instancias de ContentPage , o derivados de ContentPage .

Rellenar CarouselPage con una colección de páginas


El ejemplo de código XAML siguiente muestra una instancia de CarouselPage con tres instancias de ContentPage :
<CarouselPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CarouselPageNavigation.MainPage">
<ContentPage>
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS, Android" Value="0,40,0,0" />
</OnPlatform>
</ContentPage.Padding>
<StackLayout>
<Label Text="Red" FontSize="Medium" HorizontalOptions="Center" />
<BoxView Color="Red" WidthRequest="200" HeightRequest="200" HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
<ContentPage>
...
</ContentPage>
<ContentPage>
...
</ContentPage>
</CarouselPage>

En el ejemplo de código siguiente se muestra la interfaz de usuario equivalente en C#:


public class MainPageCS : CarouselPage
{
public MainPageCS ()
{
Thickness padding;
switch (Device.RuntimePlatform)
{
case Device.iOS:
case Device.Android:
padding = new Thickness(0, 40, 0, 0);
break;
default:
padding = new Thickness();
break;
}

var redContentPage = new ContentPage {


Padding = padding,
Content = new StackLayout {
Children = {
new Label {
Text = "Red",
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
HorizontalOptions = LayoutOptions.Center
},
new BoxView {
Color = Color.Red,
WidthRequest = 200,
HeightRequest = 200,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
}
}
}
};
var greenContentPage = new ContentPage {
Padding = padding,
Content = new StackLayout {
...
}
};
var blueContentPage = new ContentPage {
Padding = padding,
Content = new StackLayout {
...
}
};

Children.Add (redContentPage);
Children.Add (greenContentPage);
Children.Add (blueContentPage);
}
}

Cada ContentPage muestra simplemente un elemento Label para un color determinado y un elemento BoxView
de ese color.

NOTE
La instancia de CarouselPage no admite la virtualización de la interfaz de usuario. Por lo tanto, el rendimiento puede verse
afectado si CarouselPage contiene demasiados elementos secundarios.

Si hay una instancia de CarouselPage insertada en la página Detail de MasterDetailPage , la propiedad


MasterDetailPage.IsGestureEnabled debe establecerse en false para evitar conflictos entre CarouselPage y
MasterDetailPage .
Para obtener más información sobre la instancia de CarouselPage , vea el capítulo 25 del libro sobre
Xamarin.Forms de Charles Petzold.
Rellenar CarouselPage con una plantilla
El ejemplo de código XAML siguiente muestra una instancia de CarouselPage construida mediante la asignación
de DataTemplate a la propiedad ItemTemplate para devolver páginas para objetos de la colección:

<CarouselPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CarouselPageNavigation.MainPage">
<CarouselPage.ItemTemplate>
<DataTemplate>
<ContentPage>
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS, Android" Value="0,40,0,0" />
</OnPlatform>
</ContentPage.Padding>
<StackLayout>
<Label Text="{Binding Name}" FontSize="Medium" HorizontalOptions="Center" />
<BoxView Color="{Binding Color}" WidthRequest="200" HeightRequest="200"
HorizontalOptions="Center" VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
</DataTemplate>
</CarouselPage.ItemTemplate>
</CarouselPage>

CarouselPage se rellena con datos al establecer la propiedad ItemsSource en el constructor para el archivo de
código subyacente:

public MainPage ()
{
...
ItemsSource = ColorsDataModel.All;
}

En el ejemplo de código siguiente se muestra la instancia de CarouselPage equivalente creada en C#:


public class MainPageCS : CarouselPage
{
public MainPageCS ()
{
Thickness padding;
switch (Device.RuntimePlatform)
{
case Device.iOS:
case Device.Android:
padding = new Thickness(0, 40, 0, 0);
break;
default:
padding = new Thickness();
break;
}

ItemTemplate = new DataTemplate (() => {


var nameLabel = new Label {
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
HorizontalOptions = LayoutOptions.Center
};
nameLabel.SetBinding (Label.TextProperty, "Name");

var colorBoxView = new BoxView {


WidthRequest = 200,
HeightRequest = 200,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};
colorBoxView.SetBinding (BoxView.ColorProperty, "Color");

return new ContentPage {


Padding = padding,
Content = new StackLayout {
Children = {
nameLabel,
colorBoxView
}
}
};
});

ItemsSource = ColorsDataModel.All;
}
}

Cada ContentPage muestra simplemente un elemento Label para un color determinado y un elemento BoxView
de ese color.

NOTE
La instancia de CarouselPage no admite la virtualización de la interfaz de usuario. Por lo tanto, el rendimiento puede verse
afectado si CarouselPage contiene demasiados elementos secundarios.

Si hay una instancia de CarouselPage insertada en la página Detail de MasterDetailPage , la propiedad


MasterDetailPage.IsGestureEnabled debe establecerse en false para evitar conflictos entre CarouselPage y
MasterDetailPage .
Para obtener más información sobre la instancia de CarouselPage , vea el capítulo 25 del libro sobre
Xamarin.Forms de Charles Petzold.
Resumen
En este artículo se ha explicado cómo usar una instancia de CarouselPage para navegar por una colección de
páginas. La instancia de CarouselPage es una página que los usuarios pueden deslizar de lado a lado para navegar
por páginas de contenido, como una galería.

Vínculos relacionados
Páginas de Xamarin.Forms
CarouselPage (ejemplo)
CarouselPageTemplate (ejemplo)
CarouselPage
MasterDetailPage de Xamarin.Forms
11/07/2019 • 16 minutes to read • Edit Online

Descargar el ejemplo
MasterDetailPage de Xamarin.Forms es una página que administra dos páginas relacionadas de información: una
página maestra que presenta elementos y una página de detalles que presenta detalles sobre los elementos de la
página maestra. En este artículo se explica cómo usar una instancia de MasterDetailPage y cómo navegar entre
sus páginas de información.

Información general
Normalmente, una página maestra presenta una lista de elementos, como se muestra en las siguientes capturas de
pantalla:

La ubicación de la lista de elementos es idéntica en cada plataforma; al seleccionar uno de los elementos, se le lleva
a la página de detalles correspondiente. Además, la página maestra también incluye una barra de navegación que
contiene un botón que se puede usar para ir a la página de detalles activa:
En iOS, la barra de navegación se encuentra en la parte superior de la página y tiene un botón que lleva a la
página de detalles. Además, se puede ir a la página de detalles activa si se desliza la página maestra hacia la
izquierda.
En Android, la barra de navegación se encuentra en la parte superior de la página y presenta un título, un icono
y un botón que lleva a la página de detalles. El icono se define en el atributo [Activity] que decora la clase
MainActivity en el proyecto específico de la plataforma Android. Además, se puede ir a la página de detalles
activa si se desliza la página maestra hacia la izquierda, si se puntea en el extremo derecho de la pantalla en la
página de detalles y si se pulsa el botón Atrás situado en la parte inferior de la pantalla.
En Plataforma universal de Windows (UWP ), la barra de navegación se encuentra en la parte superior de la
página y tiene un botón que lleva a la página de detalles.
Una página de detalles presenta datos correspondientes al elemento seleccionado en la página maestra; los
componentes principales de la página de detalles se muestran en las capturas de pantalla siguientes:
La página de detalles contiene una barra de navegación cuyo contenido depende de la plataforma:
En iOS, la barra de navegación se encuentra en la parte superior de la página, muestra un título y tiene un
botón que devuelve a la página maestra, siempre que la instancia de la página de detalles esté incluida en la
instancia de NavigationPage . Además, se puede volver a la página maestra si se desliza la página de detalles
hacia la derecha.
En Android, hay una barra de navegación en la parte superior de la página que muestra un título, un icono y un
botón que devuelve a la página maestra. El icono se define en el atributo [Activity] que decora la clase
MainActivity en el proyecto específico de la plataforma Android.
En UWP, la barra de navegación se encuentra en la parte superior de la página, muestra un título y tiene un
botón que devuelve a la página maestra.
Comportamiento de navegación
El comportamiento de la experiencia de navegación entre las páginas maestra y de detalles depende de la
plataforma:
En iOS, la página de detalles se desliza hacia la derecha cuando la página maestra se desliza desde la izquierda,
y la parte izquierda de la página de detalles sigue siendo visible.
En Android, las páginas maestra y de detalles se superponen.
En UWP, la página maestra se desplaza desde el lateral izquierdo de la página de detalles, siempre que la
propiedad MasterBehavior esté establecida en Popover . Para obtener más información, consulte Control del
comportamiento de presentación de la página de detalles.
En el modo horizontal se observa un comportamiento similar, salvo que la página maestra en iOS y Android tiene
un ancho similar al de la página maestra en modo vertical, así que se ve más superficie de la página de detalles.
Para obtener información sobre cómo controlar el comportamiento de navegación, vea Control del
comportamiento de presentación de la página de detalles.

Creación de MasterDetailPage
Una instancia de MasterDetailPage contiene propiedades Master y Detail que son de tipo Page y se usan para
obtener y establecer las páginas maestra y de detalles, respectivamente.

IMPORTANT
Una instancia de MasterDetailPage está diseñada para ser una página raíz. Su empleo como página secundaria en otros
tipos de páginas podría dar lugar a un comportamiento inesperado e incoherente. Además, se recomienda que la página
maestra de una instancia de MasterDetailPage siempre sea una instancia de ContentPage y que la página de detalles
solo se rellene con instancias de TabbedPage , NavigationPage y ContentPage . Esto ayuda a garantizar una experiencia
de usuario coherente en todas las plataformas.

El ejemplo de código XAML siguiente muestra una instancia de MasterDetailPage que establece las propiedades
Master y Detail :
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MasterDetailPageNavigation;assembly=MasterDetailPageNavigation"
x:Class="MasterDetailPageNavigation.MainPage">
<MasterDetailPage.Master>
<local:MasterPage x:Name="masterPage" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<local:ContactsPage />
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>

En el ejemplo de código siguiente se muestra la instancia de MasterDetailPage equivalente creada en C#:

public class MainPageCS : MasterDetailPage


{
MasterPageCS masterPage;

public MainPageCS ()
{
masterPage = new MasterPageCS ();
Master = masterPage;
Detail = new NavigationPage (new ContactsPageCS ());
...
}
...
}

La propiedad MasterDetailPage.Master está establecida en una instancia de ContentPage . La propiedad


MasterDetailPage.Detail está establecida en una instancia de NavigationPage que contiene una instancia de
ContentPage .
Creación de la página maestra
El ejemplo de código XAML siguiente muestra la declaración del objeto MasterPage , al que se hace referencia
mediante la propiedad MasterDetailPage.Master :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="using:MasterDetailPageNavigation"
x:Class="MasterDetailPageNavigation.MasterPage"
Padding="0,40,0,0"
IconImageSource="hamburger.png"
Title="Personal Organiser">
<StackLayout>
<ListView x:Name="listView" x:FieldModifier="public">
<ListView.ItemsSource>
<x:Array Type="{x:Type local:MasterPageItem}">
<local:MasterPageItem Title="Contacts" IconSource="contacts.png" TargetType="{x:Type
local:ContactsPage}" />
<local:MasterPageItem Title="TodoList" IconSource="todo.png" TargetType="{x:Type
local:TodoListPage}" />
<local:MasterPageItem Title="Reminders" IconSource="reminders.png" TargetType="{x:Type
local:ReminderPage}" />
</x:Array>
</ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding IconSource}" />
<Label Grid.Column="1" Text="{Binding Title}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>

La página consta de un elemento ListView que se rellena con datos de XAML al establecer su propiedad
ItemsSource en una matriz de instancias de MasterPageItem . Cada MasterPageItem define propiedades Title ,
IconSource y TargetType .

Se asigna un elemento DataTemplate a la propiedad ListView.ItemTemplate para mostrar cada MasterPageItem .


DataTemplate contiene un elemento ViewCell que consta de un elemento Image y un Label . Image muestra el
valor de la propiedad IconSource y Label muestra el valor de la propiedad Title , para cada MasterPageItem .
La página tiene establecidas sus propiedades Title y IconImageSource . El icono aparece en la página de detalles,
siempre que esta tenga una barra de título. Esta debe habilitarse en iOS al incluir la instancia de la página de
detalles en una instancia de NavigationPage .

NOTE
La página MasterDetailPage.Master debe tener su propiedad Title establecida, o se produce una excepción.

En el ejemplo de código siguiente se muestra la página equivalente creada en C#:


public class MasterPageCS : ContentPage
{
public ListView ListView { get { return listView; } }

ListView listView;

public MasterPageCS ()
{
var masterPageItems = new List<MasterPageItem> ();
masterPageItems.Add (new MasterPageItem {
Title = "Contacts",
IconSource = "contacts.png",
TargetType = typeof(ContactsPageCS)
});
masterPageItems.Add (new MasterPageItem {
Title = "TodoList",
IconSource = "todo.png",
TargetType = typeof(TodoListPageCS)
});
masterPageItems.Add (new MasterPageItem {
Title = "Reminders",
IconSource = "reminders.png",
TargetType = typeof(ReminderPageCS)
});

listView = new ListView {


ItemsSource = masterPageItems,
ItemTemplate = new DataTemplate (() => {
var grid = new Grid { Padding = new Thickness(5, 10) };
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(30) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Star });

var image = new Image();


image.SetBinding(Image.SourceProperty, "IconSource");
var label = new Label { VerticalOptions = LayoutOptions.FillAndExpand };
label.SetBinding(Label.TextProperty, "Title");

grid.Children.Add(image);
grid.Children.Add(label, 1, 0);

return new ViewCell { View = grid };


}),
SeparatorVisibility = SeparatorVisibility.None
};

IconImageSource = "hamburger.png";
Title = "Personal Organiser";
Content = new StackLayout
{
Children = { listView }
};
}
}

En las capturas de pantalla siguientes se muestra la página maestra en cada plataforma:


Creación y presentación de la página de detalles
La instancia de MasterPage contiene una propiedad ListView que expone su instancia de ListView para que el
elemento MainPage de la instancia de MasterDetailPage pueda registrar un controlador de eventos para controlar
el evento ItemSelected . Esto permite a la instancia de MainPage establecer la propiedad Detail en la página que
representa al elemento seleccionado ListView . En el ejemplo de código siguiente se muestra el controlador de
eventos:

public partial class MainPage : MasterDetailPage


{
public MainPage ()
{
...
masterPage.listView.ItemSelected += OnItemSelected;
}

void OnItemSelected (object sender, SelectedItemChangedEventArgs e)


{
var item = e.SelectedItem as MasterPageItem;
if (item != null) {
Detail = new NavigationPage ((Page)Activator.CreateInstance (item.TargetType));
masterPage.listView.SelectedItem = null;
IsPresented = false;
}
}
}

El método OnItemSelected realiza las siguientes acciones:


Recupera el elemento SelectedItem de la instancia de ListView y, siempre que no sea null , establece la
página de detalles en una nueva instancia del tipo de página almacenado en la propiedad TargetType de
MasterPageItem . El tipo de página se incluye en una instancia de NavigationPage para asegurarse de que el
icono al que se hace referencia mediante la propiedad IconImageSource en MasterPage se muestre en la página
de detalles en iOS.
El elemento seleccionado en ListView se estable en null para asegurarse de que ninguno de los elementos
ListView se seleccione la próxima vez que el elemento MasterPage esté presente.
La página de detalles se presenta al usuario al establecer la propiedad MasterDetailPage.IsPresented en false .
Esta propiedad controla si se presenta la página maestra o de detalles. Se debe establecer en true para
mostrar la página maestra y en false para mostrar la página de detalles.

Las capturas de pantalla siguientes muestran la página de detalles ContactPage , que se presenta después de
haberse seleccionado en la página maestra:

Control del comportamiento de presentación de la página de detalles


La forma en que MasterDetailPage administre las páginas maestra y de detalles depende de si la aplicación se
ejecuta en un teléfono o tableta, de la orientación del dispositivo y del valor de la propiedad MasterBehavior . Esta
propiedad determina cómo se muestra la página de detalles. Sus posibles valores son:
Default: las páginas se muestran con el valor predeterminado de la plataforma.
Popover: la página de detalles cubre, o cubre parcialmente, la página maestra.
Split: la página maestra se muestra a la izquierda y la página de detalles a la derecha.
SplitOnLandscape: se usa una pantalla dividida cuando el dispositivo está en orientación horizontal.
SplitOnPortrait: se usa una pantalla dividida cuando el dispositivo está en orientación vertical.
En el siguiente ejemplo de código XAML se muestra cómo establecer la propiedad MasterBehavior en una
instancia de MasterDetailPage :
<?xml version="1.0" encoding="UTF-8"?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MasterDetailPageNavigation.MainPage"
MasterBehavior="Popover">
...
</MasterDetailPage>

En el ejemplo de código siguiente se muestra la instancia de MasterDetailPage equivalente creada en C#:

public class MainPageCS : MasterDetailPage


{
MasterPageCS masterPage;

public MainPageCS ()
{
MasterBehavior = MasterBehavior.Popover;
...
}
}

Pero el valor de la propiedad MasterBehavior solo afecta a las aplicaciones que se ejecutan en el escritorio o en
tabletas. Las aplicaciones que se ejecutan en teléfonos siempre tienen el comportamiento Popover.

Resumen
En este artículo se ha explicado cómo usar una instancia de MasterDetailPage y cómo navegar entre sus páginas
de información. MasterDetailPage de Xamarin.Forms es una página que administra dos páginas de información
relacionada: una página maestra que presenta elementos y una página de detalles que muestra detalles sobre
elementos de la página maestra.

Vínculos relacionados
Páginas de Xamarin.Forms
MasterDetailPage (ejemplo)
MasterDetailPage
Páginas modales de Xamarin.Forms
11/07/2019 • 12 minutes to read • Edit Online

Descargar el ejemplo
Xamarin.Forms es compatible con las páginas modales. Una página modal anima a los usuarios a completar una
tarea autocontenida que no se puede abandonar mientras no se complete o se cancele la tarea. En este artículo se
muestra cómo navegar a páginas modales.
En este artículo se tratan los siguientes temas:
Realizar la navegación: insertar páginas en la pila modal, sacar páginas de la pila modal, deshabilitar el botón
Atrás y animar las transiciones de página.
Pasar datos al navegar: pasar datos a través de un constructor de página y de un BindingContext .

Información general
Una página modal puede ser cualquiera de los tipos Página compatibles con Xamarin.Forms. Para mostrar una
página modal, la aplicación la insertará en la pila modal, donde se convertirá en la página activa, como se muestra
en el siguiente diagrama:

Para volver a la página anterior, la aplicación mostrará la página actual de la pila modal y la nueva página de nivel
superior se convertirá en la página activa, tal como se muestra en el siguiente diagrama:

Realizar la navegación
Los métodos de navegación modal se exponen mediante la propiedad Navigation en cualquier tipo Page
derivado. Estos métodos proporcionan la capacidad de insertar páginas modales en la pila modal, y sacar páginas
modales de la pila modal.
La propiedad Navigation también expone una propiedad ModalStack desde la que se pueden obtener las páginas
modales de la pila modal. Pero no existe el concepto de realizar una manipulación de pila modal o extraer la
página raíz de la navegación modal. Esto se debe a que estas operaciones no se admiten de forma universal en las
plataformas subyacentes.

NOTE
No es necesaria una instancia de NavigationPage para realizar la navegación de páginas modal.

Inserción de páginas en la pila modal


Para navegar a ModalPage , es necesario invocar el método PushModalAsync en la propiedad Navigation de la
página actual, como se muestra en el ejemplo de código siguiente:

async void OnItemSelected (object sender, SelectedItemChangedEventArgs e)


{
if (listView.SelectedItem != null) {
var detailPage = new DetailPage ();
...
await Navigation.PushModalAsync (detailPage);
}
}

Esto hace que la instancia de ModalPage se inserte en la pila modal, donde se convertirá en la página activa,
siempre que se haya seleccionado un elemento en la ListView en la instancia de MainPage . La instancia de
ModalPage se muestra en la siguiente captura de pantalla:

Al invocar PushModalAsync , ocurre lo siguiente:


La página que llama a PushModalAsync invoca su invalidación OnDisappearing , siempre que la plataforma
subyacente no sea Android.
Se invoca la invalidación de OnAppearing de la página a la que se ha navegado.
La tarea PushAsync finaliza.

Con todo, el orden exacto en el que se producen estos eventos depende de la plataforma. Para obtener más
información, consulte el capítulo 24 del libro sobre Xamarin.Forms de Charles Petzold.

NOTE
Las llamadas a OnDisappearing y las invalidaciones de OnAppearing no se pueden tratar como indicaciones garantizadas
de navegación de páginas. Por ejemplo, en iOS, la invalidación de OnDisappearing se llama en la página activa cuando la
aplicación finaliza.

Sacar páginas de la pila modal


La página activa se puede extraer de la pila modal. Para ello, pulse el botón Atrás del dispositivo,
independientemente de si se trata de un botón físico en el dispositivo o de un botón en la pantalla.
Para volver mediante programación a la página original, la instancia ModalPage debe invocar el método
PopModalAsync , como se muestra en el ejemplo de código siguiente:
async void OnDismissButtonClicked (object sender, EventArgs args)
{
await Navigation.PopModalAsync ();
}

Esto hace que la instancia de ModalPage se quite de la pila modal y que la nueva página de nivel superior se
convierta en la página activa. Al invocar PopModalAsync , ocurre lo siguiente:
Se invoca la invalidación de OnDisappearing de la página que llama a PopModalAsync .
Se invoca la invalidación de OnAppearing de la página a la que se vuelve, siempre que la plataforma subyacente
no sea Android.
Se devuelve la tarea PopModalAsync .
Con todo, el orden exacto en el que se producen estos eventos depende de la plataforma. Para obtener más
información, consulte el capítulo 24 del libro sobre Xamarin.Forms de Charles Petzold.
Deshabilitación del botón Atrás
En Android, el usuario siempre puede volver a la página anterior presionando el botón Atrás estándar en el
dispositivo. Si la página modal requiere que el usuario complete una tarea independiente antes de salir de la
página, la aplicación debe deshabilitar el botón Atrás. Esto puede realizarse invalidando el método
Page.OnBackButtonPressed en la página modal. Para obtener más información, consulte el capítulo 24 del libro
sobre Xamarin.Forms de Charles Petzold.
Animación de transiciones de página
La propiedad Navigation de cada página también proporciona métodos de inserción y extracción invalidados que
incluyen un parámetro boolean que controla si se debe mostrar una animación de página durante la navegación,
como se muestra en el ejemplo de código siguiente:

async void OnNextPageButtonClicked (object sender, EventArgs e)


{
// Page appearance not animated
await Navigation.PushModalAsync (new DetailPage (), false);
}

async void OnDismissButtonClicked (object sender, EventArgs args)


{
// Page appearance not animated
await Navigation.PopModalAsync (false);
}

Al establecer el parámetro boolean en false , la animación de transición de página se deshabilita, mientras que al
establecer el parámetro en true , la animación de transición de página se habilita, siempre que la plataforma
subyacente lo admita. Sin embargo, los métodos de inserción y extracción que carecen de este parámetro habilitan
la animación de manera predeterminada.

Pasar datos al navegar


A veces es necesario que una página pase datos a otra durante la navegación. Dos técnicas para llevar a cabo esto
son pasar datos a través de un constructor de página y establecer el BindingContext de la página nueva en los
datos. En las siguientes secciones se explicarán de uno en uno.
Pasar datos a través de un constructor de página
La técnica más sencilla para pasar datos a otra página durante la navegación es hacerlo a través de un parámetro
de constructor de página, que se muestra en el siguiente ejemplo de código:
public App ()
{
MainPage = new MainPage (DateTime.Now.ToString ("u")));
}

Este código crea una instancia de MainPage y pasa la fecha y la hora actuales en formato ISO8601.
La instancia de MainPage recibe los datos a través de un parámetro de constructor, tal como se muestra en el
ejemplo de código siguiente:

public MainPage (string date)


{
InitializeComponent ();
dateLabel.Text = date;
}

Después, se establece la propiedad Label.Text para que los datos se muestren en la página.
Pasar datos a través de un objeto BindingContext
Un enfoque alternativo para pasar datos a otra página durante la navegación es establecer el BindingContext de la
página nueva en los datos, como se muestra en el siguiente ejemplo de código:

async void OnItemSelected (object sender, SelectedItemChangedEventArgs e)


{
if (listView.SelectedItem != null) {
var detailPage = new DetailPage ();
detailPage.BindingContext = e.SelectedItem as Contact;
listView.SelectedItem = null;
await Navigation.PushModalAsync (detailPage);
}
}

Este código establece el BindingContext de la instancia de DetailPage en la instancia de Contact y después


navega a la DetailPage .
Después, la DetailPage utiliza el enlace de datos para mostrar los datos de la instancia de Contact , como se
muestra en el ejemplo de código XAML siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ModalNavigation.DetailPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0,40,0,0" />
</OnPlatform>
</ContentPage.Padding>
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<StackLayout Orientation="Horizontal">
<Label Text="Name:" FontSize="Medium" HorizontalOptions="FillAndExpand" />
<Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" />
</StackLayout>
...
<Button x:Name="dismissButton" Text="Dismiss" Clicked="OnDismissButtonClicked" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

En el ejemplo de código siguiente se muestra cómo se puede realizar el enlace de datos en C#:
public class DetailPageCS : ContentPage
{
public DetailPageCS ()
{
var nameLabel = new Label {
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
FontAttributes = FontAttributes.Bold
};
nameLabel.SetBinding (Label.TextProperty, "Name");
...
var dismissButton = new Button { Text = "Dismiss" };
dismissButton.Clicked += OnDismissButtonClicked;

Thickness padding;
switch (Device.RuntimePlatform)
{
case Device.iOS:
padding = new Thickness(0, 40, 0, 0);
break;
default:
padding = new Thickness();
break;
}

Padding = padding;
Content = new StackLayout {
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Children = {
new StackLayout {
Orientation = StackOrientation.Horizontal,
Children = {
new Label{ Text = "Name:", FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
HorizontalOptions = LayoutOptions.FillAndExpand },
nameLabel
}
},
...
dismissButton
}
};
}

async void OnDismissButtonClicked (object sender, EventArgs args)


{
await Navigation.PopModalAsync ();
}
}

Después, una serie de controles Label muestran los datos en la página.


Para más información sobre el enlace de datos, consulte Data Binding Basics (Aspectos básicos del enlace de
datos).

Resumen
En este artículo se mostró cómo navegar a páginas modales. Una página modal anima a los usuarios a completar
una tarea autocontenida que no se puede abandonar mientras no se complete o se cancele la tarea.

Vínculos relacionados
Page Navigation (Navegación de páginas)
Modal (sample) (Modal [ejemplo])
PassingData (sample) (PassingData [ejemplo])
Xamarin.Forms Shell
11/07/2019 • 3 minutes to read • Edit Online

Introducción
Xamarin.Forms Shell reduce la complejidad del desarrollo de aplicaciones móviles al proporcionar las
características fundamentales que requieren la mayoría de aplicaciones móviles. Esto incluye una experiencia de
usuario de navegación común, un esquema de navegación basado en URI y un controlador de búsqueda
integrada.

Creación de una aplicación de Xamarin.Forms Shell


El proceso para crear una aplicación de Xamarin.Forms Shell consiste en crear un archivo XAML que sirva de
subclase de la clase Shell , establecer la propiedad MainPage de la clase App de la aplicación en el objeto Shell
con sublclases y, a continuación, describir la jerarquía visual de la aplicación en la clase Shell con subclases.

Control flotante
El control flotante es el menú raíz de una aplicación de Shell y es accesible por medio de un icono o al deslizar el
dedo desde el lado de la pantalla. El control flotante consta de un encabezado opcional, elementos de control
flotante y elementos de menú opcionales.

Pestañas
Después de un control flotante, el siguiente nivel de navegación en una aplicación de Shell es la barra de pestañas
de la parte inferior. Como alternativa, el modelo de navegación para una aplicación puede comenzar con pestañas
en la parte inferior y no usar un control flotante. En ambos casos, cuando una pestaña inferior contiene más de
una página, las páginas son navegables mediante las pestañas principales.

Configuración de la página
La clase Shell define las propiedades adjuntas que se pueden usar para configurar la apariencia de las páginas
en las aplicaciones de Xamarin.Forms Shell. Esto incluye establecer los colores de la página, deshabilitar la barra
de navegación y la barra de pestañas y mostrar las vistas en la barra de navegación.

Navegación
Las aplicaciones de Shell pueden usar un esquema de navegación basado en URI que emplea rutas para navegar
a cualquier página de la aplicación, sin tener que seguir una jerarquía de navegación establecida.

Búsqueda
Las aplicaciones de Shell pueden usar la funcionalidad de búsqueda integrada que se proporciona en un cuadro
de búsqueda que se puede agregar a la parte superior de cada página.

Representadores personalizados
Las aplicaciones de Shell son muy personalizables mediante las propiedades y los métodos que exponen las
distintas clases de Shell. Sin embargo, también es posible crear a un representador personalizado de Shell cuando
se requieren personalizaciones más sofisticadas específicas de la plataforma.
Introducción a Xamarin.Forms Shell
11/07/2019 • 2 minutes to read • Edit Online

Descargar el ejemplo
Xamarin.Forms Shell reduce la complejidad del desarrollo de aplicaciones móviles al proporcionar las
características fundamentales que requieren la mayoría de aplicaciones móviles, como por ejemplo:
Un único lugar para describir la jerarquía visual de una aplicación.
Una interfaz de usuario de navegación común.
Un esquema de navegación basado en URI que permite la navegación a cualquier página de la aplicación.
Un controlador de búsqueda integrado.
Además, las aplicaciones de Shell se benefician de una mayor velocidad de representación y un consumo reducido
de memoria.

IMPORTANT
El shell de Xamarin.Forms solo está disponible en iOS y Android. Las aplicaciones existentes de iOS y Android pueden adoptar
Shell y aprovechar inmediatamente las mejoras de navegación, rendimiento y extensibilidad.

Experiencia de navegación de Shell


Shell ofrece una experiencia de navegación bien fundamentada basada en controles flotantes y pestañas. El nivel
superior del panel de navegación en una aplicación de Shell es un control flotante o una barra de pestañas inferior,
según los requisitos de navegación de la aplicación. El ejemplo siguiente muestra una aplicación donde el nivel
superior de navegación es un control flotante:

Al seleccionar un elemento de control flotante, se selecciona y muestra la pestaña inferior que representa el
elemento:
NOTE
Cuando el control flotante no está abierto, la barra de pestañas inferior se puede considerar el nivel superior de navegación
en la aplicación.

Cada pestaña muestra un objeto ContentPage . Sin embargo, si una pestaña inferior contiene más de una página,
las páginas son navegables mediante la barra de pestañas superior:

Dentro de cada pestaña, se puede navegar a objetos adicionales ContentPage :


Vínculos relacionados
Xaminals (ejemplo)
Creación de una aplicación de Xamarin.Forms Shell
11/07/2019 • 6 minutes to read • Edit Online

Descargar el ejemplo
El proceso de creación de una aplicación de Xamarin.Forms Shell es el siguiente:
1. Cree una nueva aplicación de Xamarin.Forms, o cargue una aplicación existente que desee convertir en una
aplicación de Shell.
2. Agregue un archivo XAML al proyecto de código compartido que sirva de subclase de la clase Shell . Para
obtener más información, consulte Subclass the Shell class (Establecimiento de subclases en la clase Shell).
3. Establezca la propiedad MainPage de la clase App de la aplicación en el objeto Shell con subclases. Para
obtener más información, consulte Bootstrap the Shell application (Arranque de la aplicación Shell).
4. Describa la jerarquía visual de la aplicación en la clase Shell con subclases. Para obtener más información,
consulte Describe the visual hierarchy of the application (Descripción de la jerarquía visual de la aplicación).

Creación de subclases de la clase de Shell


El primer paso para crear una aplicación de Xamarin.Forms Shell es agregar un archivo XAML al proyecto de
código compartido que crea subclases en la clase Shell . Este archivo puede tener cualquier nombre, pero se
recomienda AppShell. El siguiente código de ejemplo muestra un archivo AppShell.xaml recién creado:

<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xaminals.AppShell">

</Shell>

En el ejemplo siguiente se muestra el código subyacente, AppShell.xaml.cs:

using Xamarin.Forms;

namespace Xaminals
{
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
}
}

Arranque de una aplicación de Shell


Después de crear el archivo XAML que crea subclases en el objeto Shell , la propiedad MainPage de la clase App
debe establecerse en el objeto Shell con subclases:
namespace Xaminals
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new AppShell();
}
...
}
}

En este ejemplo, la clase AppShell es el archivo XAML que se deriva de la clase Shell .

NOTE
Mientras se compila una aplicación de Shell en blanco, su ejecución dará como resultado la generación de
InvalidOperationException .

Descripción de la jerarquía visual de la aplicación


El último paso para crear una aplicación de Xamarin.Forms Shell consiste en describir la jerarquía visual de la
aplicación, en la clase Shell con subclases. Una clase Shell con subclases consta de tres objetos jerárquicos
principales:
FlyoutItem o TabBar . Un FlyoutItem representa uno o varios elementos en el control flotante y debe usarse
cuando el modelo de navegación de la aplicación incluye un control flotante. Un TabBar representa la barra de
pestañas de la parte inferior y debe usarse cuando el patrón de navegación de la aplicación comienza con
pestañas en la parte inferior. Cada objeto FlyoutItem o TabBar es un elemento secundario del objeto Shell .
Tab , que representa contenido agrupado, navegable mediante las pestañas inferiores. Cada objeto Tab es un
elemento secundario de un objeto FlyoutItem o un objeto TabBar .
ShellContent , que representa el objeto ContentPage en la aplicación. Cada objeto ShellContent es un
elemento secundario de un objeto Tab . Cuando hay más de un objeto ShellContent en un objeto Tab , los
objetos serán navegables mediante las pestañas superiores.
Ninguno de estos objetos representa ninguna interfaz de usuario, sino más bien la organización de la jerarquía
visual de la aplicación. Shell tomará estos elementos y generará la interfaz de usuario de navegación del contenido.
El siguiente XAML muestra un ejemplo de una clase Shell con subclases:
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell">
...
<FlyoutItem Title="Animals"
FlyoutDisplayOptions="AsMultipleItems">
<Tab Title="Domestic"
Icon="paw.png">
<ShellContent Title="Cats"
Icon="cat.png">
<views:CatsPage />
</ShellContent>
<ShellContent Title="Dogs"
Icon="dog.png">
<views:DogsPage />
</ShellContent>
</Tab>
<ShellContent Title="Monkeys"
Icon="monkey.png">
<views:MonkeysPage />
</ShellContent>
<ShellContent Title="Elephants"
Icon="elephant.png">
<views:ElephantsPage />
</ShellContent>
<ShellContent Title="Bears"
Icon="bear.png">
<views:BearsPage />
</ShellContent>
</FlyoutItem>
...
</Shell>

Cuando se ejecuta, este XAML muestra el objeto CatsPage , porque es el primer elemento de contenido declarado
en la clase Shell con subclases:

Al presionar el icono de tres barras, o al deslizar el dedo desde la izquierda, se muestra el control flotante:
IMPORTANT
En una aplicación de Shell, cada ContentPage que es un elemento secundario de un objeto ShellContent se crea durante
el inicio de la aplicación. Agregar objetos ShellContent adicionales mediante esta estrategia dará lugar a la creación de
páginas adicionales durante el inicio de la aplicación, lo que puede conducir a una experiencia de inicio deficiente. Sin
embargo, Shell también es capaz de crear páginas a petición, en respuesta a la navegación. Para más información, consulte
Carga eficiente de páginas en la guía Pestañas de Xamarin.Forms Shell.

Vínculos relacionados
Xaminals (ejemplo)
Control flotante de Xamarin.Forms Shell
11/07/2019 • 21 minutes to read • Edit Online

Descargar el ejemplo
El control flotante es el menú raíz de una aplicación de Shell y es accesible por medio de un icono o al deslizar el
dedo desde el lado de la pantalla. El control flotante consta de un encabezado opcional, elementos de control
flotante y elementos de menú opcionales:

Si es necesario, el color de fondo del control flotante se puede establecer Color mediante la propiedad enlazable
Shell.FlyoutBackgroundColor . Esta propiedad también se puede establecer con una hoja de estilo CSS. Para más
información, consulte Propiedades específicas de Xamarin.Forms Shell.

Icono de control flotante


De forma predeterminada, las aplicaciones de Shell tienen un icono de tres barras que, cuando se presiona, abre el
control flotante. Este icono se puede cambiar si se establece la propiedad enlazable Shell.FlyoutIcon , de tipo
ImageSource , en un icono adecuado:

<Shell ...
FlyoutIcon="flyouticon.png">
...
</Shell>

Comportamiento del control flotante


Al control flotante se tiene acceso mediante el icono de tres barras o al deslizar el dedo desde el lado de la pantalla.
Sin embargo, este comportamiento se puede cambiar si se establece la propiedad adjunta Shell.FlyoutBehavior
en uno de los miembros de la enumeración FlyoutBehavior :
Disabled : indica que el usuario no puede abrir el control flotante.
Flyout : indica que el usuario puede abrir y cerrar el control flotante. Este es el valor predeterminado de la
propiedad FlyoutBehavior .
Locked : indica que el usuario no puede cerrar el control flotante, y que este no solapa el contenido.

En el siguiente ejemplo se muestra cómo deshabilitar el control flotante.

<Shell ...
FlyoutBehavior="Disabled">
...
</Shell>

NOTE
La propiedad adjunta FlyoutBehavior se puede establecer en los objetos Shell , FlyoutItem , ShellContent y en
objetos de página, para invalidar el comportamiento predeterminado del control flotante.

Además, el control flotante se puede abrir y cerrar mediante programación si se establece la propiedad enlazable
Shell.FlyoutIsPresented en un valor boolean que indica si el control flotante es visible actualmente:

Shell.Current.FlyoutIsPresented = false;

Encabezado de control flotante


El encabezado de control flotante es el contenido que aparece opcionalmente en la parte superior del control
flotante y su apariencia se define mediante un elemento object que se puede establecer mediante el valor de la
propiedad Shell.FlyoutHeader :

<Shell.FlyoutHeader>
<controls:FlyoutHeader />
</Shell.FlyoutHeader>

El tipo FlyoutHeader se muestra en el ejemplo siguiente:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xaminals.Controls.FlyoutHeader"
HeightRequest="200">
<Grid BackgroundColor="Black">
<Image Aspect="AspectFill"
Source="xamarinstore.jpg"
Opacity="0.6" />
<Label Text="Animals"
TextColor="White"
FontAttributes="Bold"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
</Grid>
</ContentView>

El resultado es el siguiente encabezado de control flotante:


Como alternativa, la apariencia del encabezado de control flotante se puede definir mediante el establecimiento de
la propiedad Shell.FlyoutHeaderTemplate en un objeto DataTemplate :

<Shell.FlyoutHeaderTemplate>
<DataTemplate>
<Grid BackgroundColor="Black"
HeightRequest="200">
<Image Aspect="AspectFill"
Source="xamarinstore.jpg"
Opacity="0.6" />
<Label Text="Animals"
TextColor="White"
FontAttributes="Bold"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
</Grid>
</DataTemplate>
</Shell.FlyoutHeaderTemplate>

De forma predeterminada, el encabezado de control flotante se corregirá en el control flotante mientras el


contenido que va a continuación se desplazará si hay suficientes elementos. Sin embargo, este comportamiento se
puede cambiar si se establece la propiedad enlazable Shell.FlyoutHeaderBehavior en uno de los miembros de la
enumeración FlyoutHeaderBehavior :
Default : indica que se usará el comportamiento predeterminado para la plataforma. Este es el valor
predeterminado de la propiedad FlyoutHeaderBehavior .
Fixed : indica que el encabezado de control flotante permanece sin cambios y visible en todo momento.
Scroll : indica que el encabezado de control flotante se desplaza fuera de la vista cuando el usuario desplaza
los elementos.
CollapseOnScroll : indica que el encabezado de control flotante se contrae solo en un título, a medida que el
usuario desplaza los elementos.
En el ejemplo siguiente se muestra cómo contraer el encabezado de control flotante a medida que el usuario se
desplaza:

<Shell ...
FlyoutHeaderBehavior="CollapseOnScroll">
...
</Shell>

Elementos del control flotante


Cuando el modelo de navegación de una aplicación incluye un control flotante, el objeto Shell con subclases
debe contener uno o varios objetos FlyoutItem , donde cada objeto FlyoutItem representa un elemento del
control flotante. Cada objeto FlyoutItem debe ser un elemento secundario del objeto Shell .
En el ejemplo siguiente se crea un control flotante que contiene un encabezado y dos elementos:
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Xaminals.Controls"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell">
<Shell.FlyoutHeader>
<controls:FlyoutHeader />
</Shell.FlyoutHeader>
<FlyoutItem Title="Cats"
Icon="cat.png">
<Tab>
<ShellContent>
<views:CatsPage />
</ShellContent>
</Tab>
</FlyoutItem>
<FlyoutItem Title="Dogs"
Icon="dog.png">
<Tab>
<ShellContent>
<views:DogsPage />
</ShellContent>
</Tab>
</FlyoutItem>
</Shell>

En este ejemplo, solo se puede acceder a cada objeto ContentPage mediante elementos de control flotante:

NOTE
Cuando no existe un encabezado de control flotante, aparecen elementos de control flotante en la parte superior del control
flotante. En caso contrario, aparecen debajo del encabezado de control flotante.

Shell tiene operadores de conversión implícita que permiten simplificar la jerarquía visual de Shell, sin introducir
vistas adicionales en el árbol visual. Esto es posible porque un objeto Shell con subclases solo puede contener
objetos FlyoutItem o un objeto TabBar , que solo pueden contener objetos Tab , que solo pueden contener
objetos ShellContent . Estos operadores de conversión implícita pueden usarse para quitar los objetos FlyoutItem ,
Tab y ShellContent del ejemplo anterior:

<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Xaminals.Controls"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell">
<Shell.FlyoutHeader>
<controls:FlyoutHeader />
</Shell.FlyoutHeader>
<views:CatsPage IconImageSource="cat.png" />
<views:DogsPage IconImageSource="dog.png" />
</Shell>
Esta conversión implícita encapsula automáticamente cada objeto ContentPage en objetos ShellContent , que se
encapsulan en objetos Tab , que se encapsulan en objetos FlyoutItem .

IMPORTANT
En una aplicación de Shell, cada ContentPage que es un elemento secundario de un objeto ShellContent se crea durante
el inicio de la aplicación. Agregar objetos ShellContent adicionales mediante esta estrategia dará lugar a la creación de
páginas adicionales durante el inicio de la aplicación, lo que puede conducir a una experiencia de inicio deficiente. Sin
embargo, Shell también es capaz de crear páginas a petición, en respuesta a la navegación. Para más información, consulte
Carga eficiente de páginas en la guía Pestañas de Xamarin.Forms Shell.

Clase FlyoutItem
La clase FlyoutItem incluye las siguientes propiedades que controlan la apariencia y comportamiento del
elemento del control flotante:
FlyoutDisplayOptions , de tipo FlyoutDisplayOptions , define cómo se muestran el elemento y sus elementos
secundarios en el control flotante. El valor predeterminado es AsSingleItem .
CurrentItem , de tipo Tab , el elemento seleccionado.
Items , de tipo IList<Tab> , define todas las pestañas dentro de FlyoutItem .
FlyoutIcon , de tipo ImageSource , el icono que se usará para el elemento. Si esta propiedad no está establecida,
se volverá a usar el valor de la propiedad Icon .
Icon , de tipo ImageSource , define el icono que se mostrará en las partes del cromo que no son el control
flotante.
IsChecked , de tipo boolean , define si el elemento está actualmente resaltado en la ventana flotante.
IsEnabled , de tipo boolean , define si el elemento es seleccionable en el cromo.
IsTabStop , de tipo bool , indica si se incluye un objeto FlyoutItem en la navegación entre pestañas. Su valor
predeterminado es true , y cuando su valor es false , la infraestructura de navegación entre pestañas omite el
objeto FlyoutItem , independientemente de si se ha definido un objeto TabIndex .
TabIndex , de tipo int , indica el orden en que los objetos FlyoutItem reciben el foco cuando el usuario navega
por los elementos presionando la tecla de tabulación. El valor predeterminado de la propiedad es 0.
Title , de tipo string , el título que se mostrará en la interfaz de usuario.
Route , de tipo string , la cadena usada para abordar el elemento.

Todas estas propiedades, excepto la propiedad Route , están respaldadas por objetos BindableProperty , lo que
significa que las propiedades pueden ser destinos de los enlaces de datos.

NOTE
Todos los objetos FlyoutItem del objeto Shell en subclase se agregan a la colección Shell.Items , que define la lista de
elementos que se mostrarán en el control flotante.

Además, la clase FlyoutItem expone los siguientes métodos reemplazables:


OnTabIndexPropertyChanged , que se llama cada vez que cambia la propiedad TabIndex .
OnTabStopPropertyChanged , que se llama cada vez que cambia la propiedad IsTabStop .
TabIndexDefaultValueCreator , devuelve un elemento int , y se llama para establecer el valor predeterminado
de la propiedad TabIndex .
TabStopDefaultValueCreator , devuelve un elemento bool , y se llama para establecer el valor predeterminado
de la propiedad TabStop .
Opciones de presentación de control flotante
La enumeración FlyoutDisplayOptions define los miembros siguientes:
AsSingleItem , indica que el elemento será visible como un único elemento.
AsMultipleItems , indica que el elemento y sus elementos secundarios serán visibles en el control flotante como
un grupo de elementos.
Al establecer la propiedad FlyoutItem.FlyoutDisplayOptions en AsMultipleItems , se creará un elemento de control
flotante para cada objeto Tab dentro de un objeto FlyoutItem :

<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Xaminals.Controls"
xmlns:views="clr-namespace:Xaminals.Views"
FlyoutHeaderBehavior="CollapseOnScroll"
x:Class="Xaminals.AppShell">

<Shell.FlyoutHeader>
<controls:FlyoutHeader />
</Shell.FlyoutHeader>

<FlyoutItem Title="Animals"
FlyoutDisplayOptions="AsMultipleItems">
<Tab Title="Domestic"
Icon="paw.png">
<ShellContent Title="Cats"
Icon="cat.png"
ContentTemplate="{DataTemplate views:CatsPage}" />
<ShellContent Title="Dogs"
Icon="dog.png"
ContentTemplate="{DataTemplate views:DogsPage}" />
</Tab>
<ShellContent Title="Monkeys"
Icon="monkey.png"
ContentTemplate="{DataTemplate views:MonkeysPage}" />
<ShellContent Title="Elephants"
Icon="elephant.png"
ContentTemplate="{DataTemplate views:ElephantsPage}" />
<ShellContent Title="Bears"
Icon="bear.png"
ContentTemplate="{DataTemplate views:BearsPage}" />
</FlyoutItem>

<ShellContent Title="About"
Icon="info.png"
ContentTemplate="{DataTemplate views:AboutPage}" />
</Shell>

En este ejemplo, se crean elementos de control flotante para el objeto Tab , que es secundario del objeto
FlyoutItem , y el objeto ShellContent , que son elementos secundarios del objeto FlyoutItem . Esto ocurre porque
cada objeto ShellContent que es secundario del objeto FlyoutItem se encapsula automáticamente en un objeto
Tab . Además, se crea un elemento de control flotante para el objeto ShellContent final, que se encapsula en un
objeto Tab y, luego, en un objeto FlyoutItem .
El resultado son los siguientes elementos de control flotante:
Definición de la apariencia de FlyoutItem
Se puede personalizar la apariencia de cada FlyoutItem mediante el establecimiento de la propiedad adjunta
Shell.ItemTemplate en un objeto DataTemplate :

<Shell ...>
...
<Shell.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="0.8*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding FlyoutIcon}"
Margin="5"
HeightRequest="45" />
<Label Grid.Column="1"
Text="{Binding Title}"
FontAttributes="Italic"
VerticalTextAlignment="Center" />
</Grid>
</DataTemplate>
</Shell.ItemTemplate>
</Shell>

En este ejemplo se muestra el título de cada objeto FlyoutItem en cursiva:

NOTE
Shell proporciona las propiedades Title y FlyoutIcon para el objeto BindingContext de ItemTemplate .

Orden de tabulación de FlyoutItem


De forma predeterminada, el orden de tabulación de los objetos FlyoutItem es el mismo orden en que se
enumeran en XAML o se agregan mediante programación a una colección secundaria. Este es el orden en que se
navegará por los objetos FlyoutItem con un teclado y, con frecuencia, este orden predeterminado es el mejor
orden posible.
Se puede cambiar el orden de tabulación predeterminado mediante el establecimiento de la propiedad
FlyoutItem.TabIndex , que indica el orden en que los objetos FlyoutItem reciben el foco cuando el usuario navega
por los elementos presionando la tecla de tabulación. El valor predeterminado de la propiedad es 0, y se puede
establecer en cualquier valor int .
Las reglas siguientes aplican cuando se usa el orden de tabulación predeterminado, o cuando se establece la
propiedad TabIndex :
Los objetos FlyoutItem con un objeto TabIndex igual a 0 se agregan al orden de tabulación según su orden de
declaración en XAML o colecciones secundarias.
Los objetos FlyoutItem con un valor de TabIndex mayor que 0 se agregan al orden de tabulación en función
de su valor TabIndex .
Los objetos FlyoutItem con un valor de TabIndex menor que 0 se agregan al orden de tabulación y aparecen
antes que cualquier valor 0.
Los conflictos relacionados con TabIndex se resuelven por orden de declaración.

Tras definirse un orden de tabulación, al presionar la tecla TAB se recorrerá cíclicamente el foco a través de objetos
FlyoutItem en orden de TabIndex ascendente, con una encapsulación alrededor del principio una vez alcanzado el
control final.
Además de establecer el orden de tabulación de los objetos FlyoutItem , puede ser necesario excluir algunos
objetos de dicho orden. Para ello, se puede usar la propiedad FlyoutItem.IsTabStop , que indica si un objeto
FlyoutItem se incluye en la navegación entre pestañas. Su valor predeterminado es true , y cuando su valor es
false , la infraestructura de navegación entre pestañas omite el objeto FlyoutItem , independientemente de si se
ha definido un objeto TabIndex .

Establecimiento del objeto FlyoutItem actual


La clase Shell tiene una propiedad enlazable llamada CurrentItem , de tipo FlyoutItem , que representa
actualmente el objeto FlyoutItem seleccionado. La primera vez que se ejecuta una aplicación de Shell, esta
propiedad se establecerá en el primer objeto FlyoutItem del objeto Shell en subclase. Sin embargo, la propiedad
se puede establecer en otro objeto FlyoutItem , como se muestra en el ejemplo siguiente:

<Shell ...
CurrentItem="{x:Reference aboutItem}">
<FlyoutItem Title="Animals"
FlyoutDisplayOptions="AsMultipleItems">
...
</FlyoutItem>
<ShellContent x:Name="aboutItem"
Title="About"
Icon="info.png"
ContentTemplate="{DataTemplate views:AboutPage}" />
</Shell>

Este código establece el objeto ShellContent llamado aboutItem como la propiedad CurrentItem , que hace que
se muestre. En este ejemplo, se usa una conversión implícita para encapsular el objeto ShellContent en un objeto
Tab , que se encapsula en un objeto FlyoutItem .

El código de C# equivalente es el siguiente:


Shell.Current.CurrentItem = aboutItem;

Elementos de menú
Los elementos del menú se pueden agregar opcionalmente a la ventana flotante, y cada elemento de menú se
representa mediante un objeto MenuItem . La posición de los objetos MenuItem en el control flotante depende de su
orden de declaración en la jerarquía visual del Shell. Por lo tanto, cualquier objeto MenuItem declarado antes de los
objetos FlyoutItem aparecerá en la parte superior del control flotante, y cualquier objeto MenuItem declarado
después de los objetos FlyoutItem aparecerá en la parte inferior del control flotante.

NOTE
La clase MenuItem tiene un evento Clicked y una propiedad Command . Por lo tanto, los objetos MenuItem permiten
escenarios que ejecutan una acción en respuesta al elemento MenuItem que se pulsa. Estos escenarios incluyen realizar la
navegación y abrir un explorador web en una página web específica.

Se pueden agregar objetos MenuItem al control flotante, tal como se muestra en el ejemplo siguiente:

<Shell ...>
...
<MenuItem Text="Random"
IconImageSource="random.png"
Command="{Binding RandomPageCommand}" />
<MenuItem Text="Help"
IconImageSource="help.png"
Command="{Binding HelpCommand}"
CommandParameter="https://docs.microsoft.com/xamarin/xamarin-forms/app-fundamentals/shell" />
</Shell>

Este código agrega dos objetos MenuItem al control flotante, debajo de todos los elementos del control flotante:

El primer objeto MenuItem ejecuta un elemento ICommand llamado RandomPageCommand , que lleva a una página
aleatoria de la aplicación. El segundo objeto MenuItem ejecuta un elemento ICommand llamado HelpCommand , que
abre la dirección URL especificada por la propiedad CommandParameter en un explorador web.
NOTE
El elemento BindingContext de cada MenuItem se hereda del objeto Shell en subclase.

Definición de la apariencia de MenuItem


Se puede personalizar la apariencia de cada MenuItem mediante el establecimiento de la propiedad adjunta
Shell.MenuItemTemplate en un objeto DataTemplate :

<Shell ...>
<Shell.MenuItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="0.8*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding Icon}"
Margin="5"
HeightRequest="45" />
<Label Grid.Column="1"
Text="{Binding Text}"
FontAttributes="Italic"
VerticalTextAlignment="Center" />
</Grid>
</DataTemplate>
</Shell.MenuItemTemplate>
...
<MenuItem Text="Random"
IconImageSource="random.png"
Command="{Binding RandomPageCommand}" />
<MenuItem Text="Help"
IconImageSource="help.png"
Command="{Binding HelpCommand}"
CommandParameter="https://docs.microsoft.com/xamarin/xamarin-forms/app-fundamentals/shell" />
</Shell>

Este ejemplo adjunta el nivel de Shell MenuItemTemplate a cada objeto MenuItem , mostrando el título de cada
objeto MenuItem en cursiva:
NOTE
Shell proporciona las propiedades Text y IconImageSource para el elemento BindingContext del objeto
MenuItemTemplate .`

Dado que Shell.MenuItemTemplate es una propiedad adjunta, se pueden asociar diferentes plantillas a objetos
MenuItem específicos:

<Shell ...>
<Shell.MenuItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="0.8*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding Icon}"
Margin="5"
HeightRequest="45" />
<Label Grid.Column="1"
Text="{Binding Text}"
FontAttributes="Italic"
VerticalTextAlignment="Center" />
</Grid>
</DataTemplate>
</Shell.MenuItemTemplate>
...
<MenuItem Text="Random"
IconImageSource="random.png"
Command="{Binding RandomPageCommand}" />
<MenuItem Text="Help"
Icon="help.png"
Command="{Binding HelpCommand}"
CommandParameter="https://docs.microsoft.com/xamarin/xamarin-forms/app-fundamentals/shell">
<Shell.MenuItemTemplate>
<DataTemplate>
...
</DataTemplate>
</Shell.MenuItemTemplate>
</MenuItem>
</Shell>

Este ejemplo adjunta el MenuItemTemplate de nivel de Shell al primer objeto MenuItem y adjunta el
MenuItemTemplate alineado al segundo MenuItem .

Vínculos relacionados
Xaminals (ejemplo)
Pestañas de Xamarin.Forms Shell
17/07/2019 • 15 minutes to read • Edit Online

Descargar el ejemplo
Cuando el modelo de navegación para una aplicación incluye un control flotante, el siguiente nivel de navegación
de la aplicación es la barra de pestañas de la parte inferior. Además, cuando se cierra el control flotante, la barra
de pestañas inferior se puede considerar el nivel superior de navegación.
Como alternativa, el modelo de navegación para una aplicación puede comenzar con pestañas en la parte inferior
y no usar un control flotante. En este escenario, el elemento secundario del objeto Shell debe ser un objeto
TabBar , que representa la barra de pestañas de la parte inferior.

NOTE
El tipo TabBar deshabilita el control flotante.

Cada objeto FlyoutItem o TabBar puede contener uno o varios objetos Tab , donde cada objeto Tab representa
una pestaña en la barra de pestañas inferior. Cada objeto Tab puede contener uno o varios objetos ShellContent
, y cada objeto ShellContent mostrará un único objeto ContentPage . Cuando hay más de un objeto ShellContent
en un objeto Tab , los objetos ContentPage serán navegables mediante las pestañas superiores.
Dentro de cada objeto ContentPage , se puede navegar a objetos ContentPage adicionales. Para más información
sobre la navegación, consulte Navegación en Xamarin.Forms Shell.

Aplicación de página única


La aplicación más sencilla de Shell es una aplicación de página única, que se puede crear mediante la adición de
un único objeto Tab a un objeto TabBar . Dentro del objeto Tab , un objeto ShellContent se debe establecer en
un objeto ContentPage :

<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell">
<TabBar>
<Tab>
<ShellContent>
<views:CatsPage />
</ShellContent>
</Tab>
</TabBar>
</Shell>

Este ejemplo de código tiene como resultado la siguiente aplicación de página única:
NOTE
Si es necesario, la barra de navegación se puede ocultar mediante el establecimiento de la propiedad adjunta
Shell.NavBarIsVisible en false en el objeto ContentPage .

Shell tiene operadores de conversión implícita que permiten simplificar la jerarquía visual de Shell, sin introducir
vistas adicionales en el árbol visual. Esto es posible porque un objeto Shell con subclases solo puede contener
objetos FlyoutItem o un objeto TabBar , que solo pueden contener objetos Tab , que solo pueden contener
objetos ShellContent . Estos operadores de conversión implícita pueden usarse para quitar los objetos TabBar ,
Tab y ShellContent del ejemplo anterior:

<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell"
FlyoutBehavior="Disabled">
<views:CatsPage />
</Shell>

Esta conversión implícita encapsula automáticamente el objeto ContentPage en un objeto ShellContent , que se
encapsula en un objeto Tab , que se encapsula en un objeto FlyoutItem . Un control flotante no es necesario en
una aplicación de página única y, por lo tanto, la propiedad Shell.FlyoutBehavior está establecida en Disabled .

IMPORTANT
En una aplicación de Shell, cada ContentPage que es un elemento secundario de un objeto ShellContent se crea
durante el inicio de la aplicación. Agregar objetos ShellContent adicionales mediante esta estrategia dará lugar a la
creación de páginas adicionales durante el inicio de la aplicación, lo que puede conducir a una experiencia de inicio deficiente.
Sin embargo, Shell también es capaz de crear páginas a petición, en respuesta a la navegación. Para más información,
consulte Carga eficiente de páginas.

Pestañas inferiores
Los objetos Tab se representan como pestañas inferiores, siempre y cuando haya varios objetos Tab en un
único objeto TabBar :
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell">
<TabBar>
<Tab Title="Cats"
Icon="cat.png">
<ShellContent>
<views:CatsPage />
</ShellContent>
</Tab>
<Tab Title="Dogs"
Icon="dog.png">
<ShellContent>
<views:DogsPage />
</ShellContent>
</Tab>
</TabBar>
</Shell>

Los títulos e iconos de pestaña se establecen en cada objeto Tab , y se muestran en las pestañas inferiores:

Como alternativa, se pueden usar operadores de conversión implícita de Shell para quitar los objetos
ShellContent y Tab del ejemplo anterior:

<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell">
<TabBar>
<views:CatsPage IconImageSource="cat.png" />
<views:DogsPage IconImageSource="dog.png" />
</TabBar>
</Shell>

Esta conversión implícita encapsula automáticamente cada objeto ContentPage en un objeto ShellContent ,
ambos de los cuales se encapsulan en un objeto Tab .
IMPORTANT
En una aplicación de Shell, cada ContentPage que es un elemento secundario de un objeto ShellContent se crea
durante el inicio de la aplicación. Agregar objetos ShellContent adicionales mediante esta estrategia dará lugar a la
creación de páginas adicionales durante el inicio de la aplicación, lo que puede conducir a una experiencia de inicio deficiente.
Sin embargo, Shell también es capaz de crear páginas a petición, en respuesta a la navegación. Para más información,
consulte Carga eficiente de páginas.

Clase Tab
La clase Tab incluye las siguientes propiedades que controlan la apariencia y el comportamiento de las pestañas:
CurrentItem , de tipo , el elemento seleccionado.
ShellContent
FlyoutDisplayOptions , de tipo FlyoutDisplayOptions , define cómo se muestran el elemento y sus elementos
secundarios en el control flotante. El valor predeterminado es AsSingleItem .
FlyoutIcon , de tipo ImageSource , define el icono que se mostrará en el control flotante.
Icon , de tipo ImageSource , define el icono que se mostrará en partes del cromo que no son la ventana
flotante.
IsChecked , de tipo boolean , define si el elemento está actualmente resaltado en la ventana flotante.
IsEnabled , de tipo boolean , define si el elemento es seleccionable en el cromo.
IsTabStop , de tipo bool , indica si se incluye un objeto Tab en la navegación entre pestañas. Su valor
predeterminado es true , y cuando su valor es false , la infraestructura de navegación entre pestañas omite
el objeto Tab , independientemente de si se ha definido un objeto TabIndex .
Items , de tipo IList<ShellContent> , define todo el contenido dentro de un objeto Tab .
TabIndex , de tipo int , indica el orden en que los objetos Tab reciben el foco cuando el usuario navega por
los elementos presionando la tecla de tabulación. El valor predeterminado de la propiedad es 0.
Title , de tipo string , el título que se mostrará en la pestaña en la interfaz de usuario.

Contenido de Shell
El elemento secundario de cada objeto Tab es un objeto ShellContent cuya propiedad Content está establecida
en ContentPage :

<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell">
<TabBar>
<Tab Title="Cats"
Icon="cat.png">
<ShellContent>
<views:CatsPage />
</ShellContent>
</Tab>
<Tab Title="Dogs"
Icon="dog.png">
<ShellContent>
<views:DogsPage />
</ShellContent>
</Tab>
</TabBar>
</Shell>

Dentro de cada objeto ContentPage , se puede navegar a objetos ContentPage adicionales. Para más información
sobre la navegación, consulte Navegación en Xamarin.Forms Shell.
Clase ShellContent
La clase ShellContent incluye las siguientes propiedades que controlan la apariencia del contenido de las
pestañas y su comportamiento:
Content , de tipo , el contenido de ShellContent .
object
ContentTemplate , de tipo DataTemplate , la plantilla usada para aumentar dinámicamente el contenido de
ShellContent .
FlyoutIcon , de tipo ImageSource , define el icono que se mostrará en el control flotante.
Icon , de tipo ImageSource , define el icono que se mostrará en partes del cromo que no son la ventana
flotante.
IsChecked , de tipo boolean , define si el elemento está actualmente resaltado en la ventana flotante.
IsEnabled , de tipo boolean , define si el elemento es seleccionable en el cromo.
MenuItems , de tipo MenuItemCollection , los elementos de menú para mostrar en el control flotante cuando
ShellContent es la página presentada.
Title , de tipo string , el título que se mostrará en la interfaz de usuario.

Todas estas propiedades están respaldados por objetos BindableProperty , lo que significa que las propiedades
pueden ser destinos de los enlaces de datos.

Pestañas inferiores y superiores


Cuando hay más de un objeto ShellContent en un objeto Tab , se agrega una barra de pestañas principales a la
pestaña inferior, mediante las cuales se puede navegar por los objetos ContentPage :

<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell">
<TabBar>
<Tab Title="Domestic"
Icon="domestic.png">
<ShellContent>
<views:CatsPage />
</ShellContent>
<ShellContent>
<views:DogsPage />
</ShellContent>
</Tab>
<Tab Title="Monkeys"
Icon="monkey.png">
<ShellContent>
<views:MonkeysPage />
</ShellContent>
</Tab>
</TabBar>
</Shell>

El resultado es el diseño que se muestra en las capturas de pantalla siguientes:


Como alternativa, se pueden usar operadores de conversión implícita de Shell para quitar los objetos
ShellContent y el segundo objeto Tab del ejemplo anterior:

<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell">
<TabBar>
<Tab Title="Domestic"
Icon="domestic.png">
<views:CatsPage />
<views:DogsPage />
</Tab>
<views:MonkeysPage IconImageSource="monkey.png" />
</TabBar>
</Shell>

Esta conversión implícita encapsula automáticamente MonkeysPage en un objeto ShellContent , el cual se


encapsula en un objeto Tab . Además, CatsPage y DogsPage se encapsulan implícitamente en objetos
ShellContent .

Carga eficiente de páginas


En una aplicación de Shell, cada objeto ContentPage de un objeto ShellContent se crea durante el inicio de la
aplicación, lo que puede conducir a una experiencia de inicio deficiente. Sin embargo, Shell también permite que
las páginas se creen a petición, en respuesta a la navegación. Para ello, se puede usar la extensión de marcado
DataTemplate para convertir cada objeto ContentPage en un objeto DataTemplate y, luego, establecer el resultado
como el valor de la propiedad ShellContent.ContentTemplate :
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Xaminals.Views"
x:Class="Xaminals.AppShell">
<TabBar>
<Tab Title="Domestic"
Icon="paw.png">
<ShellContent Title="Cats"
Icon="cat.png"
ContentTemplate="{DataTemplate views:CatsPage}" />
<ShellContent Title="Dogs"
Icon="dog.png"
ContentTemplate="{DataTemplate views:DogsPage}" />
</Tab>
<ShellContent Title="Monkeys"
Icon="monkey.png"
ContentTemplate="{DataTemplate views:MonkeysPage}" />
</TabBar>
</Shell>

Este XAML crea y muestra CatsPage , porque es el primer elemento del contenido declarado en el objeto Shell
en subclase. Se puede navegar por las páginas CatsPage y MonkeysPage mediante pestañas inferiores, y estas
páginas solo se crean cuando el usuario se desplaza hasta ellas. La ventaja de este enfoque es que se evita la
experiencia de inicio deficiente, ya que las páginas se crean a petición en respuesta a la navegación, y no cuando
se inicia la aplicación.

Apariencia de las pestañas


La clase Shell define las siguientes propiedades adjuntas que controlan la apariencia de las pestañas:
TabBarBackgroundColor , de tipo Color , que define el color de fondo de la barra de pestañas. Si la propiedad no
está establecida, se usa el valor de la propiedad BackgroundColor .
TabBarDisabledColor , de tipo Color , que define el color deshabilitado de la barra de pestañas. Si la propiedad
no está establecida, se usa el valor de la propiedad DisabledColor .
TabBarForegroundColor , de tipo Color , que define el color de primer plano de la barra de pestañas. Si la
propiedad no está establecida, se usa el valor de la propiedad ForegroundColor .
TabBarTitleColor , de tipo Color , que define el color de título de la barra de pestañas. Si la propiedad no está
establecida, se usa el valor de la propiedad TitleColor .
TabBarUnselectedColor , de tipo Color , que define el color no seleccionado de la barra de pestañas. Si la
propiedad no está establecida, se usa el valor de la propiedad UnselectedColor .

Todas estas propiedades están respaldados por objetos BindableProperty , lo que significa que las propiedades
pueden ser destinos de los enlaces de datos, y se les puede aplicar estilos.
En el ejemplo siguiente se muestra un estilo XAML que establece diferentes propiedades de color de las pestañas:

<Style x:Key="BaseStyle"
TargetType="Element">
<Setter Property="Shell.TabBarBackgroundColor"
Value="#3498DB" />
<Setter Property="Shell.TabBarTitleColor"
Value="White" />
<Setter Property="Shell.TabBarUnselectedColor"
Value="#B4FFFFFF" />
</Style>

Además, también se puede aplicar estilo a las pestañas mediante Hojas de estilos CSS. Para más información,
consulte Propiedades específicas de Xamarin.Forms Shell.

Vínculos relacionados
Xaminals (ejemplo)
Navegación en Xamarin.Forms Shell
Xamarin.Forms CSS Shell specific properties (Propiedades específicas de Xamarin.Forms CSS Shell)
Configuración de la página Xamarin.Forms Shell
11/07/2019 • 7 minutes to read • Edit Online

Descargar el ejemplo
La clase Shell define las propiedades adjuntas que se pueden usar para configurar la apariencia de las páginas en
las aplicaciones de Xamarin.Forms Shell. Esto incluye establecer los colores de la página, deshabilitar la barra de
navegación y la barra de pestañas y mostrar las vistas en la barra de navegación.

Establecimiento de los colores de la página


La clase Shell define las siguientes propiedades adjuntas que pueden usarse para establecer los colores de la
página en una aplicación de Shell:
BackgroundColor , de tipo Color , que define el color de fondo en el cromo de Shell. El color no se rellena detrás
del contenido de Shell.
DisabledColor , de tipo Color , que define el color para sombrear el texto y los iconos que están deshabilitados.
ForegroundColor , de tipo Color , que define el color para sombrear el texto y los iconos.
TitleColor , de tipo Color , que define el color usado para el título de la página actual.
UnselectedColor , de tipo Color , que define el color usado para el texto y los iconos no seleccionados en el
cromo de Shell.
Todas estas propiedades están respaldadas por objetos BindableProperty , lo que significa que las propiedades
pueden ser destinos de los enlaces de datos y se les puede aplicar estilos mediante XAML. Además, estas
propiedades se pueden establecer mediante Hojas de estilo CSS. Para más información, consulte Propiedades
específicas de Xamarin.Forms Shell.

NOTE
También hay propiedades que permiten la definición de los colores de las pestañas. Para obtener más información, vea
Apariencia de las pestañas.

El XAML siguiente muestra el establecimiento de las propiedades de color en una clase Shell con subclases:

<?xml version="1.0" encoding="UTF-8"?>


<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xaminals.AppShell"
BackgroundColor="#455A64"
ForegroundColor="White"
TitleColor="White"
DisabledColor="#B4FFFFFF"
UnselectedColor="#95FFFFFF">

</Shell>

En este ejemplo, los valores de color se aplicarán a todas las páginas de la aplicación de Shell, a menos que se
invaliden en el nivel de página.
Dado que las propiedades de color son las propiedades adjuntas, también pueden establecerse en páginas
individuales, para establecer los colores de esa página:
<ContentPage ...
Shell.BackgroundColor="Gray"
Shell.ForegroundColor="White"
Shell.TitleColor="Blue"
Shell.DisabledColor="#95FFFFFF"
Shell.UnselectedColor="#B4FFFFFF">

</ContentPage>

Como alternativa, se pueden establecer las propiedades de color con un estilo XAML:

<Style x:Key="DomesticShell"
TargetType="Element" >
<Setter Property="Shell.BackgroundColor"
Value="#039BE6" />
<Setter Property="Shell.ForegroundColor"
Value="White" />
<Setter Property="Shell.TitleColor"
Value="White" />
<Setter Property="Shell.DisabledColor"
Value="#B4FFFFFF" />
<Setter Property="Shell.UnselectedColor"
Value="#95FFFFFF" />
</Style>

Para obtener más información sobre los estilos XAML, vea Styling Xamarin.Forms Apps using XAML Styles
(Aplicación de estilo a aplicaciones Xamarin.Forms mediante XAML ).

Deshabilitación de la barra de navegación


La clase Shell define la propiedad adjunta NavBarIsVisible , de tipo bool , que define si la barra de navegación
debe estar visible cuando se presenta una página. De forma predeterminada, el valor de la propiedad es true .
Aunque esta propiedad se puede establecer en un objeto Shell con subclases, normalmente se establece en todas
las páginas en las que se desea que no esté visible la barra de navegación. Por ejemplo, el siguiente XAML muestra
la deshabilitación de la barra de navegación de ContentPage :

<ContentPage ...
Shell.NavBarIsVisible="false">
...
</ContentPage>

Como resultado, la barra de navegación deja de ser visible cuando se presenta la página:
Deshabilitación de la barra de pestañas
La clase Shell define la propiedad adjunta TabBarIsVisible , de tipo bool , que define si la barra de pestañas
debe estar visible cuando se presenta una página. De forma predeterminada, el valor de la propiedad es true .
Aunque esta propiedad se puede establecer en un objeto Shell con subclases, normalmente se establece en todas
las páginas en las que se desea que no esté visible la barra de pestañas. Por ejemplo, el siguiente XAML muestra la
deshabilitación de la barra de pestañas de ContentPage :

<ContentPage ...
Shell.TabBarIsVisible="false">
...
</ContentPage>

Como resultado, la barra de pestañas deja de ser visible cuando se presenta la página:

Visualización de las vistas en la barra de navegación


La clase Shell define la propiedad adjunta TitleView , de tipo View , que permite que cualquier View de
Xamarin.Forms se muestre en la barra de navegación.
Aunque esta propiedad se puede establecer en un objeto Shell con subclases, también se puede establecer en
cualquier página en la que se desea que se muestre una vista en la barra de navegación. Por ejemplo, el siguiente
XAML muestra la visualización de Image en la barra de navegación de ContentPage :

<ContentPage ...>
<Shell.TitleView>
<Image Source="xamarin_logo.png"
HorizontalOptions="Center"
VerticalOptions="Center" />
</Shell.TitleView>
...
</ContentPage>

Esto da como resultado una imagen que se muestra en la barra de navegación de la página:

IMPORTANT
Si se ha configurado la barra de navegación para que no sea visible, con la propiedad adjunta NavBarIsVisible , la vista de
título no se mostrará.

Muchas vistas no aparecen en la barra de navegación, salvo que el tamaño de la vista se especifique con las
propiedades WidthRequest y HeightRequest , o la ubicación de la vista se especifique con las propiedades
HorizontalOptions y VerticalOptions .

Dado que la clase Layout se deriva de la clase View , la propiedad adjunta TitleView se puede establecer para
mostrar una clase de diseño que contiene varias vistas. De igual manera, dado que la clase ContentView se deriva
en última instancia de la clase View , la propiedad adjunta TitleView se puede establecer para mostrar un
ContentView que contenga una sola vista.

Vínculos relacionados
Xaminals (ejemplo)
Aplicación de estilos para aplicaciones Xamarin.Forms con estilos XAML
Xamarin.Forms CSS Shell specific properties (Propiedades específicas de Xamarin.Forms CSS Shell)
Navegación en Xamarin.Forms Shell
11/07/2019 • 20 minutes to read • Edit Online

Descargar el ejemplo
Xamarin.Forms Shell incluye una experiencia de navegación basada en el URI que emplea rutas para navegar a
cualquier página de la aplicación, sin tener que seguir una jerarquía de navegación establecida. También ofrece la
posibilidad de navegar hacia atrás sin tener que visitar todas las páginas de la pila de navegación.
Shell define las siguientes propiedades relacionadas con la navegación:
BackButtonBehavior , de tipo BackButtonBehavior , una propiedad adjunta que define el comportamiento del
botón Atrás.
CurrentItem , de tipo FlyoutItem , el valor de FlyoutItem seleccionado actualmente.
CurrentState , de tipo ShellNavigationState , el estado de navegación actual de Shell .
Current , de tipo Shell , un alias con el tipo convertido para Application.Current.MainPage .

Las propiedades BackButtonBehavior , CurrentItem y CurrentState están respaldadas por objetos


BindableProperty , lo que significa que estas propiedades pueden ser destinos de los enlaces de datos.

La navegación se realiza mediante la invocación del método GoToAsync , desde la clase Shell . Cuando la
navegación está a punto de realizarse, se activa un evento Navigating y cuando finaliza, se activa un evento
Navigated .

NOTE
La navegación todavía se puede realizar en una aplicación de Xamarin.Forms Shell mediante la propiedad Navigation. Para
más información, consulte Navegación jerárquica.

Rutas
La navegación se realiza en una aplicación de Shell mediante la especificación de un URI al que navegar. Los URI
de navegación pueden tener tres componentes:
Una ruta, que define la ruta de acceso al contenido que existe como parte de la jerarquía visual de Shell.
Una página. Las páginas que no existen en la jerarquía visual de Shell se pueden insertar en la pila de
navegación desde cualquier lugar dentro de una aplicación de Shell. Por ejemplo, una página de detalles de
elementos no se definirá en la jerarquía visual de Shell, pero se puede insertar en la pila de navegación si es
necesario.
Uno o varios parámetros de consulta. Los parámetros de consulta son parámetros que se pueden pasar a la
página de destino durante la navegación.
Cuando un URI de navegación incluye los tres componentes, la estructura es: //route/page?queryParameters
Registro de rutas
Las rutas se pueden definir en objetos FlyoutItem , Tab y ShellContent mediante sus propiedades Route :
<Shell ...>
<FlyoutItem ...
Route="animals">
<Tab ...
Route="domestic">
<ShellContent ...
Route="cats" />
<ShellContent ...
Route="dogs" />
</Tab>
<ShellContent ...
Route="monkeys" />
<ShellContent ...
Route="elephants" />
<ShellContent ...
Route="bears" />
</FlyoutItem>
<ShellContent ...
Route="about" />
...
</Shell>

NOTE
Todos los elementos de la jerarquía de Shell tienen asociada una ruta. Si el desarrollador no establece una ruta, esta se
genera en tiempo de ejecución. Sin embargo, no se garantiza que las rutas generadas sean coherentes entre distintas
sesiones de aplicación.

En este ejemplo se crea la siguiente jerarquía de ruta desde la misma aplicación, que se puede usar en la
navegación mediante programación:

animals
domestic
cats
dogs
monkeys
elephants
bears
about

Para desplazarse al objeto ShellContent de la ruta dogs , el URI de la ruta absoluta es //animals/domestic/dogs .
Igualmente, para desplazarse al objeto ShellContent de la ruta about , el URL de la ruta absoluta es //about .

IMPORTANT
Se permiten nombres duplicados de ruta. Sin embargo, no se permiten rutas duplicadas. Si se detecta una ruta duplicada,
se produce una excepción ArgumentException al inicio de la aplicación.

Registro de rutas de página


En el constructor de subclases de Shell, o en cualquier otra ubicación que se ejecute antes de invocar una ruta, se
pueden registrar explícitamente rutas adicionales para cualquier página que no esté representada en la jerarquía
visual de Shell:
Routing.RegisterRoute("monkeydetails", typeof(MonkeyDetailPage));
Routing.RegisterRoute("beardetails", typeof(BearDetailPage));
Routing.RegisterRoute("catdetails", typeof(CatDetailPage));
Routing.RegisterRoute("dogdetails", typeof(DogDetailPage));
Routing.RegisterRoute("elephantdetails", typeof(ElephantDetailPage));

En este ejemplo se registran como rutas páginas de detalles de elementos que no están definidas en la subclase
de Shell. Luego, es posible desplazarse por estas páginas desde cualquier lugar de la aplicación mediante la
navegación basada en el URI. Las rutas de estas páginas se conocen como rutas globales.

NOTE
Si es necesario, se puede cancelar el registro de las páginas cuyas rutas se han registrado con el método
Routing.RegisterRoute , con el método Routing.UnRegisterRoute .

Como alternativa, se pueden registrar las páginas en diferentes jerarquías de ruta:

Routing.RegisterRoute("monkeys/details", typeof(MonkeyDetailPage));
Routing.RegisterRoute("bears/details", typeof(BearDetailPage));
Routing.RegisterRoute("cats/details", typeof(CatDetailPage));
Routing.RegisterRoute("dogs/details", typeof(DogDetailPage));
Routing.RegisterRoute("elephants/details", typeof(ElephantDetailPage));

En este ejemplo se habilita la navegación contextual por las páginas, donde la navegación a la ruta details
desde la página de la ruta monkeys muestra MonkeyDetailPage . De forma similar, al desplazarse a la ruta
details desde la página de la ruta elephants se muestra ElephantDetailPage .

IMPORTANT
Actualmente, se permiten nombres de ruta duplicados cuando se usa el método Routing.RegisterRoute , donde el
registro duplicado sobrescribe el registro anterior.

Realización de la navegación
Para realizar la navegación, se debe obtener primero una referencia a la subclase Shell . Esta referencia se
puede obtener mediante la conversión de la propiedad App.Current.MainPage en un objeto Shell , por medio de
la propiedad Shell.Current . Luego, se puede realizar la navegación mediante la llamada al método GoToAsync
en el objeto Shell . Este método lleva a ShellNavigationState y devuelve un objeto Task que se completa una
vez completada la animación de navegación. El objeto ShellNavigationState se construye mediante el método
GoToAsync , desde una propiedad string o Uri , y tiene su propiedad Location establecida en el argumento
string o Uri .

Cuando se navega a una ruta de la jerarquía visual de Shell, no se crea una pila de navegación. Sin embargo,
cuando se navega a una página que no está en la jerarquía visual de Shell, se crea una pila de navegación.

NOTE
El estado actual de navegación de Shell se puede recuperar mediante la propiedad Shell.Current.CurrentState , que
incluye el URI de la ruta mostrada en la propiedad Location .

Rutas absolutas
Se puede realizar la navegación mediante la especificación de un URI absoluto válido como argumento del
método GoToAsync :

await Shell.Current.GoToAsync("//animals/monkeys");

En este ejemplo se navega a la página de la ruta monkeys , donde dicha ruta se define en un objeto ShellContent .
El objeto ShellContent que representa la ruta monkeys es un elemento secundario de un objeto FlyoutItem ,
cuya ruta es animals .
Rutas relativas
La navegación se puede realizar también mediante la especificación de un URI relativo válido como argumento
del método GoToAsync . El sistema de enrutamiento intenta hacer coincidir el URI con un objeto ShellContent .
Por lo tanto, si todas las rutas de una aplicación son únicas, la navegación se puede realizar con solo especificar
el nombre de ruta único como un URI relativo:

await Shell.Current.GoToAsync("monkeydetails");

En este ejemplo se navega a la página de la ruta monkeydetails .


Además, se admiten los siguientes formatos de ruta relativa:

FORMATO DESCRIPCIÓN

//route En la jerarquía de ruta se buscará la ruta especificada, hacia


arriba desde la ruta mostrada actualmente.

///route En la jerarquía de ruta se buscará la ruta especificada, hacia


abajo desde la ruta mostrada actualmente.

Navegación contextual
Las rutas relativas permiten la navegación contextual. Por ejemplo, considere la siguiente jerarquía de ruta:

monkeys
details
bears
details

Cuando se muestra la página registrada para la ruta monkeys , al navegar a la ruta details se mostrará la
página registrada para la ruta monkeys/details . Igualmente, cuando se muestra la página registrada para la ruta
bears , al navegar a la ruta details se mostrará la página registrada para la ruta bears/details . Para
información sobre cómo registrar las rutas en este ejemplo, consulte Registro de rutas de página.
Rutas no válidas
Los siguientes formatos de ruta no son válidos:

FORMATO EXPLICACIÓN

route o /route Las rutas de la jerarquía visual no se pueden insertar en la


pila de navegación.

//page o ///page Actualmente, las rutas globales no pueden ser la única


página en la pila de navegación. Por lo tanto, no se admite el
enrutamiento absoluto a rutas globales.

El uso de cualquiera de estos formatos de ruta dará como resultado una excepción Exception .
IMPORTANT
Al intentar navegar a una ruta inexistente, se producirá una excepción ArgumentException .

Depuración de la navegación
Algunas de las clases de Shell se representan con DebuggerDisplayAttribute , que especifica cómo el depurador
muestra una clase o campo. Esto puede ayudar a depurar las solicitudes de navegación puesto que se muestran
los datos relacionados con la solicitud de navegación. Por ejemplo, en la captura de pantalla siguiente se
muestran las propiedades CurrentItem y CurrentState del objeto Shell.Current :

En este ejemplo, la propiedad CurrentItem , de tipo FlyoutItem , muestra el título y la ruta del objeto FlyoutItem .
Igualmente, la propiedad CurrentState , de tipo ShellNavigationState , muestra el URI de la ruta mostrada
dentro de la aplicación de Shell.
Clase Tab
La clase Tab define una propiedad Stack , de tipo IReadOnlyList<Page> , que representa la pila de navegación
actual insertada en Tab . La clase también proporciona los siguientes métodos de navegación reemplazables:
GetNavigationStack , devuelve IReadOnlyList<Page >, la pila de navegación actual.
OnInsertPageBefore , que se llama cuando se llama a INavigation.InsertPageBefore .
OnPopAsync , devuelve Task<Page> , y se llama cuando se llama a INavigation.PopAsync .
OnPopToRootAsync , devuelve Task , y se llama cuando se llama a INavigation.OnPopToRootAsync .
OnPushAsync , devuelve Task , y se llama cuando se llama a INavigation.PushAsync .
OnRemovePage , que se llama cuando se llama a INavigation.RemovePage .

Eventos de navegación
La clase Shell define un evento Navigating , que se desencadena cuando está a punto de realizarse la
navegación, ya sea debido a la navegación mediante programación o a la interacción del usuario. El objeto
ShellNavigatingEventArgs que acompaña al evento Navigating proporciona las siguientes propiedades:

PROPIEDAD. TIPO DESCRIPCIÓN

Current ShellNavigationState Identificador URI de la página actual.

Source ShellNavigationSource El tipo de navegación que se ha


producido.

Target ShellNavigationState Identificador URI que representa el


destino de la navegación.

CanCancel bool Valor que indica si es posible cancelar la


navegación.

Cancelled bool Valor que indica si la navegación se ha


cancelado.

Además, la clase ShellNavigatingEventArgs proporciona un método Cancel que se puede usar para cancelar la
navegación.
NOTE
El evento Navigated se desencadena por el método OnNavigating reemplazable de la clase Shell .

La clase Shell también define un evento Navigated , que se desencadena cuando se ha completado la
navegación. El objeto ShellNavigatedEventArgs que acompaña al evento Navigating proporciona las siguientes
propiedades:

PROPIEDAD. TIPO DESCRIPCIÓN

Current ShellNavigationState Identificador URI de la página actual.

Previous ShellNavigationState Identificador URI de la página anterior.

Source ShellNavigationSource El tipo de navegación que se ha


producido.

NOTE
El evento Navigating se desencadena por el método OnNavigated reemplazable de la clase Shell .

Las clases y ShellNavigatingEventArgs tienen ambas propiedades


ShellNavigatedEventArgs Source , de tipo
ShellNavigationSource . Esta enumeración proporciona los valores siguientes:

Unknown
Push
Pop
PopToRoot
Insert
Remove
ShellItemChanged
ShellSectionChanged
ShellContentChanged

Por lo tanto, en un controlador del evento Navigating , es posible interceptar la navegación y realizar acciones en
función del origen de navegación. Por ejemplo, el código siguiente muestra cómo cancelar la navegación hacia
atrás si no se han guardado los datos de la página:

void OnNavigating(object sender, ShellNavigatingEventArgs e)


{
// Cancel back navigation if data is unsaved
if (e.Source == ShellNavigationSource.Pop && !dataSaved)
{
e.Cancel();
}
}

Pasar datos
Al realizar la navegación mediante programación, los datos pueden pasarse como parámetros de consulta. Por
ejemplo, el código siguiente se ejecuta en la aplicación de ejemplo cuando un usuario selecciona un elefante en
ElephantsPage :

async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)


{
string elephantName = (e.CurrentSelection.FirstOrDefault() as Animal).Name;
await Shell.Current.GoToAsync($"//animals/elephants/elephantdetails?name={elephantName}");
}

En este ejemplo de código se recupera el elefante actualmente seleccionado en CollectionView y se navega a la


ruta elephantdetails ; además, se pasa elephantName como parámetro de consulta. Tenga en cuenta que los
parámetros de consulta van a ser codificados con URL para la navegación, por lo que "Indian Elephant" se
convertirá en "Indian%20Elephant".
Para recibir datos, la clase que representa la página a la que se navega, o la clase de BindingContext de la página,
se debe decorar con un elemento QueryPropertyAttribute para cada parámetro de consulta:

[QueryProperty("Name", "name")]
public partial class ElephantDetailPage : ContentPage
{
public string Name
{
set
{
BindingContext = ElephantData.Elephants.FirstOrDefault(m => m.Name ==
Uri.UnescapeDataString(value));
}
}
...
}

El primer argumento de QueryPropertyAttribute especifica el nombre de la propiedad que recibirá los datos,
mientras que el segundo argumento especifica el identificador del parámetro de consulta. Por tanto, el elemento
QueryPropertyAttribute del ejemplo anterior especifica que la propiedad Name recibirá los datos pasados al
parámetro de consulta name del URI en la llamada al método GoToAsync . La propiedad Name decodifica
entonces el valor del parámetro de consulta en una dirección URL y lo usa para establecer el objeto
BindingContext de la página en el objeto que se mostrará.

NOTE
Una clase se puede representar con varios objetos QueryPropertyAttribute .

Comportamiento del botón Atrás


La clase BackButtonBehavior define las siguientes propiedades que controlan la apariencia y el comportamiento
del botón Atrás:
Command , de tipo , que se ejecuta cuando se presiona el botón Atrás.
ICommand
CommandParameter , de tipo object , que es el parámetro que se pasa a Command .
IconOveride , de tipo ImageSource , el icono usado para el botón Atrás.
IsEnabled , de tipo boolean , indica si se ha habilitado el botón Atrás. El valor predeterminado es true .
TextOverride , de tipo string , el texto usado para el botón Atrás.

Todas estas propiedades están respaldados por objetos BindableProperty , lo que significa que las propiedades
pueden ser destinos de los enlaces de datos.
La clase BackButtonBehavior se puede consumir mediante el establecimiento de la propiedad adjunta
Shell.BackButtonBehavior a un objeto BackButtonBehavior :

<ContentPage ...>
<Shell.BackButtonBehavior>
<BackButtonBehavior Command="{Binding BackCommand}"
IconOverride="back.png" />
</Shell.BackButtonBehavior>
...
</ContentPage>

El código de C# equivalente es el siguiente:

Shell.SetBackButtonBehavior(this, new BackButtonBehavior


{
Command = new Command(() =>
{
...
}),
IconOverride = "back.png"
});

La propiedad Command se establece en ICommand para ejecutarse cuando se presiona el botón Atrás, y la
propiedad IconOverride se establece en el icono que se usa para el botón Atrás:

Vínculos relacionados
Xaminals (ejemplo)
Búsqueda de Xamarin.Forms Shell
11/07/2019 • 19 minutes to read • Edit Online

Descargar el ejemplo
Xamarin.Forms Shell incluye la funcionalidad de búsqueda integrada que proporciona la clase SearchHandler . La
funcionalidad de búsqueda se puede agregar a una página mediante el establecimiento de la propiedad adjunta
Shell.SearchHandler en un objeto SearchHandler en subclase. El resultado es un cuadro de búsqueda que se
agrega en la parte superior de la página:

Cuando se escribe una consulta en el cuadro de búsqueda, la propiedad Query se actualiza y, en cada
actualización, se ejecuta el método OnQueryChanged . Este método se puede invalidar para rellenar el área de
sugerencias de búsqueda con datos:

Luego, cuando se selecciona un resultado del área de sugerencias de búsqueda, se ejecuta el método
OnItemSelected . Este método se puede invalidar para responder de forma adecuada; por ejemplo, navegando a
una página de detalles.

Creación de una clase SearchHandler


La funcionalidad de búsqueda se puede agregar a una aplicación de Shell mediante la creación de subclases de la
clase SearchHandler y la invalidación de los métodos OnQueryChanged y OnItemSelected :
public class MonkeySearchHandler : SearchHandler
{
protected override void OnQueryChanged(string oldValue, string newValue)
{
base.OnQueryChanged(oldValue, newValue);

if (string.IsNullOrWhiteSpace(newValue))
{
ItemsSource = null;
}
else
{
ItemsSource = MonkeyData.Monkeys
.Where(monkey => monkey.Name.ToLower().Contains(newValue.ToLower()))
.ToList<Animal>();
}
}

protected override async void OnItemSelected(object item)


{
base.OnItemSelected(item);

// Note: strings will be URL encoded for navigation (e.g. "Blue Monkey" becomes "Blue%20Monkey").
Therefore, decode at the receiver.
await (App.Current.MainPage as Xamarin.Forms.Shell).GoToAsync($"monkeydetails?name=
{((Animal)item).Name}");
}
}

La invalidación OnQueryChanged tiene dos argumentos: oldValue , que contiene la consulta de búsqueda anterior, y
newValue , que contiene la consulta de búsqueda actual. El área de sugerencias de búsqueda se puede actualizar
mediante el establecimiento de la propiedad SearchHandler.ItemsSource en una colección IEnumerable que
contiene elementos que coinciden con la consulta de búsqueda actual.
Cuando el usuario selecciona un resultado de búsqueda, se ejecuta la invalidación OnItemSelected y se establece la
propiedad SelectedItem . En este ejemplo, el método dirige a otra página que muestra datos sobre el elemento
Animal seleccionado. Para más información sobre la navegación, consulte Navegación en Xamarin.Forms Shell.

NOTE
Se pueden establecer propiedades SearchHandler adicionales para controlar la apariencia del cuadro de búsqueda.

Consumo de una clase SearchHandler


La clase SearchHandler en subclase se puede consumir mediante el establecimiento de la propiedad adjunta
Shell.SearchHandler en un objeto del tipo en subclase:

<ContentPage ...
xmlns:controls="clr-namespace:Xaminals.Controls">
<Shell.SearchHandler>
<controls:MonkeySearchHandler Placeholder="Enter search term"
ShowsResults="true"
DisplayMemberName="Name" />
</Shell.SearchHandler>
...
</ContentPage>

El código de C# equivalente es el siguiente:


Shell.SetSearchHandler(this, new MonkeySearchHandler
{
Placeholder = "Enter search term",
ShowsResults = true,
DisplayMemberName = "Name"
});

El método MonkeySearchHandler.OnQueryChanged devuelve un elemento List de objetos Animal . La propiedad


DisplayMemberName se establece en la propiedad Name de cada objeto Animal , por lo que los datos mostrados en
el área de sugerencias serán el nombre de cada animal.
La propiedad ShowsResults está establecida en true , de modo que se muestran sugerencias de búsqueda cuando
el usuario escribe una consulta de búsqueda:

A medida que cambia la consulta de búsqueda, se actualiza el área de sugerencias de búsqueda:

Cuando se selecciona un resultado de búsqueda, hay un desplazamiento hasta MonkeyDetailPage y se muestran


datos sobre el mono seleccionado:

Definición de la apariencia de los elemento de los resultados de


búsqueda
Además de mostrar datos de string en los resultados de búsqueda, se puede definir la apariencia de cada uno de
los elementos de los resultados de búsqueda mediante el establecimiento de la propiedad
SearchHandler.ItemTemplate en DataTemplate :

<ContentPage ...
xmlns:controls="clr-namespace:Xaminals.Controls">
<Shell.SearchHandler>
<controls:MonkeySearchHandler Placeholder="Enter search term"
ShowsResults="true">
<controls:MonkeySearchHandler.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.15*" />
<ColumnDefinition Width="0.85*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="40"
WidthRequest="40" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold" />
</Grid>
</DataTemplate>
</controls:MonkeySearchHandler.ItemTemplate>
</controls:MonkeySearchHandler>
</Shell.SearchHandler>
...
</ContentPage>

El código de C# equivalente es el siguiente:

Shell.SetSearchHandler(this, new MonkeySearchHandler


{
Placeholder = "Enter search term",
ShowsResults = true,
DisplayMemberName = "Name",
ItemTemplate = new DataTemplate(() =>
{
Grid grid = new Grid { Padding = 10 };
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.15, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.85, GridUnitType.Star) });

Image image = new Image { Aspect = Aspect.AspectFill, HeightRequest = 40, WidthRequest = 40 };


image.SetBinding(Image.SourceProperty, "ImageUrl");
Label nameLabel = new Label { FontAttributes = FontAttributes.Bold };
nameLabel.SetBinding(Label.TextProperty, "Name");

grid.Children.Add(image);
grid.Children.Add(nameLabel, 1, 0);
return grid;
})
});

Los elementos especificados en DataTemplate definen la apariencia de cada elemento en el área de sugerencias. En
este ejemplo, el diseño dentro de DataTemplate se administra mediante un objeto Grid . El objeto Grid contiene
un objeto Image y un objeto Label , que enlazan ambos con las propiedades de cada objeto Monkey .
Las capturas de pantalla siguientes muestran el resultado de crear plantillas para cada elemento del área de
sugerencias:

Para obtener más información sobre las plantillas de datos, consulte Plantillas de datos de Xamarin.Forms.

Visibilidad del cuadro de búsqueda


Cuando se agrega un SearchHandler en la parte superior de una página, de forma predeterminada el cuadro de
búsqueda está visible y totalmente expandido. Pero este comportamiento se puede cambiar estableciendo la
propiedad SearchHandler.SearchBoxVisibility en uno de los miembros de la enumeración SearchBoxVisibility :
Hidden : el cuadro de búsqueda no es visible ni accesible.
Collapsible : el cuadro de búsqueda está oculto hasta que el usuario realiza una acción para mostrarlo.
Expanded : el cuadro de búsqueda está visible y totalmente expandido.

En el ejemplo siguiente se muestra cómo ocultar el cuadro de búsqueda:

<ContentPage ...
xmlns:controls="clr-namespace:Xaminals.Controls">
<Shell.SearchHandler>
<controls:MonkeySearchHandler SearchBoxVisibility="Hidden"
... />
</Shell.SearchHandler>
...
</ContentPage>

Enfoque en el cuadro de búsqueda


Al pulsar en un cuadro de búsqueda aparece el teclado en pantalla, y el cuadro de búsqueda adquiere el foco de
entrada. Esto también se consigue mediante programación llamando al método Focus , que intenta establecer el
foco de entrada en el cuadro de búsqueda, y devuelve true si se realiza correctamente. Cuando un cuadro de
búsqueda obtiene el foco, se desencadena el evento Focus y se llama al método OnFocused reemplazable.
Cuando un cuadro de búsqueda tiene el foco de entrada, al pulsar en otro lugar en la pantalla desaparece el
teclado en pantalla y el cuadro de búsqueda pierde el foco de entrada. Esto también se consigue mediante
programación llamando al método Unfocus . Cuando un cuadro de búsqueda pierde el foco, se desencadena el
evento Unfocused y se llama al método OnUnfocus reemplazable.
Se puede recuperar el estado del foco de un cuadro de búsqueda a través de la propiedad IsFocused , que
devuelve true si un SearchHandler tiene actualmente el foco de entrada.

Apariencia SearchHandler
La clase SearchHandler define las siguientes propiedades que afectan a su apariencia:
BackgroundColor , del tipo Color , es el color de fondo para el texto del cuadro de búsqueda.
CancelButtonColor , del tipo Color , es el color del botón Cancelar.
FontAttributes , del tipo FontAttributes , indica si el texto del cuadro de búsqueda está en negrita o cursiva.
FontFamily , del tipo string , es la familia de fuentes utilizada para el texto del cuadro de búsqueda.
FontSize , del tipo double , es el tamaño del texto del cuadro de búsqueda.
HorizontalTextAlignment , del tipo TextAlignment , es la alineación horizontal del texto del cuadro de búsqueda.
PlaceholderColor , del tipo Color , es el color del texto del cuadro de búsqueda del marcador de posición.
TextColor , del tipo Color , es el color del texto del cuadro de búsqueda.

Teclado SearchHandler
El teclado que aparece cuando los usuarios interactúan con un SearchHandler se puede establecer mediante
programación a través de la propiedad Keyboard en una de las siguientes propiedades desde la clase Keyboard :
Chat : se usa para el texto y los lugares donde los emoji son útiles.
Default : el teclado predeterminado.
Email : se usa al especificar direcciones de correo electrónico.
Numeric : se usa al escribir números.
Plain : se usa al escribir texto, sin ningún KeyboardFlags especificado.
Telephone : se usa al escribir números de teléfono.
Text : se usa al escribir texto.
Url : se usa para especificar las rutas de acceso de archivo y direcciones web.

Esto se puede lograr en XAML de la siguiente manera:

<SearchHandler Keyboard="Email" />

El código de C# equivalente es el siguiente:

SearchHandler searchHandler = new SearchHandler { Keyboard = Keyboard.Email };

La clase Keyboard tiene también un patrón de diseño Factory Method Create que puede usarse para personalizar
un teclado mediante la especificación del comportamiento de las mayúsculas y minúsculas, el corrector ortográfico
y las sugerencias. Los valores de enumeración KeyboardFlags se especifican como argumentos para el método,
con la devolución de un Keyboard personalizado. La enumeración KeyboardFlags contiene los valores siguientes:
None : no se agregan características al teclado.
CapitalizeSentence : indica que la primera letra de la primera palabra de cada frase se escribirá
automáticamente en mayúsculas.
Spellcheck : indica que se pasará el corrector ortográfico al texto especificado.
Suggestions : indica que se ofrecerán finalizaciones de palabra para el texto especificado.
CapitalizeWord : indica que las primeras letras de todas las palabras se escribirán automáticamente en
mayúsculas.
CapitalizeCharacter : indica que todos los caracteres se escribirán automáticamente en mayúsculas.
CapitalizeNone : indica que no se producirá ningún uso automático de mayúsculas.
All : indica que se pasará el corrector automático, se ofrecerán finalizaciones de palabras y las frases
empezarán en mayúsculas en el texto especificado.
El ejemplo de código XAML siguiente muestra cómo personalizar el Keyboard predeterminado para ofrecer
finalizaciones de palabras y poner en mayúsculas todos los caracteres especificados:

<SearchHandler Placeholder="Enter search terms">


<SearchHandler.Keyboard>
<Keyboard x:FactoryMethod="Create">
<x:Arguments>
<KeyboardFlags>Suggestions,CapitalizeCharacter</KeyboardFlags>
</x:Arguments>
</Keyboard>
</SearchHandler.Keyboard>
</SearchHandler>

El código de C# equivalente es el siguiente:

SearchHandler searchHandler = new SearchHandler { Placeholder = "Enter search terms" };


searchHandler.Keyboard = Keyboard.Create(KeyboardFlags.Suggestions | KeyboardFlags.CapitalizeCharacter);

Referencia de SearchHandler
La clase SearchHandler define las siguientes propiedades que controlan su apariencia y comportamiento:
BackgroundColor , del tipo Color , es el color de fondo para el texto del cuadro de búsqueda.
CancelButtonColor , del tipo Color , es el color del botón Cancelar.
ClearIcon , de tipo ImageSource , el icono que aparece para borrar el contenido del cuadro de búsqueda.
ClearIconHelpText , de tipo string , el texto de ayuda accesible para el icono de borrar.
ClearIconName , de tipo string , el nombre del icono de borrar para usar con los lectores de pantalla.
ClearPlaceholderCommand , de tipo ICommand , que se ejecuta cuando se pulsa ClearPlaceholderIcon .
ClearPlaceholderCommandParameter , de tipo object , que es el parámetro que se pasa a ClearPlaceholderCommand .
ClearPlaceholderEnabled , de tipo bool , que determina si se puede ejecutar ClearPlaceholderCommand . El valor
predeterminado es true .
ClearPlaceholderHelpText , de tipo string , el texto de ayuda accesible para el icono de borrar marcador de
posición.
ClearPlaceholderIcon , de tipo ImageSource , el icono de borrar marcador de posición que se muestra cuando el
cuadro de búsqueda está vacío.
ClearPlaceholderName , de tipo string , el nombre del icono de borrar marcador de posición para su uso con los
lectores de pantalla.
Command , de tipo ICommand , que se ejecuta cuando se confirma la consulta de búsqueda.
CommandParameter , de tipo object , que es el parámetro que se pasa a Command .
DisplayMemberName , de tipo string , que representa el nombre o la ruta de acceso de la propiedad que se
muestra para cada elemento de datos de la colección ItemsSource .
FontAttributes , del tipo FontAttributes , indica si el texto del cuadro de búsqueda está en negrita o cursiva.
FontFamily , del tipo string , es la familia de fuentes utilizada para el texto del cuadro de búsqueda.
FontSize , del tipo double , es el tamaño del texto del cuadro de búsqueda.
HorizontalTextAlignment , del tipo TextAlignment , es la alineación horizontal del texto del cuadro de búsqueda.
IsFocused , del tipo bool , que representa si un SearchHandler actualmente tiene foco de entrada.
IsSearchEnabled , de tipo bool , que representa el estado habilitado del cuadro de búsqueda. El valor
predeterminado es true .
ItemsSource , de tipo IEnumerable , especifica la colección de elementos que se mostrarán en el área de
sugerencias, y tiene un valor predeterminado de null .
ItemTemplate , de tipo DataTemplate , especifica la plantilla que se aplicará a cada elemento de la colección de
elementos que se mostrará en el área de sugerencias.
Keyboard , del tipo Keyboard , es el teclado para el SearchHandler .
Placeholder , de tipo string , el texto que se muestra cuando el cuadro de búsqueda está vacío.
PlaceholderColor , del tipo Color , es el color del texto del cuadro de búsqueda del marcador de posición.
Query , de tipo string , el texto especificado por el usuario en el cuadro de búsqueda.
QueryIcon , de tipo ImageSource , el icono utilizado para indicar al usuario que la búsqueda está disponible.
QueryIconHelpText , de tipo string , el texto de ayuda accesible para el icono de consulta.
QueryIconName , de tipo string , el nombre del icono de consulta para su uso con los lectores de pantalla.
SearchBoxVisibility , de tipo SearchBoxVisibility , la visibilidad del cuadro de búsqueda. De forma
predeterminada, el cuadro de búsqueda está visible y totalmente expandido.
SelectedItem , de tipo object , el elemento seleccionado en los resultados de búsqueda. Esta propiedad es de
solo lectura y tiene un valor predeterminado de null .
ShowsResults , de tipo bool , indica si se deben esperar resultados de búsqueda en el área de sugerencias, al
escribir texto. El valor predeterminado es false .
TextColor , del tipo Color , es el color del texto del cuadro de búsqueda.

Todas estas propiedades están respaldados por objetos BindableProperty , lo que significa que las propiedades
pueden ser destinos de los enlaces de datos.
Además, la clase SearchHandler proporciona los métodos reemplazables siguientes:
OnClearPlaceholderClicked , que se llama cada vez que se pulsa ClearPlaceholderIcon .
OnItemSelected , que se llama cada vez que el usuario selecciona un resultado de búsqueda.
OnFocused , al que se llama cuando un SearchHandler adquiere el foco de entrada.
OnQueryChanged , que se llama cuando cambia la propiedad Query .
OnQueryConfirmed , que se llama cada vez que el usuario presiona Entrar o confirma su consulta en el cuadro de
búsqueda.
OnUnfocus , al que se llama cuando un SearchHandler pierde el foco de entrada.

Vínculos relacionados
Xaminals (ejemplo)
Navegación en Xamarin.Forms Shell
Representadores personalizados de Xamarin.Forms
Shell
11/07/2019 • 6 minutes to read • Edit Online

Una de las ventajas de las aplicaciones de Xamarin.Forms Shell es que su apariencia y comportamiento es muy
personalizable mediante las propiedades y los métodos que exponen las distintas clases de Shell. Sin embargo,
también es posible crear a un representador personalizado de Shell cuando se requieren personalizaciones más
sofisticadas específicas de la plataforma. Al igual que con otros representadores personalizados, se puede agregar
un representador personalizado de Shell a solo un proyecto de plataforma para personalizar la apariencia y el
comportamiento, mientras se permite el comportamiento predeterminado en la otra plataforma; o se puede
agregar un representador personalizado de Shell diferente a cada proyecto de plataforma para personalizar la
apariencia y el comportamiento en iOS y Android.
Las aplicaciones de Shell se representan mediante la clase ShellRenderer en iOS y Android. En iOS, la clase
ShellRenderer se puede encontrar en el espacio de nombres Xamarin.Forms.Platform.iOS . En Android, la clase
ShellRenderer se puede encontrar en el espacio de nombres Xamarin.Forms.Platform.Android .

El proceso para crear un representador personalizado de Shell es el siguiente:


1. Cree la subclase de la clase Shell . Esta acción ya se realizará en la aplicación de Shell.
2. Consuma la clase Shell en subclase. Esta acción ya se realizará en la aplicación de Shell.
3. Cree una clase de representador personalizado que se derive de la clase ShellRenderer , en las plataformas
necesarias.

Creación de una clase de representador personalizado


El proceso para crear una clase de representador personalizado de Shell es el siguiente:
1. Se crea una subclase de la clase ShellRenderer .
2. Invalide los métodos necesarios para llevar a cabo la personalización necesaria.
3. Agregue un elemento ExportRendererAttribute a la subclase ShellRenderer para especificar que se usará para
representar la aplicación de Shell. Este atributo se usa para registrar al representador personalizado con
Xamarin.Forms.

NOTE
Es opcional para proporcionar un representador personalizado de Shell en cada proyecto de plataforma. Si no está registrado
un representador personalizado, se usará entonces la clase ShellRenderer predeterminada.

La clase ShellRenderer expone los siguientes métodos reemplazables:


IOS ANDROID

SetElementSize CreateFragmentForPage
CreateFlyoutRenderer CreateShellFlyoutContentRenderer
CreateNavBarAppearanceTracker CreateShellFlyoutRenderer
CreatePageRendererTracker CreateShellItemRenderer
CreateShellFlyoutContentRenderer CreateShellSectionRenderer
CreateShellItemRenderer CreateTrackerForToolbar
CreateShellItemTransition CreateToolbarAppearanceTracker
CreateShellSearchResultsRenderer CreateTabLayoutAppearanceTracker
CreateShellSectionRenderer CreateBottomNavViewAppearanceTracker
CreateTabBarAppearanceTracker OnElementPropertyChanged
Dispose OnElementSet
OnCurrentItemChanged SwitchFragment
OnElementPropertyChanged Dispose
OnElementSet
UpdateBackgroundColor

Las clases FlyoutItem y TabBar son alias de la clase ShellItem , y la clase Tab es un alias de la clase
ShellSection . Por lo tanto, el método CreateShellItemRenderer se debe invalidar al crear un representador
personalizado para objetos FlyoutItem , y el método CreateShellSectionRenderer se debe invalidar al crear un
representador personalizado para objetos Tab .

IMPORTANT
Hay clases de representador adicionales de Shell, como ShellSectionRenderer y ShellItemRenderer , en iOS y Android.
Sin embargo, estas clases de representador adicionales se crean mediante invalidaciones en la clase ShellRenderer . Por lo
tanto, para personalizar el comportamiento de estas clases de representador adicionales, puede crear una subclase de ellas y
una instancia de la subclase en la invalidación adecuada de la clase ShellRenderer en subclase.

Ejemplo de iOS
En el ejemplo de código siguiente se muestra una clase ShellRenderer en subclase para iOS, que establece una
imagen de fondo en la barra de navegación de la aplicación de Shell:

using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(Xaminals.AppShell), typeof(Xaminals.iOS.MyShellRenderer))]


namespace Xaminals.iOS
{
public class MyShellRenderer : ShellRenderer
{
protected override IShellSectionRenderer CreateShellSectionRenderer(ShellSection shellSection)
{
var renderer = base.CreateShellSectionRenderer(shellSection);
if (renderer != null)
{
(renderer as
ShellSectionRenderer).NavigationBar.SetBackgroundImage(UIImage.FromFile("monkey.png"), UIBarMetrics.Default);
}
return renderer;
}
}
}

La clase MyShellRenderer invalida el método CreateShellSectionRenderer y recupera el representador creado por


la clase base. Luego, modifica al representador mediante el establecimiento de una imagen de fondo en la barra
de navegación, antes de volver al representador.
Ejemplo de Android
En el ejemplo de código siguiente se muestra una clase ShellRenderer en subclase para Android, que establece
una imagen de fondo en la barra de navegación de la aplicación de Shell:

using Android.Content;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(Xaminals.AppShell), typeof(Xaminals.Droid.MyShellRenderer))]


namespace Xaminals.Droid
{
public class MyShellRenderer : ShellRenderer
{
public MyShellRenderer(Context context) : base(context)
{
}

protected override IShellToolbarAppearanceTracker CreateToolbarAppearanceTracker()


{
return new MyShellToolbarAppearanceTracker(this);
}
}
}

La clase invalida el método CreateToolbarAppearanceTracker y devuelve una instancia de la clase


MyShellRenderer
MyShellToolbarAppearanceTracker . La clase MyShellToolbarAppearanceTracker , que se deriva de la clase
ShellToolbarAppearanceTracker , se muestra en el ejemplo siguiente:

using Android.Support.V7.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

namespace Xaminals.Droid
{
public class MyShellToolbarAppearanceTracker : ShellToolbarAppearanceTracker
{
public MyShellToolbarAppearanceTracker(IShellContext context) : base(context)
{
}

public override void SetAppearance(Toolbar toolbar, IShellToolbarTracker toolbarTracker,


ShellAppearance appearance)
{
base.SetAppearance(toolbar, toolbarTracker, appearance);
toolbar.SetBackgroundResource(Resource.Drawable.monkey);
}
}
}

La clase MyShellToolbarAppearanceTracker invalida el método SetAppearance y modifica la barra de herramientas


mediante el establecimiento de una imagen de fondo en ella.

IMPORTANT
Solo es necesario agregar el objeto ExportRendererAttribute a un representador personalizado que se deriva de la clase
ShellRenderer . Se crean clases de representador de Shell en subclase adicionales mediante la clase ShellRenderer en
subclase.
Vínculos relacionados
Representadores personalizados de Xamarin.Forms
Plantillas de control de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Las plantillas de control proporcionan una separación clara entre el aspecto de una página y su contenido, lo que
permite crear páginas a las que se les puede aplicar temas fácilmente.

Introducción
Las plantillas de control de Xamarin.Forms permiten crear y cambiar fácilmente el tema de las páginas de la
aplicación en tiempo de ejecución. En este artículo se proporciona una introducción a las plantillas de control.

Creación de una clase ControlTemplate


Las plantillas de control pueden definirse en el nivel de aplicación o en el nivel de página. En este artículo se
explica cómo crear y consumir plantillas de control.

Enlazar desde una plantilla de control


Los enlaces de plantilla permiten a los controles en una plantilla de control enlazar datos a propiedades públicas,
lo que permite que los valores de propiedad en los controles de la plantilla de control se puedan cambiar
fácilmente. En este artículo se muestra cómo usar enlaces de plantilla para realizar el enlace de datos desde una
plantilla de control.
Introducción a las plantillas de control de
Xamarin.Forms
11/07/2019 • 6 minutes to read • Edit Online

Las plantillas de control de Xamarin.Forms permiten crear y cambiar fácilmente el tema de las páginas de la
aplicación en tiempo de ejecución. En este artículo se proporciona una introducción a las plantillas de control.
Los controles tienen propiedades diferentes, como BackgroundColor y TextColor , que pueden definir los aspectos
de la apariencia del control. Estas propiedades se pueden establecer mediante estilos, que se pueden cambiar en
tiempo de ejecución para implementar la creación de temas básicos. Pero los estilos no mantienen una separación
clara entre la apariencia de una página y su contenido, y los cambios que se pueden realizar mediante el
establecimiento de estas propiedades son limitados.
Las plantillas de control proporcionan una separación clara entre el aspecto de una página y su contenido, lo que
permite crear páginas a las que se les puede aplicar temas fácilmente. Por ejemplo, una aplicación puede contener
plantillas de control de nivel de aplicación que proporcionan un tema oscuro y un tema claro. Se pueden crear
temas de todos los elementos ContentPage de la aplicación si se aplica una de las plantillas de control sin cambiar
el contenido mostrado por cada página. Además, los temas que proporcionan las plantillas de control no están
limitados al cambio de las propiedades de los controles. También pueden cambiar los controles que se usan para
implementar el tema.

Creación de una plantilla de control


Un elemento ControlTemplate especifica la apariencia de una página o vista, y contiene un diseño raíz que incluye
los controles que implementan la plantilla. Normalmente, un elemento ControlTemplate usará un elemento
ContentPresenter para marcar dónde va a aparecer el contenido que la página o vista va a mostrar. Después, la
página o vista que consume el elemento ControlTemplate definirá el contenido que ContentPresenter va a mostrar.
En el diagrama siguiente se ilustra un elemento ControlTemplate para una página que contiene una serie
controles, incluido un control ContentPresenter marcado por un rectángulo de color azul:
Se puede aplicar ControlTemplate a los tipos siguientes si se establecen sus propiedades ControlTemplate :
ContentPage
ContentView
TemplatedPage
TemplatedView

Cuando se crea un elemento ControlTemplate y se asigna a estos tipos, cualquier apariencia existente se reemplaza
con la que se define en el elemento ControlTemplate . Además de establecer la apariencia con la propiedad
ControlTemplate , también se pueden aplicar plantillas de control mediante estilos para ampliar más las funciones
del tema.

NOTE
¿Qué son los tipos TemplatedPage y TemplatedView ? TemplatedPage es la clase base para ContentPage y es el tipo de
página más básico proporcionado por Xamarin.Forms. A diferencia de ContentPage , TemplatedPage no tiene una
propiedad Content . Por tanto, no se puede agregar contenido directamente a una instancia de TemplatedPage . En su
lugar, el contenido se agrega mediante el establecimiento de la plantilla de control para la instancia de TemplatedPage . De
forma similar, TemplatedView es la clase base para ContentView . A diferencia de ContentView , TemplatedView no tiene
una propiedad Content . Por tanto, no se puede agregar contenido directamente a una instancia de TemplatedView . En su
lugar, el contenido se agrega mediante el establecimiento de la plantilla de control para la instancia de TemplatedView .

Las plantillas de control se pueden crear en XAML y en C#:


Las plantillas de control creadas en XAML se definen en un objeto ResourceDictionary que se asigna a la
colección Resources de una página, o bien generalmente a la colección Resources de la aplicación.
Las plantillas de control creadas en C# se suelen definir en la clase de página, o bien en una clase a la que se
puede acceder de forma global.
La elección de dónde se puede definir una instancia de ControlTemplate afecta a dónde se puede usar:
Las instancias de ControlTemplate definidas en el nivel de página solo se pueden aplicar a la página.
Las instancias de ControlTemplate definidas en el nivel de aplicación se pueden aplicar a las páginas de toda la
aplicación.
Las plantillas de control situadas más abajo en la jerarquía de vistas tienen prioridad sobre las definidas más
arriba. Por ejemplo, un elemento ControlTemplate denominado DarkTheme que se define en el nivel de página
tendrá prioridad sobre una plantilla con el mismo nombre definida en el nivel de aplicación. Por tanto, una plantilla
de control que defina un tema que se va a aplicar a cada página de una aplicación se debe definir en el nivel de
aplicación.

Vínculos relacionados
Estilos
ControlTemplate
ContentPresenter
Creación de una clase ControlTemplate
11/07/2019 • 9 minutes to read • Edit Online

Descargar el ejemplo
Las plantillas de control se pueden definir en el nivel de aplicación o en el de página. En este artículo se explica
cómo crear y consumir plantillas de control.

Creación de una clase ControlTemplate en XAML


Para definir un elemento ControlTemplate en el nivel de aplicación, se debe agregar un objeto ResourceDictionary
a la clase App . De forma predeterminada, en todas las aplicaciones de Xamarin.Forms creadas a partir de una
plantilla se usa la clase App para implementar la subclase Application . Para declarar un elemento
ControlTemplate en el nivel de aplicación, en el objeto ResourceDictionary de la aplicación con XAML, la clase
App predeterminada se debe reemplazar por una clase App de XAML y el código subyacente asociado, como se
muestra en el ejemplo de código siguiente:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SimpleTheme.App">
<Application.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="TealTemplate">
<Grid>
...
<BoxView ... />
<Label Text="Control Template Demo App"
TextColor="White"
VerticalOptions="Center" ... />
<ContentPresenter ... />
<BoxView Color="Teal" ... />
<Label Text="(c) Xamarin 2016"
TextColor="White"
VerticalOptions="Center" ... />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="AquaTemplate">
...
</ControlTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>

Cada instancia de ControlTemplate se crea como un objeto reutilizable en un elemento ResourceDictionary . Esto se
consigue mediante la asignación de un atributo x:Key único a cada declaración, para proporcionarle una clave
descriptiva en el objeto ResourceDictionary .
En el ejemplo de código siguiente se muestra el código subyacente de App asociado:
public partial class App : Application
{
public App ()
{
InitializeComponent ();
MainPage = new HomePage ();
}
}

Además de establecer la propiedad MainPage , el código subyacente también debe llamar al método
InitializeComponent para cargar y analizar el código XAML asociado.

En el ejemplo de código siguiente se muestra un elemento ContentPage que aplica TealTemplate a ContentView :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SimpleTheme.HomePage">
<ContentView x:Name="contentView" Padding="0,20,0,0"
ControlTemplate="{StaticResource TealTemplate}">
<StackLayout VerticalOptions="CenterAndExpand">
<Label Text="Welcome to the app!" HorizontalOptions="Center" />
<Button Text="Change Theme" Clicked="OnButtonClicked" />
</StackLayout>
</ContentView>
</ContentPage>

El elemento TealTemplate se asigna a la propiedad ContentView.ControlTemplate mediante la extensión de


marcado StaticResource . La propiedad ContentView.Content se establece en un elemento StackLayout que define
el contenido que se mostrará en ContentPage . Este contenido se mostrará mediante el elemento ContentPresenter
incluido en TealTemplate . El resultado es el aspecto que se muestra en las capturas de pantalla siguientes:

Cambio de los temas de una aplicación en tiempo de ejecución


Al hacer clic en el botón Cambiar tema se ejecuta el método OnButtonClicked , que se muestra en el ejemplo de
código siguiente:

void OnButtonClicked (object sender, EventArgs e)


{
originalTemplate = !originalTemplate;
contentView.ControlTemplate = (originalTemplate) ? tealTemplate : aquaTemplate;
}
Este método reemplaza la instancia activa de ControlTemplate con la instancia de ControlTemplate alternativa, lo
que da como resultado la captura de pantalla siguiente:

NOTE
En un elemento ContentPage , se puede asignar la propiedad Content y también se puede establecer la propiedad
ControlTemplate . En ese caso, si ControlTemplate contiene una instancia de ContentPresenter , el contenido asignado
a la propiedad Content se presentará por medio del elemento ContentPresenter de ControlTemplate .

Configuración de una clase ControlTemplate con un estilo


Una clase ControlTemplate también se puede aplicar mediante un elemento Style para expandir la capacidad del
tema. Esto se puede lograr mediante la creación de un estilo implícito o explícito para la vista de destino en un
objeto ResourceDictionary , y estableciendo la propiedad ControlTemplate de la vista de destino en la instancia de
Style . En el ejemplo de código siguiente se muestra un estilo implícito que se ha agregado al objeto
ResourceDictionary de nivel de aplicación:

<Style TargetType="ContentView">
<Setter Property="ControlTemplate" Value="{StaticResource TealTemplate}" />
</Style>

Como la instancia de Style es implícita, se aplicará a todas las instancias de ContentView de la aplicación. Por
tanto, ya no es necesario establecer la propiedad ContentView.ControlTemplate , como se muestra en el ejemplo de
código siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SimpleTheme.HomePage">
<ContentView x:Name="contentView" Padding="0,20,0,0">
...
</ContentView>
</ContentPage>

Para obtener más información sobre los estilos, vea Estilos.


Creación de una clase ControlTemplate en el nivel de página
Además de crear instancias de ControlTemplate en el nivel de aplicación, también se pueden crear en el nivel de
página, como se muestra en el ejemplo de código siguiente:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SimpleTheme.HomePage">
<ContentPage.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="TealTemplate">
...
</ControlTemplate>
<ControlTemplate x:Key="AquaTemplate">
...
</ControlTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<ContentView ... ControlTemplate="{StaticResource TealTemplate}">
...
</ContentView>
</ContentPage>

Cuando se agrega un elemento ControlTemplate en el nivel de página, se agrega un objeto ResourceDictionary a


ContentPage y, después, se incluyen las instancias de ControlTemplate en el objeto ResourceDictionary .

Creación de una clase ControlTemplate en C#


Para definir un elemento ControlTemplate en el nivel de aplicación, se debe crear un objeto class que represente
el elemento ControlTemplate . La clase se debe derivar del diseño que se usa para la plantilla, como se muestra en
el ejemplo de código siguiente:

class TealTemplate : Grid


{
public TealTemplate ()
{
...
var contentPresenter = new ContentPresenter ();
Children.Add (contentPresenter, 0, 1);
Grid.SetColumnSpan (contentPresenter, 2);
...
}
}

class AquaTemplate : Grid


{
...
}

La clase AquaTemplate es idéntica a la clase TealTemplate , salvo que se usan otros colores para las propiedades
BoxView.Color y Label.TextColor .
En el ejemplo de código siguiente se muestra un elemento ContentPage que aplica TealTemplate a ContentView :
public class HomePageCS : ContentPage
{
...
ControlTemplate tealTemplate = new ControlTemplate (typeof(TealTemplate));
ControlTemplate aquaTemplate = new ControlTemplate (typeof(AquaTemplate));

public HomePageCS ()
{
var button = new Button { Text = "Change Theme" };
var contentView = new ContentView {
Padding = new Thickness (0, 20, 0, 0),
Content = new StackLayout {
VerticalOptions = LayoutOptions.CenterAndExpand,
Children = {
new Label { Text = "Welcome to the app!", HorizontalOptions = LayoutOptions.Center },
button
}
},
ControlTemplate = tealTemplate
};
...
Content = contentView;
}
}

Las instancias de ControlTemplate se crean mediante la especificación del tipo de las clases que definen las
plantillas de control, en el constructor ControlTemplate .
La propiedad ContentView.Content se establece en un elemento StackLayout que define el contenido que se
mostrará en ContentPage . Este contenido se mostrará mediante el elemento ContentPresenter incluido en
TealTemplate . El mismo mecanismo descrito anteriormente se usa para cambiar el tema en tiempo de ejecución
para el objeto AquaTheme .

Obtención de un elemento con nombre desde una plantilla


Los elementos con nombre dentro de una plantilla de control se pueden recuperar una vez que se crea una
instancia de la plantilla. Esto se puede lograr con el método GetTemplateChild , el que devuelve el elemento con
nombre en el árbol visual ControlTemplate con instancias.
Una vez que se crearon instancias de una plantilla de control, se llama al método OnApplyTemplate de la plantilla.
Por lo tanto, se debe llamar al método GetTemplateChild desde la invalidación OnApplyTemplate en una página
derivada TemplatedPage , como ContentPage , o una vista derivada TemplatedView , como ContentView .

IMPORTANT
Se debe llamar al método GetTemplateChild solo una vez que se ha llamado al método OnApplyTemplate .

En el ejemplo siguiente se muestra la plantilla de control de un control personalizado:

<controls:MyCustomControl ...>
<controls:MyCustomControl.ControlTemplate>
<ControlTemplate>
<Label x:Name="myLabel" />
</ControlTemplate>
<controls:MyCustomControl.ControlTemplate>
</controls:MyCustomControl>

Se le asigna un nombre al elemento Label y, por lo tanto, se puede recuperar en el código subyacente del control
personalizado. Para lograrlo, se llama al método GetTemplateChild desde la invalidación OnApplyTemplate del
control personalizado:

class MyCustomControl : ContentView


{
Label myLabel;

protected override OnApplyTemplate()


{
myLabel = GetTemplateChild("myLabel");
}
//...
}

En este ejemplo, se recupera el objeto Label con nombre myLabel . Luego, la clase MyCustomControl puede
acceder a myLabel y manipularlo.

Vínculos relacionados
Estilos
Tema sencillo (ejemplo)
ControlTemplate
ContentPresenter
ContentView
ResourceDictionary
Enlace de una clase ControlTemplate de
Xamarin.Forms
11/07/2019 • 9 minutes to read • Edit Online

Descargar el ejemplo
Los enlaces a plantilla permiten a los controles en una plantilla de control enlazar datos a propiedades públicas, lo
que permite que los valores de propiedad en los controles de la plantilla de control se puedan cambiar fácilmente.
En este artículo se muestra cómo usar enlaces a plantilla para realizar el enlace de datos desde una plantilla de
control.
TemplateBinding se usa para enlazar la propiedad de un control en una plantilla de control a una propiedad
enlazable en el elemento primario de la vista de destino propietaria de la plantilla de control. Por ejemplo, en lugar
de definir el texto mostrado por las instancias de Label dentro de ControlTemplate , se podría usar un enlace a
plantilla para enlazar la propiedad Label.Text a propiedades enlazables que definen el texto que se va a mostrar.
Un elemento TemplateBinding es similar a un elemento Binding existente, salvo que el origen de
TemplateBinding siempre se establece de forma automática en el elemento primario de la vista de destino
propietaria de la plantilla de control. Pero tenga en cuenta que no se admite el uso de TemplateBinding fuera de
ControlTemplate .

Creación de TemplateBinding en XAML


En XAML, un elemento TemplateBinding se crea mediante la extensión de marcado TemplateBinding , como se
muestra en el ejemplo de código siguiente:

<ControlTemplate x:Key="TealTemplate">
<Grid>
...
<Label Text="{TemplateBinding Parent.HeaderText}" ... />
...
<Label Text="{TemplateBinding Parent.FooterText}" ... />
</Grid>
</ControlTemplate>

En lugar de establecer las propiedades Label.Text en texto estático, las propiedades pueden usar enlaces a
plantilla para enlazar a propiedades enlazables del elemento primario de la vista de destino propietaria del
elemento ControlTemplate . Pero tenga en cuenta que los enlaces a plantilla se enlazan a Parent.HeaderText y
Parent.FooterText , en lugar de a HeaderText y FooterText . En este ejemplo, esto se debe a que las propiedades
enlazables se definen en el elemento primario principal de la vista de destino, en lugar del elemento primario,
como se muestra en el ejemplo de código siguiente:

<ContentPage ...>
<ContentView ... ControlTemplate="{StaticResource TealTemplate}">
...
</ContentView>
</ContentPage>

El origen del enlace a plantilla siempre se establece de forma automática en el elemento primario de la vista de
destino propietaria de la plantilla de control, que aquí es la instancia de ContentView . El enlace a plantilla usa la
propiedad Parent para devolver el elemento primario de la instancia de ContentView , que es la instancia de
ContentPage . Por tanto, al usar TemplateBinding en el elemento ControlTemplate para enlazar a
Parent.HeaderText y Parent.FooterText se buscan las propiedades enlazables definidas en ContentPage , como se
muestra en el ejemplo de código siguiente:

public static readonly BindableProperty HeaderTextProperty =


BindableProperty.Create ("HeaderText", typeof(string), typeof(HomePage), "Control Template Demo App");
public static readonly BindableProperty FooterTextProperty =
BindableProperty.Create ("FooterText", typeof(string), typeof(HomePage), "(c) Xamarin 2016");

public string HeaderText {


get { return (string)GetValue (HeaderTextProperty); }
}

public string FooterText {


get { return (string)GetValue (FooterTextProperty); }
}

El resultado es el aspecto que se muestra en las capturas de pantalla siguientes:

Creación de TemplateBinding en C#
En C#, un elemento TemplateBinding se crea mediante el constructor de TemplateBinding , como se muestra en el
ejemplo de código siguiente:

class TealTemplate : Grid


{
public TealTemplate ()
{
...
var topLabel = new Label { TextColor = Color.White, VerticalOptions = LayoutOptions.Center };
topLabel.SetBinding (Label.TextProperty, new TemplateBinding ("Parent.HeaderText"));
...
var bottomLabel = new Label { TextColor = Color.White, VerticalOptions = LayoutOptions.Center };
bottomLabel.SetBinding (Label.TextProperty, new TemplateBinding ("Parent.FooterText"));
...
}
}

En lugar de establecer las propiedades Label.Text en texto estático, las propiedades pueden usar enlaces a
plantilla para enlazar a propiedades enlazables del elemento primario de la vista de destino propietaria del
elemento ControlTemplate . El enlace a plantilla se crea con el método SetBinding , y se especifica una instancia de
TemplateBinding como segundo parámetro. Tenga en cuenta que los enlaces a plantilla enlazan a
Parent.HeaderText y Parent.FooterText , porque las propiedades enlazables se definen en el elemento primario
principal de la vista de destino, en lugar del elemento primario, como se muestra en el ejemplo de código
siguiente:

public class HomePageCS : ContentPage


{
...
public HomePageCS ()
{
Content = new ContentView {
ControlTemplate = tealTemplate,
Content = new StackLayout {
...
},
...
};
...
}
}

Las propiedades enlazables se definen en ContentPage , como se describe anteriormente.


Enlace de BindableProperty a una propiedad ViewModel
Como se ha indicado antes, TemplateBinding se usa para enlazar la propiedad de un control en una plantilla de
control a una propiedad enlazable en el elemento primario de la vista de destino propietaria de la plantilla de
control. A su vez, estas propiedades enlazables se pueden enlazar a propiedades del modelo de vista.
En el ejemplo de código siguiente se definen dos propiedades en un modelo de vista:

public class HomePageViewModel


{
public string HeaderText { get { return "Control Template Demo App"; } }
public string FooterText { get { return "(c) Xamarin 2016"; } }
}

Las propiedades de modelo de vista HeaderText y FooterText se pueden enlazar a lo siguiente, como se muestra
en este ejemplo de código XAML:

<ContentPage xmlns:local="clr-namespace:SimpleTheme;assembly=SimpleTheme"
HeaderText="{Binding HeaderText}" FooterText="{Binding FooterText}" ...>
<ContentPage.BindingContext>
<local:HomePageViewModel />
</ContentPage.BindingContext>
<ContentView ControlTemplate="{StaticResource TealTemplate}" ...>
...
</ContentView>
</ContentPage>

Las propiedades enlazables HeaderText y FooterText se enlazan a las propiedades HomePageViewModel.HeaderText


y HomePageViewModel.FooterText , debido al establecimiento de BindingContext en una instancia de la clase
HomePageViewModel . En general, esto da como resultado el enlace de las propiedades de control de
ControlTemplate a las instancias de BindableProperty en ContentPage , que a su vez se enlazan a propiedades del
modelo de vista.
El código de C# equivalente se muestra en el ejemplo de código siguiente:
public class HomePageCS : ContentPage
{
...
public HomePageCS ()
{
BindingContext = new HomePageViewModel ();
this.SetBinding (HeaderTextProperty, "HeaderText");
this.SetBinding (FooterTextProperty, "FooterText");
...
}
}

También puede enlazar a las propiedades del modelo de vista directamente, para que no sea necesario declarar
elementos BindableProperty para HeaderText y FooterText en el elemento ContentPage , si se enlaza la plantilla
de control a Parent.BindingContext.NombreDePropiedad, por ejemplo:

<ControlTemplate x:Key="TealTemplate">
<Grid>
...
<Label Text="{TemplateBinding Parent.BindingContext.HeaderText}" ... />
...
<Label Text="{TemplateBinding Parent.BindingContext.FooterText}" ... />
</Grid>
</ControlTemplate>

Para obtener más información sobre el enlace de datos a modelos de vista, vea Enlaces de datos a MVVM.

Resumen
En este artículo se ha mostrado cómo usar enlaces a plantilla para realizar el enlace de datos desde una plantilla
de control. Los enlaces a plantilla permiten a los controles en una plantilla de control enlazar datos a propiedades
públicas, lo que permite que los valores de propiedad en los controles de la plantilla de control se puedan cambiar
fácilmente.

Vínculos relacionados
Conceptos básicos del enlace de datos
Enlaces de datos a MVVM
Tema sencillo con enlace a plantilla (ejemplo)
Tema sencillo con enlace a plantilla y modelo de vista (ejemplo)
TemplateBinding
ControlTemplate
ContentView
Plantillas de datos de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Descargar el ejemplo
Las plantillas de datos se usan para especificar el aspecto de los datos en los controles admitidos y normalmente
se enlaza a los datos que se van a mostrar.

Introducción
Las plantillas de datos de Xamarin.Forms permiten definir la presentación de los datos en los controles
admitidos. En este artículo se ofrece una introducción a las plantillas de datos y se analiza por qué son
necesarias.

Crear una plantilla de datos


Las plantillas de datos se pueden crear en línea, en ResourceDictionary , o a partir de un tipo personalizado o un
tipo de celda de Xamarin.Forms adecuado. Las plantillas en línea deben usarse si no es necesario volver a usar la
plantilla de datos en otro lugar. Como alternativa, se puede reutilizar una plantilla de datos si se define como un
tipo personalizado, o como un recurso de nivel de página o el nivel de aplicación de nivel de control.

Crear un DataTemplateSelector
Un DataTemplateSelector se puede usar para elegir DataTemplate en tiempo de ejecución según el valor de una
propiedad enlazada a datos. Esto permite aplicar varias instancias de DataTemplate al mismo tipo de objeto para
personalizar la apariencia de objetos concretos. En este artículo se explica cómo crear y consumir un
DataTemplateSelector .

Vínculos relacionados
Plantillas de datos (ejemplo)
Introducción a las plantillas de Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

Descargar el ejemplo
Las plantillas de datos de Xamarin.Forms permiten definir la presentación de los datos en los controles admitidos.
En este artículo se ofrece una introducción a las plantillas de datos y se analiza por qué son necesarias.
Considere la posibilidad de un elemento ListView que muestra una colección de objetos Person . En el ejemplo
de código siguiente se muestra la definición de la clase Person :

public class Person


{
public string Name { get; set; }
public int Age { get; set; }
public string Location { get; set; }
}

La clase Person define las propiedades Name , Age y Location , que se pueden establecer al crear un objeto
Person . El objeto ListView se usa para mostrar la colección de objetos Person , como se muestra en el ejemplo
de código XAML siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataTemplates"
...>
<StackLayout Margin="20">
...
<ListView Margin="0,20,0,0">
<ListView.ItemsSource>
<x:Array Type="{x:Type local:Person}">
<local:Person Name="Steve" Age="21" Location="USA" />
<local:Person Name="John" Age="37" Location="USA" />
<local:Person Name="Tom" Age="42" Location="UK" />
<local:Person Name="Lucas" Age="29" Location="Germany" />
<local:Person Name="Tariq" Age="39" Location="UK" />
<local:Person Name="Jane" Age="30" Location="USA" />
</x:Array>
</ListView.ItemsSource>
</ListView>
</StackLayout>
</ContentPage>

Se agregan elementos al objeto ListView en XAML mediante la inicialización de la propiedad ItemsSource a


partir de una matriz de instancias de Person .

NOTE
Tenga en cuenta que el elemento x:Array requiere un atributo Type que indica el tipo de los elementos de la matriz.

La página de C# equivalente se muestra en el ejemplo siguiente de código, en el que se inicializa la propiedad


ItemsSource en un elemento List de instancias de Person :
public WithoutDataTemplatePageCS()
{
...
var people = new List<Person>
{
new Person { Name = "Steve", Age = 21, Location = "USA" },
new Person { Name = "John", Age = 37, Location = "USA" },
new Person { Name = "Tom", Age = 42, Location = "UK" },
new Person { Name = "Lucas", Age = 29, Location = "Germany" },
new Person { Name = "Tariq", Age = 39, Location = "UK" },
new Person { Name = "Jane", Age = 30, Location = "USA" }
};

Content = new StackLayout


{
Margin = new Thickness(20),
Children = {
...
new ListView { ItemsSource = people, Margin = new Thickness(0, 20, 0, 0) }
}
};
}

ListView llama a ToString cuando se muestran los objetos de la colección. Como no hay ninguna invalidación de
Person.ToString , ToString devuelve el nombre de tipo de cada objeto, como se muestra en las capturas de
pantalla siguientes:

El objeto Person puede invalidar el método ToString para mostrar datos significativos, como se muestra en el
ejemplo de código siguiente:

public class Person


{
...
public override string ToString ()
{
return Name;
}
}

Como resultado, ListView muestra el valor de propiedad Person.Name para cada objeto de la colección, como se
muestra en las capturas de pantalla siguientes:
La invalidación de Person.ToString podría devolver una cadena con formato formada por las propiedades Name ,
Age y Location . Pero este enfoque solo ofrece un control limitado sobre la apariencia de cada elemento de datos.
Para obtener más flexibilidad, se puede crear un elemento DataTemplate que defina la apariencia de los datos.

Creación de una plantilla de datos


DataTemplate se usa para especificar la apariencia de los datos, y normalmente se utiliza el enlace de datos para
mostrar los datos. Un escenario de uso común es cuando se muestran datos de una colección de objetos en un
control ListView . Por ejemplo, cuando se enlaza un elemento ListView a una colección de objetos Person , la
propiedad ListView.ItemTemplate se establecerá en un elemento DataTemplate que define la apariencia de cada
objeto Person de ListView . DataTemplate contendrá los elementos que se enlazan a los valores de propiedad de
cada objeto Person . Para más información sobre el enlace de datos, consulte Data Binding Basics (Aspectos
básicos del enlace de datos).
Se puede usar un elemento DataTemplate como un valor para las propiedades siguientes:
ListView.HeaderTemplate
ListView.FooterTemplate
ListView.GroupHeaderTemplate
ItemsView.ItemTemplate , que es heredado por ListView .
MultiPage.ItemTemplate , que es heredado por CarouselPage , MasterDetailPage y TabbedPage .

NOTE
Tenga en cuenta que aunque TableView usa objetos Cell , no usa un elemento DataTemplate . Esto se debe a que los
enlaces de datos siempre se establecen directamente en objetos Cell .

Una instancia de DataTemplate que se coloca como un elemento secundario directo de las propiedades
enumeradas anteriormente se conoce como una plantilla insertada. Como alternativa, se puede definir un
elemento DataTemplate como un recurso de nivel de control, de página o de aplicación. La elección de dónde se
puede definir una instancia de DataTemplate afecta a dónde se puede usar:
Una instancia de DataTemplate definida en el nivel de control solo se puede aplicar al control.
Un instancia de DataTemplate definida en el nivel de página se puede aplicar a varios controles válidos de la
página.
Una instancia de DataTemplate definida en el nivel de aplicación se puede aplicar a los controles válidos de toda
la aplicación.
Las plantillas de datos situadas más abajo en la jerarquía de vistas tienen prioridad sobre las definidas más arriba
cuando comparten atributos x:Key . Por ejemplo, una plantilla de datos de nivel de aplicación se reemplazará por
una plantilla de datos de nivel de página, y una plantilla de datos de nivel de página se reemplazará por una
plantilla de datos de nivel de control, o bien una plantilla de datos insertada.

Vínculos relacionados
Apariencia de etiqueta
Plantillas de datos (ejemplo)
DataTemplate
Creación de una plantilla de datos de Xamarin.Forms
11/07/2019 • 9 minutes to read • Edit Online

Descargar el ejemplo
Las plantillas de datos se pueden crear insertadas, en un objeto ResourceDictionary, o bien a partir de un tipo
personalizado o un tipo de celda de Xamarin.Forms adecuado. En este artículo se explora cada una de las técnicas.
Un escenario de uso común para un elemento DataTemplate es mostrar datos de una colección de objetos en un
control ListView . La apariencia de los datos de cada celda del control ListView se puede administrar mediante el
establecimiento de la propiedad ListView.ItemTemplate en un elemento DataTemplate . Se pueden usar varias
técnicas para realizar esta acción:
Creación de una plantilla de datos insertada.
Creación de una plantilla de datos con un tipo.
Creación de una plantilla de datos como un recurso.
Independientemente de la técnica que se use, el resultado es que la apariencia de cada celda del control ListView
se define mediante un elemento DataTemplate , como se muestra en las capturas de pantalla siguientes:

Creación de una plantilla de datos insertada


La propiedad ListView.ItemTemplate se puede establecer en un elemento DataTemplate insertado. Una plantilla
insertada, que es la que se coloca como elemento secundario directo de una propiedad de control adecuada, se
debe usar si no hay ninguna necesidad de reutilizar la plantilla de datos en otros puntos. Los elementos
especificados en el elemento DataTemplate definen la apariencia de cada celda, como se muestra en el ejemplo de
código XAML siguiente:
<ListView Margin="0,20,0,0">
<ListView.ItemsSource>
<x:Array Type="{x:Type local:Person}">
<local:Person Name="Steve" Age="21" Location="USA" />
<local:Person Name="John" Age="37" Location="USA" />
<local:Person Name="Tom" Age="42" Location="UK" />
<local:Person Name="Lucas" Age="29" Location="Germany" />
<local:Person Name="Tariq" Age="39" Location="UK" />
<local:Person Name="Jane" Age="30" Location="USA" />
</x:Array>
</ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
...
<Label Text="{Binding Name}" FontAttributes="Bold" />
<Label Grid.Column="1" Text="{Binding Age}" />
<Label Grid.Column="2" Text="{Binding Location}" HorizontalTextAlignment="End" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

El elemento secundario de un elemento DataTemplate insertado debe ser de tipo Cell , o bien derivarse de él. En
este ejemplo se usa un ViewCell , que procede de Cell . Aquí, el diseño dentro de ViewCell se administra
mediante un control Grid . Grid contiene tres instancias de Label que enlazan sus propiedades Text a las
propiedades adecuadas de cada objeto Person de la colección.
El código de C# equivalente se muestra en el ejemplo de código siguiente:
public class WithDataTemplatePageCS : ContentPage
{
public WithDataTemplatePageCS()
{
...
var people = new List<Person>
{
new Person { Name = "Steve", Age = 21, Location = "USA" },
...
};

var personDataTemplate = new DataTemplate(() =>


{
var grid = new Grid();
...
var nameLabel = new Label { FontAttributes = FontAttributes.Bold };
var ageLabel = new Label();
var locationLabel = new Label { HorizontalTextAlignment = TextAlignment.End };

nameLabel.SetBinding(Label.TextProperty, "Name");
ageLabel.SetBinding(Label.TextProperty, "Age");
locationLabel.SetBinding(Label.TextProperty, "Location");

grid.Children.Add(nameLabel);
grid.Children.Add(ageLabel, 1, 0);
grid.Children.Add(locationLabel, 2, 0);

return new ViewCell { View = grid };


});

Content = new StackLayout


{
Margin = new Thickness(20),
Children = {
...
new ListView { ItemsSource = people, ItemTemplate = personDataTemplate, Margin = new
Thickness(0, 20, 0, 0) }
}
};
}
}

En C#, el elemento DataTemplate insertado se crea mediante una sobrecarga del constructor que especifica un
argumento Func .

Creación de una plantilla de datos con un tipo


La propiedad ListView.ItemTemplate también se puede establecer en un elemento DataTemplate creado a partir
de un tipo de celda. La ventaja de este enfoque es que la apariencia definida por el tipo de celda se puede reutilizar
en varias plantillas de datos en toda la aplicación. En el código XAML siguiente se muestra un ejemplo de este
enfoque:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataTemplates"
...>
<StackLayout Margin="20">
...
<ListView Margin="0,20,0,0">
<ListView.ItemsSource>
<x:Array Type="{x:Type local:Person}">
<local:Person Name="Steve" Age="21" Location="USA" />
...
</x:Array>
</ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<local:PersonCell />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>

En este caso, la propiedad ListView.ItemTemplate se establece en un elemento DataTemplate creado a partir de un


tipo personalizado que define el aspecto de la celda. El tipo personalizado se debe derivar del tipo ViewCell , como
se muestra en el ejemplo de código siguiente:

<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataTemplates.PersonCell">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" FontAttributes="Bold" />
<Label Grid.Column="1" Text="{Binding Age}" />
<Label Grid.Column="2" Text="{Binding Location}" HorizontalTextAlignment="End" />
</Grid>
</ViewCell>

Dentro de ViewCell , el diseño se administra mediante un control Grid . Grid contiene tres instancias de Label
que enlazan sus propiedades Text a las propiedades adecuadas de cada objeto Person de la colección.
El código de C# equivalente se muestra en el ejemplo siguiente:
public class WithDataTemplatePageFromTypeCS : ContentPage
{
public WithDataTemplatePageFromTypeCS()
{
...
var people = new List<Person>
{
new Person { Name = "Steve", Age = 21, Location = "USA" },
...
};

Content = new StackLayout


{
Margin = new Thickness(20),
Children = {
...
new ListView { ItemTemplate = new DataTemplate(typeof(PersonCellCS)), ItemsSource = people,
Margin = new Thickness(0, 20, 0, 0) }
}
};
}
}

En C#, DataTemplate se crea mediante una sobrecarga del constructor que especifica el tipo de celda como
argumento. El tipo de celda se debe derivar del tipo ViewCell , como se muestra en el ejemplo de código siguiente:

public class PersonCellCS : ViewCell


{
public PersonCellCS()
{
var grid = new Grid();
...
var nameLabel = new Label { FontAttributes = FontAttributes.Bold };
var ageLabel = new Label();
var locationLabel = new Label { HorizontalTextAlignment = TextAlignment.End };

nameLabel.SetBinding(Label.TextProperty, "Name");
ageLabel.SetBinding(Label.TextProperty, "Age");
locationLabel.SetBinding(Label.TextProperty, "Location");

grid.Children.Add(nameLabel);
grid.Children.Add(ageLabel, 1, 0);
grid.Children.Add(locationLabel, 2, 0);

View = grid;
}
}

NOTE
Tenga en cuenta que Xamarin.Forms también incluye tipos de celda que se pueden usar para mostrar datos simples en
celdas ListView . Para obtener más información, vea Apariencia de una celda.

Creación de una plantilla de datos con un recurso


Las plantillas de datos también se pueden crear como objetos reutilizables en un objeto ResourceDictionary . Esto
se consigue mediante la asignación de un atributo x:Key único a cada declaración, para proporcionarle una clave
descriptiva en el objeto ResourceDictionary , como se muestra en el ejemplo de código XAML siguiente:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
...>
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="personTemplate">
<ViewCell>
<Grid>
...
</Grid>
</ViewCell>
</DataTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="20">
...
<ListView ItemTemplate="{StaticResource personTemplate}" Margin="0,20,0,0">
<ListView.ItemsSource>
<x:Array Type="{x:Type local:Person}">
<local:Person Name="Steve" Age="21" Location="USA" />
...
</x:Array>
</ListView.ItemsSource>
</ListView>
</StackLayout>
</ContentPage>

El elemento se asigna a la propiedad ListView.ItemTemplate mediante la extensión de marcado


DataTemplate
StaticResource . Tenga en cuenta que aunque el elemento DataTemplate se defina en el objeto ResourceDictionary
de la página, también se puede definir en el nivel de control o aplicación.
En el ejemplo de código siguiente se muestra la página equivalente en C#:

public class WithDataTemplatePageCS : ContentPage


{
public WithDataTemplatePageCS ()
{
...
var personDataTemplate = new DataTemplate (() => {
var grid = new Grid ();
...
return new ViewCell { View = grid };
});

Resources = new ResourceDictionary ();


Resources.Add ("personTemplate", personDataTemplate);

Content = new StackLayout {


Margin = new Thickness(20),
Children = {
...
new ListView { ItemTemplate = (DataTemplate)Resources ["personTemplate"], ItemsSource = people };
}
};
}
}

El elemento DataTemplate se agrega a ResourceDictionary con el método Add , que especifica una cadena Key
que se usa para hacer referencia al elemento DataTemplate al recuperarlo.

Resumen
En este artículo se ha explicado cómo crear plantillas de datos, insertadas, a partir de un tipo personalizado, o bien
en un objeto ResourceDictionary . Una plantilla insertada se debe usar si no es necesario volver a utilizarla en otro
lugar. Como alternativa, se puede reutilizar una plantilla de datos si se define como un tipo personalizado, o bien
como un recurso de nivel de página, nivel de aplicación o de nivel de control.

Vínculos relacionados
Apariencia de etiqueta
Plantillas de datos (ejemplo)
DataTemplate
Creación de un elemento DataTemplateSelector de
Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

Descargar el ejemplo
Un elemento DataTemplateSelector se puede usar para elegir una plantilla de datos en tiempo de ejecución
según el valor de una propiedad enlazada a datos. Esto permite aplicar varias instancias de DataTemplate al
mismo tipo de objeto, para personalizar la apariencia de objetos concretos. En este artículo se explica cómo crear
y consumir una instancia de DataTemplateSelector.
Un selector de plantillas de datos habilita escenarios como el enlace de ListView a una colección de objetos,
donde la apariencia de cada objeto de ListView se puede elegir en tiempo de ejecución mediante el selector de
plantillas de datos devolviendo un elemento DataTemplate determinado.

Creación de DataTemplateSelector
Un selector de plantillas de datos se implementa mediante la creación de una clase que hereda de
DataTemplateSelector . Después, se reemplaza el método OnSelectTemplate para devolver un elemento
DataTemplate , como se muestra en el ejemplo de código siguiente:

public class PersonDataTemplateSelector : DataTemplateSelector


{
public DataTemplate ValidTemplate { get; set; }
public DataTemplate InvalidTemplate { get; set; }

protected override DataTemplate OnSelectTemplate (object item, BindableObject container)


{
return ((Person)item).DateOfBirth.Year >= 1980 ? ValidTemplate : InvalidTemplate;
}
}

El método OnSelectTemplate devuelve la plantilla adecuada en función del valor de la propiedad DateOfBirth . La
plantilla que se devuelve es el valor de las propiedades ValidTemplate o InvalidTemplate , que se establecen
cuando se consume PersonDataTemplateSelector .
Después, se puede asignar una instancia de la clase de selector de plantilla de datos a propiedades de control de
Xamarin.Forms como ListView.ItemTemplate . Para obtener una lista de las propiedades válidas, vea Creación de
una plantilla de datos.
Limitaciones
Las instancias de DataTemplateSelector tienen las limitaciones siguientes:
La subclase DataTemplateSelector siempre debe devolver la misma plantilla para los mismos datos si se
consultan varias veces.
La subclase DataTemplateSelector no debe devolver otra subclase DataTemplateSelector .
La subclase DataTemplateSelector no debe devolver nuevas instancias de DataTemplate en cada llamada. En
su lugar, se debe devolver la misma instancia. De lo contrario, se creará una fuga de memoria y se
deshabilitará la virtualización.
En Android, no puede haber más de 20 plantillas de datos diferentes por ListView .
Consumo de una instancia de DataTemplateSelector en XAML
En XAML, se pueden crear instancias de PersonDataTemplateSelector si se declara como un recurso, como se
muestra en el ejemplo de código siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Selector;assembly=Selector"
x:Class="Selector.HomePage">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="validPersonTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<local:PersonDataTemplateSelector x:Key="personDataTemplateSelector"
ValidTemplate="{StaticResource validPersonTemplate}"
InvalidTemplate="{StaticResource invalidPersonTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>

Este objeto ResourceDictionary de nivel de página define dos instancias de DataTemplate y una instancia de
PersonDataTemplateSelector . La instancia de PersonDataTemplateSelector establece sus propiedades
ValidTemplate y InvalidTemplate en las instancias de DataTemplate correspondientes mediante la extensión de
marcado StaticResource . Tenga en cuenta que aunque los recursos se definen en el objeto ResourceDictionary
de la página, también se pueden definir en el nivel de control o aplicación.
La instancia de PersonDataTemplateSelector se consume asignándola a la propiedad ListView.ItemTemplate ,
como se muestra en el ejemplo de código siguiente:

<ListView x:Name="listView" ItemTemplate="{StaticResource personDataTemplateSelector}" />

En tiempo de ejecución, ListView llama al método PersonDataTemplateSelector.OnSelectTemplate para cada uno


de los elementos de la colección subyacente, y la llamada pasa el objeto de datos como el parámetro item .
Después, el elemento DataTemplate devuelto por el método se aplica a ese objeto.
En las capturas de pantalla siguientes se muestra el resultado de la aplicación de PersonDataTemplateSelector por
parte de ListView a cada objeto de la colección subyacente:
Cualquier objeto Person que tenga un valor de propiedad DateOfBirth mayor o igual a 1980 se muestra en
color verde, y los demás objetos se muestran en color rojo.

Consumo de una instancia de DataTemplateSelector en C#


En C#, se pueden crear instancias de PersonDataTemplateSelector y asignarlas a la propiedad
ListView.ItemTemplate , como se muestra en el ejemplo de código siguiente:

public class HomePageCS : ContentPage


{
DataTemplate validTemplate;
DataTemplate invalidTemplate;

public HomePageCS ()
{
...
SetupDataTemplates ();
var listView = new ListView {
ItemsSource = people,
ItemTemplate = new PersonDataTemplateSelector {
ValidTemplate = validTemplate,
InvalidTemplate = invalidTemplate }
};

Content = new StackLayout {


Margin = new Thickness (20),
Children = {
...
listView
}
};
}
...
}

La instancia de PersonDataTemplateSelector establece sus propiedades ValidTemplate y InvalidTemplate en las


instancias de DataTemplate correspondientes creadas por el método SetupDataTemplates . En tiempo de
ejecución, ListView llama al método PersonDataTemplateSelector.OnSelectTemplate para cada uno de los
elementos de la colección subyacente, y la llamada pasa el objeto de datos como el parámetro item . Después, el
elemento DataTemplate devuelto por el método se aplica a ese objeto.
Resumen
En este artículo se ha explicado cómo crear y consumir un elemento DataTemplateSelector .
DataTemplateSelector se puede usar para elegir un elemento DataTemplate en tiempo de ejecución según el
valor de una propiedad enlazada a datos. Esto permite aplicar varias instancias de DataTemplate al mismo tipo de
objeto para personalizar la apariencia de objetos concretos.

Vínculos relacionados
Selector de plantillas de datos (ejemplo)
DataTemplateSelector
Desencadenadores de Xamarin.Forms
11/07/2019 • 12 minutes to read • Edit Online

Descargar el ejemplo
Los desencadenadores permiten expresar acciones de forma declarativa en XAML que cambian la apariencia de
controles en función de eventos o cambios en propiedades.
Puede asignar un desencadenador directamente a un control o agregarlo a un diccionario de recursos de nivel de
aplicación o página que se vaya a aplicar a varios controles.
Hay cuatro tipos de desencadenadores:
Desencadenador de propiedades: se produce cuando una propiedad en un control se establece en un valor
determinado.
Desencadenador de datos: usa enlaces de datos para desencadenar basándose en las propiedades de otro
control.
Desencadenador de eventos: se produce cuando tiene lugar un evento en el control.
Multi-desencadenador: permite establecer varias condiciones de desencadenador antes de que se
produzca una acción.

Desencadenadores de propiedad
Un desencadenador simple se puede expresar puramente en XAML, mediante la incorporación de un elemento
Trigger a la colección de desencadenadores de un control. En este ejemplo se muestra un desencadenador que
cambia un color de fondo Entry cuando recibe el foco:

<Entry Placeholder="enter name">


<Entry.Triggers>
<Trigger TargetType="Entry"
Property="IsFocused" Value="True">
<Setter Property="BackgroundColor" Value="Yellow" />
</Trigger>
</Entry.Triggers>
</Entry>

Las partes importantes de la declaración del desencadenador son:


TargetType: tipo de control al que se aplica el desencadenador.
Property: propiedad en el control que se supervisa.
Value: valor, cuando se produce para la propiedad supervisada, que hace que el desencadenador se active.
Setter: colección de elementos Setter que se puede agregar cuando se cumple la condición del
desencadenador. Debe especificar los elementos Property y Value que se van a establecer.
EnterActions y ExitActions (no mostrados): se escriben en código y se pueden usar además de los
elementos Setter (o en su lugar). Se describen abajo.
Aplicar un desencadenador mediante un estilo
Los desencadenadores también se pueden agregar a una declaración Style en un control, en una página o una
aplicación ResourceDictionary . Este ejemplo declara un estilo implícito (es decir, no se establece Key ), lo que
significa que se aplica a todos los controles Entry en la página.

<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Entry">
<Style.Triggers>
<Trigger TargetType="Entry"
Property="IsFocused" Value="True">
<Setter Property="BackgroundColor" Value="Yellow" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ContentPage.Resources>

Desencadenadores de datos
Los desencadenadores de datos usan enlaces de datos para supervisar otro control a fin de que se llame a los
elementos Setter . En lugar del atributo Property de un desencadenador de propiedad, establezca el atributo
Binding para supervisar el valor especificado.

El ejemplo siguiente usa la sintaxis de enlace de datos {Binding Source={x:Reference entry}, Path=Text.Length} ,
que es como se hace referencia a las propiedades de otro control. Cuando la longitud de entry es cero, el
desencadenador se activa. En este ejemplo el desencadenador deshabilita el botón cuando la entrada está vacía.

<!-- the x:Name is referenced below in DataTrigger-->


<!-- tip: make sure to set the Text="" (or some other default) -->
<Entry x:Name="entry"
Text=""
Placeholder="required field" />

<Button x:Name="button" Text="Save"


FontSize="Large"
HorizontalOptions="Center">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding Source={x:Reference entry},
Path=Text.Length}"
Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Button.Triggers>
</Button>

Sugerencia: al evaluar Path=Text.Length , proporcione siempre un valor predeterminado para la propiedad de


destino (p. ej., Text="" ) porque, de lo contrario, será null y el desencadenador no funcionará según lo
esperado.
Además de especificar los elementos Setter , también puede proporcionar EnterActions y ExitActions .

Desencadenadores de eventos
El elemento EventTrigger solo requiere una propiedad Event , como "Clicked" , en el ejemplo siguiente.

<EventTrigger Event="Clicked">
<local:NumericValidationTriggerAction />
</EventTrigger>
Tenga en cuenta que no hay elementos Setter , sino una referencia a una clase definida por
local:NumericValidationTriggerAction que requiere que se declare xmlns:local en el código XAML de la página:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:WorkingWithTriggers;assembly=WorkingWithTriggers"

La propia clase implementa TriggerAction , lo que significa que debe proporcionar una invalidación para el
método Invoke al que se llama cada vez que se produce el evento desencadenador.
Una implementación de acción de desencadenador debe:
Implementar la clase genérica TriggerAction<T> , con el parámetro genérico correspondiente al tipo de
control al que se va a aplicar el desencadenador. Puede usar superclases como VisualElement para escribir
acciones de desencadenador que funcionen con una serie de controles, o especificar un tipo de control
como Entry .
Invalidar el método Invoke : se llama a este método cada vez que se cumplen los criterios del
desencadenador.
Opcionalmente, exponer propiedades que se pueden establecer en el código XAML cuando se declara el
desencadenador (como Anchor , Scale y Length en este ejemplo).

public class NumericValidationTriggerAction : TriggerAction<Entry>


{
protected override void Invoke (Entry entry)
{
double result;
bool isValid = Double.TryParse (entry.Text, out result);
entry.TextColor = isValid ? Color.Default : Color.Red;
}
}

Las propiedades expuestas por la acción de desencadenador se pueden establecer en la declaración de XAML de
este modo:

<EventTrigger Event="TextChanged">
<local:NumericValidationTriggerAction />
</EventTrigger>

Tenga cuidado al compartir desencadenadores en una instancia de ResourceDictionary , ya que una instancia se
comparte entre controles, con lo que cualquier estado que se configure una vez se va a aplicar a todos ellos.
Tenga en cuenta que los desencadenadores de eventos no admiten los elementos EnterActions y ExitActions
descritos abajo.

Multi-desencadenadores
Un elemento MultiTrigger se parece Trigger o DataTrigger , salvo que en él puede haber más de una
condición. Todas las condiciones deben cumplirse para que se desencadenen los elementos Setter .
Este es el ejemplo de un desencadenador de un botón que se enlaza a dos entradas diferentes ( email y phone ):
<MultiTrigger TargetType="Button">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference email},
Path=Text.Length}"
Value="0" />
<BindingCondition Binding="{Binding Source={x:Reference phone},
Path=Text.Length}"
Value="0" />
</MultiTrigger.Conditions>

<Setter Property="IsEnabled" Value="False" />


<!-- multiple Setter elements are allowed -->
</MultiTrigger>

La colección Conditions también puede contener elementos PropertyCondition como este:

<PropertyCondition Property="Text" Value="OK" />

Compilar un multi-desencadenador "require all"


El multi-desencadenador solo actualiza su control cuando se cumplen todas las condiciones. Las pruebas con
"todas las longitudes de campo son cero" (como una página de inicio de sesión donde deben completarse todas
las entradas) son complicadas, porque se quiere una condición "where Text.Length > 0", pero esto no se puede
expresar en XAML.
Se puede hacer con un elemento IValueConverter . El código convertidor siguiente transforma el enlace
Text.Length en un bool que indica si un campo está vacío o no:

public class MultiTriggerConverter : IValueConverter


{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if ((int)value > 0) // length > 0 ?
return true; // some data has been entered
else
return false; // input is empty
}

public object ConvertBack(object value, Type targetType,


object parameter, CultureInfo culture)
{
throw new NotSupportedException ();
}
}

Para usar este convertidor en un multi-desencadenador, primero agréguelo al diccionario de recursos de la


página (junto con una definición de espacio de nombres personalizada xmlns:local ):

<ResourceDictionary>
<local:MultiTriggerConverter x:Key="dataHasBeenEntered" />
</ResourceDictionary>

A continuación se muestra el código XAML. Observe las siguientes diferencias con respecto al primer ejemplo de
multi-desencadenador:
El botón tiene IsEnabled="false" establecido de forma predeterminada.
Las condiciones del multi-desencadenador usan el convertidor para convertir el valor Text.Length en un
.
boolean
Cuando todas las condiciones son true , el establecedor convierte en true la propiedad IsEnabled del
botón.

<Entry x:Name="user" Text="" Placeholder="user name" />

<Entry x:Name="pwd" Text="" Placeholder="password" />

<Button x:Name="loginButton" Text="Login"


FontSize="Large"
HorizontalOptions="Center"
IsEnabled="false">
<Button.Triggers>
<MultiTrigger TargetType="Button">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference user},
Path=Text.Length,
Converter={StaticResource dataHasBeenEntered}}"
Value="true" />
<BindingCondition Binding="{Binding Source={x:Reference pwd},
Path=Text.Length,
Converter={StaticResource dataHasBeenEntered}}"
Value="true" />
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiTrigger>
</Button.Triggers>
</Button>

Estas capturas de pantalla muestran la diferencia entre los dos ejemplos de multi-desencadenadores anteriores.
En la parte superior de las pantallas, la entrada de texto con un solo elemento Entry basta para habilitar el botón
Guardar. En la parte inferior de las pantallas, el botón Iniciar sesión permanece inactivo hasta que ambos
campos contienen datos.

EnterActions y ExitActions
Otra forma de implementar cambios cuando se produce un desencadenador es mediante la incorporación de
colecciones EnterActions y ExitActions y la especificación de implementaciones TriggerAction<T> .
Puede proporcionar tanto EnterActions como ExitActions , así como Setter en un desencadenador, pero tenga
en cuenta que se llama a los elementos Setter de inmediato (no se espera a que se completen EnterAction o
ExitAction ). También puede hacer todo en el código y no usar elementos Setter en absoluto.

<Entry Placeholder="enter job title">


<Entry.Triggers>
<Trigger TargetType="Entry"
Property="Entry.IsFocused" Value="True">
<Trigger.EnterActions>
<local:FadeTriggerAction StartsFrom="0"" />
</Trigger.EnterActions>

<Trigger.ExitActions>
<local:FadeTriggerAction StartsFrom="1" />
</Trigger.ExitActions>
<!-- You can use both Enter/Exit and Setter together if required -->
</Trigger>
</Entry.Triggers>
</Entry>

Como siempre, cuando se hace referencia a una clase en XAML, se debe declarar un espacio de nombres como
xmlns:local , como se muestra aquí:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:WorkingWithTriggers;assembly=WorkingWithTriggers"

El código FadeTriggerAction se muestra a continuación:

public class FadeTriggerAction : TriggerAction<VisualElement>


{
public FadeTriggerAction() {}

public int StartsFrom { set; get; }

protected override void Invoke (VisualElement visual)


{
visual.Animate("", new Animation( (d)=>{
var val = StartsFrom==1 ? d : 1-d;
visual.BackgroundColor = Color.FromRgb(1, val, 1);

}),
length:1000, // milliseconds
easing: Easing.Linear);
}
}

Nota: EnterActions y ExitActions se omiten en desencadenadores de eventos.

Vínculos relacionados
Ejemplo de desencadenadores
Documentación de la API de Xamarin.Forms
Vistas de interfaz de usuario de Xamarin.Forms
12/07/2019 • 8 minutes to read • Edit Online

descargar el ejemplo
Cómo usar las vistas de Xamarin.Forms

ActivityIndicator
El ActivityIndicator es un control animado que se indica a los usuarios que la aplicación esté implicada en una
actividad larga, sin dar ninguna indicación del progreso.

Animación
Xamarin.Forms incluye su propia infraestructura de animación es sencillo para crear animaciones sencillas, pero
también lo suficientemente versátil para crear animaciones complejas.

BoxView
El BoxView es un simple rectángulo de color, pero puede utilizarse para elementos decorativos, gráficos
rudimentarias y para obtener la entrada táctil iterativo.

Button
El Button responde a un pulse o haga clic en que se dirige a una aplicación para llevar a cabo una tarea
determinada.

CheckBox
El CheckBox es un tipo de botón que puede ser activado ni estar vacío. Cuando se activa una casilla de verificación,
se considera en. Cuando una casilla de verificación está vacía, se considera estar apagado.

CollectionView
El CollectionView es una vista flexible y eficaz para presentar las listas de datos con las especificaciones de diseño
diferente.

Colores
Definición y uso de colores en las plataformas pueden ser complicados cuando cada plataforma tiene su propio
estándares y los valores predeterminados.

Controls Reference (Referencia de controles)


Este documento es una referencia rápida a las vistas de interfaz de usuario que componen el marco de
Xamarin.Forms, como páginas, diseños, vistas y celdas.

DataPages
DataSourceControl proporciona una API para enlazar de forma rápida y sencilla un origen de datos a vistas
precompiladas. Elementos de lista y páginas de detalle representarán los datos de forma automática y
personalizadas utilizando temas.

DatePicker
El DatePicker permite al usuario seleccionar una fecha dentro de un intervalo especificado. Se implementa
mediante el selector de fecha admitido por la plataforma concreta donde se ejecuta la aplicación.

Mostrar elementos emergentes


Xamarin.Forms proporciona dos elementos de la interfaz de usuario similares a elementos emergentes: una alerta
y una hoja de acción. Estos elementos de interfaz pueden usarse para mostrar cuadros de diálogo que formular
preguntas sencillas de los usuarios y guiar a los usuarios a través de tareas.

Gráficos con SkiaSharp


Cómo incorporar gráficos en una aplicación de Xamarin.Forms con SkiaSharp.

Imágenes
Las imágenes se pueden compartir entre plataformas con Xamarin.Forms, se pueden cargar específicamente para
cada plataforma, o se pueden descargar para su presentación.

ImageButton
La ImageButton muestra una imagen y responde a un pulse o haga clic en que se dirige a una aplicación para llevar
a cabo una tarea determinada.

Diseños
Xamarin.Forms tiene varios diseños para organizar contenidos en pantalla. StackLayout , Grid , FlexLayout ,
AbsoluteLayout , ScrollView , y RelativeLayout pueden ser utilizadas para crear interfaces de usuario atractivas y
con capacidad de respuesta.

ListView
Xamarin.Forms proporciona un control de vista de lista para mostrar las filas de desplazamiento de datos. El
control incluye acciones contextuales, ajuste de tamaño automático de HasUnevenRows , personalización de
separador, Deslizar para actualizar y encabezados y pies de página.

Mapas
Agregar mapas requiere una descarga del paquete NuGet adicional y alguna configuración específica de la
plataforma. Mapas y marcadores de pin pueden agregarse en unas pocas líneas de código una vez que se realiza la
configuración.

Selector
El Picker vista es un control para seleccionar un elemento de texto en una lista de datos.

Barra de progreso
El ProgressBar es un control que se representa visualmente el progreso como una barra horizontal que se rellena
en función de una propiedad de tipo float.
Slider
El Slider permite al usuario seleccionar un valor numérico de un intervalo continuo.

Control de incremento
El Stepper permite al usuario seleccionar un valor numérico de un intervalo de valores. Consta de dos botones
etiquetados con menos y signos más. Manipulación de los dos botones cambia el valor seleccionado de forma
incremental.

Estilos
Fuente, color y otros atributos se pueden agrupar en estilos que se pueden compartir entre los controles, diseños o
toda la aplicación mediante objetos ResourceDictionary.

Switch
El Switch es un tipo de botón que se puede alternar entre activar y desactivar los Estados.

TableView
La vista de tabla es similar a una vista de lista, pero en lugar de que se está diseñando para las listas largas de datos
está pensado para las pantallas de estilo de entrada de datos de desplazamiento de los controles o los menús
desplegables simple.

Texto
Xamarin.Forms tiene varias vistas para presentar y recibir el texto. Las vistas de texto pueden formatearlos y
personalizadas para las plataformas. Configuración de la fuente específica puede habilitar la compatibilidad con
características de accesibilidad.

Temas
Los temas de Xamarin.Forms definen una apariencia visual específica para los controles estándar. Una vez que
agregue un tema para el diccionario de recursos de la aplicación, cambiará la apariencia de los controles estándar.

TimePicker
El TimePicker permite al usuario seleccionar una hora. Se implementa mediante el selector de hora compatible
con la plataforma concreta donde se ejecuta la aplicación.

Visual
Xamarin.Forms Material Visual puede utilizarse para crear aplicaciones de Xamarin.Forms que tienen un aspecto
idénticos o idéntica en gran medida en iOS y Android.

Administrador de estado visual


Visual State Manager proporciona una manera estructurada para desencadenar cambios en la interfaz de usuario
desde el código, incluido el diseño que se adapta a los cambios en el tamaño o la orientación del dispositivo.

WebView
Xamarin.Forms usa el control del explorador web nativos en cada plataforma y puede mostrar cadenas Html
generadas, los sitios Web y los recursos locales.

Vínculos relacionados
Galería de Xamarin.Forms (ejemplo)
Xamarin.Forms ActivityIndicator
12/07/2019 • 2 minutes to read • Edit Online

Descargar el ejemplo
Xamarin.Forms ActivityIndicator es un control que muestra una animación para mostrar que la aplicación esté
implicada en una actividad larga. A diferencia de la ProgressBar , el ActivityIndicator no proporciona ninguna
indicación de progreso. El ActivityIndicator hereda View .
La siguiente captura de pantalla muestra un ActivityIndicator control en iOS y Android:

El ActivityIndicator control define las siguientes propiedades:


IsRunning es un valor que indica si el ActivityIndicator debe ser visible y animación u oculto. Cuando
bool
el valor es false el ActivityIndicator no será visible.
Color es un Color valor que define el color de pantalla de la ActivityIndicator .

Estas propiedades están respaldadas por BindableProperty objetos, lo que significa que el ActivityIndicator
puede cambiar el estilo y ser el destino de los enlaces de datos.

Crear un ActivityIndicator
Un ActivityIndicator se pueden crear instancias en XAML. Su IsRunning se puede establecer la propiedad para
determinar si el control está visible y animación. Si el IsRunning propiedad no está establecida, el valor
predeterminado es false y ActivityIndicator no será visible. El ejemplo siguiente muestra cómo crear una
instancia de un ActivityIndicator en XAML con el elemento opcional IsRunning conjunto de propiedades:

<ActivityIndicator IsRunning="true" />

Un ActivityIndicator también se pueden crear en el código:

ActivityIndicator activityIndicator = new ActivityIndicator { IsRunning = true };

Propiedades de apariencia ActivityIndicator


El Color propiedad puede establecerse para definir el ActivityIndicator color. El ejemplo siguiente muestra
cómo crear una instancia de un ActivityIndicator en XAML con el Color conjunto de propiedades:

<ActivityIndicator Color="Orange" />

El Color propiedad también se puede establecer al crear un ActivityIndicator en el código:


ActivityIndicator activityIndicator = new ActivityIndicator { Color = Color.Orange };

La siguiente captura de pantalla muestra la ActivityIndicator con el Color propiedad establecida en


Color.Orange en iOS y Android:

Vínculos relacionados
Demostraciones de ActivityIndicator
Barra de progreso
Animación de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Xamarin.Forms incluye su propia infraestructura de animación es sencillo para crear animaciones sencillas, pero
también lo suficientemente versátil para crear animaciones complejas.
Las clases de animación de Xamarin.Forms tienen como objetivo diferentes propiedades de elementos visuales.
Una animación típica que cambia de forma progresiva una propiedad de un valor a otro durante un período de
tiempo. Tenga en cuenta que no hay ninguna interfaz XAML para las clases de animación de Xamarin.Forms. Sin
embargo, estas se pueden encapsular en comportamientos y se puede hacer referencia a ellas posteriormente
desde XAML.

Animaciones simples
La clase ViewExtensions proporciona métodos de extensión que pueden usarse para construir animaciones simples
que giran, escalan, traducen y desvanecen instancias VisualElement . En este artículo se muestra cómo crear y
cancelar animaciones mediante la clase ViewExtensions .

Funciones de aceleración
Xamarin.Forms incluye la clase Easing , que permite especificar una función de transferencia que controla cómo se
aceleran o ralentizan las animaciones durante su ejecución. En este artículo se muestra cómo usar las funciones de
aceleración predefinidas y cómo crear funciones de aceleración personalizadas.

Animaciones personalizadas
La clase Animation es el bloque de creación de todas las animaciones de Xamarin.Forms. Con los métodos de
extensión de la clase ViewExtensions se pueden crar uno o varios objetos Animation . En este artículo se muestra
cómo usar la clase Animation para crear y cancelar animaciones, sincronizar varias animaciones y crear
animaciones personalizadas que animan propiedades que no están animadas mediante métodos existentes de
animación.
Animaciones sencillas en Xamarin.Forms
11/07/2019 • 19 minutes to read • Edit Online

descargar el ejemplo
La clase ViewExtensions proporciona métodos de extensión que se pueden usar para construir las animaciones
sencillas. En este artículo muestra cómo crear y cancelar las animaciones mediante la clase ViewExtensions.
El ViewExtensions clase proporciona los siguientes métodos de extensión que pueden usarse para crear
animaciones sencillas:
TranslateTo anima el TranslationX y TranslationY las propiedades de un VisualElement .
ScaleTo anima el Scale propiedad de un VisualElement .
RelScaleTo se aplica un aumento incremental animado o disminución en el Scale propiedad de un
VisualElement .
RotateTo anima el Rotation propiedad de un VisualElement .
RelRotateTo se aplica un aumento incremental animado o disminución en el Rotation propiedad de un
VisualElement .
RotateXTo anima el RotationX propiedad de un VisualElement .
RotateYTo anima el RotationY propiedad de un VisualElement .
FadeTo anima el Opacity propiedad de un VisualElement .

De forma predeterminada, cada animación tardará 250 milisegundos. Sin embargo, se puede especificar una
duración de cada animación al crear la animación.
El ViewExtensions clase también incluye un CancelAnimations método que puede usarse para cancelar las
animaciones.

NOTE
El ViewExtensions clase proporciona un LayoutTo método de extensión. Sin embargo, este método está pensado para
usarse diseños para animar las transiciones entre Estados de diseño que contienen el tamaño y posición de los cambios. Por
lo tanto, que debe usarse únicamente por Layout subclases.

Los métodos de extensión de la animación de la clase ViewExtensions son asincrónicos y devuelven un objeto
Task<bool> . El valor devuelto es false si se completa la animación y true si esta se cancela. Por lo tanto, los
métodos de animación normalmente deben usarse con el operador await , lo que permite determinar fácilmente
cuándo se ha completado una animación. Además, después se pueden crear animaciones secuenciales con
métodos de animación posteriores que se ejecutan después de que se haya completado el método anterior. Para
obtener más información, consulte Animaciones compuestas.
Si hay un requisito para permitir que una animación completa en segundo plano, el await se puede omitir el
operador. En este escenario, los métodos de extensión de la animación se devolverá rápidamente después de
iniciar la animación, con la animación que se producen en segundo plano. Esta operación puede aprovecharse al
crear animaciones compuestas. Para obtener más información, consulte animaciones compuesto.
Para obtener más información sobre la await operador, consulte información general de soporte técnico de
Async.
Animaciones únicas
Cada método de extensión en el ViewExtensions implementa una operación de animación única que
progresivamente cambia una propiedad de un valor a otro valor durante un período de tiempo. Esta sección
explora cada operación de animación.
Giro
En el ejemplo de código siguiente se muestra cómo utilizar el RotateTo método se va a animar el Rotation
propiedad de un Image :

await image.RotateTo (360, 2000);


image.Rotation = 0;

Este código se anima la Image instancia girando hasta 360 grados superior a 2 segundos (2.000 milisegundos). El
RotateTo método obtiene la actual Rotation propiedad valor para el inicio de la animación y, a continuación, se
gira desde ese valor a su primer argumento (360). Una vez que la animación se complete, la imagen Rotation
propiedad se restablece a 0. Esto garantiza que el Rotation propiedad no se queda en 360 después de que
concluya la animación, que podrían impedir que las rotaciones adicionales.
Las capturas de pantalla siguientes muestran la rotación en curso en cada plataforma:

Giro relativa
En el ejemplo de código siguiente se muestra cómo utilizar el RelRotateTo método para aumentar o disminuir de
forma incremental el Rotation propiedad de un Image :

await image.RelRotateTo (360, 2000);


Este código se anima la Image instancia girando superior a 2 segundos (2.000 milisegundos) de 360 grados desde
su posición inicial. El RelRotateTo método obtiene la actual Rotation propiedad valor para el inicio de la
animación y, a continuación, se gira desde ese valor para el valor más su primer argumento (360). Esto garantiza
que cada animación siempre será un giro de 360 grados desde la posición inicial. Por lo tanto, si se invoca una
nueva animación mientras una animación ya está en curso, se iniciará desde la posición actual y puede terminar en
una posición que no es un incremento de 360 grados.
Las capturas de pantalla siguientes muestran el giro relativo en curso en cada plataforma:

Cambiar escala
En el ejemplo de código siguiente se muestra cómo utilizar el ScaleTo método se va a animar el Scale propiedad
de un Image :

await image.ScaleTo (2, 2000);

Este código se anima la Image instancia mediante el escalamiento vertical a dos veces su tamaño superior a 2
segundos (2.000 milisegundos). El ScaleTo método obtiene la actual Scale el valor de propiedad (valor
predeterminado de 1) para el inicio de la animación y, a continuación, se escala desde ese valor a su primer
argumento (2). Esto tiene el efecto de aumentar el tamaño de la imagen a dos veces su tamaño.
Las capturas de pantalla siguientes muestran el escalado en curso en cada plataforma:
NOTE
El VisualElement también define la clase ScaleX y ScaleY propiedades, que se pueden escalar el VisualElement
manera diferente en el direcciones horizontal y vertical. Estas propiedades se pueden animar con la Animation clase. Para
obtener más información, consulte animaciones personalizadas en Xamarin.Forms.

Relativa al ajuste de escala


En el ejemplo de código siguiente se muestra cómo utilizar el RelScaleTo método se va a animar el Scale
propiedad de un Image :

await image.RelScaleTo (2, 2000);

Este código se anima la Image instancia mediante el escalamiento vertical a dos veces su tamaño superior a 2
segundos (2.000 milisegundos). El RelScaleTo método obtiene la actual Scale valor de propiedad para el inicio
de la animación y, a continuación, se escala desde ese valor para el valor más su primer argumento (2). Esto
garantiza que cada animación siempre será un escalamiento de 2 desde la posición inicial.
Escalado y la rotación con delimitadores
El AnchorX y AnchorY propiedades establecen el centro de ajuste de escala o rotación para el Rotation y Scale
propiedades. Por lo tanto, sus valores también afectan a la RotateTo y ScaleTo métodos.
Dado un Image que se ha colocado en el centro de un diseño, en el ejemplo de código siguiente se muestra la
rotación de la imagen en torno al centro del diseño estableciendo su AnchorY propiedad:

image.AnchorY = (Math.Min (absoluteLayout.Width, absoluteLayout.Height) / 2) / image.Height;


await image.RotateTo (360, 2000);
Para girar el Image instancia en torno al centro del diseño, el AnchorX y AnchorY propiedades deben establecerse
en valores en relación con el ancho y alto de la Image . En este ejemplo, el centro de la Image se define como en el
centro del diseño de modo que el valor predeterminado AnchorX valor de 0,5 no es necesario cambiar. Sin
embargo, el AnchorY propiedad se vuelve a definir para que sea un valor de la parte superior de la Image al punto
central del diseño. Esto garantiza que el Image hace una rotación completa de 360 grados alrededor del punto
central del diseño, como se muestra en las capturas de pantalla siguiente:

Conversión
En el ejemplo de código siguiente se muestra cómo utilizar el TranslateTo método se va a animar el
TranslationX y TranslationY las propiedades de un Image :

await image.TranslateTo (-100, -100, 1000);

Este código se anima la Image instancia convirtiendo, vertical y horizontalmente en 1 segundo (1000
milisegundos). El TranslateTo método traduce al mismo tiempo la imagen 100 píxeles a la izquierda y 100 píxeles
hacia arriba. Esto es porque el primer y segundo argumentos son ambos números negativos. Proporcionar los
números positivos traduciría la imagen a la derecha y abajo.
Las capturas de pantalla siguientes muestran la traducción en curso en cada plataforma:
NOTE
Si un elemento se distribuyen inicialmente fuera de la pantalla y, a continuación, se traduce en la pantalla, después de la
traducción, diseño de entrada del elemento permanece fuera de la pantalla y el usuario no puede interactuar con él. Por lo
tanto, se recomienda que una vista debe disponerse en su posición final y, a continuación, los necesarios traducciones
realizadas.

Corrección selectiva
En el ejemplo de código siguiente se muestra cómo utilizar el FadeTo método se va a animar el Opacity
propiedad de un Image :

image.Opacity = 0;
await image.FadeTo (1, 4000);

Este código se anima la Image instancia por desvanezcan en más de 4 segundos (de 4000 milisegundos). El
FadeTo método obtiene la actual Opacity valor de propiedad para el inicio de la animación y, a continuación,
fundidos en desde ese valor a su primer argumento (1).
Las capturas de pantalla siguientes muestran la atenuación en curso en cada plataforma:
Animaciones compuestas
Una animación compuesta es una combinación secuencial de las animaciones y se pueden crear con el await
operador, como se muestra en el ejemplo de código siguiente:

await image.TranslateTo (-100, 0, 1000); // Move image left


await image.TranslateTo (-100, -100, 1000); // Move image up
await image.TranslateTo (100, 100, 2000); // Move image diagonally down and right
await image.TranslateTo (0, 100, 1000); // Move image left
await image.TranslateTo (0, 0, 1000); // Move image up

En este ejemplo, el Image se traduce en 6 segundos (6.000 milisegundos). La traducción de la Image utiliza cinco
animaciones con la await operador que indica que cada animación se ejecuta de forma secuencial. Por lo tanto,
los métodos de la animación siguiente ejecutan después de que se ha completado el método anterior.

Animaciones de composición
Una animación compuesta es una combinación de animaciones donde se ejecutan simultáneamente dos o más de
las animaciones. Las animaciones de composición se pueden crear mezclando animaciones esperadas y que no es
"awaited", como se muestra en el ejemplo de código siguiente:

image.RotateTo (360, 4000);


await image.ScaleTo (2, 2000);
await image.ScaleTo (1, 2000);

En este ejemplo, el Image es escalar y girar simultáneamente más de 4 segundos (de 4000 milisegundos). El
escalado de la Image utiliza dos animaciones secuenciales que se producen al mismo tiempo como la rotación. El
RotateTo método se ejecuta sin un await operador y se devuelve inmediatamente, con la primera ScaleTo , a
continuación, a partir de animación. El await operador en la primera ScaleTo retrasa la llamada al método el
segundo ScaleTo hasta la primera llamada a un método ScaleTo ha completado la llamada al método. En este
momento la RotateTo animación es la mitad forma completa y el Image estará gira 180 grados. Durante los
últimos 2 segundos (2.000 milisegundos), el segundo ScaleTo animación y RotateTo animación ambos
completar.
Ejecución simultánea de varios métodos asincrónicos
El static Task.WhenAny y Task.WhenAll métodos se utilizan para ejecutar varios métodos asincrónicos
simultáneamente y, por lo tanto, puede usarse para crear animaciones compuestas. Ambos métodos devuelven un
Task de objetos y Aceptar una colección de métodos que cada valor devuelto una Task objeto. El Task.WhenAny
método se completa cuando cualquier método en su colección completa su ejecución, como se muestra en el
ejemplo de código siguiente:

await Task.WhenAny<bool>
(
image.RotateTo (360, 4000),
image.ScaleTo (2, 2000)
);
await image.ScaleTo (1, 2000);

En este ejemplo, el Task.WhenAny llamada de método contiene dos tareas. La primera tarea gira la imagen más de
4 segundos (de 4000 milisegundos) y la segunda tarea escala la imagen superior a 2 segundos (2.000
milisegundos). Cuando se completa la segunda tarea, el Task.WhenAny se completa la llamada al método. Sin
embargo, aunque el RotateTo método todavía se está ejecutando, el segundo ScaleTo método puede comenzar.
El Task.WhenAll método se completa cuando se hayan completado todos los métodos en su colección, como se
muestra en el ejemplo de código siguiente:

// 10 minute animation
uint duration = 10 * 60 * 1000;

await Task.WhenAll (
image.RotateTo (307 * 360, duration),
image.RotateXTo (251 * 360, duration),
image.RotateYTo (199 * 360, duration)
);

En este ejemplo, el Task.WhenAll llamada de método contiene tres tareas, cada uno de los cuales se ejecuta más
de 10 minutos. Cada Task hace que sea un número diferente de rotaciones de 360 grados: 307 rotaciones para
RotateTo , 251 rotaciones para RotateXTo y 199 rotaciones para RotateYTo . Estos valores son números primos,
por lo tanto, lo que garantiza que las rotaciones no están sincronizadas y, por tanto, no origine patrones
repetitivos.
Las capturas de pantalla siguientes muestran las rotaciones varias en curso en cada plataforma:
Cancelar las animaciones
Una aplicación puede cancelar una o varias animaciones con una llamada a la static
ViewExtensions.CancelAnimations método, como se muestra en el ejemplo de código siguiente:

ViewExtensions.CancelAnimations (image);

Esta operación cancelará inmediatamente todas las animaciones que se están ejecutando en el Image instancia.

Resumen
En este artículo se muestra creando y cancelación de animaciones con la ViewExtensions clase. Esta clase
proporciona métodos de extensión que se pueden usar para construir las animaciones sencillas que giran,
escalarán, traducirán y atenuación VisualElement instancias.

Vínculos relacionados
Información general sobre la compatibilidad con Async
Animación básica (ejemplo)
ViewExtensions
Funciones de aceleración en Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms incluye una clase de entrada y salida lenta que le permite especificar una función de transferencia
que controla cómo las animaciones aceleran o ralentizar se están ejecutando. En este artículo se muestra cómo
utilizar las funciones de aceleración predefinidas y cómo crear funciones de aceleración.
El Easing clase define una serie de funciones de aceleración que puede consumir las animaciones:
El BounceIn función de aceleración rebota la animación al principio.
El BounceOut función de aceleración rebota la animación al final.
El CubicIn lentamente en función de aceleración acelera la animación.
El CubicInOut función de aceleración acelera la animación al principio y ralentiza la animación al final.
El CubicOut rápidamente en función de aceleración disminuye la velocidad de la animación.
El Linear función de aceleración usa una velocidad constante y es el valor predeterminado función de
aceleración.
El SinIn sin problemas en función de aceleración acelera la animación.
El SinInOut sin problemas en función de aceleración acelera la animación al principio y disminuye la velocidad
sin problemas la animación al final.
El SinOut sin problemas en función de aceleración disminuye la velocidad de la animación.
El SpringIn función de aceleración hace que la animación acelerar rápidamente hacia el final.
El SpringOut función de aceleración hace que la animación que se ralentice rápidamente hacia el final.
El In y Out sufijos indican si el efecto de la función de aceleración es apreciable al principio de la animación, al
final, o ambos.
Además, se pueden crear funciones de aceleración. Para obtener más información, consulte funciones de
aceleración personalizada.

Consumo de una función de aceleración


Los métodos de extensión de la animación en el ViewExtensions clase permite que una función de aceleración que
se especifique como parámetro del método final, como se muestra en el ejemplo de código siguiente:

await image.TranslateTo(0, 200, 2000, Easing.BounceIn);


await image.ScaleTo(2, 2000, Easing.CubicIn);
await image.RotateTo(360, 2000, Easing.SinInOut);
await image.ScaleTo(1, 2000, Easing.CubicOut);
await image.TranslateTo(0, -200, 2000, Easing.BounceOut);

Al especificar una función de aceleración para una animación, la velocidad de animación se convierte en no lineal
y genera el efecto de la función de aceleración. Si se omite una función de aceleración al crear una animación hace
que la animación que utilice el valor predeterminado Linear función, lo que produce una velocidad lineal de
aceleración.
Para obtener más información sobre el uso de los métodos de extensión de la animación en el ViewExtensions de
clases, vea las animaciones sencillas. Funciones de aceleración también pueden utilizarse en el Animation clase.
Para obtener más información, consulte animaciones personalizadas.
Funciones de aceleración personalizadas
Existen tres enfoques principales para crear una función de aceleración personalizada:
1. Crear un método que toma un double argumento y devuelve un double resultado.
2. Creará un control Func<double, double> .
3. Especifique la función de aceleración como argumento para el Easing constructor.
En los tres casos, la función de aceleración personalizada debe devolver 0 para un argumento de 0 y 1 para un
argumento de 1. Sin embargo, se puede devolver cualquier valor entre los valores de argumento de 0 y 1. A su
vez ahora se explicará cada enfoque.
Método de aceleración personalizadas
Se puede definir una función de aceleración personalizada como un método que toma un double argumento y
devuelve un double como resultado, como se muestra en el ejemplo de código siguiente:

await image.TranslateTo(0, 200, 2000, CustomEase);

double CustomEase (double t)


{
return t == 0 || t == 1 ? t : (int)(5 * t) / 5.0;
}

El CustomEase método trunca el valor entrante a los valores 0, 0,2, 0,4, 0.6, 0.8 y 1. Por lo tanto, el Image instancia
se traduce en saltos discretos, en lugar de forma fluida.
Aceleración Func personalizadas
También se puede definir una función de aceleración personalizada como un Func<double, double> , tal y como se
muestra en el ejemplo de código siguiente:

Func<double, double> CustomEase = t => 9 * t * t * t - 13.5 * t * t + 5.5 * t;


await image.TranslateTo(0, 200, 2000, CustomEase));

El CustomEase Func representa una función de entradas y salidas lenta que comienza rápido, se ralentiza e
invierte el curso y, a continuación, invierte nuevo curso para acelerar rápidamente hacia el final. Por tanto,
mientras el movimiento general de la Image instancia está hacia abajo, invierte también temporalmente a mitad
de camino a través de la animación del curso.
Constructor de aceleración personalizadas
También se puede definir una función de aceleración personalizada como el argumento de la Easing constructor,
tal y como se muestra en el ejemplo de código siguiente:

await image.TranslateTo (0, 200, 2000, new Easing (t => 1 - Math.Cos (10 * Math.PI * t) * Math.Exp (-5 * t)));

La función de aceleración personalizada se especifica como un argumento de función lambda a la Easing


constructor y se usa el Math.Cos método para crear un efecto de lento que se atenúa por la Math.Exp método. Por
lo tanto, el Image instancia se traduce para que aparezca a colocar en su lugar definitiva de almacenamiento final.

Resumen
En este artículo se muestra cómo utilizar las funciones de aceleración predefinidas y cómo crear funciones de
aceleración. Xamarin.Forms incluye la clase Easing , que permite especificar una función de transferencia que
controla cómo se aceleran o ralentizan las animaciones durante su ejecución.
Vínculos relacionados
Información general sobre la compatibilidad con Async
Funciones de aceleración (ejemplo)
Aceleración
ViewExtensions
Animaciones personalizadas en Xamarin.Forms
11/07/2019 • 20 minutes to read • Edit Online

descargar el ejemplo
La clase de animación es el bloque de creación de todas las animaciones de Xamarin.Forms, con los métodos de
extensión en la clase ViewExtensions crear uno o varios objetos de animación. En este artículo se muestra cómo
usar la clase de animación para crear y cancelar las animaciones, sincronizar varias animaciones y crear
animaciones personalizadas que animan las propiedades que no se animación mediante los métodos de
animación existentes.
Se debe especificar un número de parámetros al crear un Animation objeto, incluidos los valores inicial y final de
la propiedad que se anima y una devolución de llamada que cambia el valor de la propiedad. Un Animation
objeto también puede mantener una colección de animaciones secundarias que se pueden ejecutar y
sincronizado. Para obtener más información, consulte animaciones secundarias.
Ejecuta una animación que se creó con la Animation (clase), que puede o no incluir animaciones secundarias, se
logra mediante una llamada a la Commit método. Este método especifica la duración de la animación y, entre
otros elementos, una devolución de llamada que controla si se debe repetir la animación.

Creación de una animación


Al crear un Animation objeto, por lo general, un mínimo de tres parámetros son necesarios, como se muestra en
el ejemplo de código siguiente:

var animation = new Animation (v => image.Scale = v, 1, 2);

Este código define una animación de la Scale propiedad de un Image la instancia de un valor de 1 a un valor de
2. El valor animado, que se deriva mediante Xamarin.Forms, se pasa a la devolución de llamada especificado
como el primer argumento, donde se usa para cambiar el valor de la Scale propiedad.
La animación se inicia con una llamada a la Commit método, como se muestra en el ejemplo de código siguiente:

animation.Commit (this, "SimpleAnimation", 16, 2000, Easing.Linear, (v, c) => image.Scale = 1, () => true);

Tenga en cuenta que el Commit método no devuelve un Task objeto. En su lugar, las notificaciones se
proporcionan a través de métodos de devolución de llamada.
Los siguientes argumentos se especifican en el Commit método:
El primer argumento (propietario) identifica al propietario de la animación. Esto puede ser el elemento visual
en el que se aplica la animación u otro elemento visual, como la página.
El segundo argumento (nombre) identifica la animación con un nombre. El nombre se combina con el
propietario para identificar de forma única la animación. Esta identificación única, a continuación, puede usarse
para determinar si se está ejecutando la animación ( AnimationIsRunning ), o para cancelarla ( AbortAnimation ).
El tercer argumento (tasa) indica el número de milisegundos entre cada llamada al método de devolución de
llamada definido en el Animation constructor
El cuarto argumento ( longitud) indica la duración de la animación, en milisegundos.
El quinto argumento (aceleración) define la función de aceleración que se usará en la animación. Como
alternativa, la función de aceleración se puede especificar como argumento a la Animation constructor. Para
obtener más información acerca de funciones de aceleración, vea funciones de aceleración.
El sexto argumento (terminado) es una devolución de llamada que se ejecutará cuando se haya completado la
animación. Esta devolución de llamada toma dos argumentos, con el primer argumento que indica un valor
final y el segundo argumento es un bool que se establece en true si se ha cancelado la animación. Como
alternativa, el terminado devolución de llamada se puede especificar como argumento a la Animation
constructor. Sin embargo, con una animación única, si terminado las devoluciones de llamada se especifican
tanto en el Animation constructor y el Commit método, solo la devolución de llamada especificado en el
Commit se ejecutará el método.
El séptimo argumento (repita) es una devolución de llamada que permite que la animación se repita. Se llama
al final de la animación y devolver true indica que se debe repetir la animación.

El efecto general consiste en crear una animación que aumenta la Scale propiedad de un Image de 1 a 2,
superior a 2 segundos (2.000 milisegundos), mediante el Linear función de aceleración. Cada vez que finaliza la
animación, su Scale propiedad se restablece en 1 y se repite la animación.

NOTE
Las animaciones simultáneas, que se ejecutan de forma independiente entre sí se pueden construir mediante la creación de
un Animation para cada animación de objetos y, a continuación, llamar a la Commit método en cada animación.

Animaciones secundarias
El Animation clase también admite animaciones secundarias, que implica la creación de un Animation objeto qué
Sí Animation se agregan objetos. Esto permite una serie de animaciones que se ejecutará y se sincronizan. En el
ejemplo de código siguiente se muestra cómo crear y ejecutar animaciones secundarias:

var parentAnimation = new Animation ();


var scaleUpAnimation = new Animation (v => image.Scale = v, 1, 2, Easing.SpringIn);
var rotateAnimation = new Animation (v => image.Rotation = v, 0, 360);
var scaleDownAnimation = new Animation (v => image.Scale = v, 2, 1, Easing.SpringOut);

parentAnimation.Add (0, 0.5, scaleUpAnimation);


parentAnimation.Add (0, 1, rotateAnimation);
parentAnimation.Add (0.5, 1, scaleDownAnimation);

parentAnimation.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true,
false));

Como alternativa, el ejemplo de código se puede escribir más concisa, como se muestra en el ejemplo de código
siguiente:

new Animation {
{ 0, 0.5, new Animation (v => image.Scale = v, 1, 2) },
{ 0, 1, new Animation (v => image.Rotation = v, 0, 360) },
{ 0.5, 1, new Animation (v => image.Scale = v, 2, 1) }
}.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true, false));

En ambos ejemplos de código, un elemento primario Animation se crea el objeto, a la que adicionales Animation
, a continuación, se agregan objetos. Los dos primeros argumentos para el Add método especifique cuándo debe
comenzar y finalizar la animación secundarios. Los valores de argumento deben estar comprendido entre 0 y 1 y
representar el período relativo dentro de la animación principal que se activará la animación secundario
especificado. Por lo tanto, en este ejemplo el scaleUpAnimation estará activa durante la primera mitad de la
animación, la scaleDownAnimation estará activa para la segunda mitad de la animación y el rotateAnimation
estará activa durante toda la duración.
El efecto general es que la animación se produce más de 4 segundos (de 4000 milisegundos). El
scaleUpAnimation anima la Scale propiedad de 1 a 2, más de 2 segundos. El scaleDownAnimation luego anima la
Scale propiedad de 2 a 1, más de 2 segundos. Mientras se lleva a cabo ambas animaciones de escalado, la
rotateAnimation anima la Rotation propiedad comprendido entre 0 y 360, más de 4 segundos. Tenga en cuenta
que las animaciones de escalado también usar funciones de aceleración. El SpringIn función de aceleración hace
que el Image reducir inicialmente antes de obtener mayor y el SpringOut función de aceleración hace que la
Image sea menor que su tamaño real hacia el final de la animación completa.

Hay varias diferencias entre una Animation objeto que usa animaciones secundarias y que no:
Al usar animaciones secundarias, el terminado devolución de llamada en una animación secundaria indica
cuando se haya completado el elemento secundario y el terminado devolución de llamada se pasa a la Commit
método indica cuándo el ha completado la animación completa.
Al usar animaciones secundarias, devolver true desde el repita devolución de llamada en el Commit método
no hará que la animación se repita, pero la animación se seguirá ejecutando sin los nuevos valores.
Al incluir una función de aceleración en el Commit método y la función de aceleración devuelve un valor
mayor que 1, se terminará la animación. Si la función de aceleración devuelve un valor menor que 0, el valor
se fija en 0. Para utilizar una función de entradas y salidas lenta que devuelve un valor menor que 0 o mayor
que 1, debe especificar en una de las animaciones secundarias, en lugar de en el Commit método.

El Animation también incluye la clase WithConcurrent métodos que pueden usarse para agregar animaciones
secundarias a un elemento primario Animation objeto. Sin embargo, sus comenzar y finalizar valores de
argumento no se restringen a 0 a 1, pero solo esa parte de la animación de secundarios que se corresponde con
un intervalo de 0 a 1 estará activa. Por ejemplo, si un WithConcurrent llamada al método define una animación
secundaria que tiene como destino un Scale propiedad desde 1 a 6, pero con comenzar y finalizar valores de -2
y 3, el comenzar valor-2 corresponde a un Scale valor 1 y el finalizar valor 3 corresponde a un Scale valor de 6.
Dado que los valores fuera del intervalo de 0 y 1 no reproducción ninguna parte de una animación, la Scale
propiedad solo se pueden animar de 3 a 6.

Cancelación de una animación


Una aplicación puede cancelar una animación con una llamada a la AbortAnimation método de extensión, como
se muestra en el ejemplo de código siguiente:

this.AbortAnimation ("SimpleAnimation");

Tenga en cuenta que las animaciones se identifican mediante una combinación de propietario de la animación y el
nombre de la animación. Por lo tanto, el propietario y el nombre especifican al ejecutar la animación debe
especificarse para cancelar la animación. Por lo tanto, el ejemplo de código inmediatamente cancelará la
animación denominada SimpleAnimation que sea propiedad de la página.

Creación de una animación personalizada


Los ejemplos aquí mostrados hasta ahora han demostrado que las animaciones que podrían obtenerse
igualmente con los métodos en el ViewExtensions clase. Sin embargo, la ventaja de la Animation es de clase que
tiene acceso al método de devolución de llamada, que se ejecuta cuando cambia el valor animado. Esto permite
que la devolución de llamada implementar cualquier animación deseado. Por ejemplo, en el ejemplo de código
siguiente se anima la BackgroundColor propiedad de una página estableciendo en Color valores creados por la
Color.FromHsla método con valores de matiz comprendida entre 0 y 1:
new Animation (callback: v => BackgroundColor = Color.FromHsla (v, 1, 0.5),
start: 0,
end: 1).Commit (this, "Animation", 16, 4000, Easing.Linear, (v, c) => BackgroundColor = Color.Default);

La animación resultante proporciona la apariencia de avanzar el fondo de página a través de los colores del arco
iris.
Para obtener más ejemplos de creación de animaciones complejas, incluidas una animación de curva de Bézier,
consulte capítulo 22 de Creating Mobile Apps with Xamarin.Forms.

Creación de un método de extensión de animación personalizada


Los métodos de extensión en el ViewExtensions clase animar una propiedad de su valor actual en un valor
especificado. Esto dificulta crear, por ejemplo, un ColorTo método de animación que se puede usar para animar
un color en un valor a otro, porque:
La única Color propiedad definida por el VisualElement clase es BackgroundColor , lo que no siempre es el
deseado Color propiedad se va a animar.
A menudo el valor actual de un Color propiedad es Color.Default , lo que no es un color real y que no se
puede usar en los cálculos de interpolación.
La solución a este problema es que no la ColorTo como destino un determinado método Color propiedad. En su
lugar, puede escribirse con un método de devolución de llamada que pasa el interpolada Color valor al llamador.
Además, el método tomará el inicio y finalización Color argumentos.
El ColorTo método puede implementarse como un método de extensión que usa el Animate método en el
AnimationExtensions clase para proporcionar su funcionalidad. Esto es porque el Animate método puede
utilizarse para las propiedades de destino que no son de tipo double , como se muestra en el ejemplo de código
siguiente:

public static class ViewExtensions


{
public static Task<bool> ColorTo(this VisualElement self, Color fromColor, Color toColor, Action<Color>
callback, uint length = 250, Easing easing = null)
{
Func<double, Color> transform = (t) =>
Color.FromRgba(fromColor.R + t * (toColor.R - fromColor.R),
fromColor.G + t * (toColor.G - fromColor.G),
fromColor.B + t * (toColor.B - fromColor.B),
fromColor.A + t * (toColor.A - fromColor.A));
return ColorAnimation(self, "ColorTo", transform, callback, length, easing);
}

public static void CancelAnimation(this VisualElement self)


{
self.AbortAnimation("ColorTo");
}

static Task<bool> ColorAnimation(VisualElement element, string name, Func<double, Color> transform,


Action<Color> callback, uint length, Easing easing)
{
easing = easing ?? Easing.Linear;
var taskCompletionSource = new TaskCompletionSource<bool>();

element.Animate<Color>(name, transform, callback, 16, length, easing, (v, c) =>


taskCompletionSource.SetResult(c));
return taskCompletionSource.Task;
}
}
El Animate método requiere un transformar argumento, que es un método de devolución de llamada. La entrada
para esta devolución de llamada es siempre un double comprendida entre 0 y 1. Por lo tanto, el ColorTo método
define su propia transformación Func que acepta un double comprendida entre 0 y 1 y que devuelve un Color
valor correspondiente a ese valor. El Color valor se calcula mediante la interpolación de la R , G , B , y A los
valores de los dos proporcionados Color argumentos. El Color valor, a continuación, se pasa al método de
devolución de llamada para la aplicación a una propiedad determinada.
Este enfoque permite la ColorTo método animar cualquiera Color propiedad, como se muestra en el ejemplo de
código siguiente:

await Task.WhenAll(
label.ColorTo(Color.Red, Color.Blue, c => label.TextColor = c, 5000),
label.ColorTo(Color.Blue, Color.Red, c => label.BackgroundColor = c, 5000));
await this.ColorTo(Color.FromRgb(0, 0, 0), Color.FromRgb(255, 255, 255), c => BackgroundColor = c, 5000);
await boxView.ColorTo(Color.Blue, Color.Red, c => boxView.Color = c, 4000);

En este ejemplo de código, el ColorTo método anima la TextColor y BackgroundColor las propiedades de un
Label , el BackgroundColor propiedad de una página y el Color propiedad de un BoxView .

Resumen
En este artículo se muestra cómo usar el Animation clase para crear y cancelar las animaciones, sincronizar varias
animaciones y crear animaciones personalizadas que animan las propiedades que no se animación la animación
existente métodos. La Animation clase es el bloque de creación de todas las animaciones de Xamarin.Forms.

Vínculos relacionados
Animaciones personalizadas (ejemplo)
Animación
AnimationExtensions
Xamarin.Forms BoxView
11/07/2019 • 29 minutes to read • Edit Online

descargar el ejemplo
BoxView Representa un rectángulo simple de un ancho especificado, alto y color. Puede usar BoxView como
decoración, gráficos rudimentarias y para la interacción con el usuario a través del tacto.
Dado que Xamarin.Forms no tiene un sistema de gráficos vectoriales integrados, la BoxView ayuda a compensar.
Algunos de los programas de ejemplo que se describe en este artículo se usa BoxView para representar gráficos.
El BoxView puede tendrán el tamaño que se asemeje a una línea de un ancho específico y el grosor y, a
continuación, girar cualquier ángulo utilizando la Rotation propiedad.
Aunque BoxView puede imitar los gráficos sencillos, quizá desee investigar utilizando SkiaSharp en
Xamarin.Forms para conocer los requisitos de gráficos más sofisticados.
En este artículo se trata los temas siguientes:
Establecer el tamaño y BoxView Color – establecer el BoxView propiedades.
Decoraciones de texto de representación – utilizar un BoxView para las líneas de representación.
Mostrar lista de colores con BoxView – mostrar todo el sistema de colores en un ListView .
Reproduce el juego de la vida mediante la creación de subclases BoxView – implementar un famoso
autómata celular.
Creación de un reloj Digital – simular una presentación de la matriz de puntos.
Creación de un reloj analógico – transformar y animar BoxView elementos.

Configuración BoxView Color y tamaño


Normalmente, establecerá las propiedades siguientes de BoxView :
Color Para establecer su color.
CornerRadius Para establecer el radio de redondeo.
WidthRequest Para establecer el ancho de la BoxView en unidades independientes del dispositivo.
HeightRequest Para establecer el alto de la BoxView .

El Color propiedad es de tipo Color ; se puede establecer la propiedad a cualquier Color valor, incluidos los 141
campos estáticos de sólo lectura de comprendido por orden alfabético de colores con nombre AliceBlue a
YellowGreen .

El CornerRadius propiedad es de tipo CornerRadius ; la propiedad puede establecerse en un único double


uniforme de valor de radio de la esquina, o un CornerRadius estructura definida por cuatro double valores que se
aplican a la parte superior izquierda, superior derecha, inferior izquierda y la parte inferior derecha de la BoxView .
El WidthRequest y HeightRequest propiedades reproducen solo una función de si el BoxView es sin restricciones
en diseño. Este es el caso cuando el contenedor de diseño debe saber el elemento secundario del tamaño, por
ejemplo, cuando el BoxView es un elemento secundario de una celda de tamaño automático de la Grid diseño.
Un BoxView también está sin restricciones cuando su HorizontalOptions y VerticalOptions propiedades se
establecen en valores distintos de LayoutOptions.Fill . Si el BoxView está restringida, pero la WidthRequest y
HeightRequest no se han establecido las propiedades y, después, el ancho o alto se establecen en valores
predeterminados de 40 unidades, o aproximadamente 1/4 pulgadas en dispositivos móviles.
El WidthRequest y HeightRequest se omiten las propiedades si la BoxView es restringida en el diseño, en el que el
caso del contenedor de diseño impone su propio tamaño en el BoxView .
Un BoxView puede restringir en una dimensión y sin restricciones en el otro. Por ejemplo, si la BoxView es un
elemento secundario de un vertical StackLayout , la dimensión vertical de la BoxView es sin restricciones y
generalmente se limita su dimensión horizontal. Sin embargo, hay excepciones para que la dimensión horizontal:
Si el BoxView tiene su HorizontalOptions propiedad establecida en algo distinto LayoutOptions.Fill , entonces la
dimensión horizontal también es sin restricciones. También es posible que el StackLayout para tener una
dimensión horizontal sin restricciones, en cuyo caso el BoxView también será horizontalmente sin restricciones.
El BasicBoxView ejemplo muestra un cuadrado de pulgada de uno sin restricciones BoxView en el centro de su
página:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BasicBoxView"
x:Class="BasicBoxView.MainPage">

<BoxView Color="CornflowerBlue"
CornerRadius="10"
WidthRequest="160"
HeightRequest="160"
VerticalOptions="Center"
HorizontalOptions="Center" />

</ContentPage>

Este es el resultado:

Si el VerticalOptions y HorizontalOptions se quitan las propiedades de la BoxView una etiqueta o se establecen


en Fill , el BoxView pasa a ser restringido por el tamaño de la página y se expande para rellenar la página.
Un BoxView también puede ser un elemento secundario de un AbsoluteLayout . En ese caso, la ubicación y el
tamaño de la BoxView se establecen mediante el LayoutBounds propiedad enlazable adjunta. El AbsoluteLayout se
describe en el artículo AbsoluteLayout.
Verá ejemplos de todos estos casos en los programas de ejemplo siguientes.

Representación de decoraciones de texto


Puede usar el BoxView para agregar algunos decoraciones sencillas en sus páginas en forma de líneas
horizontales y verticales. El TextDecoration muestra esto. Todos los objetos visuales del programa se definen en
el MainPage.xaml archivo, que contiene varias Label y BoxView elementos en el StackLayout se muestra aquí:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TextDecoration"
x:Class="TextDecoration.MainPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
</OnPlatform>
</ContentPage.Padding>

<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="BoxView">
<Setter Property="Color" Value="Black" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<ScrollView Margin="15">
<StackLayout>

···

</StackLayout>
</ScrollView>
</ContentPage>

Todo el marcado siguiente son elementos secundarios de la StackLayout . Este marcado se compone de varios
tipos de decorativos BoxView elementos utilizados con el Label elemento:

El encabezado elegante en la parte superior de la página se logra con una AbsoluteLayout cuyos elementos
secundarios son cuatro BoxView elementos y un Label , todos los de las cuales se asigna ubicaciones específicas y
tamaños:
<AbsoluteLayout>
<BoxView AbsoluteLayout.LayoutBounds="0, 10, 200, 5" />
<BoxView AbsoluteLayout.LayoutBounds="0, 20, 200, 5" />
<BoxView AbsoluteLayout.LayoutBounds="10, 0, 5, 65" />
<BoxView AbsoluteLayout.LayoutBounds="20, 0, 5, 65" />
<Label Text="Stylish Header"
FontSize="24"
AbsoluteLayout.LayoutBounds="30, 25, AutoSize, AutoSize"/>
</AbsoluteLayout>

En el archivo XAML, el AbsoluteLayout va seguido de un Label con formato de texto que describe el
AbsoluteLayout .

Una cadena de texto se puede subrayar incluyendo tanto la Label y BoxView en un StackLayout que tiene su
HorizontalOptions valor establecido en algo distinto Fill . El ancho de la StackLayout , a continuación, se rige
por el ancho de la Label , que luego impone ese ancho en el BoxView . El BoxView se asigna solo una altura
explícita:

<StackLayout HorizontalOptions="Center">
<Label Text="Underlined Text"
FontSize="24" />
<BoxView HeightRequest="2" />
</StackLayout>

No se puede usar esta técnica para subrayar palabras individuales dentro de las cadenas más largas de texto o un
párrafo.
También es posible usar un BoxView similar a HTML hr elemento (regla horizontal). Simplemente dejar que el
ancho de la BoxView determinarse mediante su contenedor primario, que en este caso es el StackLayout :

<BoxView HeightRequest="3" />

Por último, puede dibujar una línea vertical en un lado de un párrafo de texto, incluya ambos el BoxView y Label
en horizontal StackLayout . En este caso, el alto de la BoxView es el mismo que el alto de StackLayout , que se rige
por el alto de la Label :

<StackLayout Orientation="Horizontal">
<BoxView WidthRequest="4"
Margin="0, 0, 10, 0" />
<Label>

···

</Label>
</StackLayout>

Mostrar lista de colores con BoxView


El BoxView es útil para mostrar colores. Este programa usa un ListView para enumerar todos los estáticos de
sólo lectura campos públicos de Xamarin.Forms Color estructura:
El ListViewColors programa incluye una clase denominada NamedColor . El constructor estático utiliza la reflexión
para tener acceso a todos los campos de la Color estructurar y crear un NamedColor objeto para cada uno de
ellos. Estos se almacenan en estático All propiedad:

public class NamedColor


{
// Instance members.
private NamedColor()
{
}

public string Name { private set; get; }

public string FriendlyName { private set; get; }

public Color Color { private set; get; }

public string RgbDisplay { private set; get; }

// Static members.
static NamedColor()
{
List<NamedColor> all = new List<NamedColor>();
StringBuilder stringBuilder = new StringBuilder();

// Loop through the public static fields of the Color structure.


foreach (FieldInfo fieldInfo in typeof(Color).GetRuntimeFields ())
{
if (fieldInfo.IsPublic &&
fieldInfo.IsStatic &&
fieldInfo.FieldType == typeof (Color))
{
// Convert the name to a friendly name.
string name = fieldInfo.Name;
stringBuilder.Clear();
int index = 0;

foreach (char ch in name)


{
if (index != 0 && Char.IsUpper(ch))
{
stringBuilder.Append(' ');
}
stringBuilder.Append(ch);
index++;
}
}

// Instantiate a NamedColor object.


Color color = (Color)fieldInfo.GetValue(null);

NamedColor namedColor = new NamedColor


{
Name = name,
FriendlyName = stringBuilder.ToString(),
Color = color,
RgbDisplay = String.Format("{0:X2}-{1:X2}-{2:X2}",
(int)(255 * color.R),
(int)(255 * color.G),
(int)(255 * color.B))
};

// Add it to the collection.


all.Add(namedColor);
}
}
all.TrimExcess();
All = all;
}

public static IList<NamedColor> All { private set; get; }


}

Se describen los objetos visuales de programa en el archivo XAML. El ItemsSource propiedad de la ListView está
establecido en estático NamedColor.All propiedad, lo que significa que el ListView muestra todos los individuales
NamedColor objetos:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ListViewColors"
x:Class="ListViewColors.MainPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="10, 20, 10, 0" />
<On Platform="Android, UWP" Value="10, 0" />
</OnPlatform>
</ContentPage.Padding>

<ListView SeparatorVisibility="None"
ItemsSource="{x:Static local:NamedColor.All}">
<ListView.RowHeight>
<OnPlatform x:TypeArguments="x:Int32">
<On Platform="iOS, Android" Value="80" />
<On Platform="UWP" Value="90" />
</OnPlatform>
</ListView.RowHeight>

<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ContentView Padding="5">
<Frame OutlineColor="Accent"
Padding="10">
<StackLayout Orientation="Horizontal">
<BoxView Color="{Binding Color}"
WidthRequest="50"
HeightRequest="50" />
<StackLayout>
<Label Text="{Binding FriendlyName}"
FontSize="22"
VerticalOptions="StartAndExpand" />
<Label Text="{Binding RgbDisplay, StringFormat='RGB = {0}'}"
FontSize="16"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</StackLayout>
</Frame>
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>

El NamedColorobjetos se les ha aplicado el ViewCell objeto que se establece como la plantilla de datos de la
ListView . Esta plantilla incluye un BoxView cuyo Color propiedad está enlazada a la Color propiedad de la
NamedColor objeto.

El juego del ciclo de vida mediante la creación de subclases BoxView


El juego del ciclo de vida es un autómata celular inventado por matemático John Conway y popularizado en las
páginas de Scientific American en la década de 1970. El artículo de Wikipedia proporciona una buena introducción
juego del ciclo de vida de Conway.
Xamarin.Forms GameOfLife programa define una clase denominada LifeCell que se deriva de BoxView . Esta
clase encapsula la lógica de una celda individual en el juego del ciclo de vida:
class LifeCell : BoxView
{
bool isAlive;

public event EventHandler Tapped;

public LifeCell()
{
BackgroundColor = Color.White;

TapGestureRecognizer tapGesture = new TapGestureRecognizer();


tapGesture.Tapped += (sender, args) =>
{
Tapped?.Invoke(this, EventArgs.Empty);
};
GestureRecognizers.Add(tapGesture);
}

public int Col { set; get; }

public int Row { set; get; }

public bool IsAlive


{
set
{
if (isAlive != value)
{
isAlive = value;
BackgroundColor = isAlive ? Color.Black : Color.White;
}
}
get
{
return isAlive;
}
}
}

LifeCell agrega tres propiedades más BoxView : el Col y Row propiedades almacenan la posición de la celda
dentro de la cuadrícula y el IsAlive propiedad indica su estado. El IsAlive también establece la propiedad la
Color propiedad de la BoxView en negro si la celda está activo y en blanco si la celda no está activa.

LifeCell También se instala un TapGestureRecognizer para permitir al usuario alternar el estado de las celdas
punteando en ellos. La clase traduce la Tapped eventos del reconocedor de gestos en su propio Tapped eventos.
El GameOfLife programa también incluye un LifeGrid clase que encapsula gran parte de la lógica del juego, y
un MainPage clase que controla los objetos visuales del programa. Estos incluyen una superposición que se
describe las reglas del juego. Este es el programa en que se muestra un par de cientos de acción LifeCell objetos
en la página:
Creación de un reloj Digital
El DotMatrixClock programa crea 210 BoxView elementos para simular los puntos de una presentación del 5 al
7 matricial anticuado. Puede leer el tiempo en modo vertical u horizontal, pero es más grande en horizontal:

El archivo XAML poco más que crear una instancia del AbsoluteLayout utiliza el reloj:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DotMatrixClock"
x:Class="DotMatrixClock.MainPage"
Padding="10"
SizeChanged="OnPageSizeChanged">

<AbsoluteLayout x:Name="absoluteLayout"
VerticalOptions="Center" />
</ContentPage>

Todo lo demás se produce en el archivo de código subyacente. La lógica de visualización matriciales se simplifica
enormemente la definición de varias matrices que describen los puntos correspondientes a cada uno de los 10
dígitos y dos puntos:

public partial class MainPage : ContentPage


{
// Total dots horizontally and vertically.
const int horzDots = 41;
const int vertDots = 7;

// 5 x 7 dot matrix patterns for 0 through 9.


static readonly int[, ,] numberPatterns = new int[10, 7, 5]
{
{
{ 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 1, 0, 0, 1, 1}, { 1, 0, 1, 0, 1},
{ 1, 1, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}
},
{
{ 0, 0, 1, 0, 0}, { 0, 1, 1, 0, 0}, { 0, 0, 1, 0, 0}, { 0, 0, 1, 0, 0},
{ 0, 0, 1, 0, 0}, { 0, 0, 1, 0, 0}, { 0, 1, 1, 1, 0}
},
{
{ 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 0, 0, 0, 0, 1}, { 0, 0, 0, 1, 0},
{ 0, 0, 1, 0, 0}, { 0, 1, 0, 0, 0}, { 1, 1, 1, 1, 1}
},
{
{ 1, 1, 1, 1, 1}, { 0, 0, 0, 1, 0}, { 0, 0, 1, 0, 0}, { 0, 0, 0, 1, 0},
{ 0, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}
},
{
{ 0, 0, 0, 1, 0}, { 0, 0, 1, 1, 0}, { 0, 1, 0, 1, 0}, { 1, 0, 0, 1, 0},
{ 1, 1, 1, 1, 1}, { 0, 0, 0, 1, 0}, { 0, 0, 0, 1, 0}
},
{
{ 1, 1, 1, 1, 1}, { 1, 0, 0, 0, 0}, { 1, 1, 1, 1, 0}, { 0, 0, 0, 0, 1},
{ 0, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}
},
{
{ 0, 0, 1, 1, 0}, { 0, 1, 0, 0, 0}, { 1, 0, 0, 0, 0}, { 1, 1, 1, 1, 0},
{ 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}
},
{
{ 1, 1, 1, 1, 1}, { 0, 0, 0, 0, 1}, { 0, 0, 0, 1, 0}, { 0, 0, 1, 0, 0},
{ 0, 1, 0, 0, 0}, { 0, 1, 0, 0, 0}, { 0, 1, 0, 0, 0}
},
{
{ 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0},
{ 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}
},
{
{ 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 1},
{ 0, 0, 0, 0, 1}, { 0, 0, 0, 1, 0}, { 0, 1, 1, 0, 0}
},
};

// Dot matrix pattern for a colon.


static readonly int[,] colonPattern = new int[7, 2]
{
{ 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 }
};

// BoxView colors for on and off.


static readonly Color colorOn = Color.Red;
static readonly Color colorOff = new Color(0.5, 0.5, 0.5, 0.25);

// Box views for 6 digits, 7 rows, 5 columns.


BoxView[, ,] digitBoxViews = new BoxView[6, 7, 5];

···
}

Estos campos concluyan con una matriz tridimensional de BoxView elementos para almacenar los patrones de
punto para los seis dígitos.
El constructor crea todas la BoxView elementos para los dígitos y dos puntos y también inicializa la Color
propiedad de la BoxView elementos de los dos puntos:

public partial class MainPage : ContentPage


{

···

public MainPage()
{
InitializeComponent();

// BoxView dot dimensions.


double height = 0.85 / vertDots;
double width = 0.85 / horzDots;

// Create and assemble the BoxViews.


double xIncrement = 1.0 / (horzDots - 1);
double yIncrement = 1.0 / (vertDots - 1);
double x = 0;

for (int digit = 0; digit < 6; digit++)


{
for (int col = 0; col < 5; col++)
{
double y = 0;

for (int row = 0; row < 7; row++)


{
// Create the digit BoxView and add to layout.
BoxView boxView = new BoxView();
digitBoxViews[digit, row, col] = boxView;
absoluteLayout.Children.Add(boxView,
new Rectangle(x, y, width, height),
AbsoluteLayoutFlags.All);
y += yIncrement;
}
x += xIncrement;
}
x += xIncrement;

// Colons between the hours, minutes, and seconds.


if (digit == 1 || digit == 3)
{
int colon = digit / 2;

for (int col = 0; col < 2; col++)


{
double y = 0;

for (int row = 0; row < 7; row++)


{
// Create the BoxView and set the color.
BoxView boxView = new BoxView
{
Color = colonPattern[row, col] == 1 ?
colorOn : colorOff
};
absoluteLayout.Children.Add(boxView,
new Rectangle(x, y, width, height),
AbsoluteLayoutFlags.All);
y += yIncrement;
}
x += xIncrement;
}
x += xIncrement;
}
}

// Set the timer and initialize with a manual call.


Device.StartTimer(TimeSpan.FromSeconds(1), OnTimer);
OnTimer();
}

···

Este programa usa la característica de tamaño y posición relativa AbsoluteLayout . El ancho y alto de cada BoxView
se establecen en valores fraccionarios, específicamente el 85% 1 dividido por el número de puntos horizontales y
verticales. Las posiciones también se establecen en valores fraccionarios.
Dado que todas las posiciones y tamaños son en relación con el tamaño total de la AbsoluteLayout , el
SizeChanged solo necesita establecer el controlador de la página una HeightRequest de la AbsoluteLayout :

public partial class MainPage : ContentPage


{

···

void OnPageSizeChanged(object sender, EventArgs args)


{
// No chance a display will have an aspect ratio > 41:7
absoluteLayout.HeightRequest = vertDots * Width / horzDots;
}

···

El ancho de la AbsoluteLayout se establece automáticamente porque ajusta a todo el ancho de la página.


El código final de la MainPage clase procesa la devolución de llamada de temporizador y colores de los puntos de
cada dígito. La definición de las matrices multidimensionales al principio del archivo de código subyacente le
ayuda a realizar esta lógica en la parte más sencilla del programa:
public partial class MainPage : ContentPage
{

···

bool OnTimer()
{
DateTime dateTime = DateTime.Now;

// Convert 24-hour clock to 12-hour clock.


int hour = (dateTime.Hour + 11) % 12 + 1;

// Set the dot colors for each digit separately.


SetDotMatrix(0, hour / 10);
SetDotMatrix(1, hour % 10);
SetDotMatrix(2, dateTime.Minute / 10);
SetDotMatrix(3, dateTime.Minute % 10);
SetDotMatrix(4, dateTime.Second / 10);
SetDotMatrix(5, dateTime.Second % 10);
return true;
}

void SetDotMatrix(int index, int digit)


{
for (int row = 0; row < 7; row++)
for (int col = 0; col < 5; col++)
{
bool isOn = numberPatterns[digit, row, col] == 1;
Color color = isOn ? colorOn : colorOff;
digitBoxViews[index, row, col].Color = color;
}
}
}

Creación de un reloj analógico


Puede parecer un reloj de la matriz de puntos que puede ser una aplicación obvia de BoxView , pero BoxView
elementos también son capaces de darse cuenta de un reloj analógico:

Todos los objetos visuales en el BoxViewClock programa son elementos secundarios de un AbsoluteLayout .
Estos elementos tienen el tamaño mediante el LayoutBounds propiedad adjunta y girado usando el Rotation
propiedad.
Los tres BoxView elementos para las manecillas del reloj se crea una instancia en el archivo XAML, pero no
colocados o tamaño:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BoxViewClock"
x:Class="BoxViewClock.MainPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
</OnPlatform>
</ContentPage.Padding>

<AbsoluteLayout x:Name="absoluteLayout"
SizeChanged="OnAbsoluteLayoutSizeChanged">

<BoxView x:Name="hourHand"
Color="Black" />

<BoxView x:Name="minuteHand"
Color="Black" />

<BoxView x:Name="secondHand"
Color="Black" />
</AbsoluteLayout>
</ContentPage>

El constructor del archivo de código subyacente crea una instancia de la 60 BoxView elementos para las marcas de
graduación alrededor de la circunferencia del reloj:

public partial class MainPage : ContentPage


{

···

BoxView[] tickMarks = new BoxView[60];

public MainPage()
{
InitializeComponent();

// Create the tick marks (to be sized and positioned later).


for (int i = 0; i < tickMarks.Length; i++)
{
tickMarks[i] = new BoxView { Color = Color.Black };
absoluteLayout.Children.Add(tickMarks[i]);
}

Device.StartTimer(TimeSpan.FromSeconds(1.0 / 60), OnTimerTick);


}

···

El ajuste de tamaño y la posición de todos los BoxView elementos se produce en el SizeChanged controlador para
el AbsoluteLayout . Llama una pequeña estructura interna de la clase HandParams describe el tamaño de cada una
de las manos en relación con el tamaño total del reloj tres:
public partial class MainPage : ContentPage
{
// Structure for storing information about the three hands.
struct HandParams
{
public HandParams(double width, double height, double offset) : this()
{
Width = width;
Height = height;
Offset = offset;
}

public double Width { private set; get; } // fraction of radius


public double Height { private set; get; } // ditto
public double Offset { private set; get; } // relative to center pivot
}

static readonly HandParams secondParams = new HandParams(0.02, 1.1, 0.85);


static readonly HandParams minuteParams = new HandParams(0.05, 0.8, 0.9);
static readonly HandParams hourParams = new HandParams(0.125, 0.65, 0.9);

···

El SizeChanged controlador determina el centro y el radio de la AbsoluteLayout y, a continuación, tamaños y


posiciones de los 60 BoxView elementos utilizados como marcas de graduación. El for bucle concluye
estableciendo el Rotation propiedad de cada uno de estos BoxView elementos. Al final de la SizeChanged
controlador, el LayoutHand método se llama para cambiar el tamaño y la posición de las manecillas del reloj tres:
public partial class MainPage : ContentPage
{

···

void OnAbsoluteLayoutSizeChanged(object sender, EventArgs args)


{
// Get the center and radius of the AbsoluteLayout.
Point center = new Point(absoluteLayout.Width / 2, absoluteLayout.Height / 2);
double radius = 0.45 * Math.Min(absoluteLayout.Width, absoluteLayout.Height);

// Position, size, and rotate the 60 tick marks.


for (int index = 0; index < tickMarks.Length; index++)
{
double size = radius / (index % 5 == 0 ? 15 : 30);
double radians = index * 2 * Math.PI / tickMarks.Length;
double x = center.X + radius * Math.Sin(radians) - size / 2;
double y = center.Y - radius * Math.Cos(radians) - size / 2;
AbsoluteLayout.SetLayoutBounds(tickMarks[index], new Rectangle(x, y, size, size));
tickMarks[index].Rotation = 180 * radians / Math.PI;
}

// Position and size the three hands.


LayoutHand(secondHand, secondParams, center, radius);
LayoutHand(minuteHand, minuteParams, center, radius);
LayoutHand(hourHand, hourParams, center, radius);
}

void LayoutHand(BoxView boxView, HandParams handParams, Point center, double radius)


{
double width = handParams.Width * radius;
double height = handParams.Height * radius;
double offset = handParams.Offset;

AbsoluteLayout.SetLayoutBounds(boxView,
new Rectangle(center.X - 0.5 * width,
center.Y - offset * height,
width, height));

// Set the AnchorY property for rotations.


boxView.AnchorY = handParams.Offset;
}

···

El LayoutHand método tamaños y los coloca cada mano para que apunte directamente hasta la posición 12:00. Al
final del método, el AnchorY propiedad está establecida en la posición correspondiente en el centro del reloj. Esto
indica que el centro de giro.
Las manos se giran en la función de devolución de llamada de temporizador:
public partial class MainPage : ContentPage
{

···

bool OnTimerTick()
{
// Set rotation angles for hour and minute hands.
DateTime dateTime = DateTime.Now;
hourHand.Rotation = 30 * (dateTime.Hour % 12) + 0.5 * dateTime.Minute;
minuteHand.Rotation = 6 * dateTime.Minute + 0.1 * dateTime.Second;

// Do an animation for the second hand.


double t = dateTime.Millisecond / 1000.0;

if (t < 0.5)
{
t = 0.5 * Easing.SpringIn.Ease(t / 0.5);
}
else
{
t = 0.5 * (1 + Easing.SpringOut.Ease((t - 0.5) / 0.5));
}

secondHand.Rotation = 6 * (dateTime.Second + t);


return true;
}
}

El segundero se trata de un poco diferente: Se aplica una animación en función de aceleración para que parezca
que el movimiento tiene mecánica en lugar de smooth. En cada paso, el segundero extrae volver un poco y, a
continuación, se insertan su destino. Este pequeño fragmento de código agrega mucho el realismo del
movimiento.

Conclusión
El BoxView puede parecer simple al principio, pero a medida hemos visto, puede ser bastante versátil, y pueden
reproducir casi objetos visuales que normalmente sólo son posibles gráficos vectoriales. Para los gráficos más
sofisticados, consulte utilizando SkiaSharp en Xamarin.Forms.

Vínculos relacionados
BoxView básica (ejemplo)
Decoración de texto (ejemplo)
Color ListBox (ejemplo)
S Game of Life (ejemplo)
Matriz de puntos de reloj (ejemplo)
Reloj BoxView (ejemplo)
BoxView
Botón de Xamarin.Forms
11/07/2019 • 34 minutes to read • Edit Online

descargar el ejemplo
El botón se responde a un pulse o haga clic en que se dirige a una aplicación para llevar a cabo una tarea
determinada.
El Button es el control interactivo más fundamental en todas las de Xamarin.Forms. El Button normalmente
muestra una cadena de texto breve que indica un comando, pero también puede mostrar una imagen de mapa de
bits, o una combinación de texto y una imagen. El usuario presiona el Button con un dedo o hace clic en él con el
mouse para iniciar ese comando.
La mayoría de los temas se describe a continuación corresponden a las páginas de la ButtonDemos ejemplo.

Control de botón hace clic en


Button define un Clicked evento que se desencadena cuando el usuario pulsa el Button con un puntero dedo o
el mouse. El evento se desencadena cuando se suelta el botón dedo o el mouse de la superficie de la Button . El
Button debe tener su IsEnabled propiedad establecida en true para que responda a las pulsaciones.

El haga clic en botón básica página en el ButtonDemos ejemplo muestra cómo crear una instancia de un
Button en XAML y controle su Clicked eventos. El BasicButtonClickPage.xaml archivo contiene un
StackLayout con ambos un Label y un Button :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ButtonDemos.BasicButtonClickPage"
Title="Basic Button Click">
<StackLayout>

<Label x:Name="label"
Text="Click the Button below"
FontSize="Large"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center" />

<Button Text="Click to Rotate Text!"


VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Clicked="OnButtonClicked" />

</StackLayout>
</ContentPage>

El Button tiende a ocupar todo el espacio permitido para él. Por ejemplo, si no establece la HorizontalOptions
propiedad de Button a algo distinto Fill , el Button ocupará todo el ancho de su elemento primario.
De forma predeterminada, el Button es rectangular, pero puede dar TI redondeada esquinas mediante el
CornerRadius propiedad, como se describe a continuación, en la sección botón apariencia .

El Text propiedad especifica el texto que aparece en el Button . El Clicked evento está establecido en un
controlador de eventos denominado OnButtonClicked . Este controlador se encuentra en el archivo de código
subyacente, BasicButtonClickPage.xaml.cs:
public partial class BasicButtonClickPage : ContentPage
{
public BasicButtonClickPage ()
{
InitializeComponent ();
}

async void OnButtonClicked(object sender, EventArgs args)


{
await label.RelRotateTo(360, 1000);
}
}

Cuando el Button se pulsa, el OnButtonClicked método se ejecuta. El sender argumento es el Button objeto
responsable de este evento. Se puede usar para tener acceso a la Button objeto, o para distinguir entre varias
Button objetos que comparten el mismo Clicked eventos.

Esta particular Clicked controlador llama a una función de animación que gira la Label 360 grados en 1000
milisegundos. Este es el programa que se ejecutan en dispositivos iOS y Android y como una aplicación de
plataforma Universal de Windows (UWP ) en el escritorio de Windows 10:

Tenga en cuenta que el OnButtonClicked método incluye el async modificador porque await se usa en el
controlador de eventos. Un Clicked controlador de eventos requiere la async modificador solo si usa el cuerpo
del controlador await .
Cada plataforma representa el Button en su propia manera específica. En el botón apariencia sección, podrá
ver cómo establecer colores y hacer el Button borde visible para los aspectos más personalizadas. Button
implementa el IFontElement interfaz, por lo que incluye FontFamily , FontSize , y FontAttributes propiedades.

Creación de un botón en el código


Es habitual para crear instancias de un Button en XAML, pero también puede crear un Button en el código. Esto
podría ser conveniente cuando la aplicación necesita para crear varios botones basados en datos que son
enumerables con un foreach bucle.
El Click del botón de código página muestra cómo crear una página que es funcionalmente equivalente a la
haga clic en botón básica página pero totalmente en C#:
public class CodeButtonClickPage : ContentPage
{
public CodeButtonClickPage ()
{
Title = "Code Button Click";

Label label = new Label


{
Text = "Click the Button below",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.Center
};

Button button = new Button


{
Text = "Click to Rotate Text!",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.Center
};
button.Clicked += async (sender, args) => await label.RelRotateTo(360, 1000);

Content = new StackLayout


{
Children =
{
label,
button
}
};
}
}

Todo lo que se realiza en el constructor de clase. Dado que el Clicked controlador es solo una instrucción larga,
puede adjuntarse al evento muy sencillo:

button.Clicked += async (sender, args) => await label.RelRotateTo(360, 1000);

Por supuesto, también puede definir el controlador de eventos como un método independiente (al igual que el
OnButtonClick método haga clic en botón básica ) y asocie ese método para el evento:

button.Clicked += OnButtonClicked;

Deshabilitar el botón
A veces, una aplicación está en un estado determinado donde un determinado Button haga clic en no es una
operación válida. En esos casos, el Button debe deshabilitarse estableciendo su IsEnabled propiedad false . El
ejemplo clásico es un Entry control para un nombre de archivo acompañada de un archivo-abrir Button : El
Button debe habilitarse solo si se ha escrito algún texto en el Entry . Puede usar un DataTrigger para esta tarea,
como se muestra en el datos desencadenadores artículo.

Mediante la interfaz de comandos


Es posible que una aplicación responda a Button derivaciones sin control el Clicked eventos. El Button
implementa un mecanismo de notificación alternativo denominado el comando o comandos interfaz. Consta de
dos propiedades:
Command de tipo ICommand , una interfaz definida en el System.Windows.Input espacio de nombres.
CommandParameter propiedad de tipo Object .

Este enfoque es especialmente adecuado en relación con el enlace de datos y especialmente al implementar la
arquitectura Model-View -ViewModel (MVVM ). Estos temas se tratan en los artículos enlace de datos, desde los
enlaces de datos a MVVM, y MVVM.
En una aplicación MVVM, ViewModel define las propiedades de tipo ICommand que están conectados, a
continuación, en el XAML Button elementos con enlaces de datos. Xamarin.Forms también define Command y
Command<T> las clases que implementan la ICommand interfaz y ayudar a definir las propiedades de tipo
ViewModel ICommand .
Los comandos se describen con más detalle en el artículo la interfaz de comandos pero la básica de botón de
comando página en el ButtonDemos muestra el enfoque básico.
El CommandDemoViewModel clase es una clase ViewModel muy simple que define una propiedad de tipo double
denominado Number y dos propiedades de tipo ICommand denominado MultiplyBy2Command y DivideBy2Command :

class CommandDemoViewModel : INotifyPropertyChanged


{
double number = 1;

public event PropertyChangedEventHandler PropertyChanged;

public CommandDemoViewModel()
{
MultiplyBy2Command = new Command(() => Number *= 2);

DivideBy2Command = new Command(() => Number /= 2);


}

public double Number


{
set
{
if (number != value)
{
number = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Number"));
}
}
get
{
return number;
}
}

public ICommand MultiplyBy2Command { private set; get; }

public ICommand DivideBy2Command { private set; get; }


}

Los dos ICommandpropiedades se inicializan en el constructor de clase con dos objetos de tipo Command . El
Command constructores incluyen una pequeña función (denominado el execute argumento de constructor ) que
duplica o mitades el Number propiedad.
El BasicButtonCommand.xaml archivo establece su BindingContext a una instancia de CommandDemoViewModel .
El Label elemento y dos Button elementos contengan enlaces a las tres propiedades en CommandDemoViewModel :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ButtonDemos"
x:Class="ButtonDemos.BasicButtonCommandPage"
Title="Basic Button Command">

<ContentPage.BindingContext>
<local:CommandDemoViewModel />
</ContentPage.BindingContext>

<StackLayout>
<Label Text="{Binding Number, StringFormat='Value is now {0}'}"
FontSize="Large"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center" />

<Button Text="Multiply by 2"


VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Command="{Binding MultiplyBy2Command}" />

<Button Text="Divide by 2"


VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Command="{Binding DivideBy2Command}" />
</StackLayout>
</ContentPage>

Como los dos Button se puntea elementos, los comandos se ejecutan, y el número de cambios de valor:

La ventaja de este enfoque sobre Clicked controladores es que toda la lógica que implican la funcionalidad de
esta página se encuentra en el modelo de vista en lugar de en el archivo de código subyacente, para lograr una
mejor separación de la interfaz de usuario de la lógica de negocios.
También es posible que el Command objetos para controlar la habilitación y deshabilitación de la Button
elementos. Por ejemplo, suponga que desea limitar el intervalo de valores numéricos entre 2 10 y 2–10. Puede
agregar otra función al constructor (denominado el canExecute argumento) que devuelve true si el Button
debe estar habilitada. Esta es la modificación de la CommandDemoViewModel constructor:
class CommandDemoViewModel : INotifyPropertyChanged
{
···
public CommandDemoViewModel()
{
MultiplyBy2Command = new Command(
execute: () =>
{
Number *= 2;
((Command)MultiplyBy2Command).ChangeCanExecute();
((Command)DivideBy2Command).ChangeCanExecute();
},
canExecute: () => Number < Math.Pow(2, 10));

DivideBy2Command = new Command(


execute: () =>
{
Number /= 2;
((Command)MultiplyBy2Command).ChangeCanExecute();
((Command)DivideBy2Command).ChangeCanExecute();
},
canExecute: () => Number > Math.Pow(2, -10));
}
···
}

Las llamadas a la ChangeCanExecute método Command son necesarios para que el Command puede llamar al
método el canExecute método y determinar si el Button debe deshabilitarse o no. Con este cambio de código,
como el número alcanza el límite, el Button está deshabilitado:

Es posible que dos o más Button elementos que se van a enlazarse a la misma ICommand propiedad. El Button
elementos se pueden distinguir mediante el CommandParameter propiedad de Button . En este caso, querrá usar
genérico Command<T> clase. El CommandParameter objeto, a continuación, se pasa como argumento a la execute y
canExecute métodos. Esta técnica se muestra en detalle en la comandos básicos sección de la comando
interfaz artículo.
El ButtonDemos ejemplo también usa esta técnica en su MainPage clase. El MainPage.xaml archivo contiene
un Button para cada página del ejemplo:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ButtonDemos"
x:Class="ButtonDemos.MainPage"
Title="Button Demos">
<ScrollView>
<FlexLayout Direction="Column"
JustifyContent="SpaceEvenly"
AlignItems="Center">

<Button Text="Basic Button Click"


Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:BasicButtonClickPage}" />

<Button Text="Code Button Click"


Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:CodeButtonClickPage}" />

<Button Text="Basic Button Command"


Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:BasicButtonCommandPage}" />

<Button Text="Press and Release Button"


Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:PressAndReleaseButtonPage}" />

<Button Text="Button Appearance"


Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:ButtonAppearancePage}" />

<Button Text="Toggle Button Demo"


Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:ToggleButtonDemoPage}" />

<Button Text="Image Button Demo"


Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:ImageButtonDemoPage}" />

</FlexLayout>
</ScrollView>
</ContentPage>

Cada Button tiene su Command propiedad enlazada a una propiedad denominada NavigateCommand y el
CommandParameter está establecido en un Type objeto correspondiente a una de las clases de página en el
proyecto.
Que NavigateCommand propiedad es de tipo ICommand y se define en el archivo de código subyacente:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();

NavigateCommand = new Command<Type>(async (Type pageType) =>


{
Page page = (Page)Activator.CreateInstance(pageType);
await Navigation.PushAsync(page);
});

BindingContext = this;
}

public ICommand NavigateCommand { private set; get; }


}

El constructor inicializa el NavigateCommand propiedad a un Command<Type> objeto porque Type es el tipo de la


CommandParameter objeto establecido en el archivo XAML. Esto significa que el execute método tiene un
argumento de tipo Type que corresponde a este CommandParameter objeto. La función crea una instancia de la
página y, a continuación, navega a ella.
Tenga en cuenta que el constructor concluye estableciendo su BindingContext a sí mismo. Esto es necesario para
las propiedades en el archivo XAML para enlazar con el NavigateCommand propiedad.

Presionar y soltar el botón


Además el Clicked eventos, Button también define Pressed y Released eventos. El Pressed evento tiene lugar
cuando se presiona un dedo en un Button , o se presiona un botón del mouse con el puntero situado sobre el
Button . El Released evento tiene lugar cuando se suelta el botón del dedo o el mouse. Por lo general, un
Clicked también se desencadena el evento al mismo tiempo que el Released evento, pero si el puntero del dedo
o el mouse se desliza fuera de la superficie de la Button antes de que se publique el Clicked eventos podrían no
producirse.
El Pressed y Released eventos no se utilizan a menudo, pero puede usarse para fines especiales, como se
muestra en el presione y suelte el botón página. El archivo XAML contiene una Label y un Button con
controladores asociados para la Pressed y Released eventos:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ButtonDemos.PressAndReleaseButtonPage"
Title="Press and Release Button">
<StackLayout>

<Label x:Name="label"
Text="Press and hold the Button below"
FontSize="Large"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center" />

<Button Text="Press to Rotate Text!"


VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Pressed="OnButtonPressed"
Released="OnButtonReleased" />

</StackLayout>
</ContentPage>
El archivo de código subyacente se anima la Label cuando un Pressed evento se produce, pero se suspende la
rotación cuando un Released se produce el evento:

public partial class PressAndReleaseButtonPage : ContentPage


{
bool animationInProgress = false;
Stopwatch stopwatch = new Stopwatch();

public PressAndReleaseButtonPage ()
{
InitializeComponent ();
}

void OnButtonPressed(object sender, EventArgs args)


{
stopwatch.Start();
animationInProgress = true;

Device.StartTimer(TimeSpan.FromMilliseconds(16), () =>
{
label.Rotation = 360 * (stopwatch.Elapsed.TotalSeconds % 1);

return animationInProgress;
});
}

void OnButtonReleased(object sender, EventArgs args)


{
animationInProgress = false;
stopwatch.Stop();
}
}

El resultado es que el Label solo gira mientras un dedo se encuentra en contacto con el Button y se detiene
cuando se suelta el dedo:

Este tipo de comportamiento tiene aplicaciones de juegos: Un dedo mantenido en un Button podría provocar
que un objeto de la pantalla en movimiento en una dirección determinada.

Apariencia del botón


El Button hereda o define varias propiedades que afectan a su aspecto:
TextColor es el color de la Button texto
BackgroundColor es el color del fondo para que el texto
BorderColor es el color de un área que rodea el Button
FontFamily se usa la familia de fuentes del texto
FontSize es el tamaño del texto
FontAttributes indica si el texto está en negrita o cursiva
BorderWidth es el ancho del borde
CornerRadius es el radio de redondeo de la Button

NOTE
El Button clase también tiene Margin y Padding las propiedades que controlan el comportamiento de diseño de la
Button . Para obtener más información, consulte margen y relleno.

Los efectos de seis de estas propiedades (excepto FontFamily y FontAttributes ) se muestran en el apariencia
del botón página. Otra propiedad Image , se describe en la sección usando mapas de bits con el botón.
Todos los enlaces de datos y vistas en el apariencia del botón página se definen en el archivo XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ButtonDemos"
x:Class="ButtonDemos.ButtonAppearancePage"
Title="Button Appearance">
<StackLayout>
<Button x:Name="button"
Text="Button"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
TextColor="{Binding Source={x:Reference textColorPicker},
Path=SelectedItem.Color}"
BackgroundColor="{Binding Source={x:Reference backgroundColorPicker},
Path=SelectedItem.Color}"
BorderColor="{Binding Source={x:Reference borderColorPicker},
Path=SelectedItem.Color}" />

<StackLayout BindingContext="{x:Reference button}"


Padding="10">

<Slider x:Name="fontSizeSlider"
Maximum="48"
Minimum="1"
Value="{Binding FontSize}" />

<Label Text="{Binding Source={x:Reference fontSizeSlider},


Path=Value,
StringFormat='FontSize = {0:F0}'}"
HorizontalTextAlignment="Center" />

<Slider x:Name="borderWidthSlider"
Minimum="-1"
Maximum="12"
Value="{Binding BorderWidth}" />

<Label Text="{Binding Source={x:Reference borderWidthSlider},


Path=Value,
StringFormat='BorderWidth = {0:F0}'}"
HorizontalTextAlignment="Center" />

<Slider x:Name="cornerRadiusSlider"
Minimum="-1"
Maximum="24"
Value="{Binding CornerRadius}" />
Value="{Binding CornerRadius}" />

<Label Text="{Binding Source={x:Reference cornerRadiusSlider},


Path=Value,
StringFormat='CornerRadius = {0:F0}'}"
HorizontalTextAlignment="Center" />

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Grid.Resources>
<Style TargetType="Label">
<Setter Property="VerticalOptions" Value="Center" />
</Style>
</Grid.Resources>

<Label Text="Text Color:"


Grid.Row="0" Grid.Column="0" />

<Picker x:Name="textColorPicker"
ItemsSource="{Binding Source={x:Static local:NamedColor.All}}"
ItemDisplayBinding="{Binding FriendlyName}"
SelectedIndex="0"
Grid.Row="0" Grid.Column="1" />

<Label Text="Background Color:"


Grid.Row="1" Grid.Column="0" />

<Picker x:Name="backgroundColorPicker"
ItemsSource="{Binding Source={x:Static local:NamedColor.All}}"
ItemDisplayBinding="{Binding FriendlyName}"
SelectedIndex="0"
Grid.Row="1" Grid.Column="1" />

<Label Text="Border Color:"


Grid.Row="2" Grid.Column="0" />

<Picker x:Name="borderColorPicker"
ItemsSource="{Binding Source={x:Static local:NamedColor.All}}"
ItemDisplayBinding="{Binding FriendlyName}"
SelectedIndex="0"
Grid.Row="2" Grid.Column="1" />
</Grid>
</StackLayout>
</StackLayout>
</ContentPage>

El Button en la parte superior de la página tiene sus tres Color propiedades enlazadas a Picker elementos en
la parte inferior de la página. Los elementos de la Picker elementos son los colores de la NamedColor clase
incluida en el proyecto. Tres Slider elementos contienen enlaces bidireccionales para la FontSize , BorderWidth ,
y CornerRadius propiedades de la Button .
Este programa le permite experimentar con combinaciones de todas estas propiedades:
Para ver el Button borde, deberá establecer un BorderColor a algo distinto Default y el BorderWidth en un valor
positivo.
En iOS, observará que los anchos de borde grande interfieren en el interior de la Button e interfieren con la
presentación del texto. Si opta por utilizar un elemento border con un iOS Button , probablemente deseará
comenzar y terminar la Text propiedad con espacios para conservar su visibilidad.
En UWP, seleccionar un CornerRadius que supera la mitad del alto de la Button produce una excepción.

Estados del botón visuales


Button tiene un Pressed VisualState que puede utilizarse para iniciar un cambio visual en el Button cuando
presiona por el usuario, siempre que lo esté habilitada.
En el siguiente ejemplo XAML se muestra cómo definir un estado visual para el Pressed estado:

<Button Text="Click me!"


...>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="Scale"
Value="1" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Property="Scale"
Value="0.8" />
</VisualState.Setters>
</VisualState>

</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Button>

El Pressed VisualState especifica que, cuando el Button está presionado, su Scale se cambiará la propiedad
de su valor predeterminado de 1 a 0,8. El Normal VisualState especifica que, cuando el Button está en estado
normal, su Scale propiedad se establecerá en 1. Por lo tanto, el efecto general es que cuando el Button está
presionado, se vuelve a escalar para que sea ligeramente más pequeñas pero cuando el Button está publicado,
se escalan a su tamaño predeterminado.
Para obtener más información acerca de los estados visuales, vea Xamarin.Forms Visual State Manager.

Creación de un botón de alternancia


Es posible crear subclases Button para que funcione como un conmutador activar / desactivar: Pulse el botón
una vez para el botón de alternancia en y pulse de nuevo para desactivarlo.
La siguiente ToggleButton clase se deriva de Button y define un nuevo evento denominado Toggled y una
propiedad booleana denominada IsToggled . Estas son las mismas dos definido por el Xamarin.Forms Switch :

class ToggleButton : Button


{
public event EventHandler<ToggledEventArgs> Toggled;

public static BindableProperty IsToggledProperty =


BindableProperty.Create("IsToggled", typeof(bool), typeof(ToggleButton), false,
propertyChanged: OnIsToggledChanged);

public ToggleButton()
{
Clicked += (sender, args) => IsToggled ^= true;
}

public bool IsToggled


{
set { SetValue(IsToggledProperty, value); }
get { return (bool)GetValue(IsToggledProperty); }
}

protected override void OnParentSet()


{
base.OnParentSet();
VisualStateManager.GoToState(this, "ToggledOff");
}

static void OnIsToggledChanged(BindableObject bindable, object oldValue, object newValue)


{
ToggleButton toggleButton = (ToggleButton)bindable;
bool isToggled = (bool)newValue;

// Fire event
toggleButton.Toggled?.Invoke(toggleButton, new ToggledEventArgs(isToggled));

// Set the visual state


VisualStateManager.GoToState(toggleButton, isToggled ? "ToggledOn" : "ToggledOff");
}
}

El ToggleButton constructor adjunta un controlador para el Clicked eventos, por lo que TI puede cambiar el
valor de la IsToggled propiedad. El OnIsToggledChanged método activa el Toggled eventos.
La última línea de la OnIsToggledChanged llamadas de método estático VisualStateManager.GoToState método con
el texto de dos cadenas "ToggledOn" y "ToggledOff". Puede leer acerca de este método y cómo la aplicación
puede responder a estados visuales en el artículo Xamarin.Forms Visual State Manager.
Dado que ToggleButton realiza la llamada a VisualStateManager.GoToState , la propia clase no necesita incluir
capacidades adicionales para cambiar la apariencia del botón según su IsToggled estado. Es decir, la
responsabilidad del XAML que hospeda el ToggleButton .
El demostración de botón de alternancia página contiene dos instancias de ToggleButton , incluido el
marcado Visual State Manager que establece el Text , BackgroundColor ,y TextColor del botón en función del
estado visual:

<?xml version="1.0" encoding="utf-8" ?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ButtonDemos"
x:Class="ButtonDemos.ToggleButtonDemoPage"
Title="Toggle Button Demo">

<ContentPage.Resources>
<Style TargetType="local:ToggleButton">
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
<Setter Property="HorizontalOptions" Value="Center" />
</Style>
</ContentPage.Resources>

<StackLayout Padding="10, 0">


<local:ToggleButton Toggled="OnItalicButtonToggled">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="ToggleStates">
<VisualState Name="ToggledOff">
<VisualState.Setters>
<Setter Property="Text" Value="Italic Off" />
<Setter Property="BackgroundColor" Value="#C0C0C0" />
<Setter Property="TextColor" Value="Black" />
</VisualState.Setters>
</VisualState>

<VisualState Name="ToggledOn">
<VisualState.Setters>
<Setter Property="Text" Value=" Italic On " />
<Setter Property="BackgroundColor" Value="#404040" />
<Setter Property="TextColor" Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:ToggleButton>

<local:ToggleButton Toggled="OnBoldButtonToggled">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="ToggleStates">
<VisualState Name="ToggledOff">
<VisualState.Setters>
<Setter Property="Text" Value="Bold Off" />
<Setter Property="BackgroundColor" Value="#C0C0C0" />
<Setter Property="TextColor" Value="Black" />
</VisualState.Setters>
</VisualState>

<VisualState Name="ToggledOn">
<VisualState.Setters>
<Setter Property="Text" Value=" Bold On " />
<Setter Property="BackgroundColor" Value="#404040" />
<Setter Property="TextColor" Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</local:ToggleButton>

<Label x:Name="label"
Text="Just a little passage of some sample text that can be formatted in italic or boldface by
toggling the two buttons."
FontSize="Large"
HorizontalTextAlignment="Center"
VerticalOptions="CenterAndExpand" />
VerticalOptions="CenterAndExpand" />

</StackLayout>
</ContentPage>

El Toggled controladores de eventos están en el archivo de código subyacente. Son responsables de la


configuración de la FontAttributes propiedad de la Label según el estado de los botones:

public partial class ToggleButtonDemoPage : ContentPage


{
public ToggleButtonDemoPage ()
{
InitializeComponent ();
}

void OnItalicButtonToggled(object sender, ToggledEventArgs args)


{
if (args.Value)
{
label.FontAttributes |= FontAttributes.Italic;
}
else
{
label.FontAttributes &= ~FontAttributes.Italic;
}
}

void OnBoldButtonToggled(object sender, ToggledEventArgs args)


{
if (args.Value)
{
label.FontAttributes |= FontAttributes.Bold;
}
else
{
label.FontAttributes &= ~FontAttributes.Bold;
}
}
}

Este es el programa que se ejecutan en iOS, Android y UWP:

Uso de mapas de bits con botones


El Button clase define un ImageSource propiedad que permite mostrar una imagen de mapa de bits en el
Button , por sí solo o en combinación con el texto. También puede especificar cómo se organizan el texto e
imagen.
El ImageSource propiedad es de tipo ImageSource , lo que significa que se pueden cargar los mapas de bits desde
un archivo, el recurso incrustado, el URI o la secuencia.
Cada plataforma compatible con Xamarin.Forms permite que las imágenes que se almacenará en varios tamaños
para las resoluciones de píxeles diferente de los distintos dispositivos que puede ejecutar la aplicación. Estos se
denominado varios mapas de bits o se almacenan de manera que el sistema operativo puede elegir a la mejor
coincidencia para el dispositivo de vídeo resolución de pantalla.
Para un mapa de bits en un Button , normalmente es el mejor tamaño entre 32 y 64 unidades independientes del
dispositivo, según el tamaño que desee. Las imágenes utilizadas en este ejemplo se basan en un tamaño de 48
unidades independientes del dispositivo.
En el proyecto de iOS, el recursos carpeta contiene tres tamaños de esta imagen:
Un mapa de bits 48 píxeles cuadrado almacenado como /Resources/MonkeyFace.png
Un mapa de bits 96 píxeles cuadrado almacenado como /Resource/MonkeyFace@2x.png
Un mapa de bits 144 píxeles cuadrado almacenado como /Resource/MonkeyFace@3x.png
Todas las tres mapas de bits se asignaron un acción de compilación de BundleResource.
Para el proyecto Android, los mapas de bits todas tienen el mismo nombre, pero se almacenan en subcarpetas
diferentes de la recursos carpeta:
Un mapa de bits 72 píxeles cuadrado almacenado como /Resources/drawable-hdpi/MonkeyFace.png
Un mapa de bits 96 píxeles cuadrado almacenado como /Resources/drawable-xhdpi/MonkeyFace.png
Un mapa de bits 144 píxeles cuadrado almacenado como /Resources/drawable-xxhdpi/MonkeyFace.png
Un mapa de bits 192 píxeles cuadrado almacenado como /Resources/drawable-
xxxhdpi/MonkeyFace.png
Estos se asignaron un acción de compilación de AndroidResource.
En el proyecto UWP, se pueden almacenar mapas de bits en cualquier lugar en el proyecto, pero generalmente se
almacenan en una carpeta personalizada o activos carpeta existente. El proyecto UWP contiene estos mapas de
bits:
Un mapa de bits 48 píxeles cuadrado almacenado como /Assets/MonkeyFace.scale-100.png
Un mapa de bits 96 píxeles cuadrado almacenado como /Assets/MonkeyFace.scale-200.png
Un mapa de bits 192 píxeles cuadrado almacenado como /Assets/MonkeyFace.scale-400.png
Todos se asignó un acción de compilación de contenido.
Puede especificar cómo el Text y ImageSource propiedades están organizadas en el Button utilizando el
ContentLayout propiedad de Button . Esta propiedad es de tipo ButtonContentLayout , que es una clase
incrustada en Button . El constructor tiene dos argumentos:
Un miembro de la ImagePosition enumeración: Left , Top , Right , o Bottom que indica cómo aparece el
mapa de bits en relación con el texto.
Un double valor para el espaciado entre el mapa de bits y el texto.

Los valores predeterminados son Left y 10 unidades. Dos propiedades de solo lectura de ButtonContentLayout
denominado Position y Spacing proporcione los valores de esas propiedades.
En el código, puede crear un Button y establezca el ContentLayout propiedad similar al siguiente:
Button button = new Button
{
Text = "button text",
ImageSource = new FileImageSource
{
File = "image filename"
},
ContentLayout = new Button.ButtonContentLayout(Button.ButtonContentLayout.ImagePosition.Right, 20)
};

En XAML, debe especificar solo el miembro de enumeración o el espaciado o ambos en cualquier orden separan
por comas:

<Button Text="button text"


ImageSource="image filename"
ContentLayout="Right, 20" />

El imagen Button Demo página usa OnPlatform para especificar los nombres de archivo diferente para iOS,
Android y UWP, los archivos de mapa de bits. Si desea usar el mismo nombre de archivo para cada plataforma y
evitar el uso de OnPlatform , necesitará para almacenar los mapas de bits UWP en el directorio raíz del proyecto.
La primera Button en el imagen Button Demo página establece el Image propiedad pero no la Text
propiedad:

<Button>
<Button.ImageSource>
<OnPlatform x:TypeArguments="ImageSource">
<On Platform="iOS, Android" Value="MonkeyFace.png" />
<On Platform="UWP" Value="Assets/MonkeyFace.png" />
</OnPlatform>
</Button.ImageSource>
</Button>

Si los mapas de bits UWP se almacenan en el directorio raíz del proyecto, este marcado se puede simplificar
considerablemente:

<Button ImageSource="MonkeyFace.png" />

Para evitar mucha marcado redundante en la ImageButtonDemo.xaml archivo implícita Style también se
define para establecer el ImageSource propiedad. Esto Style se aplica automáticamente a otros cinco Button
elementos. Este es el archivo XAML completo:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ButtonDemos.ImageButtonDemoPage">

<FlexLayout Direction="Column"
JustifyContent="SpaceEvenly"
AlignItems="Center">

<FlexLayout.Resources>
<Style TargetType="Button">
<Setter Property="ImageSource">
<OnPlatform x:TypeArguments="ImageSource">
<On Platform="iOS, Android" Value="MonkeyFace.png" />
<On Platform="UWP" Value="Assets/MonkeyFace.png" />
</OnPlatform>
</Setter>
</Style>
</FlexLayout.Resources>

<Button>
<Button.ImageSource>
<OnPlatform x:TypeArguments="ImageSource">
<On Platform="iOS, Android" Value="MonkeyFace.png" />
<On Platform="UWP" Value="Assets/MonkeyFace.png" />
</OnPlatform>
</Button.ImageSource>
</Button>

<Button Text="Default" />

<Button Text="Left - 10"


ContentLayout="Left, 10" />

<Button Text="Top - 10"


ContentLayout="Top, 10" />

<Button Text="Right - 20"


ContentLayout="Right, 20" />

<Button Text="Bottom - 20"


ContentLayout="Bottom, 20" />
</FlexLayout>
</ContentPage>

Los cuatro finales Button elementos hacen uso de la ContentLayout propiedad para especificar una posición y
espaciado del texto y mapa de bits:
Ahora ha visto las distintas formas que puede controlar Button eventos y cambiar el Button apariencia.

Vínculos relacionados
Ejemplo de ButtonDemos
Botón API
Casilla de verificación de Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms CheckBox es un tipo de botón que puede ser activado ni estar vacío. Cuando se activa una casilla
de verificación, se considera en. Cuando una casilla de verificación está vacía, se considera estar apagado.
CheckBox define un bool propiedad denominada IsChecked , lo que indica si el CheckBox está activada. Esta
propiedad también está respaldada por un BindableProperty objeto, lo que significa que puede cambiar el estilo y
ser el destino de los enlaces de datos.

NOTE
El IsChecked propiedad enlazable tiene un modo de enlace predeterminada de BindingMode.TwoWay .

CheckBox define un CheckedChanged evento que se desencadena cuando el IsChecked los cambios de propiedad,
mediante la manipulación de usuario o cuando una aplicación establece el IsChecked propiedad. El
CheckedChangedEventArgs objeto que acompaña a la CheckedChanged el evento tiene una propiedad única
denominada Value , del tipo bool . Cuando se desencadena el evento, el valor de la Value propiedad está
establecida en el nuevo valor de la IsChecked propiedad.

Crear un control CheckBox


El ejemplo siguiente muestra cómo crear una instancia de un CheckBox en XAML:

<CheckBox />

Este XAML hace que aparezca en la que se muestra en las capturas de pantalla siguiente:

De forma predeterminada, el CheckBox está vacío. El CheckBox puede comprobarse la manipulación del usuario, o
estableciendo la IsChecked propiedad true :

<CheckBox IsChecked="true" />

Este XAML hace que aparezca en la que se muestra en las capturas de pantalla siguiente:

Como alternativa, un CheckBox pueden crearse en el código:

CheckBox checkBox = new CheckBox { IsChecked = true };


Responder al cambio de estado de una casilla de verificación
Cuando el IsChecked los cambios de propiedad, mediante la manipulación de usuario o cuando una aplicación
establece el IsChecked propiedad, el CheckedChanged desencadena el evento. Puede registrar un controlador de
eventos para este evento para responder al cambio:

<CheckBox CheckedChanged="OnCheckBoxCheckedChanged" />

El archivo de código subyacente contiene el controlador para el CheckedChanged eventos:

void OnCheckBoxCheckedChanged(object sender, CheckedChangedEventArgs e)


{
// Perform required operation after examining e.Value
}

El sender argumento es el CheckBox responsable de este evento. Se puede usar para tener acceso a la CheckBox
objeto, o para distinguir entre varias CheckBox objetos que comparten el mismo CheckedChanged eventos.
Como alternativa, un controlador de eventos para el CheckedChanged se pueden registrar eventos en el código:

CheckBox checkBox = new CheckBox { ... };


checkBox.CheckedChanged += (sender, e) =>
{
// Perform required operation after examining e.Value
};

Enlazar datos de un control CheckBox


El CheckedChanged controlador de eventos se puede eliminar mediante el uso de enlace de datos y
desencadenadores para responder a un CheckBox está activado o está vacío:

<CheckBox x:Name="checkBox" />


<Label Text="Lorem ipsum dolor sit amet, elit rutrum, enim hendrerit augue vitae praesent sed non, lorem
aenean quis praesent pede.">
<Label.Triggers>
<DataTrigger TargetType="Label"
Binding="{Binding Source={x:Reference checkBox}, Path=IsChecked}"
Value="true">
<Setter Property="FontAttributes"
Value="Italic, Bold" />
<Setter Property="FontSize"
Value="Large" />
</DataTrigger>
</Label.Triggers>
</Label>

En este ejemplo, el Label usa una expresión de enlace en un desencadenador de datos para supervisar la
IsChecked propiedad de la CheckBox . Cuando esta propiedad se convierte en true , FontAttributes y FontSize
propiedades de la Label cambiar. Cuando el IsChecked propiedad devuelve al false , el FontAttributes y
FontSize propiedades de la Label se restablecen a su estado inicial.

En las capturas de pantalla siguiente, se muestra la captura de pantalla de iOS la Label formato cuando el
CheckBox está vacío, mientras que Android la captura de pantalla muestra la Label formato cuando el CheckBox
está activado:
Para obtener más información acerca de los desencadenadores, consulte Xamarin.Forms desencadenadores.

Deshabilitar un control Checkbox


A veces, una aplicación entra en un estado donde un CheckBox que se está comprobando no es una operación
válida. En tales casos, el CheckBox puede deshabilitarse estableciendo su IsEnabled propiedad false .

Apariencia de casilla de verificación


Además de las propiedades que CheckBox hereda el View (clase), CheckBox también define un Color propiedad
que establece el color en un Color :

<CheckBox Color="Red" />

Las capturas de pantalla siguientes muestran una serie de activado CheckBox objetos, donde cada objeto tiene su
Color propiedad establecida en otro Color :

Casilla de verificación estados visuales


CheckBox tiene un IsChecked VisualState que puede utilizarse para iniciar un cambio visual en el CheckBox
cuando se activa.
En el siguiente ejemplo XAML se muestra cómo definir un estado visual para el IsChecked estado:

<CheckBox ...>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="Color"
Value="Red" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="IsChecked">
<VisualState.Setters>
<Setter Property="Color"
Value="Green" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</CheckBox>
En este ejemplo, el IsChecked VisualState especifica que, cuando el CheckBox está activada, su Color propiedad
se establecerá en verde. El Normal VisualState especifica que, cuando el CheckBox está en estado normal, su
Color propiedad se establecerá en rojo. Por lo tanto, el efecto general es que el CheckBox está en rojo cuando
está vacía y verde cuando se activa.
Para obtener más información acerca de los estados visuales, vea Xamarin.Forms Visual State Manager.

Vínculos relacionados
Demostraciones de casilla de verificación (ejemplo)
Desencadenadores de Xamarin.Forms
Administrador de estado Visual de Xamarin.Forms
Xamarin.Forms CollectionView
11/07/2019 • 2 minutes to read • Edit Online

Introducción
El CollectionView es una vista flexible y eficaz para presentar las listas de datos con las especificaciones de diseño
diferente.

Data
Un CollectionView se rellena con datos estableciendo sus ItemsSource propiedad a cualquier colección que
implementa IEnumerable . Se puede definir la apariencia de cada elemento de la lista estableciendo la
ItemTemplate propiedad a un DataTemplate .

Diseño
De forma predeterminada, un CollectionView mostrará sus elementos en una lista vertical. Sin embargo, se
pueden especificar las cuadrículas y listas horizontales y verticales.

Selección
De forma predeterminada, CollectionView la selección está deshabilitada. Sin embargo, se puede habilitar la
selección única o múltiple.

Vistas vacías
En CollectionView , se puede especificar una vista vacía que proporciona comentarios al usuario cuando no hay
datos disponibles para su presentación. La vista vacía puede ser una cadena, una vista o varias vistas.

Desplazarse
Cuando un Deslizamientos de usuario para iniciar un desplazamiento, se puede controlar la posición final del
desplazamiento para que se muestran por completo los elementos. Además, CollectionView define dos ScrollTo
métodos, que se desplace mediante programación los elementos en la vista. Una de las sobrecargas desplaza el
elemento en el índice especificado en la vista, mientras que el otro desplaza el elemento especificado en la vista.
Introducción de Xamarin.Forms CollectionView
11/07/2019 • 5 minutes to read • Edit Online

descargar el ejemplo
CollectionView es una vista para presentar las listas de datos mediante las especificaciones de diseño diferente. Su
objetivo es proporcionar una forma más flexible y una alternativa de alto rendimiento ListView . Por ejemplo, las
capturas de pantalla siguientes muestran un CollectionView que usa una cuadrícula vertical de dos columnas, y
que permite la selección múltiple:

CollectionView está disponible en Xamarin.Forms 4.0. Sin embargo, es experimental actualmente y solo se puede
usar agregando la siguiente línea de código para su AppDelegate clase en iOS, así como su MainActivity clase en
Android, antes de llamar a Forms.Init :

Forms.SetFlags("CollectionView_Experimental");

IMPORTANT
CollectionView está disponible en iOS y Android, pero solo está parcialmente disponible en la plataforma Universal de
Windows.

Diferencias CollectionView y ListView


Mientras el CollectionView y ListView API son similares, existen algunas diferencias importantes:
CollectionView tiene un modelo de diseño flexible que permite que los datos se presentan vertical u
horizontalmente, en una lista o una cuadrícula.
CollectionView solo admite y la selección múltiple.
CollectionView no tiene ningún concepto de celdas. En su lugar, se usa una plantilla de datos para definir la
apariencia de cada elemento de datos en la lista.
CollectionView usa automáticamente la virtualización proporcionada por los controles nativos subyacentes.
CollectionView reduce la superficie de API de ListView . Muchas propiedades y eventos de ListView no
están presentes en CollectionView .
CollectionView no incluye separadores integrados.
Mover de ListView a CollectionView
ListView las implementaciones en las implementaciones existentes de Xamarin.Forms se pueden migrar a
CollectionView implementaciones con la Ayuda de la tabla siguiente:

CONCEPTO API DE LISTVIEW COLLECTIONVIEW

Datos ItemsSource Un CollectionView se rellena con


datos estableciendo sus ItemsSource
propiedad. Para obtener más
información, consulte rellenar una
colección mediante CollectionView con
datos.

Apariencia del elemento ItemTemplate La apariencia de cada elemento en un


CollectionView pueden definirse
mediante el establecimiento del
ItemTemplate propiedad a un
DataTemplate . Para obtener más
información, consulte definen la
apariencia del elemento.

Celdas TextCell , ImageCell , ViewCell CollectionView no tiene ningún


concepto de celdas. En su lugar, se usa
una plantilla de datos para definir la
apariencia de cada elemento de datos
en la lista.

Separadores de fila SeparatorColor , CollectionView no incluye


SeparatorVisibility separadores integrados. Estos se
pueden proporcionar, si lo desea, en la
plantilla de elemento.

Selección SelectionMode , SelectedItem CollectionView solo admite y la


selección múltiple. Para obtener más
información, consulte Xamarin.Forms
CollectionView selección.

Alto de fila HasUnevenRows , RowHeight En un CollectionView , el alto de fila


de cada elemento viene determinada
por la ItemSizingStrategy propiedad.
Para obtener más información, consulte
ajuste de tamaño del elemento.

Almacenamiento en memoria caché CachingStrategy CollectionView usa automáticamente


la virtualización proporcionada por los
controles nativos subyacentes.

Encabezados y pies de página Header , HeaderElement , Encabezados y pies de página no son


HeaderTemplate , Footer , actualmente compatibles
FooterElement , FooterTemplate CollectionView , pero se agregará en
una versión futura.

Agrupar GroupDisplayBinding , Agrupación no se admite actualmente


GroupHeaderTemplate , en CollectionView , pero se agregará
GroupShortNameBinding , en una versión futura.
IsGroupingEnabled
CONCEPTO API DE LISTVIEW COLLECTIONVIEW

Deslizar para actualizar IsPullToRefreshEnabled , Deslizar para actualizar no se admite


IsRefreshing , RefreshAllowed , actualmente en CollectionView , pero
RefreshCommand , se agregará en una versión futura.
RefreshControlColor ,
BeginRefresh() , EndRefresh()

Acciones de contexto ContextActions Acciones de contexto no se admiten


actualmente en CollectionView , pero
se agregará en una versión futura.

Desplazarse ScrollTo() CollectionView define ScrollTo


métodos, que desplazar los elementos
en la vista. Para obtener más
información, consulte desplazamiento.

Vínculos relacionados
CollectionView (ejemplo)
Xamarin.Forms CollectionView datos
11/07/2019 • 7 minutes to read • Edit Online

descargar el ejemplo
CollectionView define las siguientes propiedades que definen los datos que se mostrarán y su apariencia:
ItemsSource , del tipo IEnumerable , especifica la colección de elementos que se mostrarán, y tiene un valor
predeterminado de null .
ItemTemplate , del tipo DataTemplate , especifica la plantilla para aplicar a cada elemento de la colección de
elementos que se mostrará.
Estas propiedades están respaldadas por BindableProperty objetos, lo que significa que las propiedades pueden
ser destinos de enlaces de datos.

Rellenar una colección mediante CollectionView con datos


Un CollectionView se rellena con datos estableciendo sus ItemsSource propiedad a cualquier colección que
implementa IEnumerable . Se pueden agregar elementos en XAML, inicialice el ItemsSource propiedad desde una
matriz de cadenas:

<CollectionView>
<CollectionView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Baboon</x:String>
<x:String>Capuchin Monkey</x:String>
<x:String>Blue Monkey</x:String>
<x:String>Squirrel Monkey</x:String>
<x:String>Golden Lion Tamarin</x:String>
<x:String>Howler Monkey</x:String>
<x:String>Japanese Macaque</x:String>
</x:Array>
</CollectionView.ItemsSource>
</CollectionView>

NOTE
Tenga en cuenta que el elemento x:Array requiere un atributo Type que indica el tipo de los elementos de la matriz.

El código de C# equivalente es:


CollectionView collectionView = new CollectionView();
collectionView.ItemsSource = new string[]
{
"Baboon",
"Capuchin Monkey",
"Blue Monkey",
"Squirrel Monkey",
"Golden Lion Tamarin",
"Howler Monkey",
"Japanese Macaque"
};

IMPORTANT
Si el CollectionView es necesario actualizar como elementos se agregan, quitados o cambiados en la colección subyacente,
la colección subyacente debe ser un IEnumerable que envía la propiedad de colección de notificaciones de cambios, como
ObservableCollection .

De forma predeterminada, CollectionView muestra elementos en una lista vertical, como se muestra en las
capturas de pantalla siguiente:

Para obtener información sobre cómo cambiar la CollectionView diseño, vea especificar un diseño. Para obtener
información sobre cómo definir la apariencia de cada elemento en el CollectionView , consulte definen la
apariencia del elemento.
Enlace de datos
CollectionView se pueden rellenar con datos mediante el uso de enlace de datos para enlazar su ItemsSource
propiedad a un IEnumerable colección. En XAML, esto se logra con la Binding extensión de marcado:

<CollectionView ItemsSource="{Binding Monkeys}" />

El código de C# equivalente es:

CollectionView collectionView = new CollectionView();


collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

En este ejemplo, el ItemsSource datos de la propiedad se enlaza a la Monkeys propiedad del modelo de vista
conectada.
NOTE
Enlaces compilados se pueden habilitar para mejorar el rendimiento de enlace de datos en las aplicaciones de Xamarin.Forms.
Para obtener más información, consulte compilado enlaces.

Para obtener más información sobre el enlace de datos, vea Enlace de datos de Xamarin.Forms.

Definir la apariencia del elemento


La apariencia de cada elemento de la CollectionView puede definirse estableciendo el
CollectionView.ItemTemplate propiedad a un DataTemplate :

<CollectionView ItemsSource="{Binding Monkeys}">


<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold" />
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Location}"
FontAttributes="Italic"
VerticalOptions="End" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
...
</CollectionView>

El código de C# equivalente es:


CollectionView collectionView = new CollectionView();
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

collectionView.ItemTemplate = new DataTemplate(() =>


{
Grid grid = new Grid { Padding = 10 };
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

Image image = new Image { Aspect = Aspect.AspectFill, HeightRequest = 60, WidthRequest = 60 };


image.SetBinding(Image.SourceProperty, "ImageUrl");

Label nameLabel = new Label { FontAttributes = FontAttributes.Bold };


nameLabel.SetBinding(Label.TextProperty, "Name");

Label locationLabel = new Label { FontAttributes = FontAttributes.Italic, VerticalOptions =


LayoutOptions.End };
locationLabel.SetBinding(Label.TextProperty, "Location");

Grid.SetRowSpan(image, 2);

grid.Children.Add(image);
grid.Children.Add(nameLabel, 1, 0);
grid.Children.Add(locationLabel, 1, 1);

return grid;
});

Los elementos especificados en el DataTemplate definen la apariencia de cada elemento en la lista. En el ejemplo,
la disposición dentro de la DataTemplate está administrado por un Grid . El Grid contiene un Image objeto y
dos Label objetos, que todos se enlazan a las propiedades de la Monkey clase:

public class Monkey


{
public string Name { get; set; }
public string Location { get; set; }
public string Details { get; set; }
public string ImageUrl { get; set; }
}

Las capturas de pantalla siguientes muestran el resultado de las plantillas cada elemento en la lista:
Para obtener más información sobre las plantillas de datos, consulte Plantillas de datos de Xamarin.Forms.

Elija la apariencia del elemento en tiempo de ejecución


La apariencia de cada elemento en el CollectionView se puede elegir en tiempo de ejecución, según el valor del
elemento, estableciendo el CollectionView.ItemTemplate propiedad a un DataTemplateSelector objeto:

<ContentPage ...
xmlns:controls="clr-namespace:CollectionViewDemos.Controls">
<ContentPage.Resources>
<DataTemplate x:Key="AmericanMonkeyTemplate">
...
</DataTemplate>

<DataTemplate x:Key="OtherMonkeyTemplate">
...
</DataTemplate>

<controls:MonkeyDataTemplateSelector x:Key="MonkeySelector"
AmericanMonkey="{StaticResource AmericanMonkeyTemplate}"
OtherMonkey="{StaticResource OtherMonkeyTemplate}" />
</ContentPage.Resources>

<CollectionView ItemsSource="{Binding Monkeys}"


ItemTemplate="{StaticResource MonkeySelector}" />
</ContentPage>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
ItemTemplate = new MonkeyDataTemplateSelector { ... }
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

El ItemTemplate propiedad está establecida en un MonkeyDataTemplateSelector objeto. El ejemplo siguiente se


muestra la MonkeyDataTemplateSelector clase:

public class MonkeyDataTemplateSelector : DataTemplateSelector


{
public DataTemplate AmericanMonkey { get; set; }
public DataTemplate OtherMonkey { get; set; }

protected override DataTemplate OnSelectTemplate(object item, BindableObject container)


{
return ((Monkey)item).Location.Contains("America") ? AmericanMonkey : OtherMonkey;
}
}

La clase MonkeyDataTemplateSelector define las propiedades AmericanMonkey y OtherMonkey de tipo DataTemplate


que se establecen para diferentes plantillas de datos. El OnSelectTemplate invalidar devuelve el AmericanMonkey
plantilla, que muestra la ubicación y el nombre del objeto monkey en verde azulado, cuando el nombre de objeto
monkey contiene "America". Cuando el nombre de objeto monkey no contiene "Estados Unidos", la
OnSelectTemplate invalidar devuelve el OtherMonkey plantilla, que muestra el nombre del objeto monkey y la
ubicación de plata:
Para obtener más información acerca de los selectores de plantilla de datos, vea crear un Xamarin.Forms
DataTemplateSelector.

Vínculos relacionados
CollectionView (ejemplo)
Enlace de datos en Xamarin.Forms
Plantillas de datos de Xamarin.Forms
Crear un Xamarin.Forms DataTemplateSelector
Diseño de Xamarin.Forms CollectionView
11/07/2019 • 15 minutes to read • Edit Online

descargar el ejemplo
CollectionView define las siguientes propiedades que controlan el diseño:
ItemsLayout , del tipo IItemsLayout , especifica el diseño que se usará.
ItemSizingStrategy , del tipo ItemSizingStrategy , especifica la estrategia de medida del elemento que se
usará.
Estas propiedades están respaldadas por BindableProperty objetos, lo que significa que las propiedades pueden
ser destinos de enlaces de datos.
De forma predeterminada, un CollectionView mostrará sus elementos en una lista vertical. Sin embargo,
cualquiera de los diseños siguientes pueden utilizarse:
Lista vertical: una lista de columna única que aumenta de tamaño verticalmente conforme se agregan nuevos
elementos.
Lista horizontal: una lista de fila única que crece horizontalmente cuando se agregan nuevos elementos.
Cuadrícula vertical: una cuadrícula de varias columna que aumenta de tamaño verticalmente conforme se
agregan nuevos elementos.
Cuadrícula horizontal: una cuadrícula de varias filas que crece horizontalmente cuando se agregan nuevos
elementos.
Estos diseños se pueden especificar estableciendo el ItemsLayout propiedad a la clase que deriva el ItemsLayout
clase. Esta clase define las siguientes propiedades:
Orientation , del tipo ItemsLayoutOrientation , especifica la dirección en que el CollectionView se expande
cuando se agregan elementos.
SnapPointsAlignment , del tipo SnapPointsAlignment , especifica cómo se alinean los puntos de acoplamiento
con elementos.
SnapPointsType , del tipo SnapPointsType , especifica el comportamiento de puntos de acoplamiento al
desplazarse.
Estas propiedades están respaldadas por BindableProperty objetos, lo que significa que las propiedades pueden
ser destinos de enlaces de datos. Para obtener más información acerca de los puntos de ajuste, vea ajustar puntos
en el Xamarin.Forms CollectionView desplazamiento guía.
El ItemsLayoutOrientation enumeración define los miembros siguientes:
Vertical indica que el CollectionView expandirá verticalmente cuando se agregan elementos.
Horizontal indica que el CollectionView expandirá horizontalmente cuando se agregan elementos.

El ListItemsLayout clase hereda de la ItemsLayout clase y define un ItemSpacing propiedad de tipo double , que
representa el espacio vacío alrededor de cada elemento. El valor predeterminado de esta propiedad es 0, y su
valor debe ser siempre mayor o igual que 0. El ListItemsLayout clase define también estático Vertical y
Horizontal miembros. Estos miembros se pueden usar para crear listas horizontales o verticales,
respectivamente. Como alternativa, un ListItemsLayout objeto puede crearse, especificando un
ItemsLayoutOrientation miembro de enumeración como argumento.

El GridItemsLayout clase hereda de la ItemsLayout clase y define las siguientes propiedades:


VerticalItemSpacing , del tipo double , que representa el espacio vacío vertical alrededor de cada elemento. El
valor predeterminado de esta propiedad es 0, y su valor debe ser siempre mayor o igual que 0.
HorizontalItemSpacing , del tipo double , que representa el espacio vacío horizontal alrededor de cada
elemento. El valor predeterminado de esta propiedad es 0, y su valor debe ser siempre mayor o igual que 0.
Span , del tipo int , que representa el número de columnas o filas que se muestran en la cuadrícula. El valor
predeterminado de esta propiedad es 1, y su valor debe ser siempre mayor o igual que 1.
Estas propiedades están respaldadas por BindableProperty objetos, lo que significa que las propiedades pueden
ser destinos de enlaces de datos.

NOTE
CollectionView usa los motores de diseño nativo para realizar el diseño.

Lista vertical
De forma predeterminada, CollectionView mostrará sus elementos en un diseño de la lista vertical. Por lo tanto,
no es necesario establecer la ItemsLayout propiedad que se utilizará este diseño:

<CollectionView ItemsSource="{Binding Monkeys}">


<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold" />
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Location}"
FontAttributes="Italic"
VerticalOptions="End" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

Sin embargo, para proporcionar información completa, una CollectionView puede configurarse para mostrar sus
elementos en una lista vertical estableciendo su ItemsLayout propiedad VerticalList :
<CollectionView ItemsSource="{Binding Monkeys}"
ItemsLayout="VerticalList">
...
</CollectionView>

Como alternativa, esto también puede realizarse estableciendo el ItemsLayout propiedad a un objeto de la
ListItemsLayout clase especificando el Vertical ItemsLayoutOrientation miembro de enumeración como
argumento:

<CollectionView ItemsSource="{Binding Monkeys}">


<CollectionView.ItemsLayout>
<ListItemsLayout>
<x:Arguments>
<ItemsLayoutOrientation>Vertical</ItemsLayoutOrientation>
</x:Arguments>
</ListItemsLayout>
</CollectionView.ItemsLayout>
...
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
...
ItemsLayout = ListItemsLayout.Vertical
};

Esto da como resultado una lista de columna única, lo que aumenta de tamaño verticalmente conforme se
agregan nuevos elementos:

Lista horizontal
CollectionView puede mostrar sus elementos en una lista horizontal estableciendo su ItemsLayout propiedad
HorizontalList :
<CollectionView ItemsSource="{Binding Monkeys}"
ItemsLayout="HorizontalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold"
LineBreakMode="TailTruncation" />
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Location}"
LineBreakMode="TailTruncation"
FontAttributes="Italic"
VerticalOptions="End" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

Como alternativa, esto también puede realizarse estableciendo el ItemsLayout propiedad a un ListItemsLayout
objeto, especificando el Horizontal ItemsLayoutOrientation miembro de enumeración como argumento:

<CollectionView ItemsSource="{Binding Monkeys}">


<CollectionView.ItemsLayout>
<ListItemsLayout>
<x:Arguments>
<ItemsLayoutOrientation>Horizontal</ItemsLayoutOrientation>
</x:Arguments>
</ListItemsLayout>
</CollectionView.ItemsLayout>
...
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
...
ItemsLayout = ListItemsLayout.Horizontal
};

Esto da como resultado una lista de fila única, lo que aumenta de tamaño horizontalmente conforme se agregan
nuevos elementos:
Cuadrícula vertical
CollectionView puede mostrar sus elementos en una cuadrícula vertical estableciendo su ItemsLayout propiedad
a un GridItemsLayout cuyo Orientation propiedad está establecida en Vertical :

<CollectionView ItemsSource="{Binding Monkeys}">


<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold"
LineBreakMode="TailTruncation" />
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Location}"
LineBreakMode="TailTruncation"
FontAttributes="Italic"
VerticalOptions="End" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
...
ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Vertical)
};

De forma predeterminada, una vertical GridItemsLayout mostrará los elementos en una sola columna. Sin
embargo, en este ejemplo se establece la GridItemsLayout.Span propiedad en 2. Esto da como resultado una
cuadrícula de dos columnas, lo que aumenta de tamaño verticalmente conforme se agregan nuevos elementos:
Cuadrícula horizontal
CollectionView puede mostrar sus elementos en una cuadrícula horizontal estableciendo su ItemsLayout
propiedad a un GridItemsLayout cuyo Orientation propiedad está establecida en Horizontal :

<CollectionView ItemsSource="{Binding Monkeys}">


<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Horizontal"
Span="4" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="140" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold"
LineBreakMode="TailTruncation" />
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Location}"
LineBreakMode="TailTruncation"
FontAttributes="Italic"
VerticalOptions="End" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

El código de C# equivalente es:


CollectionView collectionView = new CollectionView
{
...
ItemsLayout = new GridItemsLayout(4, ItemsLayoutOrientation.Horizontal)
};

De forma predeterminada, una horizontal GridItemsLayout mostrará los elementos en una sola fila. Sin embargo,
en este ejemplo se establece la GridItemsLayout.Span propiedad a 4. Esto da como resultado una cuadrícula de
cuatro filas, lo que aumenta de tamaño horizontalmente conforme se agregan nuevos elementos:

Espaciado de elemento
De forma predeterminada, cada elemento en un CollectionView no tiene un espacio vacío alrededor de ella. Se
puede cambiar este comportamiento estableciendo las propiedades en el diseño de elementos utilizado por el
CollectionView .

Cuando un CollectionView establece su ItemsLayout propiedad a un ListItemsLayout objeto, el


ListItemsLayout.ItemSpacing propiedad puede establecerse en un double valor que representa el espacio vacío
alrededor de cada elemento:

<CollectionView ItemsSource="{Binding Monkeys}">


<CollectionView.ItemsLayout>
<ListItemsLayout ItemSpacing="20">
<x:Arguments>
<ItemsLayoutOrientation>Vertical</ItemsLayoutOrientation>
</x:Arguments>
</ListItemsLayout>
</CollectionView.ItemsLayout>
...
</CollectionView>

NOTE
El ListItemsLayout.ItemSpacing propiedad tiene un conjunto de devolución de llamada de validación, lo que garantiza
que el valor de la propiedad es siempre mayor o igual que 0.

El código de C# equivalente es:


CollectionView collectionView = new CollectionView
{
...
ItemsLayout = new ListItemsLayout(ItemsLayoutOrientation.Vertical)
{
ItemSpacing = 20
}
};

Este código da como resultado una lista vertical de columna única, que tiene un espaciado de 20 alrededor de
cada elemento:

Cuando un CollectionView establece su ItemsLayout propiedad a un GridItemsLayout objeto, el


GridItemsLayout.VerticalItemSpacing y GridItemsLayout.HorizontalItemSpacing las propiedades pueden ser
establecido en double valores que representan el espacio en blanco vertical y horizontalmente alrededor de cada
elemento:

<CollectionView ItemsSource="{Binding Monkeys}">


<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2"
VerticalItemSpacing="20"
HorizontalItemSpacing="30" />
</CollectionView.ItemsLayout>
...
</CollectionView>

NOTE
El GridItemsLayout.VerticalItemSpacing y GridItemsLayout.HorizontalItemSpacing propiedades tienen las
devoluciones de llamada de validación se establece, que garantizan que los valores de las propiedades son siempre mayor o
igual que 0.

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
...
ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Vertical)
{
VerticalItemSpacing = 20,
HorizontalItemSpacing = 30
}
};

Este código da como resultado una cuadrícula vertical de dos columnas, que tiene un espaciado vertical de 20
alrededor de cada elemento y un espaciado horizontal de 30 alrededor de cada elemento:
Ajuste de tamaño de elemento
De forma predeterminada, cada elemento en un CollectionView es individualmente mide y tamaño, siempre que
los elementos de interfaz de usuario en el DataTemplate no especificar tamaños fijos. Este comportamiento, que
se puede cambiar, especificado por el CollectionView.ItemSizingStrategy valor de propiedad. Este valor de
propiedad puede establecerse en uno de los ItemSizingStrategy miembros de enumeración:
MeasureAllItems – individualmente se mide cada elemento. Este es el valor predeterminado.
MeasureFirstItem : se mide solo el primer elemento, con todos los elementos subsiguientes del mismo tamaño
que el primer elemento determinados.

IMPORTANT
El MeasureFirstItem estrategia de ajuste de tamaño dará como resultado un aumento del rendimiento cuando se utiliza
en situaciones donde el tamaño del elemento debe ser uniforme en todos los elementos.

En el ejemplo de código siguiente se muestra cómo establecer el ItemSizingStrategy propiedad:

<CollectionView ...
ItemSizingStrategy="MeasureFirstItem">
...
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
...
ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem
};

Cambio de tamaño dinámico de elementos


Los elementos de un CollectionView puede cambiarse dinámicamente en tiempo de ejecución cambiando diseño
relacionados con las propiedades de elementos dentro de la DataTemplate . Por ejemplo, el código siguiente
ejemplo se cambia el HeightRequest y WidthRequest las propiedades de un Image objeto:

void OnImageTapped(object sender, EventArgs e)


{
Image image = sender as Image;
image.HeightRequest = image.WidthRequest = image.HeightRequest.Equals(60) ? 100 : 60;
}
El OnImageTapped controlador de eventos se ejecuta en respuesta a una Image de objetos que se pulsa y cambia
las dimensiones de la imagen para que lo esté viendo más fácilmente:

Diseño de derecha a izquierda


CollectionView Puede diseño su contenido en una dirección de flujo de derecha a izquierda estableciendo su
FlowDirection propiedad RightToLeft . Sin embargo, el FlowDirection propiedad lo ideal es que debe
establecerse en un diseño de página o raíz, lo que hace que todos los elementos dentro de la página o el diseño de
la raíz, para responder a la dirección de flujo:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CollectionViewDemos.Views.VerticalListFlowDirectionPage"
Title="Vertical list (RTL FlowDirection)"
FlowDirection="RightToLeft">
<StackLayout Margin="20">
<CollectionView ItemsSource="{Binding Monkeys}">
...
</CollectionView>
</StackLayout>
</ContentPage>

El valor predeterminado FlowDirection para un elemento con un elemento primario es MatchParent . Por lo
tanto, el CollectionView hereda el FlowDirection valor de propiedad de la StackLayout , que a su vez hereda el
FlowDirection valor de propiedad de la ContentPage . Esto da como resultado el diseño de derecha a izquierda
que se muestra en las capturas de pantalla siguiente:

Para obtener más información acerca de la dirección del flujo, consulte localización de derecha a izquierda.

Vínculos relacionados
CollectionView (ejemplo)
Localización de derecha a izquierda
Desplazamiento de Xamarin.Forms CollectionView
Selección de Xamarin.Forms CollectionView
11/07/2019 • 12 minutes to read • Edit Online

descargar el ejemplo
CollectionView define las siguientes propiedades que controlan la selección de elementos:
SelectionMode , del tipo SelectionMode , el modo de selección.
SelectedItem , del tipo object , el elemento seleccionado en la lista. Esta propiedad no tiene un modo de
enlace predeterminada de TwoWay y tiene un null valor cuando se selecciona ningún elemento.
SelectedItems , del tipo IList<object> , los elementos seleccionados en la lista. Esta propiedad no tiene un
modo de enlace predeterminada de OneWay y tiene un null valor cuando no hay elementos seleccionados.
SelectionChangedCommand , del tipo ICommand , que se ejecuta cuando cambia el elemento seleccionado.
SelectionChangedCommandParameter , del tipo object , que es el parámetro que se pasa a la
SelectionChangedCommand .

Todas estas propiedades están respaldados por objetos BindableProperty , lo que significa que las propiedades
pueden ser destinos de los enlaces de datos.
De forma predeterminada, CollectionView la selección está deshabilitada. Sin embargo, se puede cambiar este
comportamiento estableciendo el SelectionMode valor de propiedad en uno de los SelectionMode miembros de
enumeración:
None : indica que no se pueden seleccionar elementos. Este es el valor predeterminado.
Single : indica que se puede seleccionar un solo elemento con el elemento seleccionado se resalta.
Multiple : indica que se pueden seleccionar varios elementos con los elementos seleccionados resaltados.

CollectionView define un SelectionChanged evento que se desencadena cuando el SelectedItem los cambios de
propiedad, ya sea debido a la selección de un elemento de la lista, o cuando una aplicación establece la propiedad
del usuario. Además, este evento también se desencadena cuando el SelectedItems los cambios de propiedad. El
SelectionChangedEventArgs objeto que acompaña a la SelectionChanged eventos tiene dos propiedades, ambos de
tipo IReadOnlyList<object> :
PreviousSelection : la lista de elementos que se seleccionaron antes de cambia la selección.
CurrentSelection : la lista de elementos que estén seleccionados, después del cambio de selección.

Selección única
Cuando el SelectionMode propiedad está establecida en Single , un único elemento en el CollectionView puede
seleccionarse. Cuando se selecciona un elemento, el SelectedItem propiedad se establecerá en el valor del
elemento seleccionado. Cuando esta propiedad cambia, el SelectionChangedCommand se ejecuta (con el valor de la
SelectionChangedCommandParameter que se pasan a la ICommand ) y el SelectionChanged desencadena el evento.

El siguiente ejemplo XAML se muestra un CollectionView que puede responder a un solo elemento selección:
<CollectionView ItemsSource="{Binding Monkeys}"
SelectionMode="Single"
SelectionChanged="OnCollectionViewSelectionChanged">
...
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
SelectionMode = SelectionMode.Single
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");
collectionView.SelectionChanged += OnCollectionViewSelectionChanged;

En este ejemplo de código, el OnCollectionViewSelectionChanged se ejecuta el controlador de eventos cuando el


SelectionChanged desencadena el evento, con el controlador de eventos recupera el elemento seleccionado
previamente y el elemento seleccionado actualmente:

void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)


{
string previous = (e.PreviousSelection.FirstOrDefault() as Monkey)?.Name;
string current = (e.CurrentSelection.FirstOrDefault() as Monkey)?.Name;
...
}

IMPORTANT
El SelectionChanged eventos pueden activar los cambios que se producen como resultado de cambiar el SelectionMode
propiedad.

Las capturas de pantalla siguientes muestran la selección de elemento único en un CollectionView :

Selección múltiple
Cuando el SelectionMode propiedad está establecida en Multiple , varios elementos en el CollectionView puede
seleccionarse. Cuando se seleccionan elementos, la SelectedItems propiedad se establecerá en los elementos
seleccionados. Cuando esta propiedad cambia, el SelectionChangedCommand se ejecuta (con el valor de la
SelectionChangedCommandParameter que se pasan a la ICommand ) y el SelectionChanged desencadena el evento.

El siguiente ejemplo XAML se muestra un CollectionView que puede responder a la selección de varios
elementos:
<CollectionView ItemsSource="{Binding Monkeys}"
SelectionMode="Multiple"
SelectionChanged="OnCollectionViewSelectionChanged">
...
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
SelectionMode = SelectionMode.Multiple
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");
collectionView.SelectionChanged += OnCollectionViewSelectionChanged;

En este ejemplo de código, el OnCollectionViewSelectionChanged se ejecuta el controlador de eventos cuando el


SelectionChanged desencadena el evento, con el controlador de eventos recupera los elementos seleccionados
previamente y los elementos seleccionados actualmente:

void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)


{
var previous = e.PreviousSelection;
var current = e.CurrentSelection;
...
}

IMPORTANT
El SelectionChanged eventos pueden activar los cambios que se producen como resultado de cambiar el SelectionMode
propiedad.

Las capturas de pantalla siguientes muestran la selección de varios elementos en un CollectionView :

Selección previa única


Cuando el SelectionMode propiedad está establecida en Single , un único elemento en el CollectionView pueden
seleccionarse previamente estableciendo el SelectedItem propiedad al elemento. El siguiente ejemplo XAML se
muestra un CollectionView que previamente selecciona un solo elemento:

<CollectionView ItemsSource="{Binding Monkeys}"


SelectionMode="Single"
SelectedItem="{Binding SelectedMonkey}">
...
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
SelectionMode = SelectionMode.Single
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");
collectionView.SetBinding(SelectableItemsView.SelectedItemProperty, "SelectedMonkey");

NOTE
El SelectedItem propiedad tiene un modo de enlace predeterminada de TwoWay .

El SelectedItem datos de la propiedad se enlaza a la SelectedMonkey propiedad del modelo de vista conectada,
que es de tipo Monkey . De forma predeterminada, un TwoWay enlace se usa por lo que ese if el usuario cambia el
elemento seleccionado, el valor de la SelectedMonkey se establecerá la propiedad seleccionada Monkey objeto. El
SelectedMonkey propiedad está definida en el MonkeysViewModel clase y se establece en el cuarto elemento de la
Monkeys colección:

public class MonkeysViewModel : INotifyPropertyChanged


{
...
public ObservableCollection<Monkey> Monkeys { get; private set; }

Monkey selectedMonkey;
public Monkey SelectedMonkey
{
get
{
return selectedMonkey;
}
set
{
if (selectedMonkey != value)
{
selectedMonkey = value;
}
}
}

public MonkeysViewModel()
{
...
selectedMonkey = Monkeys.Skip(3).FirstOrDefault();
}
...
}

Por lo tanto, cuando el CollectionView aparece, se preselecciona el cuarto elemento de la lista:


Selección múltiple anterior
Cuando el SelectionMode propiedad está establecida en Multiple , varios elementos en el CollectionView pueden
seleccionarse previamente. El siguiente ejemplo XAML se muestra un CollectionView que permitirá a la selección
previa de varios elementos:

<CollectionView x:Name="collectionView"
ItemsSource="{Binding Monkeys}"
SelectionMode="Multiple"
SelectedItems="{Binding SelectedMonkeys}">
...
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
SelectionMode = SelectionMode.Multiple
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");
collectionView.SetBinding(SelectableItemsView.SelectedItemsProperty, "SelectedMonkeys");

NOTE
El SelectedItems propiedad tiene un modo de enlace predeterminada de OneWay .

El SelectedItems datos de la propiedad se enlaza a la SelectedMonkeys propiedad del modelo de vista conectada,
que es de tipo ObservableCollection<object> . El SelectedMonkeys propiedad está definida en el MonkeysViewModel
clase y se establece en el segundo, cuarta y quinta los elementos de la Monkeys colección:
namespace CollectionViewDemos.ViewModels
{
public class MonkeysViewModel : INotifyPropertyChanged
{
...
ObservableCollection<object> selectedMonkeys;
public ObservableCollection<object> SelectedMonkeys
{
get
{
return selectedMonkeys;
}
set
{
if (selectedMonkeys != value)
{
selectedMonkeys = value;
}
}
}

public MonkeysViewModel()
{
...
SelectedMonkeys = new ObservableCollection<object>()
{
Monkeys[1], Monkeys[3], Monkeys[4]
};
}
...
}
}

Por lo tanto, cuando el CollectionView aparece, el segundo, cuarto, y están preseleccionados quinto elementos de
la lista:

Borrar selecciones
El SelectedItem y SelectedItems propiedades pueden eliminarse mediante el establecimiento ellos o los objetos
que enlazan, a null .

Cambiar el color del elemento seleccionado


CollectionView tiene un Selected VisualState que puede utilizarse para iniciar un cambio visual en el elemento
seleccionado en el CollectionView . Caso de uso común para esto VisualState consiste en cambiar el color de
fondo del elemento seleccionado, que se muestra en el siguiente ejemplo XAML:
<ContentPage ...>
<ContentPage.Resources>
<Style TargetType="Grid">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="LightSkyBlue" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ContentPage.Resources>
<StackLayout Margin="20">
<CollectionView ItemsSource="{Binding Monkeys}"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
...
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>

IMPORTANT
El Style que contiene el Selected VisualState debe tener un TargetType valor de propiedad que es el tipo del
elemento raíz de la DataTemplate , que se establece como el ItemTemplate valor de propiedad.

En este ejemplo, el Style.TargetType el valor de propiedad se establece en Grid porque el elemento raíz de la
ItemTemplate es un Grid . El Selected VisualState especifica que cuando un elemento de la CollectionView
está activada, el BackgroundColor del elemento se establecerá en LightSkyBlue :

[ ] (selection-images/single-selection-color-
large.png#lightbox " CollectionView lista vertical con un color personalizado de selección única")
Para obtener más información acerca de los estados visuales, vea Xamarin.Forms Visual State Manager.

Deshabilitar la selección
CollectionView selección está deshabilitada de forma predeterminada. Sin embargo, si un CollectionView tiene
selección habilitada, se puede deshabilitar estableciendo la SelectionMode propiedad None :
<CollectionView ...
SelectionMode="None" />

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
...
SelectionMode = SelectionMode.None
};

Cuando el SelectionMode propiedad está establecida en None , los elementos de la CollectionView no puede
seleccionarse el SelectedItem propiedad permanecer null y el SelectionChanged no se desencadenará el evento.

NOTE
Cuando se selecciona un elemento y el SelectionMode se cambia la propiedad de Single a None , SelectedItem
propiedad se establecerá en null y el SelectionChanged se desencadenará el evento con un valor vacío
CurrentSelection propiedad.

Vínculos relacionados
CollectionView (ejemplo)
Administrador de estado Visual de Xamarin.Forms
Xamarin.Forms CollectionView EmptyView
11/07/2019 • 11 minutes to read • Edit Online

descargar el ejemplo
CollectionView define las siguientes propiedades que pueden usarse para proporcionar comentarios de los
usuarios cuando no hay ningún dato para mostrar:
EmptyView , del tipo object , la cadena, el enlace o la vista que será que se muestra cuando el ItemsSource
propiedad es null , o cuando la colección especificado por el ItemsSource propiedad es null o está vacío. El
valor predeterminado es null .
EmptyViewTemplate , del tipo DataTemplate , la plantilla para dar formato especificado a EmptyView . El valor
predeterminado es null .
Estas propiedades están respaldadas por BindableProperty objetos, lo que significa que las propiedades pueden
ser destinos de enlaces de datos.
Los escenarios de uso principal para la configuración de la EmptyView propiedad se muestran los comentarios de
los usuarios cuando una operación de filtrado en un CollectionView produce no tiene datos y mostrar
comentarios de los usuarios mientras se recuperan datos de un servicio web.

NOTE
El EmptyView propiedad puede establecerse en una vista que incluya contenido interactivo si es necesario.

Para obtener más información sobre las plantillas de datos, consulte Plantillas de datos de Xamarin.Forms.

Mostrar una cadena cuando los datos no están disponibles


El EmptyView propiedad puede establecerse en una cadena, que será que se muestra cuando el ItemsSource
propiedad es null , o cuando la colección especificado por el ItemsSource propiedad es null o está vacío. El
XAML siguiente muestra un ejemplo de este escenario:

<CollectionView ItemsSource="{Binding EmptyMonkeys}"


EmptyView="No items to display" />

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
EmptyView = "No items to display"
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "EmptyMonkeys");

El resultado es que, dado que los datos de colección enlazan es null , Establece la cadena como el EmptyView se
muestra el valor de propiedad:

Mostrar las vistas cuando los datos no están disponibles


El EmptyView propiedad puede establecerse en una vista, que será que se muestra cuando el ItemsSource
propiedad es null , o cuando la colección especificado por el ItemsSource propiedad es null o está vacío. Puede
tratarse de una vista única o una vista que contiene varias vistas secundarias. El siguiente ejemplo XAML muestra
el EmptyView propiedad establecida en una vista que contiene varias vistas secundarias:

<StackLayout Margin="20">
<SearchBar x:Name="searchBar"
SearchCommand="{Binding FilterCommand}"
SearchCommandParameter="{Binding Source={x:Reference searchBar}, Path=Text}"
Placeholder="Filter" />
<CollectionView ItemsSource="{Binding Monkeys}">
<CollectionView.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.EmptyView>
<StackLayout>
<Label Text="No results matched your filter."
Margin="10,25,10,10"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Fill"
HorizontalTextAlignment="Center" />
<Label Text="Try a broader filter?"
FontAttributes="Italic"
FontSize="12"
HorizontalOptions="Fill"
HorizontalTextAlignment="Center" />
</StackLayout>
</CollectionView.EmptyView>
</CollectionView>
</StackLayout>

El código de C# equivalente es:


SearchBar searchBar = new SearchBar { ... };
CollectionView collectionView = new CollectionView
{
EmptyView = new StackLayout
{
Children =
{
new Label { Text = "No results matched your filter.", ... },
new Label { Text = "Try a broader filter?", ... }
}
}
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

Cuando el SearchBar ejecuta el FilterCommand , la colección mostrada por el CollectionView se filtra para el
término de búsqueda se almacena en el SearchBar.Text propiedad. Si el resultado de la operación de filtrado
ningún dato, la StackLayout establecer como el EmptyView se muestra el valor de propiedad:

Mostrar el tipo de un plantilla personalizado cuando los datos no están


disponibles
El EmptyView propiedad puede establecerse en un tipo personalizado, cuya plantilla es que se muestra cuando el
ItemsSource propiedad es null , o cuando la colección especificada por el ItemsSource propiedad es null o está
vacío. El EmptyViewTemplate propiedad puede establecerse en un DataTemplate que define la apariencia de la
EmptyView . El XAML siguiente muestra un ejemplo de este escenario:
<StackLayout Margin="20">
<SearchBar x:Name="searchBar"
SearchCommand="{Binding FilterCommand}"
SearchCommandParameter="{Binding Source={x:Reference searchBar}, Path=Text}"
Placeholder="Filter" />
<CollectionView ItemsSource="{Binding Monkeys}">
<CollectionView.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.EmptyView>
<views:FilterData Filter="{Binding Source={x:Reference searchBar}, Path=Text}" />
</CollectionView.EmptyView>
<CollectionView.EmptyViewTemplate>
<DataTemplate>
<Label Text="{Binding Filter, StringFormat='Your filter term of {0} did not match any
records.'}"
Margin="10,25,10,10"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Fill"
HorizontalTextAlignment="Center" />
</DataTemplate>
</CollectionView.EmptyViewTemplate>
</CollectionView>
</StackLayout>

El código de C# equivalente es:

SearchBar searchBar = new SearchBar { ... };


CollectionView collectionView = new CollectionView
{
EmptyView = new FilterData { Filter = searchBar.Text },
EmptyViewTemplate = new DataTemplate(() =>
{
return new Label { ... };
})
};

El FilterData tipo define un Filter propiedad y su correspondiente BindableProperty :

public class FilterData : BindableObject


{
public static readonly BindableProperty FilterProperty = BindableProperty.Create(nameof(Filter),
typeof(string), typeof(FilterData), null);

public string Filter


{
get { return (string)GetValue(FilterProperty); }
set { SetValue(FilterProperty, value); }
}
}

El EmptyViewpropiedad está establecida en un FilterData objeto y el Filter datos de la propiedad se enlaza a la


SearchBar.Text propiedad. Cuando el SearchBar ejecuta el FilterCommand , la colección mostrada por el
CollectionView se filtra para el término de búsqueda se almacena en el Filter propiedad. Si el resultado de la
operación de filtrado ningún dato, la Label definido en el DataTemplate , que se establece como el
EmptyViewTemplate es el valor de propiedad muestra:
NOTE
Cuando se muestra el tipo de un plantilla personalizado cuando los datos no están disponibles, el EmptyViewTemplate
propiedad puede establecerse en una vista que contiene varias vistas secundarias.

Elija un EmptyView en tiempo de ejecución


Las vistas que se mostrará como un EmptyView cuando los datos no están disponibles, puede definirse como
ContentView objetos en un ResourceDictionary . El EmptyView , a continuación, se puede establecer la propiedad a
un determinado ContentView , en función de alguna lógica de negocios, en tiempo de ejecución. En el siguiente
ejemplo XAML se muestra un ejemplo de este escenario:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CollectionViewDemos.Views.EmptyViewSwapPage"
Title="EmptyView (swap)">
<ContentPage.Resources>
<ContentView x:Key="BasicEmptyView">
<StackLayout>
<Label Text="No items to display."
Margin="10,25,10,10"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Fill"
HorizontalTextAlignment="Center" />
</StackLayout>
</ContentView>
<ContentView x:Key="AdvancedEmptyView">
<StackLayout>
<Label Text="No results matched your filter."
Margin="10,25,10,10"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Fill"
HorizontalTextAlignment="Center" />
<Label Text="Try a broader filter?"
FontAttributes="Italic"
FontSize="12"
HorizontalOptions="Fill"
HorizontalTextAlignment="Center" />
</StackLayout>
</ContentView>
</ContentPage.Resources>

<StackLayout Margin="20">
<SearchBar x:Name="searchBar"
SearchCommand="{Binding FilterCommand}"
SearchCommandParameter="{Binding Source={x:Reference searchBar}, Path=Text}"
Placeholder="Filter" />
<StackLayout Orientation="Horizontal">
<Label Text="Toggle EmptyViews" />
<Switch Toggled="OnEmptyViewSwitchToggled" />
</StackLayout>
<CollectionView x:Name="collectionView"
ItemsSource="{Binding Monkeys}">
<CollectionView.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>

Este XAML define dos ContentView objetos en el nivel de página ResourceDictionary , con el Switch controlar
que el objeto ContentView objeto que se establecerá como el EmptyView valor de propiedad. Cuando el Switch se
activa, el OnEmptyViewSwitchToggled ejecuta el controlador de eventos el ToggleEmptyView método:

void ToggleEmptyView(bool isToggled)


{
collectionView.EmptyView = isToggled ? Resources["BasicEmptyView"] : Resources["AdvancedEmptyView"];
}

El ToggleEmptyView método establece el EmptyView propiedad de la collectionView objeto en uno de los dos
ContentView objetos almacenados en el ResourceDictionary , según el valor de la Switch.IsToggled propiedad.
Cuando el SearchBar ejecuta el FilterCommand , la colección mostrada por el CollectionView se filtra para el
término de búsqueda se almacena en el SearchBar.Text propiedad. Si el resultado de la operación de filtrado
ningún dato, la ContentView objeto establecido como el EmptyView propiedad se muestra:

Para obtener más información acerca de los diccionarios de recursos, consulte diccionarios de recursos de
Xamarin.Forms.

Elija un EmptyViewTemplate en tiempo de ejecución


La apariencia de la se puede elegir en tiempo de ejecución, según su valor, estableciendo el
EmptyView
CollectionView.EmptyViewTemplate propiedad a un DataTemplateSelector objeto:

<ContentPage ...
xmlns:controls="clr-namespace:CollectionViewDemos.Controls">
<ContentPage.Resources>
<DataTemplate x:Key="AdvancedTemplate">
...
</DataTemplate>

<DataTemplate x:Key="BasicTemplate">
...
</DataTemplate>

<controls:SearchTermDataTemplateSelector x:Key="SearchSelector"
DefaultTemplate="{StaticResource AdvancedTemplate}"
OtherTemplate="{StaticResource BasicTemplate}" />
</ContentPage.Resources>

<StackLayout Margin="20">
<SearchBar x:Name="searchBar"
SearchCommand="{Binding FilterCommand}"
SearchCommandParameter="{Binding Source={x:Reference searchBar}, Path=Text}"
Placeholder="Filter" />
<CollectionView ItemsSource="{Binding Monkeys}"
EmptyView="{Binding Source={x:Reference searchBar}, Path=Text}"
EmptyViewTemplate="{StaticResource SearchSelector}" />
</StackLayout>
</ContentPage>

El código de C# equivalente es:

SearchBar searchBar = new SearchBar { ... };


CollectionView collectionView = new CollectionView
{
EmptyView = searchBar.Text,
EmptyViewTemplate = new SearchTermDataTemplateSelector { ... }
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

El EmptyView propiedad está establecida en el SearchBar.Text propiedad y el EmptyViewTemplate propiedad está


establecida en un SearchTermDataTemplateSelector objeto.
Cuando el SearchBar ejecuta el FilterCommand , la colección mostrada por el CollectionView se filtra para el
término de búsqueda se almacena en el SearchBar.Text propiedad. Si el resultado de la operación de filtrado
ningún dato, la DataTemplate elegido por el SearchTermDataTemplateSelector objeto está establecido como el
EmptyViewTemplate propiedad y se muestran.

El ejemplo siguiente se muestra la SearchTermDataTemplateSelector clase:

public class SearchTermDataTemplateSelector : DataTemplateSelector


{
public DataTemplate DefaultTemplate { get; set; }
public DataTemplate OtherTemplate { get; set; }

protected override DataTemplate OnSelectTemplate(object item, BindableObject container)


{
string query = (string)item;
return query.ToLower().Equals("xamarin") ? OtherTemplate : DefaultTemplate;
}
}

La clase define las propiedades DefaultTemplate y OtherTemplate de tipo


SearchTermTemplateSelector
DataTemplate que se establecen para diferentes plantillas de datos. El OnSelectTemplate invalidar devuelve
DefaultTemplate , que muestra un mensaje al usuario, cuando la consulta de búsqueda no es igual a "xamarin".
Cuando la consulta de búsqueda es igual a "xamarin", la OnSelectTemplate invalidar devuelve OtherTemplate , que
muestra un mensaje básico para el usuario:

Para obtener más información acerca de los selectores de plantilla de datos, vea crear un Xamarin.Forms
DataTemplateSelector.

Vínculos relacionados
CollectionView (ejemplo)
Plantillas de datos de Xamarin.Forms
Diccionarios de recursos de Xamarin.Forms
Crear un Xamarin.Forms DataTemplateSelector
Desplazamiento de Xamarin.Forms CollectionView
11/07/2019 • 9 minutes to read • Edit Online

descargar el ejemplo
CollectionView define dos ScrollTo métodos, que desplazar los elementos en la vista. Una de las sobrecargas
desplaza el elemento en el índice especificado en la vista, mientras que el otro desplaza el elemento especificado
en la vista. Ambas sobrecargas tienen argumentos adicionales que pueden especificarse para indicar la posición
exacta del elemento después de que el desplazamiento se ha completado y si se va a animar el desplazamiento.
CollectionView define un evento que se desencadena cuando uno de los ScrollTo métodos
ScrollToRequested
se invocan. El ScrollToRequestedEventArgs objeto que acompaña a la ScrollToRequested eventos tiene muchas
propiedades, como IsAnimated , Index , Item , y ScrollToPosition . Estas propiedades se establecen en los
argumentos especificados en el ScrollTo llamadas al método.
Cuando un Deslizamientos de usuario para iniciar un desplazamiento, se puede controlar la posición final del
desplazamiento para que se muestran por completo los elementos. Esta característica se conoce como ajuste,
porque los elementos de ajuste para colocar cuando se detiene el desplazamiento. Para obtener más información,
consulte ajustar puntos.

Desplazar un elemento en un índice en la vista


La primera ScrollTo sobrecarga del método desplaza el elemento en el índice especificado en la vista. Dado un
CollectionView objeto denominado collectionView , en el ejemplo siguiente se muestra cómo desplazar el
elemento en el índice 12 en la vista:

collectionView.ScrollTo(12);

Desplazar un elemento en la vista


El segundo ScrollTosobrecarga del método desplaza el elemento especificado en la vista. Dado un
CollectionView objeto denominado collectionView , en el ejemplo siguiente se muestra cómo desplazar el
elemento especificado en la vista:

MonkeysViewModel viewModel = BindingContext as MonkeysViewModel;


Monkey monkey = viewModel.Monkeys.FirstOrDefault(m => m.Name == "Proboscis Monkey");
collectionView.ScrollTo(monkey);

Posición de desplazamiento del control


Al desplazarse de un elemento en la vista, se puede especificar la posición exacta del elemento después de que el
desplazamiento se ha completado con el position argumento de la ScrollTo métodos. Este argumento acepta
un ScrollToPosition miembro de enumeración.
MakeVisible
El ScrollToPosition.MakeVisible miembro indica que se debe desplazar el elemento hasta que esté visible en la
vista:

collectionView.ScrollTo(monkey, position: ScrollToPosition.MakeVisible);

Este código de ejemplo genera el desplazamiento mínimo requerido para desplazar el elemento en la vista:

NOTE
El ScrollToPosition.MakeVisible miembro se usa de forma predeterminada, si la position no se especifica un
argumento al llamar a la ScrollTo método.

Iniciar
El ScrollToPosition.Start miembro indica que el elemento se debe desplazar al principio de la vista:

collectionView.ScrollTo(monkey, position: ScrollToPosition.Start);

Este código de ejemplo genera el elemento que se va a desplazar al principio de la vista:


Centrar
El ScrollToPosition.Center miembro indica que el elemento se debe desplazar al centro de la vista:

collectionView.ScrollTo(monkey, position: ScrollToPosition.Center);

Este código de ejemplo genera el elemento que se va a desplazar al centro de la vista:

Fin
El ScrollToPosition.End miembro indica que se debe desplazar el elemento al final de la vista:

collectionView.ScrollTo(monkey, position: ScrollToPosition.End);

Este código de ejemplo genera el elemento que se desplaza hasta el final de la vista:

Deshabilitar la animación de desplazamiento


Se muestra una animación de desplazamiento al desplazarse a un elemento en la vista. Sin embargo, esta
animación se puede deshabilitar estableciendo la animate argumento de la ScrollTo método false :
collectionView.ScrollTo(monkey, animate: false);

Puntos de ajuste
Cuando un Deslizamientos de usuario para iniciar un desplazamiento, se puede controlar la posición final del
desplazamiento para que se muestran por completo los elementos. Esta característica se conoce como ajuste,
porque los elementos de ajuste para colocar cuando el desplazamiento se detiene y se controla mediante las
siguientes propiedades de la ItemsLayout clase:
SnapPointsType , del tipo SnapPointsType , especifica el comportamiento de puntos de acoplamiento al
desplazarse.
SnapPointsAlignment , del tipo SnapPointsAlignment , especifica cómo se alinean los puntos de acoplamiento
con elementos.
Estas propiedades están respaldadas por BindableProperty objetos, lo que significa que las propiedades pueden
ser destinos de enlaces de datos.

NOTE
Cuando se produce el ajuste, se producirá en la dirección que genera la menor cantidad de movimiento.

Tipo de puntos de ajuste


El SnapPointsType enumeración define los miembros siguientes:
None indica que el desplazamiento no se ajusta a los elementos.
Mandatory indica que el contenido siempre se ajusta a la más cercano complemento hacia donde el
desplazamiento naturalmente detendría, a lo largo de la dirección de la inercia.
MandatorySingle indica el mismo comportamiento que Mandatory , pero solo se desplaza a un elemento a la
vez.
De forma predeterminada, el SnapPointsType propiedad está establecida en SnapPointsType.None , lo que
garantiza que el desplazamiento no ajusta los elementos, como se muestra en las capturas de pantalla siguiente:

Alineación de puntos de ajuste


El SnapPointsAlignment enumeración define Start , Center ,y End miembros.
IMPORTANT
El valor de la SnapPointsAlignment propiedad solo es cuando respeta el SnapPointsType propiedad está establecida en
Mandatory , o MandatorySingle .

Iniciar
El SnapPointsAlignment.Start miembro indica que los puntos de acoplamiento se alinean con el borde inicial de
elementos.
De forma predeterminada, el SnapPointsAlignment propiedad está establecida en SnapPointsAlignment.Start . Sin
embargo, por razones de integridad, en el siguiente ejemplo XAML se muestra cómo establecer a este miembro
de enumeración:

<CollectionView x:Name="collectionView"
ItemsSource="{Binding Monkeys}">
<CollectionView.ItemsLayout>
<ListItemsLayout SnapPointsType="MandatorySingle"
SnapPointsAlignment="Start">
<x:Arguments>
<ItemsLayoutOrientation>Vertical</ItemsLayoutOrientation>
</x:Arguments>
</ListItemsLayout>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
ItemsLayout = new ListItemsLayout(ItemsLayoutOrientation.Vertical)
{
SnapPointsType = SnapPointsType.MandatorySingle,
SnapPointsAlignment = SnapPointsAlignment.Start
},
ItemTemplate = new DataTemplate(() =>
{
return null;
})
};

Cuando un Deslizamientos de usuario para iniciar un desplazamiento, el elemento superior se alineará con la
parte superior de la vista:
Centrar
El SnapPointsAlignment.Center miembro indica que los puntos de acoplamiento se alinean con el centro de
elementos. En el siguiente ejemplo XAML se muestra cómo establecer a este miembro de enumeración:

<CollectionView x:Name="collectionView"
ItemsSource="{Binding Monkeys}">
<CollectionView.ItemsLayout>
<ListItemsLayout SnapPointsType="MandatorySingle"
SnapPointsAlignment="Center">
<x:Arguments>
<ItemsLayoutOrientation>Vertical</ItemsLayoutOrientation>
</x:Arguments>
</ListItemsLayout>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
ItemsLayout = new ListItemsLayout(ItemsLayoutOrientation.Vertical)
{
SnapPointsType = SnapPointsType.MandatorySingle,
SnapPointsAlignment = SnapPointsAlignment.Center
},
ItemTemplate = new DataTemplate(() =>
{
return null;
})
};

Cuando un Deslizamientos de usuario para iniciar un desplazamiento, el elemento superior estará centrado en la
parte superior de la vista:
Fin
El SnapPointsAlignment.End miembro indica que los puntos de acoplamiento se alinean con el borde final de
elementos. En el siguiente ejemplo XAML se muestra cómo establecer a este miembro de enumeración:

<CollectionView x:Name="collectionView"
ItemsSource="{Binding Monkeys}">
<CollectionView.ItemsLayout>
<ListItemsLayout SnapPointsType="MandatorySingle"
SnapPointsAlignment="End">
<x:Arguments>
<ItemsLayoutOrientation>Vertical</ItemsLayoutOrientation>
</x:Arguments>
</ListItemsLayout>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

El código de C# equivalente es:

CollectionView collectionView = new CollectionView


{
ItemsLayout = new ListItemsLayout(ItemsLayoutOrientation.Vertical)
{
SnapPointsType = SnapPointsType.MandatorySingle,
SnapPointsAlignment = SnapPointsAlignment.End
},
ItemTemplate = new DataTemplate(() =>
{
return null;
})
};

Cuando un Deslizamientos de usuario para iniciar un desplazamiento, el elemento de la parte inferior se alineará
con la parte inferior de la vista:
Vínculos relacionados
CollectionView (ejemplo)
Colores de Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms proporciona una clase de Color flexible y multiplataforma.
Este artículo presentan las distintas formas la Color clase puede usarse en Xamarin.Forms.
La Color clase proporciona una serie de métodos para crear una instancia de color
Colores con nombre -una colección de comunes colores con nombre, incluidos Red , Green , y Blue .
FromHex -valor similar a la sintaxis utilizada en HTML, por ejemplo "00FF00" de cadena. Alfa, opcionalmente,
puede especificarse como el primer par de caracteres ("CC00FF00").
FromHsla -matiz, saturación y luminosidad double valores, con el valor alfa opcional (0.0-1.0).
FromRgb -rojo, verde y azul int valores (0-255).
FromRgba -rojo, verde, azul y alfa int valores (0-255).
FromUint -establecer una sola double que representa el valor argb.
Presentamos algunos colores del ejemplo, asignados a la BackgroundColor de algunas etiquetas utilizando
diferentes variaciones de la sintaxis permitida:

var red = new Label { Text = "Red", BackgroundColor = Color.Red };


var orange = new Label { Text = "Orange",BackgroundColor = Color.FromHex("FF6A00") };
var yellow = new Label { Text = "Yellow",BackgroundColor = Color.FromHsla(0.167, 1.0, 0.5, 1.0) };
var green = new Label { Text = "Green", BackgroundColor = Color.FromRgb (38, 127, 0) };
var blue = new Label { Text = "Blue", BackgroundColor = Color.FromRgba(0, 38, 255, 255) };
var indigo = new Label { Text = "Indigo",BackgroundColor = Color.FromRgb (0, 72, 255) };
var violet = new Label { Text = "Violet",BackgroundColor = Color.FromHsla(0.82, 1, 0.25, 1) };

var transparent = new Label { Text = "Transparent",BackgroundColor = Color.Transparent };


var @default = new Label { Text = "Default", BackgroundColor = Color.Default };
var accent = new Label { Text = "Accent", BackgroundColor = Color.Accent };

Estos colores se muestran en cada plataforma a continuación. Tenga en cuenta el color final - Accent -es un color
blue-ish para iOS y Android; este valor se define mediante Xamarin.Forms.

Color.Default
Use el Default para establecer un valor de color en el valor predeterminado de plataforma (Esto representa un
color subyacente diferente en cada plataforma para cada propiedad de descripción) (o volver a establecer).
Los desarrolladores pueden usar este valor para establecer un Color propiedad pero no debería no consultar
esta instancia para sus valores RGB de componente (que están todo listo en -1).

Color.Transparent
Establezca el color para borrar.

Color.Accent
En iOS y Android, esta instancia se establece en un color de contraste que está visible en el fondo predeterminado,
pero no es el mismo que el color del texto de forma predeterminada.

Métodos adicionales
Color instancias incluyen métodos adicionales que pueden usarse para crear nuevos colores:
AddLuminosity -devuelve un nuevo color si modifica la luminosidad por el delta proporcionado.
WithHue -devuelve un nuevo color, reemplazando el matiz con el valor proporcionado.
WithLuminosity -devuelve un nuevo color, reemplazando la luminosidad con el valor proporcionado.
WithSaturation -devuelve un nuevo color, reemplazando la saturación con el valor proporcionado.
MultiplyAlpha -devuelve un nuevo color mediante la modificación de la versión alfa, multiplicarlo por el valor
alfa proporcionado.

Conversiones implícitas
Conversión implícita entre el Xamarin.Forms.Color y System.Drawing.Color tipos se pueden realizar:

Xamarin.Forms.Color xfColor = Xamarin.Forms.Color.FromRgb(0, 72, 255);


System.Drawing.Color sdColor = System.Drawing.Color.FromArgb(38, 127, 0);

// Implicity convert from a Xamarin.Forms.Color to a System.Drawing.Color


System.Drawing.Color sdColor2 = xfColor;

// Implicitly convert from a System.Drawing.Color to a Xamarin.Forms.Color


Xamarin.Forms.Color xfColor2 = sdColor;

Device.RuntimePlatform
Este fragmento de código usa el Device.RuntimePlatform propiedad para establecer el color de un
ActivityIndicator :

ActivityIndicator activityIndicator = new ActivityIndicator


{
Color = Device.RuntimePlatform == Device.iOS ? Color.Black : Color.Default,
IsRunning = true
};

Uso de XAML
También se puedan encontrar fácilmente los colores en XAML mediante los nombres de colores definido o las
representaciones hexadecimales que se muestra aquí:
<Label Text="Sea color" BackgroundColor="Aqua" />
<Label Text="RGB" BackgroundColor="#00FF00" />
<Label Text="Alpha plus RGB" BackgroundColor="#CC00FF00" />
<Label Text="Tiny RGB" BackgroundColor="#0F0" />
<Label Text="Tiny Alpha plus RGB" BackgroundColor="#C0F0" />

NOTE
Cuando se usa la compilación de XAML, los nombres de colores distinguen mayúsculas de minúsculas y por lo tanto, pueden
escribirse en minúsculas. Para obtener más información sobre la compilación de XAML, vea compilación XAML.

Resumen
Xamarin.Forms Color clase se utiliza para crear referencias de color basadas en la plataforma. Se puede usar en
código compartido y XAML.

Vínculos relacionados
ColorsSample
Selector de enlazable (ejemplo)
Controls Reference (Referencia de controles)
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Una descripción de todos los elementos visuales que se usa para construir una aplicación de Xamarin.Forms.
La interfaz visual de una aplicación de Xamarin.Forms se construye de objetos que se asignan a los controles
nativos de cada plataforma de destino. Esto permite que aplicaciones específicas de la plataforma para iOS,
Android y la plataforma Universal de Windows usar el código de Xamarin.Forms contenido en un biblioteca .NET
Standard o un proyecto compartido.
En estos cuatro artículos se muestran los cuatro grupos de control principal usados para crear la interfaz de
usuario de una aplicación de Xamarin.Forms:
Páginas
Diseños
Vistas
Celdas
Por lo general, una página de Xamarin.Forms ocupa toda la pantalla. La página suele contener un diseño, que
contiene las vistas y, posiblemente, otros diseños. Las celdas son componentes especializados utilizados en
conexión con TableView y ListView .
En los artículos en cuatro páginas, diseños, vistas , y celdas, se describe cada tipo de control con vínculos a uno
o más programas de ejemplo, un artículo que describe su uso (si existe) y su documentación de API (si existen).
Cada tipo de control también está acompañado por una captura de pantalla que muestra una página de la
FormsGallery ejemplo que se ejecutan en iOS, Android y UWP dispositivos. Cada captura de pantalla siguiente
se vincula al código de origen para la página de C#, la página XAML equivalente y (cuando corresponda) el
archivo de código subyacente de C# para la página XAML.

Vínculos relacionados
Ejemplo de Xamarin.Forms FormsGallery
Documentación de la API
Páginas de Xamarin.Forms
11/07/2019 • 4 minutes to read • Edit Online

descargar el ejemplo
Las páginas de Xamarin.Forms representan pantallas de aplicaciones móviles multiplataforma.
Todos los tipos de página que se describen a continuación se derivan de Xamarin.Forms Page clase. Estos
elementos visuales ocupan todos o la mayoría de la pantalla. Un Page objeto representa un ViewController en
iOS y un Page en la plataforma Universal de Windows. En Android, cada página ocupa la pantalla como una
Activity , pero las páginas de Xamarin.Forms son no Activity objetos.

Páginas
Xamarin.Forms es compatible con los siguientes tipos de página:
ContentPage

ContentPage es el tipo más sencillo y común de página.


Establecer el Content propiedad a una sola View objeto,
que es más a menudo un Layout como StackLayout ,
Grid , o ScrollView .

Documentación de la API

Código C# para esta página / página XAML

MasterDetailPage
Un MasterDetailPage administra dos paneles de
información. Establecer el Master propiedad a una página
con carácter general que muestra una lista o un menú.
Establecer el Detail propiedad a una página que muestra
un elemento seleccionado de la página maestra. El
IsPresented propiedad controla si está visible la página
maestra o de detalle.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML con código


subyacente

NavigationPage

El NavigationPage administra la navegación entre otras


páginas con una arquitectura basada en la pila. Cuando se
utiliza la navegación de páginas en la aplicación, una
instancia de la página principal debe pasarse al constructor
de un NavigationPage objeto.

Documentación de API / guía / 1 de ejemplo, 2, y 3

Código C# para esta página / página XAML con código =


detrás

TabbedPage

TabbedPage se deriva de la clase abstracta MultiPage


clase y permite la navegación entre secundarios páginas con
pestañas. Establecer el Children propiedad a una colección
de páginas, o un conjunto el ItemsSource propiedad a una
colección de objetos de datos y el ItemTemplate propiedad
a un DataTemplate que describe cómo cada objeto se para
representar visualmente.

Documentación de API / guía / 1 de ejemplo y 2

Código C# para esta página / página XAML

CarouselPage
CarouselPage se deriva de la clase abstracta MultiPage
clase y permite la navegación entre secundarios páginas a
través de deslizar el dedo. Establecer el Children propiedad
a una colección de ContentPage objetos o conjunto el
ItemsSource propiedad a una colección de objetos de
datos y el ItemTemplate propiedad a un DataTemplate
que describe cómo cada objeto se para representar
visualmente.

Documentación de API / guía / 1 de ejemplo y 2

Código C# para esta página / página XAML

TemplatedPage

TemplatedPage Muestra el contenido de pantalla completa


con una plantilla de control, y es la clase base para
ContentPage .

Documentación de API / guía

Vínculos relacionados
Ejemplo de Xamarin.Forms FormsGallery
Xamarin.Forms Samples (Ejemplos de Xamarin.Forms)
Documentación de la API de Xamarin.Forms
Diseños de Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

descargar el ejemplo
Los diseños de Xamarin.Forms se utilizan para crear controles de interfaz de usuario en estructuras visuales.
El Layout y Layout<T> clases de Xamarin.Forms son subtipos especializadas de las vistas que actúan como
contenedores para otros diseños y vistas. El Layout propia clase se deriva de View . Un Layout derivado
normalmente contiene lógica para establecer la posición y tamaño de los elementos secundarios en las
aplicaciones de Xamarin.Forms.

Las clases que derivan de Layout pueden dividirse en dos categorías:

Diseños con contenido único


Estas clases derivan de Layout , que define Padding y IsClippedToBounds propiedades.
ContentView

ContentView contiene un único elemento secundario que


se establece con el Content propiedad. El Content
propiedad puede establecerse en cualquier View derivados,
incluidos otros Layout derivados. ContentView se usa
principalmente como un elemento estructural y actúa como
clase base para Frame .

Documentación de la API

Código C# para esta página / página XAML


Fotograma

El Frame clase se deriva de ContentView y muestra un


marco rectangular alrededor de su elemento secundario.
Frame tiene un valor predeterminado Padding valor de
20 y también define OutlineColor , CornerRadius , y
HasShadow propiedades.

Documentación de la API

Código C# para esta página / página XAML

ScrollView

ScrollView es capaz de desplazar su contenido. Establecer


el Content propiedad a una vista o diseño demasiado
grande para caber en la pantalla. (El contenido de un
ScrollView es muy a menudo un StackLayout .)
Establecer el Orientation propiedad para indicar si el
desplazamiento debe ser vertical, horizontal, o ambos.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

TemplatedView

TemplatedView Muestra el contenido con una plantilla de


control, y es la clase base para ContentView .

Documentación de API / guía

ContentPresenter
ContentPresenter es un administrador de diseño para las
vistas con plantilla, se utiliza dentro de un
ControlTemplate para marcar dónde aparece el contenido
que debe presentarse.

Documentación de API / guía

Diseños con varios elementos secundarios


Estas clases derivan de Layout<View> .
StackLayout

StackLayout coloca los elementos secundarios en una pila


horizontal o verticalmente se basa en el Orientation
propiedad. El Spacing propiedad controla el espaciado
entre los elementos secundarios y tiene un valor
predeterminado de 6.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Cuadrícula

Grid coloca sus elementos secundarios en una cuadrícula


de filas y columnas. Posición del elemento secundario se
indica mediante el propiedades adjuntas Row , Column ,
RowSpan , y ColumnSpan .

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

AbsoluteLayout
AbsoluteLayout coloca los elementos secundarios en
ubicaciones específicas en relación con su elemento primario.
Posición del elemento secundario se indica mediante el
propiedades adjuntas LayoutBounds y LayoutFlags . Un
AbsoluteLayout es útil para animar las posiciones de las
vistas.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML con código


subyacente

RelativeLayout

RelativeLayout coloca los elementos secundarios respecto


a la RelativeLayout propio o a sus elementos
relacionados. Posición del elemento secundario se indica
mediante el propiedades adjuntas que se establecen en
objetos de tipo Constraint y BoundsConstraint .

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

FlexLayout

FlexLayout se basa en la hoja CSS Flexible diseño módulo,


normalmente conocido como flex diseño o flex cuadro.
FlexLayout define propiedades enlazables seis y cinco
propiedades enlazables adjuntas que permiten a los
elementos secundarios se pueden superponer ni ajusta con
muchas opciones de alineación y orientación.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Vínculos relacionados
Ejemplo de Xamarin.Forms FormsGallery
Xamarin.Forms Samples (Ejemplos de Xamarin.Forms)
Documentación de la API de Xamarin.Forms
Vistas de Xamarin.Forms
12/07/2019 • 14 minutes to read • Edit Online

descargar el ejemplo
Las vistas de Xamarin.Forms son los bloques de creación de interfaces de usuario móviles multiplataforma.
Las vistas son objetos de interfaz de usuario, como etiquetas, botones y los controles deslizantes que se
conocen normalmente como controles o widgets en otros entornos de programación de gráficos. Las vistas
compatibles con Xamarin.Forms todos se derivan los View clase. Pueden dividirse en varias categorías:

Vistas de presentación
Etiqueta

Label Muestra las cadenas de texto de una línea o bloques


de varias líneas de texto, ya sea con formato constantes o
variables. Establecer el Text propiedad en una cadena de
constante de formato, o conjunto el FormattedText
propiedad a un FormattedString objeto de variable el
formato.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Imagen

Image muestra un mapa de bits. Los mapas de bits se


pueden descargar a través de Web, incrustados como
recursos en el proyecto común o proyectos de plataforma o
creadas con .NET Stream objeto.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

BoxView
BoxView muestra un rectángulo relleno con color de la
Color propiedad. BoxView tiene una solicitud de tamaño
predeterminado de 40 x 40. Para otros tamaños, asigne el
WidthRequest y HeightRequest propiedades.

Documentación de API / guía / 1 de ejemplo, 2, 3, 4 , 5, y 6

Código C# para esta página / página XAML

WebView

WebView Muestra las páginas Web o contenido HTML en


función de si el Source propiedad está establecida en un
UriWebViewSource o un HtmlWebViewSource objeto.

Documentación de API / guía / 1 de ejemplo y 2

Código C# para esta página / página XAML

OpenGLView

OpenGLView Muestra gráficos OpenGL en proyectos de iOS


y Android. No hay ninguna compatibilidad para la
plataforma Universal de Windows. Los proyectos de Android
y iOS requieren una referencia a la OpenTK 1.0 ensamblado
o el OpenTK ensamblado de la versión 1.0.0.0. OpenGLView
es más fácil de usar en un proyecto compartido. Si se usa en
una biblioteca .NET Standard, un servicio de dependencia
también será necesario (como se muestra en el código de
ejemplo).

Se trata de la instalación de solo gráficos que está integrada


en Xamarin.Forms, pero también puede representar una
aplicación de Xamarin.Forms utilizando gráficos SkiaSharp Código C# para esta página / página XAML con código
, o UrhoSharp . subyacente

Documentación de la API

Asignación
Map muestra un mapa. El xamarin.Forms.Maps para
debe instalarse el paquete Nuget. Android y plataforma
Universal de Windows requieren una clave de autorización
de mapa.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Vistas que inician comandos


Botón

Button es un objeto rectangular que muestra el texto, y


que se desencadena una Clicked eventos cuando se ha
presionado.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML con código


subyacente

ImageButton

ImageButton es un objeto un rectangular que muestra una


imagen y que se activa un Clicked eventos cuando se ha
presionado.

Guía de / ejemplo

Código C# para esta página / página XAML con código


subyacente

SearchBar
SearchBar muestra un área para el usuario escriba una
cadena de texto y un botón (o una tecla del teclado) que se
indica a la aplicación para realizar una búsqueda. El Text
propiedad proporciona acceso al texto y el
SearchButtonPressed evento indica que se ha presionado
el botón.

Documentación de la API

Código C# para esta página / página XAML con código


subyacente

Para establecer valores de las vistas


CheckBox

CheckBox permite al usuario seleccionar un valor booleano


mediante un tipo de botón que o bien se puede comprobar
o está vacío. El IsChecked propiedad es el estado de la
CheckBox y el CheckedChanged evento se desencadena
cuando cambia el estado.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Slider

Slider permite al usuario seleccionar un double valor


desde un intervalo continuo especificado con el Minimum y
Maximum propiedades.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Motor paso a paso


Stepper permite al usuario seleccionar un double valor
de un intervalo de valores incrementales especificado con el
Minimum , Maximum , y Increment propiedades.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Modificador

Switch adopta la forma de un modificador on/off para


permitir al usuario seleccionar un valor booleano. El
IsToggled propiedad es el estado del conmutador y el
Toggled evento se desencadena cuando cambia el estado.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

DatePicker

DatePicker permite al usuario seleccionar una fecha con el


selector de fecha de la plataforma. Establecer un intervalo de
fechas permitidos con el MinimumDate y MaximumDate
propiedades. El Date propiedad es la fecha seleccionada y
el DateSelected evento se desencadena cuando se cambia
dicha propiedad.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

TimePicker
TimePicker permite al usuario seleccionar una hora con el
selector de hora de la plataforma. El Time propiedad es la
hora seleccionada. Una aplicación puede supervisar los
cambios en el Time propiedad instalando un controlador
para el PropertyChanged eventos.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Vistas para editar texto


Estas dos clases se derivan de la InputView (clase), que define el Keyboard propiedad.
Entrada

Entry permite al usuario escribir y editar una sola línea de


texto. El texto está disponible como la Text propiedad y el
TextChanged y Completed los eventos se activan cuando
los cambios de texto o el usuario señales de finalización al
pulsar la tecla ENTRAR.

Use un Editor para escribir y editar varias líneas de texto.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Editor

Editor permite al usuario escribir y editar varias líneas de


texto. El texto está disponible como la Text propiedad y el
TextChanged y Completed los eventos se activan cuando
los cambios de texto o el usuario indica la finalización.

Use un Entry vista para escribir y editar una sola línea de


texto.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Vistas para indicar actividad


ActivityIndicator

ActivityIndicator usa una animación para mostrar que


la aplicación esté implicada en una actividad larga sin dar
ninguna indicación del progreso. El IsRunning propiedad
controla la animación.

Si se conoce el progreso de la actividad, use un


ProgressBar en su lugar.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

ProgressBar

ProgressBar usa una animación para mostrar que la


aplicación está progresando a través de una actividad larga.
Establecer el Progress propiedad a valores entre 0 y 1
para indicar el progreso.

Si se desconoce el progreso de la actividad, use un


ActivityIndicator en su lugar.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML con código


subyacente

Vistas que muestran colecciones


CollectionView

CollectionView muestra una lista desplazable de


elementos de datos seleccionable, utilizando las
especificaciones de diseño diferente. Su objetivo es
proporcionar una forma más flexible y una alternativa de alto
rendimiento ListView . Establecer el ItemsSource
propiedad a una colección de objetos y conjunto el
ItemTemplate propiedad a un DataTemplate objeto que
describe cómo los elementos son para dar formato. El
SelectionChanged eventos indica que se ha realizado una
selección, que está disponible como la SelectedItem
propiedad.

Guía de / ejemplo Código C# para esta página / página XAML

ListView
ListView se deriva de ItemsView y muestra una lista
desplazable de elementos de datos seleccionable. Establecer
el ItemsSource propiedad a una colección de objetos y
establezca el ItemTemplate propiedad a un
DataTemplate objeto que describe cómo los elementos son
para tener el formato. El ItemSelected eventos indica que
se ha realizado una selección, que está disponible como la
SelectedItem propiedad.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Selector

Picker muestra un elemento seleccionado de una lista de


cadenas de texto y permite al seleccionar este elemento
cuando se pulsa la vista. Establecer el Items propiedad a
una lista de cadenas, o la ItemsSource propiedad a una
colección de objetos. El SelectedIndexChanged evento se
desencadena cuando se selecciona un elemento.

La Picker muestra la lista de elementos solo cuando está


seleccionado. Use un ListView o TableView para
obtener una lista desplazable que permanece en la página.

Documentación de API / guía / ejemplo


Código C# para esta página / página XAML con código
subyacente

TableView

TableView muestra una lista de filas de tipo Cell con


encabezados opcionales y subencabezados. Establecer el
Root propiedad a un objeto de tipo TableRoot y agregue
TableSection objetos a los que TableRoot . Cada
TableSection es una colección de Cell objetos.

Documentación de API / guía / ejemplo

Código C# para esta página / página XAML

Vínculos relacionados
Ejemplo de Xamarin.Forms FormsGallery
Xamarin.Forms Samples (Ejemplos de Xamarin.Forms)
Documentación de la API de Xamarin.Forms
Celdas de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Las celdas de Xamarin.Forms pueden agregarse a ListView y TableViews.
Un celda es un elemento especializado utilizado para los elementos de una tabla y se describe cómo se debe
representar cada elemento en una lista. El Cell clase se deriva de Element , desde el que VisualElement
también se deriva. Una celda no es un elemento visual; en realidad es una plantilla para crear un elemento visual.
Cell se utiliza exclusivamente con ListView y TableView controles. Para obtener información sobre cómo usar
y personalizar celdas, consulte el ListView y TableView documentación.

Celdas
Xamarin.Forms es compatible con los siguientes tipos de celda:
TextCell

Un TextCell muestra uno o dos cadenas de texto.


Establecer el Text propiedad y, opcionalmente, el Detail
propiedad a estas cadenas de texto.

Documentación de API / guía

Código C# para esta página / página XAML

ImageCell

El ImageCell muestra la misma información que TextCell


pero incluye un mapa de bits que establece con el Source
propiedad.

Documentación de API / guía

Código C# para esta página / página XAML


SwitchCell

El SwitchCell contiene texto establecido con el Text


propiedad y un modificador on/off se establecen inicialmente
con el valor booleano On propiedad. Controlar la
OnChanged eventos para recibir una notificación cuando la
On los cambios de propiedad.

Documentación de API / guía

Código C# para esta página / página XAML

EntryCell

El EntryCell define un Label propiedad que identifica la


celda y una sola línea de texto editable en el Text
propiedad. Controlar la Completed eventos para recibir una
notificación cuando el usuario ha completado la entrada de
texto.

Documentación de API / guía

Código C# para esta página / página XAML

Vínculos relacionados
Ejemplo de Xamarin.Forms FormsGallery
Xamarin.Forms Samples (Ejemplos de Xamarin.Forms)
Documentación de la API de Xamarin.Forms
Xamarin.Forms DataSourceControl
11/07/2019 • 3 minutes to read • Edit Online

IMPORTANT
DataSourceControl requiere un Xamarin.Forms tema referencia a representar.

Xamarin.Forms DataSourceControl se anunciaron en Evolve 2016 y están disponible como una vista previa para
clientes probar y proporcionar comentarios.
DataSourceControl proporciona una API para enlazar de forma rápida y sencilla un origen de datos a vistas
precompiladas. Elementos de lista y páginas de detalle representarán los datos de forma automática y se pueden
personalizar utilizando temas.
Para ver cómo funciona la demostración del discurso de apertura evolucionan, consulte el Guía de introducción.

Introducción
Orígenes de datos y las páginas de datos asociadas permiten a los desarrolladores rápida y fácilmente consumir
un origen de datos admitidos y para representarlo mediante integradas se puede personalizar la interfaz de
usuario de scaffolding que con los temas.
DataSourceControl se agrega a una aplicación de Xamarin.Forms mediante la inclusión de la
Xamarin.Forms.Pages paquete Nuget.
Orígenes de datos
La versión preliminar tiene algunos orígenes de datos creada previamente disponibles para su uso:
JsonDataSource
AzureDataSource (separe Nuget)
AzureEasyTableDataSource (separe Nuget)
Consulte la Guía de introducción para obtener un ejemplo con un JsonDataSource .
Las páginas y controles
Las siguientes páginas y controles se incluyen para permitir el enlace sencillo para los orígenes de datos
proporcionado:
ListDataPage : vea la Introducción al ejemplo.
DirectoryPage : una lista con agrupación habilitada.
PersonDetailPage : un elemento de datos únicos en la vista personalizada para un tipo de objeto específico
(una entrada de contacto).
DataView : una vista para exponer datos procedentes del origen de forma genérica.
CardView : una con el estilo de vista que contiene una imagen, el texto del título y el texto de descripción.
HeroImage : una vista de la representación de imágenes.
ListItem : un pregeneradas vista con un diseño similar a los elementos de lista de Android y iOS nativo.
Consulte la DataSourceControl controla referencia para obtener ejemplos.
En segundo plano
Un origen de datos de Xamarin.Forms se adhiere a la IDataSource interfaz.
La infraestructura de Xamarin.Forms interactúa con un origen de datos a través de las siguientes propiedades:
Data : una lista de solo lectura de elementos de datos que se pueden mostrar.
IsLoading : un valor booleano que indica si los datos se cargan y están disponibles para la representación.
[key] – un indizador para recuperar elementos.

Hay dos métodos MaskKey y UnmaskKey que puede utilizarse para ocultar (o mostrar) las propiedades de
elementos de datos (es decir impida que se procesa). La clave corresponde a la una propiedad con nombre en el
objeto de elemento de datos.
Introducción a DataSourceControl
12/07/2019 • 7 minutes to read • Edit Online

descargar el ejemplo

IMPORTANT
DataSourceControl requiere un Xamarin.Forms tema referencia a representar.

Para empezar a crear una página sencilla controlada por datos mediante la versión preliminar de
DataSourceControl, siga estos pasos. Este usa demostración crea un estilo codificado de forma rígida ("eventos")
en la vista previa que sólo funciona con el formato específico de JSON en el código.

1. Agregar paquetes de NuGet


Agregue estos paquetes de Nuget a los proyectos de biblioteca y aplicación Xamarin.Forms .NET Standard:
Xamarin.Forms.Pages
Xamarin.Forms.Theme.Base
Una implementación de tema Nuget (p ej. Xamarin.Forms.Theme.Light)
2. Agregar referencia de tema
En el App.xaml , agregue un personalizado xmlns:mytheme para el tema y asegúrese de que el tema se combina
con el diccionario de recursos de la aplicación:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:mytheme="clr-namespace:Xamarin.Forms.Themes;assembly=Xamarin.Forms.Theme.Light"
x:Class="DataPagesDemo.App">
<Application.Resources>
<ResourceDictionary MergedWith="mytheme:LightThemeResources" />
</Application.Resources>
</Application>

IMPORTANT
También debe seguir los pasos necesarios para cargar ensamblados de tema (abajo) agregando código estereotipado a iOS
AppDelegate y Android MainActivity . Se mejorará en versión preliminar futura.

3. Agregue una página XAML


Agregar una nueva página XAML a la aplicación de Xamarin.Forms y cambie la clase base desde ContentPage a
Xamarin.Forms.Pages.ListDataPage . Esto debe hacerse en C# y el XAML:

Archivo de C#

public partial class SessionDataPage : Xamarin.Forms.Pages.ListDataPage // was ContentPage


{
public SessionDataPage ()
{
InitializeComponent ();
}
}

Archivo XAML
Además de cambiar el elemento raíz para <p:ListDataPage> el espacio de nombres personalizado xmlns:p
también se debe agregar:

<?xml version="1.0" encoding="UTF-8"?>


<p:ListDataPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:p="clr-namespace:Xamarin.Forms.Pages;assembly=Xamarin.Forms.Pages"
x:Class="DataPagesDemo.SessionDataPage">

<ContentPage.Content></ContentPage.Content>

</p:ListDataPage>

Subclase de la aplicación
Cambiar el App constructor de clase para que la MainPage está establecido en un NavigationPage que contiene
el nuevo SessionDataPage . Una página de navegación debe usarse.

MainPage = new NavigationPage (new SessionDataPage ());


3. Agregar el origen de datos
Eliminar el Content elemento y reemplazarlo con un p:ListDataPage.DataSource para rellenar la página con
datos. En el ejemplo siguiente Json remoto se está cargando el archivo de datos desde una dirección URL.

NOTE
La versión preliminar requiere un StyleClass atributo para proporcionar sugerencias de representación para el origen de
datos. El StyleClass="Events" hace referencia a un diseño que está predefinido en la vista previa y contiene los estilos
codificado de forma rígida para que coincida con el origen de datos JSON que se va a usar.

<?xml version="1.0" encoding="UTF-8"?>


<p:ListDataPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:p="clr-namespace:Xamarin.Forms.Pages;assembly=Xamarin.Forms.Pages"
x:Class="DataPagesDemo.SessionDataPage"
Title="Sessions" StyleClass="Events">

<p:ListDataPage.DataSource>
<p:JsonDataSource Source="http://demo3143189.mockable.io/sessions" />
</p:ListDataPage.DataSource>

</p:ListDataPage>

Datos JSON
Un ejemplo de los datos JSON desde el origen demostración se muestra a continuación:

[{
"end": "2016-04-27T18:00:00Z",
"start": "2016-04-27T17:15:00Z",
"abstract": "The new Apple TV has been released, and YOU can be one of the first developers to write apps
for it. To make things even better, you can build these apps in C#! This session will introduce the basics of
how to create a tvOS app with Xamarin, including: differences between tvOS and iOS APIs, TV user interface
best practices, responding to user input, as well as the capabilities and limitations of building apps for a
television. Grab some popcorn—this is going to be good!",
"title": "As Seen On TV … Bringing C# to the Living Room",
"presenter": "Matthew Soucoup",
"biography": "Matthew is a Xamarin MVP and Certified Xamarin Developer from Madison, WI. He founded his
company Code Mill Technologies and started the Madison Mobile .Net Developers Group. Matt regularly speaks
on .Net and Xamarin development at user groups, code camps and conferences throughout the Midwest. Matt
gardens hot peppers, rides bikes, and loves Wisconsin micro-brews and cheese.",
"image": "http://i.imgur.com/ASj60DP.jpg",
"avatar": "http://i.imgur.com/ASj60DP.jpg",
"room": "Crick"
}]

4. ¡Ejecución!
Deben dar como resultado de los pasos anteriores en una página de datos de trabajo:
Esto funciona porque el estilo pregenerado "Eventos" existe en el paquete Nuget del tema claro y tiene los
estilos definidos que coinciden con el origen de datos (p ej. "title", "image", "presentador").
Los "eventos" StyleClass está diseñado para mostrar el ListDataPage control con un personalizado CardView
control que es definido en Xamarin.Forms.Pages. El CardView control tiene tres propiedades: ImageSource , Text ,
y Detail . El tema está codificado para enlazar tres campos del origen de datos (desde el archivo JSON ) a estas
propiedades para mostrarlas.

5. Personalizar
El estilo heredado puede invalidarse especificando una plantilla y usar enlaces de orígenes de datos. El XAML
siguiente declara una plantilla personalizada para cada fila con el nuevo ListItemControl y
{p:DataSourceBinding} sintaxis que se incluye en el Xamarin.Forms.Pages Nuget:

<p:ListDataPage.DefaultItemTemplate>
<DataTemplate>
<ViewCell>
<p:ListItemControl
Title="{p:DataSourceBinding title}"
Detail="{p:DataSourceBinding room}"
ImageSource="{p:DataSourceBinding image}"
DataSource="{Binding Value}"
HeightRequest="90"
>
</p:ListItemControl>
</ViewCell>
</DataTemplate>
</p:ListDataPage.DefaultItemTemplate>

Proporcionando un DataTemplate este código invalida el StyleClass y en su lugar, usa el diseño predeterminado
para un ListItemControl .
Los programadores que prefieren C# o XAML puede crear datos de origen enlaces demasiado (Recuerde incluir
un using Xamarin.Forms.Pages; instrucción):

SetBinding (TitleProperty, new DataSourceBinding ("title"));

Es un poco más trabajo crear temas desde el principio (vea la guía temas) pero las versiones preliminares futuras
hará esto más fácil de hacer.

Solución de problemas
No se pudo cargar el archivo o ensamblado
'Xamarin.Forms.Theme.Light' o uno de sus dependencias
En la versión preliminar, los temas no pueda cargar en tiempo de ejecución. Agregue el código que se muestra a
continuación, en los proyectos correspondientes para corregir este error.
iOS
En el AppDelegate.cs agregue las líneas siguientes después de LoadApplication

var x = typeof(Xamarin.Forms.Themes.DarkThemeResources);
x = typeof(Xamarin.Forms.Themes.LightThemeResources);
x = typeof(Xamarin.Forms.Themes.iOS.UnderlineEffect);

Android
En el MainActivity.cs agregue las líneas siguientes después de LoadApplication
var x = typeof(Xamarin.Forms.Themes.DarkThemeResources);
x = typeof(Xamarin.Forms.Themes.LightThemeResources);
x = typeof(Xamarin.Forms.Themes.Android.UnderlineEffect);

Vínculos relacionados
Ejemplo de DataPagesDemo
Xamarin.Forms DatePicker
11/07/2019 • 9 minutes to read • Edit Online

descargar el ejemplo
Una vista de Xamarin.Forms que permite al usuario seleccionar una fecha.
Xamarin.Forms DatePicker invoca el control de selector de fecha de la plataforma y permite al usuario
seleccionar una fecha. DatePicker define las propiedades de ocho:
MinimumDate de tipo , que de forma predeterminada el primer día del año 1900.
DateTime
MaximumDate de tipoDateTime , que el valor predeterminado es el último día del año 2100.
Date de tipo DateTime , la fecha seleccionada, cuyo valor predeterminado es el valor DateTime.Today .
Format de tipo string , un estándar o personalizado .NET formato de cadena, cuyo valor predeterminado es
"D", patrón de fecha largo.
TextColor de tipo Color , el color utilizado para mostrar la fecha seleccionada, cuyo valor predeterminado es
Color.Default .
FontAttributes de tipo FontAttributes , cuyo valor predeterminado es FontAtributes.None .
FontFamily de tipo string , cuyo valor predeterminado es null .
FontSize de tipo double , que de forma predeterminada va de -1,0.

El DatePicker se activa un DateSelected eventos cuando el usuario selecciona una fecha.

WARNING
Al establecer MinimumDate y MaximumDate , asegúrese de que MinimumDate siempre es menor o igual que
MaximumDate . En caso contrario, DatePicker , se producirá una excepción.

Internamente, el DatePicker garantiza que Date entre MinimumDate y MaximumDate , ambos inclusive. Si
MinimumDate o MaximumDate se establece para que Date no está entre ellos, DatePicker ajustará el valor de
Date .

Todas las propiedades de ocho están respaldadas por BindableProperty objetos, lo que significa que puede
cambiar el estilo y las propiedades pueden ser destinos de enlaces de datos. El Date propiedad tiene un modo de
enlace predeterminada de BindingMode.TwoWay , lo que significa que puede ser un destino de enlace de datos en
una aplicación que utiliza el Model-View -ViewModel (MVVM ) arquitectura.

Inicializando las propiedades de fecha y hora


En el código, se puede inicializar el MinimumDate , MaximumDate ,y Date propiedades en valores de tipo DateTime :

DatePicker datePicker = new DatePicker


{
MinimumDate = new DateTime(2018, 1, 1),
MaximumDate = new DateTime(2018, 12, 31),
Date = new DateTime(2018, 6, 21)
};

Cuando un DateTime se expresa en XAML, el analizador XAML usa la DateTime.Parse método con un
CultureInfo.InvariantCulture argumento para convertir la cadena en un DateTime valor. Se deben especificar las
fechas en un formato preciso: dos dígitos meses, días de dos dígitos y años de cuatro dígitos separados por barras
diagonales:

<DatePicker MinimumDate="01/01/2018"
MaximumDate="12/31/2018"
Date="06/21/2018" />

Si el BindingContext propiedad de DatePicker está establecida en una instancia de un modelo de vista que
contiene las propiedades de tipo DateTime denominado MinDate , MaxDate , y SelectedDate (por ejemplo), puede
crear una instancia el DatePicker similar a éste :

<DatePicker MinimumDate="{Binding MinDate}"


MaximumDate="{Binding MaxDate}"
Date="{Binding SelectedDate}" />

En este ejemplo, las tres propiedades se inicializan en las propiedades correspondientes en el modelo de vista.
Dado que el Date propiedad tiene un modo de enlace de TwoWay , cualquier nueva fecha que el usuario
selecciona automáticamente se refleja en el modelo de vista.
Si el DatePicker no contiene un enlace en su Date propiedad, una aplicación debe adjuntar un controlador para
el DateSelected evento para ser informado cuando el usuario selecciona una nueva fecha.
Para obtener información sobre cómo establecer las propiedades de fuente, consulte fuentes.

DatePicker y diseño
Es posible usar una opción de diseño horizontal sin restricciones, como Center , Start ,o End con DatePicker :

<DatePicker ···
HorizontalOptions="Center"
··· />

Sin embargo, esto no se recomienda. Según la configuración de la Format propiedad, selecciona las fechas
pueden requerir los anchos de pantalla diferente. Por ejemplo, hace que la cadena de formato "D" DateTime
mostrar las fechas en un formato largo y "Miércoles, 12 de septiembre de 2018" requiere un mayor ancho de
pantalla que "Viernes, 4 de mayo de 2018". Según la plataforma, esta diferencia puede provocar la DateTime vista
para cambiar el ancho de diseño o en la presentación se trunque.

TIP
Es mejor usar el valor predeterminado HorizontalOptions de Fill con DatePicker y no se debe utilizar un ancho de
Auto al poner DatePicker en un Grid celda.

DatePicker en una aplicación


El DaysBetweenDates ejemplo incluye dos DatePicker vistas en su página. Se pueden usar para seleccionar
dos fechas, y el sistema calcula el número de días entre esas fechas. El programa no cambia la configuración de la
MinimumDate y MaximumDate propiedades, por lo que las dos fechas deben estar entre 1900 y 2100.

Este es el archivo XAML:


<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DaysBetweenDates"
x:Class="DaysBetweenDates.MainPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
</OnPlatform>
</ContentPage.Padding>

<StackLayout Margin="10">
<Label Text="Days Between Dates"
Style="{DynamicResource TitleStyle}"
Margin="0, 20"
HorizontalTextAlignment="Center" />

<Label Text="Start Date:" />

<DatePicker x:Name="startDatePicker"
Format="D"
Margin="30, 0, 0, 30"
DateSelected="OnDateSelected" />

<Label Text="End Date:" />

<DatePicker x:Name="endDatePicker"
MinimumDate="{Binding Source={x:Reference startDatePicker},
Path=Date}"
Format="D"
Margin="30, 0, 0, 30"
DateSelected="OnDateSelected" />

<StackLayout Orientation="Horizontal"
Margin="0, 0, 0, 30">
<Label Text="Include both days in total: "
VerticalOptions="Center" />
<Switch x:Name="includeSwitch"
Toggled="OnSwitchToggled" />
</StackLayout>

<Label x:Name="resultLabel"
FontAttributes="Bold"
HorizontalTextAlignment="Center" />

</StackLayout>
</ContentPage>

Cada DatePicker se asigna un Format propiedad de "D" para un formato de fecha larga. Observe también que el
endDatePicker objeto tiene un enlace que tenga como destino su MinimumDate propiedad. El origen de enlace está
seleccionado Date propiedad de la startDatePicker objeto. Esto garantiza que la fecha de finalización es siempre
posterior o igual que la fecha de inicio. Además de los dos DatePicker objetos, un Switch tiene la etiqueta
"Incluyen dos días en total".
Los dos DatePicker las vistas tienen controladores asociados a la DateSelected eventos y el Switch ha
adjuntado un controlador a su Toggled eventos. Estos controladores de eventos están en el archivo de código
subyacente y desencadenan un nuevo cálculo de los días entre las dos fechas:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}

void OnDateSelected(object sender, DateChangedEventArgs args)


{
Recalculate();
}

void OnSwitchToggled(object sender, ToggledEventArgs args)


{
Recalculate();
}

void Recalculate()
{
TimeSpan timeSpan = endDatePicker.Date - startDatePicker.Date +
(includeSwitch.IsToggled ? TimeSpan.FromDays(1) : TimeSpan.Zero);

resultLabel.Text = String.Format("{0} day{1} between dates",


timeSpan.Days, timeSpan.Days == 1 ? "" : "s");
}
}

Cuando se ejecuta el ejemplo primero, ambos DatePicker vistas se inicializan en la fecha de hoy. Captura de
pantalla siguiente muestra el programa que se ejecuta en la plataforma Universal de Windows, iOS y Android:

Al puntear en cualquiera de los DatePicker muestra invoca el selector de fecha de la plataforma. Las plataformas
de implementan este selector de fecha de maneras muy diferentes, pero cada enfoque es familiar para los
usuarios de esa plataforma:
TIP
En Android, el DatePicker cuadro de diálogo se puede personalizar invalidando el CreateDatePickerDialog método en
un representador personalizado. Esto permite, por ejemplo, botones adicionales que se agregan al cuadro de diálogo.

Una vez seleccionadas las dos fechas, la aplicación muestra el número de días entre esas fechas:

Vínculos relacionados
Ejemplo de DaysBetweenDates
DatePicker API
Mostrar elementos emergentes
11/07/2019 • 3 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms proporciona dos elementos de la interfaz de usuario similares a elementos emergentes: una alerta
y una hoja de acción. En este artículo muestra cómo utilizar la hoja de las API de alerta y la acción para mostrar
cuadros de diálogo que pedir a los usuarios preguntas sencillas y guiar a los usuarios a través de tareas.
Mostrar una alerta o pedir al usuario que seleccione una opción es una tarea común de la interfaz de usuario.
Xamarin.Forms tiene dos métodos en la clase Page para interactuar con el usuario mediante un elemento
emergente: DisplayAlert y DisplayActionSheet . Se representan con controles nativos adecuados en cada
plataforma.

Visualización de una alerta


Todas las plataformas compatibles con Xamarin.Forms tienen una ventana emergente modal para mostrar alertas
al usuario o realizar preguntas sencillas. Para mostrar estas alertas en Xamarin.Forms, use el método
DisplayAlert en cualquier elemento Page . En la siguiente línea de código, se muestra un mensaje sencillo al
usuario:

DisplayAlert ("Alert", "You have been alerted", "OK");

En este ejemplo, no se recopila información del usuario. La alerta se muestra modalmente y, después de cerrarla, el
usuario sigue interactuando con la aplicación.
El método DisplayAlert también se puede usar para capturar la respuesta de un usuario mediante la presentación
de dos botones y la devolución de un elemento boolean . Para obtener una respuesta de una alerta, agregue texto
para los dos botones y use await para esperar el método. Cuando el usuario seleccione una de las opciones, se
devolverá la respuesta al código. Observe las palabras clave async y await en el código de ejemplo siguiente:
async void OnAlertYesNoClicked (object sender, EventArgs e)
{
bool answer = await DisplayAlert ("Question?", "Would you like to play a game", "Yes", "No");
Debug.WriteLine ("Answer: " + answer);
}

Guiar a los usuarios a través de tareas


El elemento UIActionSheet es un elemento de interfaz de usuario común en iOS. El método DisplayActionSheet
de Xamarin.Forms le permite incluir este control en aplicaciones multiplataforma, lo que representa alternativas
nativas en Android y UWP.
Para mostrar una hoja de acción, use await DisplayActionSheet en cualquier elemento Page y pase el mensaje y
las etiquetas de botón como cadenas. Este método devuelve la etiqueta de cadena del botón en el que hizo clic el
usuario. A continuación, se muestra un ejemplo sencillo:

async void OnActionSheetSimpleClicked (object sender, EventArgs e)


{
string action = await DisplayActionSheet ("ActionSheet: Send to?", "Cancel", null, "Email", "Twitter",
"Facebook");
Debug.WriteLine ("Action: " + action);
}

El botón destroy se representa de forma distinta y puede dejarse como null , o bien se puede especificar como el
tercer parámetro de cadena. En el ejemplo siguiente, se usa el botón destroy :

async void OnActionSheetCancelDeleteClicked (object sender, EventArgs e)


{
string action = await DisplayActionSheet ("ActionSheet: SavePhoto?", "Cancel", "Delete", "Photo Roll",
"Email");
Debug.WriteLine ("Action: " + action);
}
Vínculos relacionados
PopupsSample
Gráficos de SkiaSharp en Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

descargar el ejemplo
Uso SkiaSharp para gráficos 2D en las aplicaciones de Xamarin.Forms
SkiaSharp es un sistema de gráficos 2D para .NET y C# funciona con el motor de gráficos de Skia de código
abierto que se usa habitualmente en productos de Google. Puede usar SkiaSharp en sus aplicaciones de
Xamarin.Forms para dibujar texto, mapas de bits y gráficos vectoriales en 2D. Consulte la plano 2D guía para
obtener información general sobre la biblioteca de SkiaSharp y algunos otros tutoriales.
En esta guía se da por supuesto que está familiarizado con la programación de Xamarin.Forms.

Webinar: SkiaSharp para Xamarin.Forms

Pasos preliminares de SkiaSharp


SkiaSharp para Xamarin.Forms está empaquetado como un paquete de NuGet. Después de crear una solución de
Xamarin.Forms en Visual Studio o Visual Studio para Mac, puede usar el Administrador de paquetes de NuGet
para buscar el SkiaSharp.Views.Forms empaquetar y agregarlo a la solución. Si activa la referencias sección de
cada proyecto después de agregar SkiaSharp, puede ver que varios SkiaSharp bibliotecas se han agregado a cada
uno de los proyectos de la solución.
Si la aplicación de Xamarin.Forms tiene como destino iOS, use la página de propiedades de proyecto para cambiar
el destino de implementación mínimo IOS 8.0.
En cualquier página de C# que utiliza SkiaSharp desea incluir un using la directiva para el SkiaSharp espacio de
nombres, que abarca todas las clases de SkiaSharp, estructuras y enumeraciones que va a usar en los gráficos la
programación. También conviene un using la directiva para el SkiaSharp.Views.Forms espacio de nombres para las
clases específicas de Xamarin.Forms. Esto es una cantidad menor espacio de nombres, con la clase más importante
que es SKCanvasView . Esta clase se deriva de Xamarin.Forms View y hospede su salida gráfica de SkiaSharp.

IMPORTANT
El SkiaSharp.Views.Forms espacio de nombres también contiene un SKGLView clase que derive de View pero usa
OpenGL para representar gráficos. Para fines de simplicidad, esta guía restringe a sí mismo a SKCanvasView , pero al usar
SKGLView en su lugar, es bastante similar.

Conceptos básicos de dibujo de SkiaSharp


Algunas de las cifras más sencillas de gráficos que puede dibujar con SkiaSharp son círculos, elipses y rectángulos.
Mostrar estas cifras, aprenderá acerca de las coordenadas de SkiaSharp, tamaños y colores. La presentación de
texto y mapas de bits es más compleja, pero estos artículos también presentan estas técnicas.

Trazados y líneas de SkiaSharp


Una ruta de acceso de gráficos es una serie de líneas rectas conectadas y curvas. Se trazan las rutas de acceso,
rellenado, o ambos. En este artículo abarca muchos aspectos del dibujo de líneas, incluidos los extremos de trazo y
uniones y guiones y las líneas de puntos, pero no permite las geometrías de curva.

Transformaciones de SkiaSharp
Las transformaciones permiten que los objetos gráficos uniformemente traducido, escalar, girar, o sesgar.En este
artículo también muestra cómo puede utilizar una matriz de transformación de 3 por 3 estándar para crear
transformaciones no afines y aplicar transformaciones a rutas de acceso.

Trazados y curvas de SkiaSharp


La exploración de las rutas de acceso se continúa con la adición de curvas para los objetos de una ruta de acceso y
aprovechando otras características eficaces de la ruta de acceso. Verá cómo puede especificar una ruta de acceso
completa en una cadena de texto conciso, cómo usar los efectos de la ruta de acceso y cómo profundizar en los
aspectos internos de ruta de acceso.

Mapas de bits de SkiaSharp


Los mapas de bits son matrices rectangulares de bits que corresponden a los píxeles de un dispositivo de pantalla.
Esta serie de artículos muestra cómo cargar, guardar, mostrar, crear, dibuje en, animar y tener acceso a los bits de
SkiaSharp mapas de bits.

Efectos de SkiaSharp
Los efectos son propiedades que cambian la visualización de gráficos, incluidos los degradados lineales y circulares
normal, disposición en mosaico de mapa de bits, blend modos de desenfoque y otros usuarios.

Vínculos relacionados
API de SkiaSharp
SkiaSharpFormsDemos (ejemplo)
SkiaSharp con Xamarin.Forms seminario Web (vídeo)
Imágenes en Xamarin.Forms
11/07/2019 • 24 minutes to read • Edit Online

descargar el ejemplo
Las imágenes se pueden compartir entre plataformas con Xamarin.Forms, se pueden cargar específicamente
para cada plataforma, o se pueden descargar para su presentación.
Las imágenes son una parte fundamental de navegación de la aplicación, la facilidad de uso y la personalización
de marca. Las aplicaciones de Xamarin.Forms deben ser capaz de compartir imágenes en todas las plataformas,
pero también puede mostrar diferentes imágenes en cada plataforma.
También se requieren para iconos y pantallas de presentación; las imágenes específicas de plataforma estas
deben configurarse según el acuerdo con la plataforma.

Mostrar imágenes
Xamarin.Forms usa el Image vista para mostrar imágenes en una página. Tiene dos propiedades importantes:
Source -Un ImageSource instancia, archivo, Uri o recurso, que establece la imagen para mostrar.
Aspect -Cómo cambiar el tamaño de la imagen dentro de los límites que se muestra dentro de (ya sea para
stretch, recortar o panorámica).
ImageSource las instancias pueden obtenerse mediante métodos estáticos para cada tipo de origen de la imagen:
FromFile -Requiere un nombre de archivo o ruta del archivo que se puede resolver en cada plataforma.
FromUri -Requiere un objeto Uri, por ejemplo. new Uri("http://server.com/image.jpg") .
FromResource -Requiere un identificador de recurso a un archivo de imagen incrustado en la aplicación o el
proyecto de biblioteca de .NET Standard con un EmbeddedResource: acción de compilación.
FromStream -Requiere un flujo que proporciona los datos de imagen.

El Aspect propiedad determina cómo la imagen se ajustarán el área de presentación:


Fill -Ajusta la imagen para completamente y exactamente rellenar el área de presentación. Esto puede dar
lugar a que la imagen se distorsiona.
AspectFill -Recorta la imagen para que rellene el área de presentación conservando el aspecto (ie. ninguna
distorsión).
AspectFit -Letterbox la imagen (si es necesario) para que quepa la imagen completa en el área de
presentación, con espacio en blanco que se agrega a la parte superior o inferior o lados dependiendo de si la
imagen es ancho o alto.
Se pueden cargar imágenes desde un archivo local, un recurso incrustado, o descargado. Además, pueden
mostrar iconos de la fuente del Image vista mediante la especificación de los datos del icono de fuente en un
FontImageSource objeto. Para obtener más información, consulte mostrar iconos de fuente en el fuentes guía.

Imágenes locales
Archivos de imagen se pueden agregar a cada proyecto de aplicación y hacer referencia desde el código de
Xamarin.Forms compartido. Este método de distribución de imágenes es necesaria cuando las imágenes son
específicos de la plataforma, como al usar distintas resoluciones en distintas plataformas o ligeramente distintos
diseños.
Para usar una imagen única en todas las aplicaciones, se debe usar el mismo nombre de archivo en todas las
plataformas, y debe ser un nombre de recurso de Android válido (es decir. se permiten solo letras minúsculas,
números, el carácter de subrayado y el período).
iOS : la preferida en forma de administrar y admitir imágenes, ya que es usar iOS 9 conjuntos de
imágenes del catálogo de activos, que debe contener todas las versiones de una imagen que son
necesarias para admitir varios dispositivos y factores de escala de un aplicación. Para obtener más
información, consulte agregar imágenes a un conjunto de imágenes de catálogo de activos.
Android -colocar imágenes en el recursos/drawable directorio con acción de compilación:
AndroidResource. También se pueden proporcionar versiones de alta y baja resolución de una imagen (en
un nombre apropiado recursos subdirectorios como drawable ldpi, drawable-hdpiy drawable xhdpi).
Plataforma universal de Windows (UWP ) -colocar imágenes en el directorio raíz de la aplicación con
acción de compilación: Contenido.

IMPORTANT
Antes de iOS 9, las imágenes normalmente se colocaron en el recursos carpeta con acción de compilación:
BundleResource. Sin embargo, este método para trabajar con imágenes en una aplicación de iOS en desuso por Apple.
Para obtener más información, consulte tamaños de imagen y nombres de archivo.

Adhesión a estas reglas de nomenclatura de los archivos y la colocación permite el XAML cargar y mostrar la
imagen en todas las plataformas siguiente:

<Image Source="waterfront.jpg" />

El código de C# equivalente es como sigue:

var image = new Image { Source = "waterfront.jpg" };

Las capturas de pantalla siguientes muestran el resultado de mostrar una imagen local en cada plataforma:

Para obtener más flexibilidad el Device.RuntimePlatform propiedad puede utilizarse para seleccionar un archivo
de imagen diferente o la ruta de acceso para algunas o todas las plataformas, como se muestra en este ejemplo
de código:

image.Source = Device.RuntimePlatform == Device.Android ? ImageSource.FromFile("waterfront.jpg") :


ImageSource.FromFile("Images/waterfront.jpg");
IMPORTANT
Para usar el mismo nombre de archivo de imagen en todas las plataformas, el nombre debe ser válido en todas las
plataformas. Recursos drawable Android tiene restricciones de nomenclatura: se permiten solo letras minúsculas, números,
caracteres de subrayado y período: y para la compatibilidad multiplataforma esto debe seguirse en todas las plataformas
demasiado. El nombre de archivo de ejemplo waterfront.png sigue las reglas, pero algunos ejemplos de nombres de
archivo no válidos incluyen "agua front.png", "WaterFront.png", "water-front.png" y "wåterfront.png".

Resoluciones nativas (Retina y valores altos de PPP)


iOS, Android y UWP incluyen compatibilidad con resoluciones de imagen diferente, donde el sistema operativo
elige la imagen adecuada en tiempo de ejecución según las capacidades del dispositivo. Xamarin.Forms usa las
API de las plataformas nativas para cargar imágenes locales, por lo que admite automáticamente las soluciones
alternativas si los archivos correctamente se denomina y ubicados en el proyecto.
El método preferido para administrar imágenes desde iOS 9 es arrastrar imágenes para cada resolución
necesaria para el conjunto de imágenes del catálogo de activos correspondiente. Para obtener más información,
consulte agregar imágenes a un conjunto de imágenes de catálogo de activos.
Antes de iOS 9, las versiones de retina de la imagen podrían colocarse en el recursos carpeta - dos y tres veces
la resolución con una @2x o @3xsufijos en el nombre de archivo antes de la extensión de archivo (p ej.
myimage@2x.png). Sin embargo, este método para trabajar con imágenes en una aplicación de iOS en desuso
por Apple. Para obtener más información, consulte tamaños de imagen y nombres de archivo.
Imágenes de Android resolución alternativo deben colocarse en directorios especialmente denominada en el
proyecto Android, como se muestra en la captura de pantalla siguiente:

Los nombres de archivo de imagen UWP puede tener el sufijo con .scale-xxx antes de la extensión de archivo,
donde xxx es el porcentaje de ajuste de escala aplicado al recurso, por ejemplo, myimage.scale 200.png. A
continuación, se pueden hacer referencia de las imágenes en el código o XAML sin el modificador de escala, por
ejemplo, simplemente myimage.png. La plataforma seleccionará la escala de activos correspondiente más
cercana en función de PPP de la pantalla actual.
Controles adicionales que se muestran las imágenes
Algunos controles tienen propiedades que se muestran una imagen, como:
Page -Cualquier tipo que derive de la página Page tiene IconImageSource y BackgroundImageSource
propiedades, que se pueden asignar un archivo, el recurso incrustado, el URI o la secuencia. En
determinadas circunstancias, como cuando un NavigationPage está mostrando un ContentPage , si es
compatible con la plataforma, se mostrará el icono.

IMPORTANT
En iOS, el Page.IconImageSource no se pueden rellenar la propiedad desde una imagen en un conjunto de
imágenes del catálogo activo. En su lugar, cargue las imágenes de icono para el Page.IconImageSource
propiedad desde un archivo, el recurso incrustado, el URI o la secuencia.

ToolbarItem -Tiene un IconImageSource propiedad que se puede establecer en una imagen que se carga
desde un archivo, el recurso incrustado, el URI o la secuencia.
ImageCell -Tiene un ImageSource recupera de la propiedad que se puede establecer en una imagen
desde un archivo, el recurso incrustado, el URI o la secuencia.

Imágenes incrustadas
También se incluyen imágenes incrustadas con una aplicación (por ejemplo, imágenes locales), pero en lugar de
tener una copia de la imagen en la estructura de archivos de la aplicación la imagen de archivo está incrustado
en el ensamblado como un recurso. Este método para distribuir las imágenes se recomienda cuando se usan
imágenes idénticas en cada plataforma y es especialmente adecuado para la creación de componentes, como la
imagen se incluye con el código.
Para incrustar una imagen en un proyecto, haga doble clic para agregar nuevos elementos y seleccione la
imagen/s que desea agregar. De forma predeterminada la imagen tendrá acción de compilación: Ninguno;
Esto se debe establecerse en acción de compilación: EmbeddedResource.
Visual Studio
Visual Studio para Mac
El acción de compilación se pueden ver y cambiar en el propiedades ventana para un archivo.
En este ejemplo es el identificador de recurso WorkingWithImages.beach.jpg. El IDE ha generado este
comportamiento predeterminado mediante la concatenación del predeterminada Namespace para este
proyecto con el nombre de archivo, con un punto (.) entre cada valor.
Si coloca las imágenes incrustadas en carpetas dentro de su proyecto, los nombres de carpeta también están
separados por puntos (.) en el identificador de recurso. Mover el beach.jpg la imagen en una carpeta
denominada MyImages daría lugar a un identificador de recurso de
WorkingWithImages.MyImages.beach.jpg
El código para cargar una imagen incrustada simplemente pasa el Id. de recurso a la ImageSource.FromResource
método tal y como se muestra a continuación:

var embeddedImage = new Image { Source = ImageSource.FromResource("WorkingWithImages.beach.jpg",


typeof(EmbeddedImages).GetTypeInfo().Assembly) };

NOTE
Para admitir mostrar imágenes incrustadas en modo de versión en la plataforma Universal de Windows, es necesario
utilizar la sobrecarga de ImageSource.FromResource que especifica el ensamblado de origen en el que se va a buscar la
imagen.
Actualmente no hay ninguna conversión implícita de los identificadores de recursos. En su lugar, debe usar
ImageSource.FromResource o new ResourceImageSource() para cargar imágenes incrustadas.

Las capturas de pantalla siguientes muestran el resultado de mostrar una imagen incrustada en cada plataforma:

Uso de XAML
Porque no hay ningún convertidor de tipos integrados de string a ResourceImageSource , estos tipos de
imágenes no se puede cargar de forma nativa por XAML. En su lugar, se puede escribir una extensión de
marcado XAML personalizada simple para cargar imágenes mediante un Id. de recurso especificado en XAML:

[ContentProperty (nameof(Source))]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }

public object ProvideValue (IServiceProvider serviceProvider)


{
if (Source == null)
{
return null;
}

// Do your translation lookup here, using whatever method you require


var imageSource = ImageSource.FromResource(Source,
typeof(ImageResourceExtension).GetTypeInfo().Assembly);

return imageSource;
}
}

NOTE
Para admitir mostrar imágenes incrustadas en modo de versión en la plataforma Universal de Windows, es necesario
utilizar la sobrecarga de ImageSource.FromResource que especifica el ensamblado de origen en el que se va a buscar la
imagen.

Para usar esta extensión agregar personalizada xmlns para el XAML, con los valores de espacio de nombres y
ensamblado correctos para el proyecto. A continuación, se puede establecer el origen de imagen con esta
sintaxis: {local:ImageResource WorkingWithImages.beach.jpg} . Un ejemplo completo de XAML se muestra a
continuación:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:WorkingWithImages;assembly=WorkingWithImages"
x:Class="WorkingWithImages.EmbeddedImagesXaml">
<StackLayout VerticalOptions="Center" HorizontalOptions="Center">
<!-- use a custom Markup Extension -->
<Image Source="{local:ImageResource WorkingWithImages.beach.jpg}" />
</StackLayout>
</ContentPage>

Solución de problemas de imágenes incrustadas


Depurar código
Dado que a veces es difícil de entender por qué no se puede cargar un recurso de imagen en particular, el
siguiente código de depuración se puede agregar temporalmente a una aplicación para ayudarle a confirmar
que los recursos están configurados correctamente. Dará como resultado conocidos todos los recursos
incrustados en el ensamblado especificado para el consola para ayudar a depurar problemas de carga de
recursos.

using System.Reflection;
// ...
// NOTE: use for debugging, not in released app code!
var assembly = typeof(EmbeddedImages).GetTypeInfo().Assembly;
foreach (var res in assembly.GetManifestResourceNames())
{
System.Diagnostics.Debug.WriteLine("found resource: " + res);
}

Imágenes incrustadas en otros proyectos


De forma predeterminada, el ImageSource.FromResource método busca solo las imágenes en el mismo
ensamblado que el código que llama el ImageSource.FromResource método. Con el código de depuración
posteriores puede determinar qué ensamblados contienen un recurso específico cambiando el typeof()
instrucción a una Type sabe que en cada ensamblado.
Sin embargo, se puede especificar el ensamblado de origen que se va a buscar una imagen incrustada como un
argumento para el ImageSource.FromResource método:

var imageSource = ImageSource.FromResource("filename.png", typeof(MyClass).GetTypeInfo().Assembly);

Descarga de imágenes
Las imágenes se pueden descargar automáticamente para su presentación, como se muestra en el XAML
siguiente:

<?xml version="1.0" encoding="utf-8" ?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WorkingWithImages.DownloadImagesXaml">
<StackLayout VerticalOptions="Center" HorizontalOptions="Center">
<Label Text="Image UriSource Xaml" />
<Image Source="https://xamarin.com/content/images/pages/forms/example-app.png" />
<Label Text="example-app.png gets downloaded from xamarin.com" />
</StackLayout>
</ContentPage>
El código de C# equivalente es como sigue:

var webImage = new Image { Source = ImageSource.FromUri(new


Uri("https://xamarin.com/content/images/pages/forms/example-app.png")) };

El ImageSource.FromUri método requiere un Uri de objetos y devuelve un nuevo UriImageSource que lee el
Uri .
También hay una conversión implícita de cadenas URI, por lo que también funcionará en el ejemplo siguiente:

webImage.Source = "https://xamarin.com/content/images/pages/forms/example-app.png";

Las capturas de pantalla siguientes muestran el resultado de mostrar una imagen remota en cada plataforma:

Almacenamiento en caché de la imagen descargada


Un UriImageSource también admite el almacenamiento en caché de imágenes descargadas, configuradas a
través de las siguientes propiedades:
CachingEnabled -Si está habilitado el almacenamiento en caché ( true de forma predeterminada).
CacheValidity -A TimeSpan que define cuánto tiempo la imagen se almacenarán localmente.

Almacenamiento en caché está habilitado de forma predeterminada y almacenará la imagen localmente durante
24 horas. Para deshabilitar el almacenamiento en caché para una imagen concreta, crear una instancia del origen
de la imagen como sigue:

image.Source = new UriImageSource { CachingEnabled = false, Uri="http://server.com/image" };

Para establecer un período de caché específica (por ejemplo, 5 días) crear una instancia del origen de la imagen
como sigue:

webImage.Source = new UriImageSource


{
Uri = new Uri("https://xamarin.com/content/images/pages/forms/example-app.png"),
CachingEnabled = true,
CacheValidity = new TimeSpan(5,0,0,0)
};

Almacenamiento en caché integrado facilita enormemente admitir escenarios como el desplazamiento de las
listas de imágenes, donde puede establecer (o enlazar) una imagen en cada celda y dejar que la memoria caché
integrada se encargue de volver a cargar la imagen cuando se desplaza por la celda en la vista.
Iconos y pantallas de presentación
Aunque no se refiere a la Image vista, los iconos de aplicación y pantallas de presentación también son un uso
importante de las imágenes en proyectos de Xamarin.Forms.
Establecer iconos y pantallas de presentación de las aplicaciones de Xamarin.Forms se realiza en cada uno de los
proyectos de aplicación. Esto significa generar correctamente un tamaño de imágenes para iOS, Android y
UWP. Estas imágenes deben ser llamadas y encuentra según los requisitos de cada las plataformas.

Iconos
Consulte la iOS trabajar con imágenes, Google iconografía, y directrices para los recursos de icono y el icono
para obtener más información sobre cómo crear estos recursos de la aplicación.
Además, pueden mostrar iconos de la fuente del Image vista mediante la especificación de los datos del icono
de fuente en un FontImageSource objeto. Para obtener más información, consulte mostrar iconos de fuente en el
fuentes guía.

Pantallas de presentación
Solo aplicaciones de iOS y UWP requieren una pantalla de presentación (también denominada una imagen de
pantalla o el valor predeterminado de inicio).
Consulte la documentación para iOS trabajar con imágenes y pantallas de presentación en el centro de
desarrollo de Windows.

Resumen
Xamarin.Forms ofrece una serie de diferentes maneras de incluir imágenes en una aplicación multiplataforma, lo
que permite para la misma imagen que se usará en las plataformas o para las imágenes específicas de la
plataforma que se especifique. Imágenes descargadas son también automáticamente en caché, automatizar un
escenario de codificación comunes.
Imágenes de pantalla de presentación y de icono de aplicación están configurados y configurado en cuanto a las
aplicaciones que no sean Xamarin.Forms - sigan las mismas instrucciones que se usa para las aplicaciones
específicas de la plataforma.

Vínculos relacionados
WorkingWithImages (ejemplo)
iOS trabajar con imágenes
Iconografía Android
Directrices para los recursos de icono y el icono
Xamarin.Forms ImageButton
11/07/2019 • 11 minutes to read • Edit Online

descargar el ejemplo
El ImageButton muestra una imagen y responde a un pulse o haga clic en que se dirige a una aplicación para
llevar a cabo una tarea determinada.
El ImageButton ver combina el Button vista y Image vista para crear un botón cuyo contenido es una imagen. El
usuario presiona el ImageButton con un dedo o hace clic en él con el mouse para dirigir la aplicación para llevar a
cabo una tarea determinada. Sin embargo, a diferencia del Button vista, la ImageButton vista no tiene ningún
concepto de texto y la apariencia del texto.

NOTE
Mientras el Button vista define un Image propiedad, que permite mostrar una imagen en el Button , esta propiedad
está pensada para utilizarse cuando se muestra un icono pequeño junto a la Button texto.

Los ejemplos de código en esta guía se toman de la FormsGallery ejemplo.

Establecer el origen de imagen


ImageButton define un Source propiedad que se debe establecer en la imagen para mostrar en el botón, con el
origen de la imagen que se va a un archivo, un URI, una secuencia o un recurso. Para obtener más información
acerca de cómo cargar imágenes de orígenes diferentes, consulte imágenes en Xamarin.Forms.
El ejemplo siguiente muestra cómo crear una instancia de un ImageButton en XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="FormsGallery.XamlExamples.ImageButtonDemoPage"
Title="ImageButton Demo">
<StackLayout>
<Label Text="ImageButton"
FontSize="50"
FontAttributes="Bold"
HorizontalOptions="Center" />

<ImageButton Source="XamarinLogo.png"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

El Source propiedad especifica la imagen que aparece en el ImageButton . En este ejemplo se establece en un
archivo local que se cargarán de cada proyecto de plataforma, lo que resulta en las capturas de pantalla siguiente:
De forma predeterminada, el ImageButton es rectangular, pero puede dar TI redondeada esquinas mediante el
CornerRadius propiedad. Para obtener más información acerca de ImageButton apariencia, consulte ImageButton
apariencia.
El ejemplo siguiente muestra cómo crear una página que es funcionalmente equivalente al ejemplo XAML
anterior, pero en su totalidad C#:

public class ImageButtonDemoPage : ContentPage


{
public ImageButtonDemoPage()
{
Label header = new Label
{
Text = "ImageButton",
FontSize = 50,
FontAttributes = FontAttributes.Bold,
HorizontalOptions = LayoutOptions.Center
};

ImageButton imageButton = new ImageButton


{
Source = "XamarinLogo.png",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};

// Build the page.


Title = "ImageButton Demo";
Content = new StackLayout
{
Children = { header, imageButton }
};
}
}

Control ImageButton hace clic en


ImageButton define un Clicked evento que se desencadena cuando el usuario pulsa el ImageButton con un
puntero dedo o el mouse. El evento se desencadena cuando se suelta el botón dedo o el mouse de la superficie de
la ImageButton . El ImageButton debe tener su IsEnabled propiedad establecida en true para responder a las
pulsaciones.
El ejemplo siguiente muestra cómo crear una instancia de un ImageButton en XAML y controle su Clicked
eventos:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="FormsGallery.XamlExamples.ImageButtonDemoPage"
Title="ImageButton Demo">
<StackLayout>
<Label Text="ImageButton"
FontSize="50"
FontAttributes="Bold"
HorizontalOptions="Center" />

<ImageButton Source="XamarinLogo.png"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Clicked="OnImageButtonClicked" />

<Label x:Name="label"
Text="0 ImageButton clicks"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

El Clicked evento está establecido en un controlador de eventos denominado OnImageButtonClicked que se


encuentra en el archivo de código subyacente:

public partial class ImageButtonDemoPage : ContentPage


{
int clickTotal;

public ImageButtonDemoPage()
{
InitializeComponent();
}

void OnImageButtonClicked(object sender, EventArgs e)


{
clickTotal += 1;
label.Text = $"{clickTotal} ImageButton click{(clickTotal == 1 ? "" : "s")}";
}
}

Cuando el ImageButton se pulsa, el OnImageButtonClicked método se ejecuta. El sender argumento es el


ImageButton responsable de este evento. Se puede usar para tener acceso a la ImageButton objeto, o para
distinguir entre varias ImageButton objetos que comparten el mismo Clicked eventos.
Esta particular Clicked controlador incrementa un contador y muestra el valor del contador en un Label :
El ejemplo siguiente muestra cómo crear una página que es funcionalmente equivalente al ejemplo XAML
anterior, pero en su totalidad C#:
public class ImageButtonDemoPage : ContentPage
{
Label label;
int clickTotal = 0;

public ImageButtonDemoPage()
{
Label header = new Label
{
Text = "ImageButton",
FontSize = 50,
FontAttributes = FontAttributes.Bold,
HorizontalOptions = LayoutOptions.Center
};

ImageButton imageButton = new ImageButton


{
Source = "XamarinLogo.png",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};
imageButton.Clicked += OnImageButtonClicked;

label = new Label


{
Text = "0 ImageButton clicks",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};

// Build the page.


Title = "ImageButton Demo";
Content = new StackLayout
{
Children =
{
header,
imageButton,
label
}
};
}

void OnImageButtonClicked(object sender, EventArgs e)


{
clickTotal += 1;
label.Text = $"{clickTotal} ImageButton click{(clickTotal == 1 ? "" : "s")}";
}
}

Deshabilitar el ImageButton
A veces, una aplicación está en un estado determinado donde un determinado ImageButton haga clic en no es
una operación válida. En esos casos, el ImageButton debe deshabilitarse estableciendo su IsEnabled propiedad
false .

Mediante la interfaz de comandos


Es posible que una aplicación responda a ImageButton derivaciones sin control el Clicked eventos. El
ImageButton implementa un mecanismo de notificación alternativo denominado el comando o comandos
interfaz. Consta de dos propiedades:
Command de tipo ICommand, una interfaz definida en el System.Windows.Input espacio de nombres.
CommandParameter propiedad de tipo Object .

Este enfoque es adecuado en relación con el enlace de datos y especialmente al implementar la arquitectura
Model-View -ViewModel (MVVM ).
Para obtener más información sobre el uso de la interfaz de comandos, consulte mediante la interfaz de
comandos en el botón guía.

Presionando y soltando el ImageButton


Además el Clicked eventos, ImageButton también define Pressed y Released eventos. El Pressed evento tiene
lugar cuando se presiona un dedo en un ImageButton , o se presiona un botón del mouse con el puntero situado
sobre el ImageButton . El Released evento tiene lugar cuando se suelta el botón del dedo o el mouse. Por lo
general, el Clicked también se desencadena el evento al mismo tiempo que el Released evento, pero si el
puntero del dedo o el mouse se desliza fuera de la superficie de la ImageButton antes de que se publique el
Clicked eventos podrían no producirse.

Para obtener más información acerca de estos eventos, vea presionando y soltando el botón en el botón guía.

Apariencia ImageButton
Además de las propiedades que ImageButton hereda el View (clase), ImageButton también define varias
propiedades que afectan a su apariencia:
Aspect es cómo la imagen se ajustarán el área de presentación.
BorderColor es el color de un área que rodea el ImageButton .
BorderWidth es el ancho del borde.
CornerRadius es el radio de redondeo de la ImageButton .

El Aspect propiedad puede establecerse en uno de los miembros de la Aspect enumeración:


Fill -ajusta la imagen para rellenar por completo y exactamente el ImageButton . Esto puede dar lugar a que
la imagen se distorsiona.
AspectFill -recorta la imagen para que rellene el ImageButton conservando la relación de aspecto.
AspectFit -letterbox la imagen (si es necesario) para que quepa la imagen completa en el ImageButton , con
espacio en blanco que se agrega a la parte superior o inferior o lados dependiendo de si la imagen es ancho o
alto. Este es el valor predeterminado de la Aspect enumeración.

NOTE
El ImageButton clase también tiene Margin y Padding las propiedades que controlan el comportamiento de diseño de
la ImageButton . Para obtener más información, consulte margen y relleno.

Estados visuales ImageButton


ImageButton tiene un Pressed VisualState que puede utilizarse para iniciar un cambio visual en el ImageButton
cuando presiona por el usuario, siempre que lo esté habilitada.
En el siguiente ejemplo XAML se muestra cómo definir un estado visual para el Pressed estado:
<ImageButton Source="XamarinLogo.png"
...>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="Scale"
Value="1" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Property="Scale"
Value="0.8" />
</VisualState.Setters>
</VisualState>

</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</ImageButton>

El Pressed VisualState especifica que, cuando el ImageButton está presionado, su Scale se cambiará la
propiedad de su valor predeterminado de 1 a 0,8. El Normal VisualState especifica que, cuando el ImageButton
está en estado normal, su Scale propiedad se establecerá en 1. Por lo tanto, el efecto general es que cuando el
ImageButton está presionado, se vuelve a escalar para que sea ligeramente más pequeñas pero cuando el
ImageButton está publicado, se escalan a su tamaño predeterminado.

Para obtener más información acerca de los estados visuales, vea Xamarin.Forms Visual State Manager.

Vínculos relacionados
Ejemplo de FormsGallery
Diseños de Xamarin.Forms
11/07/2019 • 16 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms tiene varios diseños y características para organizar el contenido en pantalla.

Vídeo de diseños de Xamarin.Forms


A continuación, se describe cada control de diseño, así como detalles sobre cómo controlar los cambios de
orientación de pantalla:
StackLayout : se usa para organizar vistas linealmente, ya sea horizontal o verticalmente. Las vistas en un
StackLayout se pueden alinear al centro, izquierdo o derecho del diseño.
AbsoluteLayout : se usa para organizar vistas mediante el establecimiento de coordenadas, ta & maño en
cuanto a los valores absolutos o proporciones. AbsoluteLayout puede utilizarse para las vistas de capas como
fijarlos a la izquierda, derecha o centrado.
RelativeLayout : se usa para organizar vistas estableciendo restricciones con respecto a las dimensiones y la
posición de su primario.
Cuadrícula : se usa para organizar vistas en una cuadrícula. En cuanto a los valores absolutos o proporciones,
se pueden especificar las filas y columnas.
FlexLayout : se usa para organizar vistas horizontal o verticalmente con ajuste.
ScrollView : se usa para proporcionar el desplazamiento cuando una vista no cabe por completo dentro de los
límites de la pantalla.
LayoutOptions : definir la alineación y la expansión de una vista, en relación con su elemento primario.
Transparencia de entrada : Especifica si un elemento recibe la entrada.
Márgenes y relleno : muestra cómo controlar el comportamiento de diseño cuando se procesa un elemento
en la interfaz de usuario.
Orientación del dispositivo : se explica cómo controlar los cambios de orientación del dispositivo.
Diseño en los dispositivos de tableta y escritorio : se muestra cómo optimizar para pantallas más grandes
en cada plataforma.
Diseños enlazables : habilitar las clases de diseño generar su contenido mediante un enlace a una colección
de elementos.
Crear un diseño personalizado : se explica cómo crear una clase de diseño personalizado.
Compresión de diseño – quita especificado diseño desde el árbol visual en un intento de mejorar el
rendimiento de representación de página.
Controles de la plataforma también pueden usarse directamente en los diseños de Xamarin.Forms con
incrustación nativa (nuevo en Xamarin.Forms 2.2), y puede crear diseños personalizados para satisfacer los
requisitos específicos.
El gráfico siguiente muestra los controles de diseño:
Elegir el diseño correcto
Los diseños que elija en la aplicación pueden ayudar a o perjudicarle dado que está creando una aplicación de
Xamarin.Forms atractiva y utilizable. Tomar algún tiempo para pensar en cómo cada funciona de diseño puede
ayudarle a escribir código de interfaz de usuario más limpio y más escalable. Una pantalla puede tener una
combinación de diseños diferentes para lograr un diseño específico.
StackLayout
El StackLayout se utiliza para mostrar las vistas a lo largo de una línea horizontal o vertical. Posición y el tamaño
en el diseño se determina basándose en una vista HeightRequest , WidthRequest , HorizontalOptions y
VerticalOptions . StackLayout a menudo se usa como el diseño de base, organizar otros diseños en la pantalla.

Para obtener un ejemplo de cuándo StackLayout podría ser una buena opción, considere la posibilidad de una
aplicación que necesita para mostrar un botón y una etiqueta, con la etiqueta que se alinea a la izquierda y el
botón alineado a la derecha.

<StackLayout Orientation="Horizontal">
<Label HorizontalOptions="StartAndExpand" Text="Label" />
<Button HorizontalOptions="End" Text="Button" />
</StackLayout>

FlexLayout
El FlexLayout es similar a StackLayout que muestra vistas secundarias horizontal o verticalmente:

<FlexLayout Direction="Column"
AlignItems="Center"
JustifyContent="SpaceEvenly">

<Label Text="FlexLayout in Action" />


<Button Text="Button" />
<Label Text="Another Label" />
</FlexLayout>

Sin embargo, si hay demasiados elementos secundarios para que quepa en una sola fila o columna propio,
FlexLayout también es capaz de ajuste de esas vistas. FlexLayout se basa en el módulo de diseño CSS cuadro
Flexible y tiene muchas de las mismas opciones integradas para colocar y alinear a sus elementos secundarios.
AbsoluteLayout
El AbsoluteLayout se utiliza para mostrar las vistas, con el tamaño y posición que se va a especificar como valores
explícitos o en relación con el tamaño del diseño. A diferencia de StackLayout y Grid , AbsoluteLayout permite
secundarios vistas se superponen. A diferencia de RelativeLayout , AbsoluteLayout no le permite colocar los
elementos fuera de la pantalla.
Para obtener un ejemplo de cuándo AbsoluteLayout podría ser una buena opción, considere la posibilidad de una
aplicación que necesita para presentar las colecciones de objetos como pilas. Esto suele aparecer cuando se
presentan los álbumes de fotografías o canciones. El código siguiente proporciona la apariencia de una pila, con
elementos girados para sugerir el contenido de la pila:
En XAML:

<AbsoluteLayout Padding="15">
<Image AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="0.5, 0, 100, 100"
Rotation="30"
Source="bottom.png" />
<Image AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="0.5, 0, 100, 100"
Rotation="60"
Source="middle.png" />
<Image AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="0.5, 0, 100, 100"
Source="cover.png" />
</AbsoluteLayout>

Tenga en cuenta los siguientes aspectos del código anterior:


Cada Image se muestra en la misma posición (en el centro del espacio horizontal)
El Padding es considerado por AbsoluteLayout , a diferencia RelativeLayout , que pasa por alto.
AbsoluteLayout.LayoutFlags Especifica cómo se interpretará los límites de diseño. En este caso
PositionProportional , significa que las coordenadas será una proporción del tamaño del diseño, mientras que
el tamaño se interpretará como un tamaño específico.
AbsoluteLayout.Layoutbounds Especifica la posición horizontal, posición vertical, ancho y alto en ese orden.

RelativeLayout
El RelativeLayout se utiliza para mostrar las vistas, con el tamaño y posición especificados como valores en
relación con los valores de la distribución o en otra vista. Valores relativos no es necesario para que coincida con la
que corresponde el valor en la vista relacionada. Por ejemplo, es posible establecer una vista Width propiedad sea
proporcional a otra vista X propiedad.
RelativeLayout se puede usar para crear interfaces de usuario que se escalan de forma proporcional entre
tamaños de los dispositivos. El XAML siguiente implementa un diseño con los cuadros en las esquinas superiores,
con un asta de la bandera con la marca en el centro:

<RelativeLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">


<BoxView Color="Blue" HeightRequest="50" WidthRequest="50"
RelativeLayout.XConstraint= "{ConstraintExpression Type=RelativeToParent, Property=Width, Factor = 0}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor = 0}" />
<BoxView Color="Red" HeightRequest="50" WidthRequest="50"
RelativeLayout.XConstraint= "{ConstraintExpression Type=RelativeToParent, Property=Width, Factor = .9}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor = 0}" />
<BoxView Color="Gray" WidthRequest="15" x:Name="pole"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=.75}"
RelativeLayout.XConstraint= "{ConstraintExpression Type=RelativeToParent, Property=Width, Factor = .45}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor = .25}"
/>
<BoxView Color="Green"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=.10,
Constant=10}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width, Factor=.2,
Constant=20}"
RelativeLayout.XConstraint= "{ConstraintExpression Type=RelativeToView, ElementName=pole, Property=X,
Constant=15}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=pole, Property=Y,
Constant=0}" />
</RelativeLayout>

Tenga en cuenta los siguientes aspectos del código anterior:


Como las restricciones se especifican los tamaños y posiciones.
Se denomina el asta de la bandera para que la marca (verde del cuadro) se puede establecer la posición en
relación con el asta de la bandera.
Las expresiones de restricción tienen Factor y Constant propiedades, que pueden utilizarse para definir las
posiciones y tamaños como múltiplos (o fracciones de segundo) de las propiedades de otros objetos, además
de una constante. Las constantes pueden ser negativas.
Grid
El Grid se utiliza para mostrar elementos en filas y columnas. Tenga en cuenta que la cuadrícula no es una tabla,
por lo que no tiene el concepto de celdas, filas de encabezado y pie de página o bordes entre filas y columnas. En
general, la cuadrícula no es adecuada para mostrar datos tabulares. Para que use, considere la posibilidad de un
ListView o TableView.
Para obtener un ejemplo de cuándo una Grid es el diseño correcto para usar, considere la posibilidad de una
entrada numérica para una calculadora. Una entrada numérica para una calculadora puede constar de cuatro filas
y tres columnas, cada uno con un botón. El código siguiente implementa este diseño:

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="1" Grid.Row="0" Grid.Column="0" />
<Button Text="2" Grid.Row="0" Grid.Column="1" />
<Button Text="3" Grid.Row="0" Grid.Column="2" />
<Button Text="4" Grid.Row="1" Grid.Column="0" />
<Button Text="5" Grid.Row="1" Grid.Column="1" />
<Button Text="6" Grid.Row="1" Grid.Column="2" />
<Button Text="7" Grid.Row="2" Grid.Column="0" />
<Button Text="8" Grid.Row="2" Grid.Column="1" />
<Button Text="9" Grid.Row="2" Grid.Column="2" />
<Button Text="0" Grid.Row="3" Grid.Column="1" />
<Button Text="&lt;-" Grid.Row="3" Grid.Column="2" />
</Grid>

Tenga en cuenta los siguientes aspectos del código anterior:


Las cuadrículas y las columnas se especifican explícitamente, no se inferirá del contenido.
Height y Width valores pueden establecerse en estrella, lo que significa que la cuadrícula establecerá esos
valores para rellenar el espacio disponible.
Posición de cada botón se especifica mediante Grid.Row & Grid.Column propiedades.
LayoutOptions
El LayoutOptions estructura puede utilizarse para definir la alineación y la expansión de una vista, en relación con
su elemento primario.
Margen y relleno
El Margin y Padding propiedades controlan el comportamiento de diseño cuando se procesa un elemento en la
interfaz de usuario.
Entrada de transparencia
Cada elemento tiene un InputTransparent propiedad que se usa para definir si el elemento recibe la entrada. Su
valor predeterminado es false , lo que garantiza que el elemento recibe la entrada.
Cuando esta propiedad está establecida en una clase de contenedor, como una clase de diseño, las transferencias
de su valor a los elementos secundarios. Por consiguiente, establecer el InputTransparent propiedad true en un
diseño de clase dará como resultado de los elementos dentro del diseño no recibir datos de entrada.
Orientación del dispositivo
Xamarin.Forms y sus diseños integrados son capaces de controlar los cambios de orientación del dispositivo.
Considere la posibilidad de que las orientaciones que admitirá la aplicación, así como cómo realizará el espacio
proporcionado en los modos vertical y horizontal.
Diseño para aplicaciones de tableta y escritorio
iOS, Android y plataforma Universal de Windows todos admiten tamaños de pantalla más grandes en
dispositivos de tableta (así como equipos portátiles y de escritorio para Windows). Xamarin.Forms le permite
optimizar la aplicación para pantallas más grandes para detectar el tipo de dispositivo y el ajusta el diseño de
página o usar una página totalmente diferente por completo para pantallas más grandes.
Diseños enlazables
El BindableLayout clase permite que cualquier clase de diseño que se deriva el Layout<T> clase para generar su
contenido mediante un enlace a una colección de elementos, con la opción para establecer la apariencia de cada
elemento con un DataTemplate .
Creación de un diseño personalizado
Xamarin.Forms define cuatro clases de diseño - StackLayout , AbsoluteLayout , RelativeLayout , y Grid , y cada
uno de ellos organiza sus elementos secundarios de forma diferente. Sin embargo, a veces su necesarias para
organizar el contenido de la página con un diseño no proporcionan Xamarin.Forms. En este artículo se explica
cómo escribir una clase de diseño personalizado y muestra una orientación distinción WrapLayout clase que sus
elementos secundarios se organiza horizontalmente en la página y, a continuación, ajusta la presentación de los
elementos secundarios subsiguientes a las filas adicionales.
Compresión de diseño
Compresión de diseño quita diseños especificados del árbol visual en un intento de mejorar el rendimiento de
representación de página. La ventaja de rendimiento que esto ofrece varía según la complejidad de una página, la
versión del sistema operativo que se va a usar y el dispositivo en el que se ejecuta la aplicación. Sin embargo, las
mejoras de rendimiento más importantes se apreciarán en los dispositivos más antiguos.

Para facilitar su elección


Tenga en cuenta que en la mayoría de los casos, puede usar más de una opción de diseño para implementar el
diseño deseado. Cuando hay varias opciones válidas, considere qué enfoque será más fácil para su situación. No
se pueden realizar la mayoría de los diseños con un solo diseño, por lo que los diseños de anidamiento como sean
necesarios para crear diseños más complejos.

Vínculos relacionados
Directrices de interfaz humana de Apple
Sitio Web de diseño de Android
Diseño (ejemplo)
Ejemplo de BusinessTumble (ejemplo)
Xamarin.Forms StackLayout
11/07/2019 • 6 minutes to read • Edit Online

descargar el ejemplo
StackLayout organiza las vistas en una línea unidimensional ("pila"), ya sea horizontal o verticalmente. Las vistas
en un StackLayout puede ajustarse según el espacio en el diseño mediante las opciones de diseño. Posición viene
determinada por el orden en que se agregaron las vistas en el diseño y las opciones de diseño de las vistas.

Propósito
StackLayout es menos complejo que otras vistas. Se pueden crear interfaces lineales simple con solo agregar
vistas a un StackLayout y las interfaces más complejas creadas por anidarlos.

Uso de & comportamiento


Espaciado
De forma predeterminada, StackLayout agregará un margen 6px entre las vistas. Puede ser controlado o
establecer tener ningún margen estableciendo el Spacing propiedad StackLayout. La siguiente muestra cómo
establecer el espaciado y el efecto de las opciones de espaciado diferente:
En XAML:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LayoutSamples.StackLayoutDemo"
Title="StackLayout Demo">
<ContentPage.Content>
<StackLayout Spacing="10" x:Name="layout">
<Button Text="StackLayout" VerticalOptions="Start"
HorizontalOptions="FillAndExpand" />
<BoxView Color="Yellow" VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand" />
<BoxView Color="Green" VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand" />
<BoxView HeightRequest="75" Color="Blue" VerticalOptions="End"
HorizontalOptions="FillAndExpand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

En C#:
public class StackLayoutCode : ContentPage
{
public StackLayoutCode ()
{
var layout = new StackLayout ();
var button = new Button { Text = "StackLayout", VerticalOptions = LayoutOptions.Start,
HorizontalOptions = LayoutOptions.FillAndExpand };
var yellowBox = new BoxView { Color = Color.Yellow, VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand };
var greenBox = new BoxView { Color = Color.Green, VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand };
var blueBox = new BoxView { Color = Color.Blue, VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand, HeightRequest = 75 };

layout.Children.Add(button);
layout.Children.Add(yellowBox);
layout.Children.Add(greenBox);
layout.Children.Add(blueBox);
layout.Spacing = 10;
Content = layout;
}
}

Espaciado = 0:

Espaciado de diez:
Ajuste de tamaño
El tamaño de una vista en un StackLayout depende de las solicitudes de alto y ancho y las opciones de diseño.
StackLayout aplicará el relleno. La siguiente LayoutOption s hará que las vistas a ocupar espacio está disponible
en el diseño:
CenterAndExpand – centra la vista dentro del diseño y se expande para ocupar espacio le asignará el diseño.
EndAndExpand – coloca la vista al final del diseño (inferior o límite más a la derecha) y se expande para
ocupar espacio le asignará el diseño.
FillAndExpand – coloca la vista para que no tiene relleno y ocupa espacio le asignará el diseño.
StartAndExpand – coloca la vista al principio del diseño y ocupa espacio proporcionará el elemento primario.
Para obtener más información, consulte expansión.
Posición
Se pueden colocar y tamaño utilizando vistas en un StackLayout LayoutOptions . Cada vista puede
proporcionarse VerticalOptions y HorizontalOptions , define cómo las vistas se posicionan en relación con el
diseño. Siguiente predefinidos LayoutOptions están disponibles:
Centro de – centra la vista dentro del diseño.
End – coloca la vista al final del diseño (inferior o límite más a la derecha).
Rellenar – coloca la vista para que no tenga ningún relleno.
Iniciar – coloca la vista al principio del diseño.
El código siguiente muestra cómo establecer las opciones de diseño:
En XAML:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LayoutSamples.StackLayoutDemo"
Title="StackLayout Demo">
<ContentPage.Content>
<StackLayout x:Name="layout">
<Button VerticalOptions="Start"
HorizontalOptions="FillAndExpand" />
<BoxView VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand" />
<BoxView VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand" />
<BoxView HeightRequest="75" VerticalOptions="End"
HorizontalOptions="FillAndExpand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

En C#:

public class StackLayoutCode : ContentPage


{
public StackLayoutCode ()
{
var layout = new StackLayout ();
var button = new Button { VerticalOptions = LayoutOptions.Start,
HorizontalOptions = LayoutOptions.FillAndExpand };
var oneBox = new BoxView { VerticalOptions = LayoutOptions.FillAndExpand, HorizontalOptions =
LayoutOptions.FillAndExpand };
var twoBox = new BoxView { VerticalOptions = LayoutOptions.FillAndExpand, HorizontalOptions =
LayoutOptions.FillAndExpand };
var threeBox = new BoxView { VerticalOptions = LayoutOptions.FillAndExpand, HorizontalOptions =
LayoutOptions.FillAndExpand };

layout.Children.Add(button);
layout.Children.Add(oneBox);
layout.Children.Add(twoBox);
layout.Children.Add(threeBox);
Content = layout;
}
}

Para obtener más información, consulte alineación.

Exploración de un diseño complejo


Cada uno de los diseños tienen ventajas y desventajas para crear diseños determinados. A lo largo de esta serie
de artículos de diseño, una aplicación de ejemplo creada con el mismo diseño de página que se implementa
mediante tres diseños diferentes.
Tenga en cuenta el XAML siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TheBusinessTumble.StackLayoutPage"
BackgroundColor="Maroon"
Title="StackLayouts">
<ContentPage.Content>
<ScrollView>
<StackLayout Spacing="0" Padding="0" BackgroundColor="Maroon">
<BoxView HorizontalOptions="FillAndExpand" HeightRequest="100"
VerticalOptions="Start" Color="Gray" />
<Button BorderRadius="30" HeightRequest="60" WidthRequest="60"
BackgroundColor="Red" HorizontalOptions="Center" VerticalOptions="Start" />
<StackLayout HeightRequest="100" VerticalOptions="Start" HorizontalOptions="FillAndExpand"
Spacing="20" BackgroundColor="Maroon">
<Label Text="User Name" FontSize="28" HorizontalOptions="Center"
VerticalOptions="Center" FontAttributes="Bold" />
<Entry Text="Bio + Hashtags" TextColor="White"
BackgroundColor="Maroon" HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand" />
</StackLayout>
<StackLayout Orientation="Horizontal" HeightRequest="50" BackgroundColor="White" Padding="5">
<StackLayout Spacing="0" BackgroundColor="White" Orientation="Horizontal"
HorizontalOptions="Start">
<BoxView BackgroundColor="Black" WidthRequest="40" HeightRequest="40"
HorizontalOptions="StartAndExpand" VerticalOptions="Center" />
<Label FontSize="14" TextColor="Black" Text="Accent Color" HorizontalOptions="StartAndExpand"
VerticalOptions="Center" />
</StackLayout>
<StackLayout Spacing="0" BackgroundColor="White" Orientation="Horizontal"
HorizontalOptions="EndAndExpand">
<BoxView BackgroundColor="Maroon" WidthRequest="40" HeightRequest="40" HorizontalOptions="Start"
VerticalOptions="Center" />
<Label FontSize="14" TextColor="Black" Text="Primary Color" HorizontalOptions="StartAndExpand"
VerticalOptions="Center" />
</StackLayout>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label FontSize="14" Text="Age:" TextColor="White" HorizontalOptions="Start"
VerticalOptions="Center" WidthRequest="100" />
<Entry HorizontalOptions="FillAndExpand" Text="35" TextColor="White" BackgroundColor="Maroon" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label FontSize="14" Text="Interests:" TextColor="White"
HorizontalOptions="Start" VerticalOptions="Center" WidthRequest="100" />
<Entry HorizontalOptions="FillAndExpand" Text="Xamarin.Forms" TextColor="White"
BackgroundColor="Maroon" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label FontSize="14" Text="Ask me about:" TextColor="White"
HorizontalOptions="Start" VerticalOptions="Center" WidthRequest="100"/>
<Entry HorizontalOptions="FillAndExpand" Text="Xamarin, C#, .NET, Mono..." TextColor="White"
BackgroundColor="Maroon" />
</StackLayout>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>

El código anterior da como resultado el siguiente diseño:


Tenga en cuenta que StackLayouts s están anidadas, porque en algunos casos los diseños de anidamiento puede
ser más sencillo que presentar todos los elementos del diseño del mismo. Observe también que, dado que
StackLayout no admite elementos superpuestos, la página no dispone de algunos de los detalles de diseño se
encuentra en las páginas de los otros diseños.

Vínculos relacionados
LayoutOptions
Diseño (ejemplo)
Ejemplo de BusinessTumble (ejemplo)
Xamarin.Forms AbsoluteLayout
11/07/2019 • 13 minutes to read • Edit Online

descargar el ejemplo
AbsoluteLayout coloca y ajusta el tamaño de los elementos secundarios proporcionales a su propio tamaño y
posición o por valores absolutos. Pueden ser vistas secundarias posición y tamaño utilizando valores
proporcionales o valores estáticos y proporcional y se pueden combinar los valores estáticos.

Este artículo se trata:


Propósito – usos comunes de AbsoluteLayout .
Uso – cómo usar AbsoluteLayout para lograr el diseño deseado.
Diseños proporcionales – comprender valores proporcionales cómo funcionan en un
AbsoluteLayout .
Especificar valores – comprender cómo se especifican proporcional y los valores absolutos.
Valores proporcionales – comprender valores proporcionales cómo funcionan.
Valores absolutos – comprender cómo funcionan los valores absolutos.

Propósito
Debido al modelo de posicionamiento de AbsoluteLayout , el diseño resulta relativamente sencilla colocar los
elementos para que sean vaciado con cualquier lado del diseño o centrada. Con las posiciones y tamaños
proporcionales, los elementos en un AbsoluteLayout puede escalar automáticamente a cualquier tamaño de la
vista. Para los elementos que debe escalarse sólo la posición, pero no el tamaño, se pueden combinar valores
proporcionales y absolutos.
AbsoluteLayout se puede usar cualquier parte los elementos se deben colocar dentro de una vista y es
especialmente útil cuando se alinean elementos a los bordes.

Uso
Diseños proporcionales
AbsoluteLayout tiene un modelo de delimitador único mediante el cual se coloca el delimitador del elemento
respecto a su elemento como el elemento se coloca en relación con el diseño cuando se usa el posicionamiento
proporcional. Cuando se usa el posicionamiento absoluto, el delimitador se encuentra en (0,0) en la vista. Esto
tiene dos consecuencias importantes:
No se puede colocar elementos fuera de la pantalla con los valores proporcionales.
Los elementos se pueden colocar de forma confiable en cualquier parte del diseño o en el centro,
independientemente del tamaño de la distribución o el dispositivo.
AbsoluteLayout , como RelativeLayout , puede colocar los elementos de forma que se superpongan.
Tenga en cuenta en la siguiente captura de pantalla, el delimitador del cuadro es un punto blanco. Tenga en
cuenta la relación entre el delimitador y el cuadro de medida que avanza el diseño:
Especificar valores
Vistas dentro de un AbsoluteLayout se sitúan mediante cuatro valores:
X – la posición de x (horizontal) del delimitador de la vista
Y – la posición y (vertical) del delimitador de la vista
Ancho – el ancho de la vista
Alto – el alto de la vista
Cada uno de estos valores puede establecerse como un proporcional valor o una absoluta valor.
Los valores se especifican como una combinación de los límites y una marca. LayoutBounds es un Rectangle que
consta de cuatro valores: x , y , width , height .
AbsoluteLayoutFlags
AbsoluteLayoutFlags Especifica cómo se interpretará los valores y tiene las siguientes opciones predefinidas:

Ninguno – interpreta todos los valores como un absoluto. Este es el valor predeterminado si no se especifica
ninguna marca de diseño.
Todos los – interpreta todos los valores como proporcional.
WidthProportional – interpreta el Width valor como proporcional y todos los demás valores como un
absoluto.
HeightProportional – interpreta solo el valor del alto como proporcional con todos los demás valores
absolutos.
XProportional – interpreta el X valor como proporcional, mientras que todos los demás valores como un
absoluto.
YProportional – interpreta el Y valor como proporcional, mientras que todos los demás valores como un
absoluto.
PositionProportional – interpreta el X y Y valores proporcionales, mientras que los valores de tamaño se
interpretan como un absoluto.
SizeProportional – interpreta el Width y Height valores proporcionales mientras que los valores de
posición son absolutos.
En XAML, límites y las marcas se establecen como parte de la definición de vistas en el diseño, utilizando el
AbsoluteLayout.LayoutBounds propiedad. Los límites se establecen como una lista separada por comas de valores,
X , Y , Width , y Height , en ese orden. También se especifican en la declaración de vistas en el diseño mediante
el AbsoluteLayout.LayoutFlags propiedad. Tenga en cuenta que se pueden combinar flags en XAML mediante
una lista separada por comas. Considere el ejemplo siguiente:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LayoutSamples.AbsoluteLayoutExploration"
Title="Absolute Layout Exploration">
<ContentPage.Content>
<AbsoluteLayout>
<Label Text="I'm centered on iPhone 4 but no other device"
AbsoluteLayout.LayoutBounds="115,150,100,100" LineBreakMode="WordWrap" />
<Label Text="I'm bottom center on every device."
AbsoluteLayout.LayoutBounds=".5,1,.5,.1" AbsoluteLayout.LayoutFlags="All"
LineBreakMode="WordWrap" />
<BoxView Color="Olive" AbsoluteLayout.LayoutBounds="1,.5, 25, 100"
AbsoluteLayout.LayoutFlags="PositionProportional" />
<BoxView Color="Red" AbsoluteLayout.LayoutBounds="0,.5,25,100"
AbsoluteLayout.LayoutFlags="PositionProportional" />
<BoxView Color="Blue" AbsoluteLayout.LayoutBounds=".5,0,100,25"
AbsoluteLayout.LayoutFlags="PositionProportional" />
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
Tenga en cuenta lo siguiente:
La etiqueta en el centro se coloca con los valores de tamaño y posición absolutos. Por este motivo, aparece
centrado en iPhone 4S y menores, pero no centrado en dispositivos de mayor tamaño.
Se coloca el texto en la parte inferior del diseño con los valores proporcionales de tamaño y posición. Siempre
aparecerá en la parte inferior central del diseño, pero aumentará su tamaño con tamaños más grandes de
diseño.
Tres colores BoxView s se colocan en los bordes superiores, izquierdos y derecho de la pantalla con
proporcional posición y tamaño absoluto.
El siguiente obtiene el mismo diseño de C#:
public class AbsoluteLayoutExplorationCode : ContentPage
{
public AbsoluteLayoutExplorationCode ()
{
Title = "Absolute Layout Exploration - Code";
var layout = new AbsoluteLayout();

var centerLabel = new Label {


Text = "I'm centered on iPhone 4 but no other device.",
LineBreakMode = LineBreakMode.WordWrap};

AbsoluteLayout.SetLayoutBounds (centerLabel, new Rectangle (115, 159, 100, 100));


// No need to set layout flags, absolute positioning is the default

var bottomLabel = new Label { Text = "I'm bottom center on every device.", LineBreakMode =
LineBreakMode.WordWrap };
AbsoluteLayout.SetLayoutBounds (bottomLabel, new Rectangle (.5, 1, .5, .1));
AbsoluteLayout.SetLayoutFlags (bottomLabel, AbsoluteLayoutFlags.All);

var rightBox = new BoxView{ Color = Color.Olive };


AbsoluteLayout.SetLayoutBounds (rightBox, new Rectangle (1, .5, 25, 100));
AbsoluteLayout.SetLayoutFlags (rightBox, AbsoluteLayoutFlags.PositionProportional);

var leftBox = new BoxView{ Color = Color.Red };


AbsoluteLayout.SetLayoutBounds (leftBox, new Rectangle (0, .5, 25, 100));
AbsoluteLayout.SetLayoutFlags (leftBox, AbsoluteLayoutFlags.PositionProportional);

var topBox = new BoxView{ Color = Color.Blue };


AbsoluteLayout.SetLayoutBounds (topBox, new Rectangle (.5, 0, 100, 25));
AbsoluteLayout.SetLayoutFlags (topBox, AbsoluteLayoutFlags.PositionProportional);

layout.Children.Add (bottomLabel);
layout.Children.Add (centerLabel);
layout.Children.Add (rightBox);
layout.Children.Add (leftBox);
layout.Children.Add (topBox);

Content = layout;
}
}

Valores proporcionales
Valores proporcionales definen una relación entre un diseño y una vista. Esta relación define la posición o el valor
de escala de una vista secundaria como una proporción del valor correspondiente del diseño del elemento
primario. Estos valores se expresan como double s con valores comprendidos entre 0 y 1.
Se usan valores proporcionales a la posición y las vistas de tamaño en el diseño. Por lo tanto, cuando se establece
el ancho de la vista como una proporción, el valor del ancho resultante es la proporción multiplicada por el
AbsoluteLayout del ancho. Por ejemplo, con un AbsoluteLayout del ancho 500 y una vista que define para tener
un ancho proporcional de.5, el ancho representado de la vista será 250 (500 x.5.
Para usar valores proporcionales, establezca LayoutBounds utilizando (x, y) proporciones y tamaños
proporcionales, a continuación, establezca LayoutFlags a All .
En XAML:

<Label Text="I'm bottom center on every device."


AbsoluteLayout.LayoutBounds=".5,1,.5,.1" AbsoluteLayout.LayoutFlags="All" />

En C#:
var label = new Label {Text = "I'm bottom center on every device."};
AbsoluteLayout.SetLayoutBounds(label, new Rectangle(.5,1,.5,.1));
AbsoluteLayout.SetLayoutFlags(label, AbsoluteLayoutFlags.All);

Valores absolutos
Valores absolutos definir explícitamente dónde deben situarse vistas dentro del diseño. A diferencia de los
valores proporcionales, valores absolutos son capaces de posicionar y ajustar el tamaño de una vista que no cabe
dentro de los límites del diseño.
Use valores absolutos para el posicionamiento puede ser peligroso cuando no se conoce el tamaño del diseño. Al
usar posiciones absolutas, se puede desplazar un elemento en el centro de la pantalla con un tamaño en
cualquier otro tamaño. Es importante probar la aplicación a través de los distintos tamaños de pantalla de los
dispositivos compatibles.
Para usar los valores de diseño absoluto, establezca LayoutBounds utilizando (x, y) coordenadas y los tamaños
explícitos, a continuación, establezca LayoutFlags a None .
En XAML:

<Label Text="I'm centered on iPhone 4 but no other device."


AbsoluteLayout.LayoutBounds="115,150,100,100" />

En C#:

var label = new Label {Text = "I'm centered on iPhone 4 but no other device."};
AbsoluteLayout.SetLayoutBounds(label, new Rectangle(115,150,100,100));

Exploración de un diseño complejo


Cada uno de los diseños tienen ventajas y desventajas para crear diseños determinados. A lo largo de esta serie
de artículos de diseño, una aplicación de ejemplo creada con el mismo diseño de página que se implementa
mediante tres diseños diferentes.
Tenga en cuenta el XAML siguiente:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TheBusinessTumble.AbsoluteLayoutPage"
Title="AbsoluteLayout">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Save" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<ScrollView>
<AbsoluteLayout BackgroundColor="Maroon">
<BoxView BackgroundColor="Gray" AbsoluteLayout.LayoutBounds="0
0,1,100" AbsoluteLayout.LayoutFlags="XProportional,YProportional,WidthProportional" />
<Button BackgroundColor="Maroon"
AbsoluteLayout.LayoutBounds=".5,55,70,70" AbsoluteLayout.LayoutFlags="XProportional"
BorderRadius="35" />
<Button BackgroundColor="Red" AbsoluteLayout.LayoutBounds=".5
60,60,60" AbsoluteLayout.LayoutFlags="XProportional" BorderRadius="30" />
<Label Text="User Name" FontAttributes="Bold" FontSize="26"
TextColor="Black" HorizontalTextAlignment="Center"
AbsoluteLayout.LayoutBounds=".5,140,1,40" AbsoluteLayout.LayoutFlags="XProportional,WidthProportional" />
<Entry Text="Bio + Hashtags" TextColor="White"
BackgroundColor="Maroon" AbsoluteLayout.LayoutBounds=".5,180,1,40"
BackgroundColor="Maroon" AbsoluteLayout.LayoutBounds=".5,180,1,40"
AbsoluteLayout.LayoutFlags="XProportional,WidthProportional" />
<AbsoluteLayout BackgroundColor="White"
AbsoluteLayout.LayoutBounds="0, 220, 1, 50"
AbsoluteLayout.LayoutFlags="XProportional,WidthProportional">
<AbsoluteLayout AbsoluteLayout.LayoutBounds="0,0,.5,1"
AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional">
<Button BackgroundColor="Black" BorderRadius="20"
AbsoluteLayout.LayoutBounds="5,.5,40,40"
AbsoluteLayout.LayoutFlags="YProportional" />
<Label Text="Accent Color" TextColor="Black"
AbsoluteLayout.LayoutBounds="50,.55,1,25"
AbsoluteLayout.LayoutFlags="YProportional,WidthProportional" />
</AbsoluteLayout>
<AbsoluteLayout AbsoluteLayout.LayoutBounds="1,0,.5,1"
AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional,XProportional">
<Button BackgroundColor="Maroon" BorderRadius="20"
AbsoluteLayout.LayoutBounds="5,.5,40,40"
AbsoluteLayout.LayoutFlags="YProportional" />
<Label Text="Primary Color" TextColor="Black"
AbsoluteLayout.LayoutBounds="50,.55,1,25"
AbsoluteLayout.LayoutFlags="YProportional,WidthProportional" />
</AbsoluteLayout>
</AbsoluteLayout>
<AbsoluteLayout AbsoluteLayout.LayoutBounds="0,270,1,50"
AbsoluteLayout.LayoutFlags="WidthProportional" Padding="5,0,0,0">
<Label Text="Age:" TextColor="White"
AbsoluteLayout.LayoutBounds="0,25,.25,50"
AbsoluteLayout.LayoutFlags="WidthProportional" />
<Entry Text="35" TextColor="White" BackgroundColor="Maroon"
AbsoluteLayout.LayoutBounds="1,10,.75,50"
AbsoluteLayout.LayoutFlags="XProportional,WidthProportional" />
</AbsoluteLayout>
<AbsoluteLayout AbsoluteLayout.LayoutBounds="0,320,1,50"
AbsoluteLayout.LayoutFlags="WidthProportional" Padding="5,0,0,0">
<Label Text="Interests:" TextColor="White"
AbsoluteLayout.LayoutBounds="0,25,.25,50"
AbsoluteLayout.LayoutFlags="WidthProportional" />
<Entry Text="Xamarin.Forms" TextColor="White"
BackgroundColor="Maroon" AbsoluteLayout.LayoutBounds="1,10,.75,50"
AbsoluteLayout.LayoutFlags="XProportional,WidthProportional" />
</AbsoluteLayout>
<AbsoluteLayout AbsoluteLayout.LayoutBounds="0,370,1,50"
AbsoluteLayout.LayoutFlags="WidthProportional" Padding="5,0,0,0">
<Label Text="Ask me about:" TextColor="White"
AbsoluteLayout.LayoutBounds="0,25,.25,50"
AbsoluteLayout.LayoutFlags="WidthProportional" />
<Entry Text="Xamarin, C#, .NET, Mono" TextColor="White"
BackgroundColor="Maroon" AbsoluteLayout.LayoutBounds="1,10,.75,50"
AbsoluteLayout.LayoutFlags="XProportional,WidthProportional" />
</AbsoluteLayout>
</AbsoluteLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>

El código anterior da como resultado el siguiente diseño:


Tenga en cuenta que AbsoluteLayout s están anidadas, porque en algunos casos los diseños de anidamiento
puede ser más sencillo que presentar todos los elementos del diseño del mismo.

Vínculos relacionados
Creación de aplicaciones móviles con Xamarin.Forms, capítulo 14
AbsoluteLayout
Diseño (ejemplo)
Ejemplo de BusinessTumble (ejemplo)
Xamarin.Forms RelativeLayout
11/07/2019 • 8 minutes to read • Edit Online

descargar el ejemplo
RelativeLayout se usa para la posición y las vistas de tamaño en relación con las propiedades de las vistas de
diseño o del mismo nivel. A diferencia de AbsoluteLayout , RelativeLayout no tiene el concepto del anclaje móvil
y no tiene medios para colocar elementos en relación con la parte inferior o el borde derecho del diseño.
RelativeLayout es compatible con elementos de posición fuera de sus propios límites.

Propósito
RelativeLayout puede utilizarse para colocar las vistas en pantalla en relación con el diseño general o a otras
vistas.

Uso
Restricciones de descripción
Posición y cambio de tamaño de una vista en un RelativeLayout se realiza con las restricciones. Una expresión
de restricción puede incluir la siguiente información:
Tipo – si la restricción es relativo al elemento primario o a otra vista.
Propiedad – qué propiedad desea usar como base para la restricción.
Factor – el factor que se aplican al valor de propiedad.
Constante – el valor que se usará como un desplazamiento del valor.
ElementName – el nombre de la vista que es relativa la restricción.
En XAML, las restricciones se expresan como ConstraintExpression s. Considere el ejemplo siguiente:

<BoxView Color="Green" WidthRequest="50" HeightRequest="50"


RelativeLayout.XConstraint =
"{ConstraintExpression Type=RelativeToParent,
Property=Width,
Factor=0.5,
Constant=-100}"
RelativeLayout.YConstraint =
"{ConstraintExpression Type=RelativeToParent,
Property=Height,
Factor=0.5,
Constant=-100}" />

En C#, las restricciones se expresan en forma un poco diferente, con funciones en lugar de expresiones en la vista.
Las restricciones se especifican como argumentos para el diseño Add método:

layout.Children.Add(box, Constraint.RelativeToParent((parent) =>


{
return (.5 * parent.Width) - 100;
}),
Constraint.RelativeToParent((parent) =>
{
return (.5 * parent.Height) - 100;
}),
Constraint.Constant(50), Constraint.Constant(50));

Tenga en cuenta los siguientes aspectos del diseño anterior:


El x y y las restricciones se especifican con sus propias restricciones.
En C#, las restricciones relativas se definen como funciones. Conceptos como Factor no están allí, pero se
puede implementar manualmente.
El cuadro x coordenada se define como la mitad del ancho del elemento primario, -100.
El cuadro y coordenada se define como la mitad del alto del elemento primario, -100.

NOTE
Debido al modo en que se definen las restricciones, es posible hacer que los diseños más complejos en C# que se pueden
especificar con XAML.

Los dos ejemplos anteriores definen restricciones como RelativeToParent – es decir, sus valores son en relación
con el elemento primario. También es posible definir restricciones en relación con otra vista. Esto permite diseños
más intuitivos (para el desarrollador) y puede hacer que la intención del código de diseño más evidentes.
Considere la posibilidad de un diseño donde un elemento debe ser menores que el otro 20 píxeles. Si ambos
elementos se definen con valores constantes, menor será podría tener su Y restricción definida como una
constante que es mayores que 20 píxeles el Y puede tomar el elemento superior. Este enfoque no es suficiente si
el elemento superior se coloca con una proporción, por lo que no se conoce el tamaño de píxel. En ese caso, es
más estable restringiendo el elemento basado en la posición de otro elemento:

<RelativeLayout>
<BoxView Color="Red" x:Name="redBox"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Height,Factor=.15,Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=1,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=.8,Constant=0}" />
<BoxView Color="Blue"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView,
ElementName=redBox,Property=Y,Factor=1,Constant=20}"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView,
ElementName=redBox,Property=X,Factor=1,Constant=20}"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.5,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=.5,Constant=0}" />
</RelativeLayout>

Para lograr el mismo diseño en C#:

layout.Children.Add (redBox, Constraint.RelativeToParent ((parent) => {


return parent.X;
}), Constraint.RelativeToParent ((parent) => {
return parent.Y * .15;
}), Constraint.RelativeToParent((parent) => {
return parent.Width;
}), Constraint.RelativeToParent((parent) => {
return parent.Height * .8;
}));
layout.Children.Add (blueBox, Constraint.RelativeToView (redBox, (Parent, sibling) => {
return sibling.X + 20;
}), Constraint.RelativeToView (blueBox, (parent, sibling) => {
return sibling.Y + 20;
}), Constraint.RelativeToParent((parent) => {
return parent.Width * .5;
}), Constraint.RelativeToParent((parent) => {
return parent.Height * .5;
}));

Esto genera el siguiente resultado, con la posición del cuadro azul determinada relativa a la posición del cuadro
rojo:
Ajuste de tamaño
Vistas planteados por RelativeLayout tiene dos opciones para especificar su tamaño:
HeightRequest & WidthRequest
RelativeLayout.WidthConstraint & RelativeLayout.HeightConstraint

HeightRequest y WidthRequest especifican el alto deseado y el ancho de la vista, pero pueden ser invalidadas por
los diseños según sea necesario. WidthConstraint y HeightConstraint admite establecer el alto y ancho como un
valor en relación con el diseño o en otra vista Propiedades, o como un valor constante.

Exploración de un diseño complejo


Cada uno de los diseños tienen ventajas y desventajas para crear diseños determinados. A lo largo de esta serie
de artículos de diseño, una aplicación de ejemplo creada con el mismo diseño de página que se implementa
mediante tres diseños diferentes.
Tenga en cuenta el XAML siguiente:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TheBusinessTumble.RelativeLayoutPage"
BackgroundColor="Maroon"
Title="RelativeLayout">
<ContentPage.Content>
<ScrollView>
<RelativeLayout>
<BoxView Color="Gray" HeightRequest="100"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width,
Factor=1}" />
<Button BorderRadius="35" x:Name="imageCircleBack"
BackgroundColor="Maroon" HeightRequest="70" WidthRequest="70" RelativeLayout.XConstraint="
BackgroundColor="Maroon" HeightRequest="70" WidthRequest="70" RelativeLayout.XConstraint="
{ConstraintExpression Type=RelativeToParent,Property=Width, Factor=.5, Constant = -35}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Factor=0, Property=Y, Constant=70}"
/>
<Button BorderRadius="30" BackgroundColor="Red" HeightRequest="60"
WidthRequest="60" RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView,
ElementName=imageCircleBack, Property=X, Factor=1,Constant=5}" RelativeLayout.YConstraint="
{ConstraintExpression Type=RelativeToParent, Factor=0, Property=Y, Constant=75}" />
<Label Text="User Name" FontAttributes="Bold" FontSize="26"
HorizontalTextAlignment="Center" RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent, Property=Y, Factor=0, Constant=140}" RelativeLayout.WidthConstraint="
{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1}" />
<Entry Text="Bio + Hashtags" TextColor="White" BackgroundColor="Maroon"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Y, Factor=0,
Constant=180}" RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width,
Factor=1}" />
<RelativeLayout BackgroundColor="White" RelativeLayout.YConstraint="
{ConstraintExpression Type=RelativeToParent, Property=Y, Factor=0, Constant=220}"
HeightRequest="60" RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Width, Factor=1}" >
<BoxView BackgroundColor="Black" WidthRequest="50"
HeightRequest="50" RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Y, Factor=0, Constant=5}" RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
Property=X, Factor=0, Constant=5}" />
<BoxView BackgroundColor="Maroon" WidthRequest="50"
HeightRequest="50" RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Y, Factor=0, Constant=5}" RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Width, Factor=0.5, Constant=}" />
<Label FontSize="14" TextColor="Black" Text="Accent Color"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Y,
Factor=0, Constant=20}" RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=X,
Factor=0, Constant=60}" />
<Label FontSize="14" TextColor="Black" Text="Primary Color"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Y,
Factor=0, Constant=20}" RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Width, Factor=0.5, Constant=55}" />
</RelativeLayout>
<RelativeLayout Padding="5,0,0,0">
<Label FontSize="14" Text="Age:" TextColor="White"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=305}"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width,
Factor=0, Constant=10}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Width,Factor=.25,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=50}" />
<Entry Text="35" TextColor="White" BackgroundColor="Maroon"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=280}"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width,
Factor=0.3, Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Width,Factor=0.75,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=50}" />
</RelativeLayout>
<RelativeLayout Padding="5,0,0,0">
<Label FontSize="14" Text="Interests:" TextColor="White"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=345}"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width,
Factor=0, Constant=10}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Width,Factor=.25,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=50}" />
<Entry Text="Xamarin.Forms" TextColor="White" BackgroundColor="Maroon"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=320}"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width,
Factor=0.3, Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Width,Factor=0.75,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=50}" />
</RelativeLayout>
<RelativeLayout Padding="5,0,0,0">
<Label FontSize="14" Text="Ask me about:" TextColor="White"
LineBreakMode="WordWrap"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=395}"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width,
Factor=0, Constant=10}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Width,Factor=.25,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=50}" />
<Entry Text="Xamarin, C#, .NET, Mono" TextColor="White"
BackgroundColor="Maroon"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=370}"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width,
Factor=0.3, Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Width,Factor=0.75,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,
Factor=0,Constant=50}" />
</RelativeLayout>
</RelativeLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>

El código anterior da como resultado el siguiente diseño:


Tenga en cuenta que RelativeLayouts s están anidadas, porque en algunos casos los diseños de anidamiento
puede ser más sencillo que presentar todos los elementos del diseño del mismo. Observe también que algunos
elementos están RelativeToView , ya que permite para el diseño más sencillo e intuitivo cuando las relaciones
entre las vistas de guían de posicionamiento.

Vínculos relacionados
Diseño (ejemplo)
Ejemplo de BusinessTumble (ejemplo)
Cuadrícula de Xamarin.Forms
11/07/2019 • 16 minutes to read • Edit Online

descargar el ejemplo
Grid permite organizar las vistas en filas y columnas. Las filas y columnas se pueden establecer para tener
tamaños proporcionales o tamaños absolutos. El Grid diseño no debe confundirse con las tablas tradicionales y
no está diseñada para presentar los datos tabulares. Grid no tiene el concepto de fila, columna o formato de
celdas. A diferencia de las tablas HTML, Grid está pensado únicamente para diseño de contenido.

Este artículo se trata:


Propósito – usos comunes de Grid .
Uso – cómo usar Grid para lograr el diseño deseado.
Las filas y columnas – especificar las filas y columnas para el Grid .
Colocación de las vistas – agregar vistas a la cuadrícula de columnas y filas específicas.
Espaciado – configurar los espacios entre las filas y columnas.
Intervalos – configurar elementos que se va a abarcar varias filas o columnas.
Propósito
Grid puede usarse para organizar las vistas en una cuadrícula. Esto es útil en un número de casos:
Organizar botones en una aplicación de calculadora
Organizar botones y alternativas en una cuadrícula, como iOS o Android pantallas de inicio
Organizar las vistas para que sean del mismo tamaño en una dimensión (como en algunas barras de
herramientas)

Uso
A diferencia de las tablas tradicionales, Grid no infiere el número y tamaño de filas y columnas del contenido. En
su lugar, Grid tiene RowDefinitions y ColumnDefinitions colecciones. Éstos contienen las definiciones de
dispuestos cuántas filas y columnas. Las vistas se agregan a Grid con la fila especificada y los índices de
columnas, que identifican qué fila y columna que se debe colocar en una vista.
Las filas y columnas
Información de fila y columna se almacena en Grid del RowDefinitions & ColumnDefinitions propiedades, que
son colecciones de cada de RowDefinition y ColumnDefinition objetos, respectivamente. RowDefinition tiene una
propiedad única, Height , y ColumnDefinition tiene una propiedad única, Width . Las opciones de alto y ancho
son los siguientes:
Auto – automáticamente los tamaños para ajustar el contenido de la fila o columna. Especificado como
GridUnitType.Auto en C# o como Auto en XAML.
Proportional(*) – cambia el tamaño de filas y columnas como una proporción del espacio restante.
Especificado como un valor y GridUnitType.Star en C# y como #* en XAML, con # que el valor deseado.
Especificación de una fila o columna con * hará que se va a rellenar el espacio disponible.
Absoluta – tamaños de las columnas y filas con valores específicos de alto y ancho fijos. Especificado como un
valor y GridUnitType.Absolute en C# y como # en XAML, con # que el valor deseado.

NOTE
Los valores de ancho para las columnas se establecen como * de forma predeterminada en Xamarin.Forms, lo que
garantiza que la columna rellenará el espacio disponible. También se establecen los valores del alto para las filas como * de
forma predeterminada.

Considere la posibilidad de una aplicación que necesita tres filas y dos columnas. La fila inferior debe ser
exactamente 200px alto y la fila superior debe ser dos veces más alto que la fila central. La columna izquierda
debe ser lo suficientemente ancho como para que quepa el contenido y la columna derecha debe rellenar el
espacio restante.
En XAML:

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="*" />
<RowDefinition Height="200" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>

En C#:

Grid grid = new Grid();


grid.RowDefinitions.Add (new RowDefinition { Height = new GridLength(2, GridUnitType.Star) });
grid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (1, GridUnitType.Star) });
grid.RowDefinitions.Add (new RowDefinition { Height = new GridLength(200)});
grid.ColumnDefinitions.Add (new ColumnDefinition{ Width = new GridLength (200) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) });

Colocación de las vistas en una cuadrícula


Para colocar las vistas en un Grid , deberá agregarlos como elementos secundarios a la cuadrícula y, después,
especifique qué fila y columna pertenecen en.
En XAML, utilice Grid.Row y Grid.Column en cada vista individual para especificar la selección de ubicación.
Tenga en cuenta que Grid.Row y Grid.Column especificar ubicación basándose en las listas de base cero de filas y
columnas. Esto significa que en una cuadrícula 4 x 4, la celda superior izquierda es (0,0) y la celda inferior derecha
es (3,3).
El Grid se muestra a continuación contiene cuatro celdas:
En XAML:

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="Top Left" Grid.Row="0" Grid.Column="0" />
<Label Text="Top Right" Grid.Row="0" Grid.Column="1" />
<Label Text="Bottom Left" Grid.Row="1" Grid.Column="0" />
<Label Text="Bottom Right" Grid.Row="1" Grid.Column="1" />
</Grid>

En C#:
var grid = new Grid();

grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star)});


grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star)});
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star)});
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star)});

var topLeft = new Label { Text = "Top Left" };


var topRight = new Label { Text = "Top Right" };
var bottomLeft = new Label { Text = "Bottom Left" };
var bottomRight = new Label { Text = "Bottom Right" };

grid.Children.Add(topLeft, 0, 0);
grid.Children.Add(topRight, 1, 0);
grid.Children.Add(bottomLeft, 0, 1);
grid.Children.Add(bottomRight, 1, 1);

El código anterior crea la cuadrícula con cuatro etiquetas, dos columnas y dos filas. Tenga en cuenta que cada
etiqueta tendrá el mismo tamaño y que las filas se expandirán para usar todo el espacio disponible.
En el ejemplo anterior, las vistas se agregan a la Grid.Children colección utilizando el Add sobrecarga que
especifique los argumentos izquierdo y superiores. Cuando se usa el Add sobrecarga que especifique a la
izquierda, derecha, superior y argumentos de la parte inferior, mientras la izquierda y argumentos superiores
siempre hará referencia a las celdas de la Grid , la derecha y argumentos de la parte inferior pueden parecer que
hacen referencia a las celdas que están fuera de la Grid . Esto es porque el argumento derecho siempre debe ser
mayor que el argumento izquierdo y el argumento final siempre debe ser mayor que el argumento superior. En el
ejemplo siguiente se muestra código equivalente que utiliza ambos Add sobrecargas:

// left, top
grid.Children.Add(topLeft, 0, 0);
grid.Children.Add(topRight, 1, 0);
grid.Children.Add(bottomLeft, 0, 1);
grid.Children.Add(bottomRight, 1, 1);

// left, right, top, bottom


grid.Children.Add(topLeft, 0, 1, 0, 1);
grid.Children.Add(topRight, 1, 2, 0, 1);
grid.Children.Add(bottomLeft, 0, 1, 1, 2);
grid.Children.Add(bottomRight, 1, 2, 1, 2);

Espaciado
Grid tiene propiedades para controlar el espaciado entre las filas y columnas. Las propiedades siguientes están
disponibles para personalizar el Grid :
ColumnSpacing – la cantidad de espacio entre las columnas. El valor predeterminado de esta propiedad es 6.
RowSpacing – la cantidad de espacio entre las filas. El valor predeterminado de esta propiedad es 6.
El XAML siguiente especifica un Grid con dos columnas, una fila y 5 px de espaciado entre columnas:

<Grid ColumnSpacing="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>

En C#:
var grid = new Grid { ColumnSpacing = 5 };
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star)});
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star)});

Intervalos
A menudo cuando se trabaja con una cuadrícula, hay un elemento que debe ocupar más de una fila o columna.
Considere la posibilidad de una aplicación de calculadora simple:

Tenga en cuenta que el botón 0 abarca dos columnas, al igual que las calculadoras integradas para cada
plataforma. Esto se logra mediante el ColumnSpan propiedad, que especifica cuántas columnas un elemento deben
ocupar. El XAML para ese botón:

<Button Text = "0" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" />

Y en C#:

Button zeroButton = new Button { Text = "0" };


controlGrid.Children.Add (zeroButton, 0, 4);
Grid.SetColumnSpan (zeroButton, 2);

Tenga en cuenta que en el código, los métodos estáticos de la Grid clase se utilizan para realizar cambios de
posicionamiento, incluidos los cambios a ColumnSpan y RowSpan . Tenga en cuenta que, con otras propiedades que
se pueden establecer en cualquier momento, a diferencia de las propiedades establecidas mediante los métodos
estáticos en ya deben puede estar también en la cuadrícula antes de modificarla.
El XAML completo para la aplicación de calculadora anterior es como sigue:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LayoutSamples.CalculatorGridXAML"
Title = "Calculator - XAML"
BackgroundColor="#404040">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="plainButton" TargetType="Button">
<Setter Property="BackgroundColor" Value="#eee"/>
<Setter Property="TextColor" Value="Black" />
<Setter Property="BorderRadius" Value="0"/>
<Setter Property="FontSize" Value="40" />
</Style>
<Style x:Key="darkerButton" TargetType="Button">
<Setter Property="BackgroundColor" Value="#ddd"/>
<Setter Property="TextColor" Value="Black" />
<Setter Property="BorderRadius" Value="0"/>
<Setter Property="FontSize" Value="40" />
</Style>
<Style x:Key="orangeButton" TargetType="Button">
<Setter Property="BackgroundColor" Value="#E8AD00"/>
<Setter Property="TextColor" Value="White" />
<Setter Property="BorderRadius" Value="0"/>
<Setter Property="FontSize" Value="40" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<Grid x:Name="controlGrid" RowSpacing="1" ColumnSpacing="1">
<Grid.RowDefinitions>
<RowDefinition Height="150" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="0" Grid.Row="0" HorizontalTextAlignment="End" VerticalTextAlignment="End"
TextColor="White"
FontSize="60" Grid.ColumnSpan="4" />
<Button Text = "C" Grid.Row="1" Grid.Column="0"
Style="{StaticResource darkerButton}" />
<Button Text = "+/-" Grid.Row="1" Grid.Column="1"
Style="{StaticResource darkerButton}" />
<Button Text = "%" Grid.Row="1" Grid.Column="2"
Style="{StaticResource darkerButton}" />
<Button Text = "div" Grid.Row="1" Grid.Column="3"
Style="{StaticResource orangeButton}" />
<Button Text = "7" Grid.Row="2" Grid.Column="0"
Style="{StaticResource plainButton}" />
<Button Text = "8" Grid.Row="2" Grid.Column="1"
Style="{StaticResource plainButton}" />
<Button Text = "9" Grid.Row="2" Grid.Column="2"
Style="{StaticResource plainButton}" />
<Button Text = "X" Grid.Row="2" Grid.Column="3"
Style="{StaticResource orangeButton}" />
<Button Text = "4" Grid.Row="3" Grid.Column="0"
Style="{StaticResource plainButton}" />
<Button Text = "5" Grid.Row="3" Grid.Column="1"
Style="{StaticResource plainButton}" />
<Button Text = "6" Grid.Row="3" Grid.Column="2"
Style="{StaticResource plainButton}" />
Style="{StaticResource plainButton}" />
<Button Text = "-" Grid.Row="3" Grid.Column="3"
Style="{StaticResource orangeButton}" />
<Button Text = "1" Grid.Row="4" Grid.Column="0"
Style="{StaticResource plainButton}" />
<Button Text = "2" Grid.Row="4" Grid.Column="1"
Style="{StaticResource plainButton}" />
<Button Text = "3" Grid.Row="4" Grid.Column="2"
Style="{StaticResource plainButton}" />
<Button Text = "+" Grid.Row="4" Grid.Column="3"
Style="{StaticResource orangeButton}" />
<Button Text = "0" Grid.ColumnSpan="2"
Grid.Row="5" Grid.Column="0" Style="{StaticResource plainButton}" />
<Button Text = "." Grid.Row="5" Grid.Column="2"
Style="{StaticResource plainButton}" />
<Button Text = "=" Grid.Row="5" Grid.Column="3"
Style="{StaticResource orangeButton}" />
</Grid>
</ContentPage.Content>
</ContentPage>

Observe que tanto la etiqueta en la parte superior de la cuadrícula y el botón cero occuping más de una columna.
Aunque se podría lograr un diseño similar con cuadrículas anidadas, la ColumnSpan & RowSpan enfoque es más
sencillo.
La implementación de C#:

public CalculatorGridCode ()
{
Title = "Calculator - C#";
BackgroundColor = Color.FromHex ("#404040");

var plainButton = new Style (typeof(Button)) {


Setters = {
new Setter { Property = Button.BackgroundColorProperty, Value = Color.FromHex ("#eee") },
new Setter { Property = Button.TextColorProperty, Value = Color.Black },
new Setter { Property = Button.BorderRadiusProperty, Value = 0 },
new Setter { Property = Button.FontSizeProperty, Value = 40 }
}
};
var darkerButton = new Style (typeof(Button)) {
Setters = {
new Setter { Property = Button.BackgroundColorProperty, Value = Color.FromHex ("#ddd") },
new Setter { Property = Button.TextColorProperty, Value = Color.Black },
new Setter { Property = Button.BorderRadiusProperty, Value = 0 },
new Setter { Property = Button.FontSizeProperty, Value = 40 }
}
};
var orangeButton = new Style (typeof(Button)) {
Setters = {
new Setter { Property = Button.BackgroundColorProperty, Value = Color.FromHex ("#E8AD00") },
new Setter { Property = Button.TextColorProperty, Value = Color.White },
new Setter { Property = Button.BorderRadiusProperty, Value = 0 },
new Setter { Property = Button.FontSizeProperty, Value = 40 }
}
};

var controlGrid = new Grid { RowSpacing = 1, ColumnSpacing = 1 };


controlGrid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (150) });
controlGrid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (1, GridUnitType.Star) });
controlGrid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (1, GridUnitType.Star) });
controlGrid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (1, GridUnitType.Star) });
controlGrid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (1, GridUnitType.Star) });
controlGrid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (1, GridUnitType.Star) });

controlGrid.ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) });


controlGrid.ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) });
controlGrid.ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) });
controlGrid.ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) });

var label = new Label {


Text = "0",
HorizontalTextAlignment = TextAlignment.End,
VerticalTextAlignment = TextAlignment.End,
TextColor = Color.White,
FontSize = 60
};
controlGrid.Children.Add (label, 0, 0);

Grid.SetColumnSpan (label, 4);

controlGrid.Children.Add (new Button { Text = "C", Style = darkerButton }, 0, 1);


controlGrid.Children.Add (new Button { Text = "+/-", Style = darkerButton }, 1, 1);
controlGrid.Children.Add (new Button { Text = "%", Style = darkerButton }, 2, 1);
controlGrid.Children.Add (new Button { Text = "div", Style = orangeButton }, 3, 1);
controlGrid.Children.Add (new Button { Text = "7", Style = plainButton }, 0, 2);
controlGrid.Children.Add (new Button { Text = "8", Style = plainButton }, 1, 2);
controlGrid.Children.Add (new Button { Text = "9", Style = plainButton }, 2, 2);
controlGrid.Children.Add (new Button { Text = "X", Style = orangeButton }, 3, 2);
controlGrid.Children.Add (new Button { Text = "4", Style = plainButton }, 0, 3);
controlGrid.Children.Add (new Button { Text = "5", Style = plainButton }, 1, 3);
controlGrid.Children.Add (new Button { Text = "6", Style = plainButton }, 2, 3);
controlGrid.Children.Add (new Button { Text = "-", Style = orangeButton }, 3, 3);
controlGrid.Children.Add (new Button { Text = "1", Style = plainButton }, 0, 4);
controlGrid.Children.Add (new Button { Text = "2", Style = plainButton }, 1, 4);
controlGrid.Children.Add (new Button { Text = "3", Style = plainButton }, 2, 4);
controlGrid.Children.Add (new Button { Text = "+", Style = orangeButton }, 3, 4);
controlGrid.Children.Add (new Button { Text = ".", Style = plainButton }, 2, 5);
controlGrid.Children.Add (new Button { Text = "=", Style = orangeButton }, 3, 5);

var zeroButton = new Button { Text = "0", Style = plainButton };


controlGrid.Children.Add (zeroButton, 0, 5);
Grid.SetColumnSpan (zeroButton, 2);

Content = controlGrid;
}

Vínculos relacionados
Creación de aplicaciones móviles con Xamarin.Forms, capítulo 17
Grid
Diseño (ejemplo)
Ejemplo de BusinessTumble (ejemplo)
El Xamarin.Forms FlexLayout
11/07/2019 • 43 minutes to read • Edit Online

descargar el ejemplo
Utilice FlexLayout para apilar o encapsula una colección de vistas secundarias.
Xamarin.Forms FlexLayout es nuevo en la versión 3.0 de Xamarin.Forms. Se basa en la hoja CSS Flexible diseño
módulo, normalmente conocido como flex diseño o flex cuadro, se denomina así porque incluye muchas opciones
flexibles para organizar los elementos secundarios en el diseño.
FlexLayout es similar a Xamarin.Forms StackLayout en que pueden organizar sus elementos secundarios
horizontal y verticalmente en una pila. Sin embargo, el FlexLayout también es capaz de ajuste sus elementos
secundarios, si hay demasiados como para caber en una sola fila o columna, y también tiene muchas opciones
para adaptarse a diversos tamaños de pantalla, la alineación y la orientación.
FlexLayout se deriva de Layout<View> y hereda un Children propiedad de tipo IList<View> .
FlexLayout define seis propiedades enlazables públicas y cinco propiedades enlazables adjuntas que afectan al
tamaño, orientación y alineación de sus elementos secundarios. (Si no está familiarizado con las propiedades
enlazables adjuntas, consulte el artículo propiedades adjuntas.) Estas propiedades se describen en detalle en las
secciones siguientes en las propiedades enlazables detalladamente y las propiedades enlazables
asociadas con detalle. Sin embargo, en este artículo comienza con una sección en algunas escenarios de uso
comunes de FlexLayout que muchas de estas propiedades describe de manera más informal. Hacia el final del
artículo, verá cómo combinar FlexLayout con hojas de estilos CSS.

Escenarios de uso comunes


El FlexLayoutDemos programa de ejemplo contiene varias páginas que se muestran algunos usos habituales de
FlexLayout y le permite experimentar con sus propiedades.

Uso de FlexLayout para una simple pila


El Simple pila página se muestra cómo FlexLayout puede sustituir un StackLayout pero con marcado más
sencillo. Todo el contenido de este ejemplo se define en la página XAML. El FlexLayout contiene cuatro
elementos secundarios:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FlexLayoutDemos"
x:Class="FlexLayoutDemos.SimpleStackPage"
Title="Simple Stack">

<FlexLayout Direction="Column"
AlignItems="Center"
JustifyContent="SpaceEvenly">

<Label Text="FlexLayout in Action"


FontSize="Large" />

<Image Source="{local:ImageResource FlexLayoutDemos.Images.SeatedMonkey.jpg}" />

<Button Text="Do-Nothing Button" />

<Label Text="Another Label" />


</FlexLayout>
</ContentPage>

Aquí es esa página se ejecuta en la plataforma Universal de Windows, iOS y Android:

Tres propiedades de FlexLayout se muestran en el SimpleStackPage.xaml archivo:


El Direction propiedad está establecida en un valor de la FlexDirection enumeración. De manera
predeterminada, es Row . Establecer la propiedad en Column hace que los elementos secundarios de la
FlexLayout se organicen en una sola columna de elementos.

Cuando los elementos de un FlexLayout se organizan en una columna, el FlexLayout se dice que tiene
una vertical eje principal y horizontal entre el eje.
El AlignItems propiedad es de tipo FlexAlignItems y especifica cómo se alinean los elementos en el eje
cruzado. El Center opción hace que cada elemento se centra horizontalmente.
Si usaba una StackLayout en lugar de un FlexLayout para esta tarea, ¿centrar todos los elementos
mediante la asignación de la HorizontalOptions propiedad de cada elemento para Center . El
HorizontalOptions propiedad no funciona para los elementos secundarios de un FlexLayout , pero la única
AlignItems propiedad cumple con el mismo objetivo. Si necesita, puede usar el AlignSelf adjunta la
propiedad enlazable para invalidar el AlignItems propiedad para elementos individuales:
<Label Text="FlexLayout in Action"
FontSize="Large"
FlexLayout.AlignSelf="Start" />

Con este cambio, éste Label se coloca en el borde izquierdo de la FlexLayout cuando el orden de lectura
es de izquierda a derecha.
El JustifyContent propiedad es de tipo FlexJustify y especifica cómo se organizan los elementos en el
eje principal. El SpaceEvenly opción asigna todo espacio vertical sobrante por igual entre todos los
elementos y por encima del primer elemento y por debajo del último elemento.
Si usaba una StackLayout, tiene que asignar el VerticalOptions propiedad de cada elemento para
CenterAndExpand para lograr un efecto similar. Pero la CenterAndExpand opción asignaría el doble de
espacio entre cada elemento que no sea anterior al primer elemento y después del último elemento. Puede
imitar la CenterAndExpand opción de VerticalOptions estableciendo el JustifyContent propiedad de
FlexLayout a SpaceAround .

Estos FlexLayout propiedades se describen con más detalle en la sección las propiedades enlazables
detalladamente a continuación.
Uso de FlexLayout de ajuste de los elementos
El foto ajuste página de la FlexLayoutDemos muestra cómo FlexLayout puede ajustar sus elementos
secundarios en filas o columnas adicionales. El archivo XAML crea instancias el FlexLayout y asigna dos
propiedades de la misma:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="FlexLayoutDemos.PhotoWrappingPage"
Title="Photo Wrapping">
<Grid>
<ScrollView>
<FlexLayout x:Name="flexLayout"
Wrap="Wrap"
JustifyContent="SpaceAround" />
</ScrollView>

<ActivityIndicator x:Name="activityIndicator"
IsRunning="True"
VerticalOptions="Center" />
</Grid>
</ContentPage>

El Direction propiedad de este FlexLayout no se establece, por lo que tiene el valor predeterminado de Row , lo
que significa que los elementos secundarios se organizan en filas y el eje principal es horizontal.
El Wrap propiedad es de un tipo de enumeración FlexWrap . Si hay demasiados elementos para que quepan en
una fila, valor de esta propiedad hace que los elementos que se va a ajustar a la siguiente fila.
Tenga en cuenta que el FlexLayout es un elemento secundario de un ScrollView . Si hay demasiadas filas para
que quepa en la página, el ScrollView tiene un valor predeterminado Orientation propiedad de Vertical y
permite el desplazamiento vertical.
El JustifyContent propiedad asigna espacio restante en el eje principal (el eje horizontal) para que cada elemento
está rodeado por la misma cantidad de espacio en blanco.
El archivo de código subyacente tiene acceso a una colección de fotos de ejemplo y los agrega a la Children
colección de los FlexLayout :
public partial class PhotoWrappingPage : ContentPage
{
// Class for deserializing JSON list of sample bitmaps
[DataContract]
class ImageList
{
[DataMember(Name = "photos")]
public List<string> Photos = null;
}

public PhotoWrappingPage ()
{
InitializeComponent ();

LoadBitmapCollection();
}

async void LoadBitmapCollection()


{
using (WebClient webClient = new WebClient())
{
try
{
// Download the list of stock photos
Uri uri = new Uri("https://raw.githubusercontent.com/xamarin/docs-
archive/master/Images/stock/small/stock.json");
byte[] data = await webClient.DownloadDataTaskAsync(uri);

// Convert to a Stream object


using (Stream stream = new MemoryStream(data))
{
// Deserialize the JSON into an ImageList object
var jsonSerializer = new DataContractJsonSerializer(typeof(ImageList));
ImageList imageList = (ImageList)jsonSerializer.ReadObject(stream);

// Create an Image object for each bitmap


foreach (string filepath in imageList.Photos)
{
Image image = new Image
{
Source = ImageSource.FromUri(new Uri(filepath))
};
flexLayout.Children.Add(image);
}
}
}
catch
{
flexLayout.Children.Add(new Label
{
Text = "Cannot access list of bitmap files"
});
}
}

activityIndicator.IsRunning = false;
activityIndicator.IsVisible = false;
}
}

Este es el programa de funcionamiento, progresivamente desplazado de arriba a abajo:


Diseño de página con FlexLayout
Hay un diseño estándar en el diseño web que llama el Santo Grial porque es un formato de diseño que es muy
conveniente, pero a menudo es difícil darse cuenta con la perfección. La presentación consta de un encabezado en
la parte superior de la página y un pie de página en la parte inferior, ambas extender a todo el ancho de la página.
Ocupa el centro de la página es el contenido principal, pero a menudo con un menú en columnas a la izquierda de
la información complementaria y de contenido (a veces denominado un reservar área) situado a la derecha.
Sección 5.4.1 de la especificación de diseño Flexible de cuadros de CSS describe cómo puede llevarse la piedra
angular diseño con un cuadro flex.
El Santo Grial diseño página de la FlexLayoutDemos muestra una implementación sencilla de este diseño
mediante uno FlexLayout anidado en otro. Dado que esta página está diseñada para un teléfono en modo
vertical, las áreas a la izquierda y derecha del área de contenido son solo 50 píxeles de ancho:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="FlexLayoutDemos.HolyGrailLayoutPage"
Title="Holy Grail Layout">

<FlexLayout Direction="Column">

<!-- Header -->


<Label Text="HEADER"
FontSize="Large"
BackgroundColor="Aqua"
HorizontalTextAlignment="Center" />

<!-- Body -->


<FlexLayout FlexLayout.Grow="1">

<!-- Content -->


<Label Text="CONTENT"
FontSize="Large"
BackgroundColor="Gray"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
FlexLayout.Grow="1" />

<!-- Navigation items-->


<BoxView FlexLayout.Basis="50"
FlexLayout.Order="-1"
Color="Blue" />

<!-- Aside items -->


<BoxView FlexLayout.Basis="50"
Color="Green" />

</FlexLayout>

<!-- Footer -->


<Label Text="FOOTER"
FontSize="Large"
BackgroundColor="Pink"
HorizontalTextAlignment="Center" />
</FlexLayout>
</ContentPage>

Aquí se está ejecutando:

Las áreas de navegación y aside se representan con un BoxView a la izquierda y derecha.


La primera FlexLayout en el XAML archivo tiene un principal eje vertical y contiene tres elementos secundarios
organizados en una columna. Estos son el encabezado, el cuerpo de la página y el pie de página. Anidado
FlexLayout tiene un eje horizontal principal con tres elementos secundarios organizados en una fila.

En este programa, se muestran tres propiedades enlazables adjuntadas:


El Order propiedad enlazable asociada se establece en la primera BoxView . Esta propiedad es un entero
con un valor predeterminado de 0. Puede utilizar esta propiedad para cambiar el orden de diseño. Por lo
general a los desarrolladores prefiere el contenido de la página que aparezca en el marcado antes de los
elementos de navegación y elementos de un lado. Establecer el Order propiedad en la primera BoxView
en un valor menor que sus hermanas otros hace que aparezca como el primer elemento de la fila. De
forma similar, puede asegurarse de que un elemento aparece en último lugar estableciendo el Order
propiedad en un valor mayor que sus elementos relacionados.
El Basis propiedad enlazable adjunta se establece en los dos BoxView elementos para darles un ancho de
50 píxeles. Esta propiedad es de tipo FlexBasis , una estructura que define una propiedad estática de tipo
FlexBasis denominado Auto , que es el valor predeterminado. Puede usar Basis para especificar un
tamaño de píxel o un porcentaje que indica cuánto espacio se ocupa el elemento en el eje principal. Se
llama una base porque especifica un tamaño de elemento que es la base del diseño posterior.
El Grow propiedad está establecida en anidado Layout y en el Label secundarios que representan el
contenido. Esta propiedad es de tipo float y tiene un valor predeterminado de 0. Cuando se establece en
un valor positivo, todo el espacio restante en el eje principal se asigna a ese elemento y a los elementos
relacionados con los valores positivos del Grow . El espacio se asigna proporcionalmente a los valores, algo
como la especificación de estrella en un Grid .
La primera Grow se establece la propiedad adjunta en anidado FlexLayout , lo que indica que este
FlexLayout es ocupar todo el espacio vertical no utilizado en el exterior FlexLayout . El segundo Grow se
establece la propiedad adjunta en el Label que representa el contenido, que indica que este contenido es
ocupar todo el espacio horizontal no utilizado en el interior FlexLayout .
También hay un proceso similar Shrink adjunta la propiedad enlazable que puede usar cuando el tamaño
de los elementos secundarios supera el tamaño de la FlexLayout pero no se desea el ajuste.
Elementos del catálogo con FlexLayout
El elementos del catálogo página en el FlexLayoutDemos es similar al ejemplo Example 1 en la sección 1.1 de
la especificación del cuadro de diseño CSS Flexexcepto en que muestran una serie de imágenes puede desplazar
horizontalmente y las descripciones de los tres objetos:
Cada uno de los tres monos es un FlexLayout contenidos en un Frame que tiene un ancho y el alto explícita, y
que también es un elemento secundario de un mayor FlexLayout . En este archivo XAML, la mayoría de las
propiedades de la FlexLayout elementos secundarios se especifican en los estilos, todos menos uno de los cuales
es un estilo implícito:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FlexLayoutDemos"
x:Class="FlexLayoutDemos.CatalogItemsPage"
Title="Catalog Items">
<ContentPage.Resources>
<Style TargetType="Frame">
<Setter Property="BackgroundColor" Value="LightYellow" />
<Setter Property="BorderColor" Value="Blue" />
<Setter Property="Margin" Value="10" />
<Setter Property="CornerRadius" Value="15" />
</Style>

<Style TargetType="Label">
<Setter Property="Margin" Value="0, 4" />
</Style>

<Style x:Key="headerLabel" TargetType="Label">


<Setter Property="Margin" Value="0, 8" />
<Setter Property="FontSize" Value="Large" />
<Setter Property="TextColor" Value="Blue" />
</Style>

<Style TargetType="Image">
<Setter Property="FlexLayout.Order" Value="-1" />
<Setter Property="FlexLayout.AlignSelf" Value="Center" />
</Style>

<Style TargetType="Button">
<Setter Property="Text" Value="LEARN MORE" />
<Setter Property="FontSize" Value="Large" />
<Setter Property="TextColor" Value="White" />
<Setter Property="BackgroundColor" Value="Green" />
<Setter Property="BorderRadius" Value="20" />
</Style>
</ContentPage.Resources>

<ScrollView Orientation="Both">
<FlexLayout>
<Frame WidthRequest="300"
HeightRequest="480">

<FlexLayout Direction="Column">
<Label Text="Seated Monkey"
Style="{StaticResource headerLabel}" />
<Label Text="This monkey is laid back and relaxed, and likes to watch the world go by."
/>
<Label Text=" &#x2022; Doesn't make a lot of noise" />
<Label Text=" &#x2022; Often smiles mysteriously" />
<Label Text=" &#x2022; Sleeps sitting up" />
<Image Source="{local:ImageResource FlexLayoutDemos.Images.SeatedMonkey.jpg}"
WidthRequest="180"
HeightRequest="180" />
<Label FlexLayout.Grow="1" />
<Button />
</FlexLayout>
</Frame>

<Frame WidthRequest="300"
HeightRequest="480">

<FlexLayout Direction="Column">
<FlexLayout Direction="Column">
<Label Text="Banana Monkey"
Style="{StaticResource headerLabel}" />
<Label Text="Watch this monkey eat a giant banana." />
<Label Text=" &#x2022; More fun than a barrel of monkeys" />
<Label Text=" &#x2022; Banana not included" />
<Image Source="{local:ImageResource FlexLayoutDemos.Images.Banana.jpg}"
WidthRequest="240"
HeightRequest="180" />
<Label FlexLayout.Grow="1" />
<Button />
</FlexLayout>
</Frame>

<Frame WidthRequest="300"
HeightRequest="480">

<FlexLayout Direction="Column">
<Label Text="Face-Palm Monkey"
Style="{StaticResource headerLabel}" />
<Label Text="This monkey reacts appropriately to ridiculous assertions and actions." />
<Label Text=" &#x2022; Cynical but not unfriendly" />
<Label Text=" &#x2022; Seven varieties of grimaces" />
<Label Text=" &#x2022; Doesn't laugh at your jokes" />
<Image Source="{local:ImageResource FlexLayoutDemos.Images.FacePalm.jpg}"
WidthRequest="180"
HeightRequest="180" />
<Label FlexLayout.Grow="1" />
<Button />
</FlexLayout>
</Frame>
</FlexLayout>
</ScrollView>
</ContentPage>

El estilo implícito para la Image incluye la configuración de dos propiedades enlazables asociadas de Flexlayout :

<Style TargetType="Image">
<Setter Property="FlexLayout.Order" Value="-1" />
<Setter Property="FlexLayout.AlignSelf" Value="Center" />
</Style>

El Orderde –causas 1 la Image elemento que se mostrarán en primer lugar en cada uno de los anidados
FlexLayout vistas independientemente de su posición dentro de la colección de elementos secundarios. El
AlignSelf propiedad de Center hace que el Image se centre en la FlexLayout . Esto reemplaza la configuración
de la AlignItems propiedad, que tiene un valor predeterminado de Stretch , lo que significa que el Label y
Button elementos secundarios se ajusta a todo el ancho de la FlexLayout .

Dentro de cada uno de los tres FlexLayout vistas, un espacio en blanco Label precede a la Button , pero tiene un
Grow de 1. Esto significa que todo el espacio vertical sobrante está asignado a este en blanco Label , que inserta
de forma eficaz el Button hasta la parte inferior.

Las propiedades enlazables en detalle


Ahora que ha visto algunas aplicaciones comunes de FlexLayout , las propiedades de FlexLayout se puede
explorar con más detalle. FlexLayout define las propiedades enlazables seis que establezca en el FlexLayout Sí,
ya sea en el código o XAML, la alineación y la orientación del control. (Una de estas propiedades, Position , no se
trata en este artículo.)
Puede experimentar con las restantes propiedades enlazables con cinco el experimentar página de la
FlexLayoutDemos ejemplo. Esta página le permite agregar o quitar elementos secundarios de un FlexLayout y
establecer las combinaciones de las cinco propiedades enlazables. Todos los elementos secundarios de la
FlexLayout son Label vistas de distintos colores y tamaños, con el Text propiedad establecida en un número
que corresponde a su posición en la Children colección.
Cuando el programa se inicia, cinco Picker vistas muestran los valores predeterminados de estos cinco
FlexLayout propiedades. El FlexLayout hacia la parte inferior de la pantalla contiene tres elementos secundarios:

Cada uno de los Label vistas tiene un fondo gris que muestra el espacio asignado a la que Label dentro de la
FlexLayout . El fondo de la FlexLayout es blanco azulado. Ocupa el área de la parte inferior de la página, excepto
un pequeño margen a la izquierda y derecha.
La propiedad Direction
El Direction propiedad es de tipo FlexDirection , una enumeración con cuatro miembros:
Column
ColumnReverse (o "columna-reverse" en XAML )
Row , el valor predeterminado
RowReverse (o "fila-reverse" en XAML )

En XAML, puede especificar el valor de esta propiedad mediante los nombres de miembro de enumeración en
minúsculas, mayúsculas, o mayúsculas y minúsculas mezcladas, o bien puede usar dos cadenas adicionales que se
muestra entre paréntesis que son los mismos que los indicadores CSS. (Las cadenas "columna-reverse" y "fila-
reverse" se definen en el FlexDirectionTypeConverter clase usada por el analizador XAML.)
Este es el experimento página que muestra (de izquierda a derecha), el Row dirección, Column dirección, y
ColumnReverse dirección:
Tenga en cuenta que para el Reverse opciones, los elementos a partir de la derecha o inferior.
La propiedad Wrap
El Wrap propiedad es de tipo FlexWrap , una enumeración con tres miembros:
NoWrap , el valor predeterminado
Wrap
Reverse (o "wrap-reverse" en XAML )

De izquierda a derecha, se muestran estas pantallas el NoWrap , Wrap y Reverse opciones para elementos
secundarios de 12:

Cuando el Wrap propiedad está establecida en NoWrap está restringido el eje principal (como se muestra en este
programa) y el eje principal no es amplia o lo suficientemente alto como para ajustarse a todos los elementos
secundarios, el FlexLayout intenta realizar los elementos más pequeños, como la captura de pantalla de iOS se
muestra. Puede controlar la shrinkness de los elementos de la Shrink propiedad enlazable adjunta.
La propiedad JustifyContent
El JustifyContent propiedad es de tipo FlexJustify , una enumeración con seis miembros:
Start (o "flex-start" en XAML ), el valor predeterminado
Center
End (o "flex-end" de XAML )
SpaceBetween (o "espacio entre" en XAML )
SpaceAround (o "espacio alrededor" en XAML )
SpaceEvenly

Esta propiedad especifica cómo se espacian los elementos en el eje principal, que es el eje horizontal en este
ejemplo:

En todas las capturas de pantalla de tres, el Wrap propiedad está establecida en Wrap . El Start predeterminado
se muestra en la captura de pantalla de Android anterior. La captura de pantalla de iOS aquí se muestra el Center
opción: todos los elementos se mueven al centro. Otras de las tres opciones a partir de la palabra Space asignar
el espacio adicional que no esté ocupado por los elementos. SpaceBetween asigna el espacio por igual entre los
elementos; SpaceAround coloca igual espacio alrededor de cada elemento, mientras que SpaceEvenly coloca igual
espacio entre cada elemento y antes del primer elemento y después del último elemento en la fila.
La propiedad AlignItems
El AlignItems propiedad es de tipo FlexAlignItems , una enumeración con cuatro miembros:
Stretch , el valor predeterminado
Center
Start (o "flex-start" en XAML )
End (o "flex-end" de XAML )

Esta es una de las dos propiedades (el otro es AlignContent ) que indica cómo se alinean los elementos
secundarios en el eje cruzado. Dentro de cada fila, los elementos secundarios se estira (como se muestra en la
captura de pantalla anterior) o alineados en el inicio, el centro o el final de cada elemento, como se muestra en las
siguientes capturas de pantalla de tres:
En la captura de pantalla de iOS, se alinean las partes superiores de todos los elementos secundarios. En las
capturas de pantalla de Android, los elementos se centran verticalmente según el elemento secundario más alto.
En la captura de pantalla UWP, la parte inferior de todos los elementos está alineada.
Para cualquier elemento individual, la AlignItems valor puede reemplazarse con el AlignSelf propiedad
enlazable adjunta.
La propiedad AlignContent
El AlignContent propiedad es de tipo FlexAlignContent , una enumeración con siete miembros:
Stretch , el valor predeterminado
Center
Start (o "flex-start" en XAML )
End (o "flex-end" de XAML )
SpaceBetween (o "espacio entre" en XAML )
SpaceAround (o "espacio alrededor" en XAML )
SpaceEvenly

Al igual que AlignItems , el AlignContent propiedad también se alinea los elementos secundarios en el eje
cruzado, pero afecta a todas las filas o columnas:

En la captura de pantalla de iOS, ambas filas están en la parte superior; en la captura de pantalla de Android que
están en el centro; y en la captura de pantalla UWP que están en la parte inferior. También se pueden espaciadas
las filas de varias maneras:

El AlignContent no tiene ningún efecto cuando hay solo una fila o columna.

Las propiedades enlazables asociadas en detalle


FlexLayout define las cinco propiedades enlazables adjuntas. Estas propiedades se establecen en los elementos
secundarios de la FlexLayout y pertenecen solo a ese elemento secundario determinado.
La propiedad AlignSelf
El AlignSelf propiedad enlazable adjunta es de tipo FlexAlignSelf , una enumeración con cinco miembros:
Auto , el valor predeterminado
Stretch
Center
Start (o "flex-start" en XAML )
End (o "flex-end" de XAML )

Para cualquier elemento secundario individual de la FlexLayout , esta configuración de invalidaciones de


propiedad el AlignItems propiedad establecida en el FlexLayout propio. El valor predeterminado de Auto
significa que se usarán el AlignItems configuración.
Para un Label elemento denominado label (o ejemplo), puede establecer el AlignSelf propiedad en el código
similar al siguiente:

FlexLayout.SetAlignSelf(label, FlexAlignSelf.Center);

Tenga en cuenta que no hay ninguna referencia a la FlexLayout primario de la Label . En XAML, establezca la
propiedad similar al siguiente:

<Label ... FlexLayout.AlignSelf="Center" ... />

La propiedad Order
El Order propiedad es de tipo int . El valor predeterminado es 0.
El Order propiedad le permite cambiar el orden que los elementos secundarios de la FlexLayout se organizan.
Normalmente, los elementos secundarios de un FlexLayout se organizan es el mismo orden que aparecen en la
Children colección. Este orden se puede invalidar estableciendo la Order propiedad enlazable se adjunta a un
valor entero distinto de cero en uno o más elementos secundarios. El FlexLayout , a continuación, organiza sus
elementos secundarios en función del valor de la Order propiedad en cada elemento secundario, pero los
elementos secundarios con el mismo Order configuración se organizan en el orden en que aparecen en la
Children colección.

La propiedad de base
El Basis propiedad enlazable adjunta indica la cantidad de espacio que se asigna a un elemento secundario de la
FlexLayout en el eje principal. El tamaño especificado por el Basis propiedad es el tamaño a lo largo del eje
principal del elemento primario FlexLayout . Por lo tanto, Basis indica el ancho de un elemento secundario
cuando los elementos secundarios se organizan en filas o el alto cuando los elementos secundarios se organizan
en columnas.
El Basis propiedad es de tipo FlexBasis , una estructura. Se puede especificar el tamaño en cualquier unidades
independientes del dispositivo o como un porcentaje del tamaño de la FlexLayout . El valor predeterminado de la
Basis es la propiedad estática FlexBasis.Auto , lo que significa que el elemento secundario solicitado se utiliza el
ancho o alto.
En el código, puede establecer el Basis propiedad para un Label denominado label a 40 unidades
independientes del dispositivo como esta:

FlexLayout.SetBasis(label, new FlexBasis(40, false));

El segundo argumento para el FlexBasis se denomina constructor isRelative e indica si el tamaño es relativo (
true ) o absoluta ( false ). El argumento tiene un valor predeterminado de false , por lo que también puede
usar el código siguiente:

FlexLayout.SetBasis(label, new FlexBasis(40));

Una conversión implícita de float a FlexBasis está definido, por lo que puede simplificar aún más:

FlexLayout.SetBasis(label, 40);

Puede establecer el tamaño del 25% de la FlexLayout primario similar al siguiente:

FlexLayout.SetBasis(label, new FlexBasis(0.25f, true));

Este valor fraccionario debe estar en el intervalo de 0 a 1.


En XAML, puede usar un número para un tamaño en unidades independientes del dispositivo:

<Label ... FlexLayout.Basis="40" ... />

O bien, puede especificar un porcentaje del intervalo de 0 a 100%:

<Label ... FlexLayout.Basis="25%" ... />

El base experimentar página de la FlexLayoutDemos ejemplo le permite experimentar con la Basis


propiedad. La página muestra una columna ajustada de cinco Label elementos con background y foreground
colores alternos. Dos Slider elementos le permiten especificar Basis valores para la segunda y cuarta Label :

La captura de pantalla de iOS a la izquierda muestra los dos Label elementos determinados altos en unidades
independientes del dispositivo. Muestra la pantalla Android determinado altos que suponen una fracción de la
altura total del FlexLayout . Si el Basis se establece en 100%, entonces el elemento secundario es el alto de la
FlexLayout y se ajustan a la columna siguiente y ocupar el alto de esa columna, como se muestra en la captura de
pantalla UWP: Parece como si los elementos cinco secundarios se organizan en una fila, pero en realidad están
ordenados en cinco columnas.
La propiedad de crecimiento
El Grow propiedad enlazable adjunta es de tipo int . El valor predeterminado es 0 y el valor debe ser mayor o
igual que 0.
El Grow propiedad desempeña un rol cuando la Wrap propiedad está establecida en NoWrap y la fila de
elementos secundarios tiene un ancho total menor que el ancho de la FlexLayout , o la columna de elementos
secundarios tiene un alto más corto que el FlexLayout . El Grow propiedad indica la forma de distribuir el espacio
entre los elementos secundarios sobrante.
En el crecer experimento página cinco Label elementos de alternancia de colores se organizan en una columna
y dos Slider elementos le permiten ajustar la Grow propiedad de la segunda y cuarta Label . La captura de
pantalla de iOS en el extremo izquierdo muestra el valor predeterminado Grow propiedades 0:

Si cualquier un elemento secundario no se especifica un positivo Grow valor, a continuación, ese elemento
secundario ocupa todo el espacio restante, como se muestra en la captura de pantalla de Android. También se
puede asignar este espacio entre dos o más elementos secundarios. En la captura de pantalla UWP, la Grow
propiedad del segundo Label se establece en 0,5, mientras el Grow propiedad de la cuarta Label es la 1.5, que
proporciona el cuarto Label tres veces más el espacio como el segundo sobrantes Label .
Cómo la vista secundaria utiliza ese espacio depende del tipo específico de elemento secundario. Para un Label ,
el texto se puede colocar dentro del espacio total de la Label mediante las propiedades HorizontalTextAlignment
y VerticalTextAlignment .
La propiedad de reducción
El Shrink propiedad enlazable adjunta es de tipo int . El valor predeterminado es 1 y el valor debe ser mayor o
igual que 0.
El Shrink propiedad desempeña un papel cuando la Wrap propiedad está establecida en NoWrap y el ancho
agregado de una fila de elementos secundarios es mayor que el ancho de la FlexLayout , o el alto agregado de
una sola columna de elementos secundarios es mayor que el alto de la FlexLayout . Normalmente el FlexLayout
mostrará estos elementos secundarios por limitante sus tamaños. El Shrink propiedad puede indicar qué
elementos secundarios tienen prioridad en el que se muestra en su tamaño completo.
El reducir experimento página crea un FlexLayout con una sola fila de cinco Label elementos secundarios que
requieren más espacio que el FlexLayout ancho. La captura de pantalla de iOS en el lado izquierdo muestra
todas las Label elementos con los valores predeterminados de 1:

En la captura de pantalla de Android, el Shrink valor para el segundo Label se establece en 0 y que Label se
muestra en su formato completo. Además, la cuarta Label tiene un Shrink valor mayor que uno, y ha reducido.
Muestra la captura de pantalla UWP Label elementos que se concede un Shrink el valor 0 para que puedan
mostrarse en su tamaño completo, si eso es posible.
Puede establecer tanto el Grow y Shrink valores para adaptarse a situaciones donde los tamaños de agregados
secundarios a veces pueden ser menor que o a veces mayor que el tamaño de la FlexLayout .

Estilo CSS con FlexLayout


Puede usar el estilos CSS característica se presentó con Xamarin.Forms 3.0 en relación con FlexLayout . El
elementos del catálogo de CSS página de la FlexLayoutDemos ejemplo duplica el diseño de la elementos
del catálogo página, pero con CSS hoja de estilos para muchos de los estilos:
La versión original CatalogItemsPage.xaml archivo tiene cinco Style definiciones en su Resources sección
con 15 Setter objetos. En el CssCatalogItemsPage.xaml archivo, que se ha reducido a dos Style definiciones
con sólo cuatro Setter objetos. Estos estilos complementan la hoja de estilos CSS para las propiedades que
actualmente no admite la característica de estilo CSS de Xamarin.Forms:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FlexLayoutDemos"
x:Class="FlexLayoutDemos.CssCatalogItemsPage"
Title="CSS Catalog Items">
<ContentPage.Resources>
<StyleSheet Source="CatalogItemsStyles.css" />

<Style TargetType="Frame">
<Setter Property="BorderColor" Value="Blue" />
<Setter Property="CornerRadius" Value="15" />
</Style>

<Style TargetType="Button">
<Setter Property="Text" Value="LEARN MORE" />
<Setter Property="BorderRadius" Value="20" />
</Style>
</ContentPage.Resources>

<ScrollView Orientation="Both">
<FlexLayout>
<Frame>
<FlexLayout Direction="Column">
<Label Text="Seated Monkey" StyleClass="header" />
<Label Text="This monkey is laid back and relaxed, and likes to watch the world go by."
/>
<Label Text=" &#x2022; Doesn't make a lot of noise" />
<Label Text=" &#x2022; Often smiles mysteriously" />
<Label Text=" &#x2022; Sleeps sitting up" />
<Image Source="{local:ImageResource FlexLayoutDemos.Images.SeatedMonkey.jpg}" />
<Label StyleClass="empty" />
<Button />
</FlexLayout>
</Frame>

<Frame>
<FlexLayout Direction="Column">
<Label Text="Banana Monkey" StyleClass="header" />
<Label Text="Watch this monkey eat a giant banana." />
<Label Text=" &#x2022; More fun than a barrel of monkeys" />
<Label Text=" &#x2022; Banana not included" />
<Image Source="{local:ImageResource FlexLayoutDemos.Images.Banana.jpg}" />
<Label StyleClass="empty" />
<Button />
</FlexLayout>
</Frame>

<Frame>
<FlexLayout Direction="Column">
<Label Text="Face-Palm Monkey" StyleClass="header" />
<Label Text="This monkey reacts appropriately to ridiculous assertions and actions." />
<Label Text=" &#x2022; Cynical but not unfriendly" />
<Label Text=" &#x2022; Seven varieties of grimaces" />
<Label Text=" &#x2022; Doesn't laugh at your jokes" />
<Image Source="{local:ImageResource FlexLayoutDemos.Images.FacePalm.jpg}" />
<Label StyleClass="empty" />
<Button />
</FlexLayout>
</Frame>
</FlexLayout>
</ScrollView>
</ContentPage>

Se hace referencia a la hoja de estilos CSS en la primera línea de la Resources sección:


<StyleSheet Source="CatalogItemsStyles.css" />

Observe también que se incluyen dos elementos en cada uno de los tres elementos StyleClass configuración:

<Label Text="Seated Monkey" StyleClass="header" />


···
<Label StyleClass="empty" />

Hacen referencia a los selectores en el CatalogItemsStyles.css hoja de estilos:

frame {
width: 300;
height: 480;
background-color: lightyellow;
margin: 10;
}

label {
margin: 4 0;
}

label.header {
margin: 8 0;
font-size: large;
color: blue;
}

label.empty {
flex-grow: 1;
}

image {
height: 180;
order: -1;
align-self: center;
}

button {
font-size: large;
color: white;
background-color: green;
}

Varios FlexLayout adjuntadas propiedades enlazables se hace referencia aquí. En el label.empty selector, verá el
flex-grow atributo, que determina el estilo vacío Label para ofrecer algún espacio en blanco por encima del
Button . El image selector contiene un order atributo y un align-self atributo, que corresponden a FlexLayout
enlazables propiedades adjuntas.
Hemos visto que puede establecer propiedades directamente en el FlexLayout y puede establecer las
propiedades enlazables adjuntas en los elementos secundarios de un FlexLayout . O bien, puede establecer estas
propiedades forma indirecta, mediante los estilos tradicionales basado en XAML o los estilos CSS. Lo importante
es conocer y comprender estas propiedades. Estas propiedades son lo que hace que el FlexLayout realmente
flexible.

FlexLayout con Xamarin.University


Vídeo de Flex Layout Xamarin.Forms 3.0
Vínculos relacionados
FlexLayoutDemos
Xamarin.Forms ScrollView
11/07/2019 • 6 minutes to read • Edit Online

descargar el ejemplo
ScrollView contiene los diseños y les permite desplazamiento fuera de la pantalla. ScrollView También se utiliza
para permitir que las vistas se mueva automáticamente a la parte visible de la pantalla cuando se muestra el
teclado.

En este artículo se tratan los aspectos siguientes:


Propósito – el propósito de ScrollView y cuando se utiliza.
Uso – uso ScrollView en la práctica.
Propiedades – propiedades públicas que se pueden leer y modificar.
Métodos – métodos públicos que se pueden llamar para desplazar la vista.
Eventos – eventos que pueden usarse para detectar cambios en los Estados de la vista.

Propósito
ScrollView puede utilizarse para asegurarse de que se muestran vistas mayor bien en los teléfonos más
pequeños. Por ejemplo, un diseño que funciona en un iPhone 6s puede quedar cortado en un iPhone 4s.
Mediante un ScrollView permitirían las partes recortadas del diseño que se mostrará en la pantalla más
pequeña.

Uso
NOTE
ScrollView s no debería estar anidado. Además, ScrollView s no debería estar anidado con otros controles que
proporcionan el desplazamiento, como ListView y WebView .

ScrollView expone un Content propiedad que se puede establecer en un diseño o vista única. Considere este
ejemplo de un diseño con un gran boxView, seguido por un Entry :

<ContentPage.Content>
<ScrollView>
<StackLayout>
<BoxView BackgroundColor="Red" HeightRequest="600" WidthRequest="150" />
<Entry />
</StackLayout>
</ScrollView>
</ContentPage.Content>
En C#:

var scroll = new ScrollView();


Content = scroll;
var stack = new StackLayout();
stack.Children.Add(new BoxView { BackgroundColor = Color.Red, HeightRequest = 600, WidthRequest = 600 });
stack.Children.Add(new Entry());

Antes de que el usuario se desplaza hacia abajo, solo el BoxView está visible:

Tenga en cuenta que cuando el usuario comienza a escribir texto en el Entry , la vista se desplaza para mantener
visible en pantalla:
Propiedades
ScrollView define las siguientes propiedades:
ContentSize Obtiene un Size valor que representa el tamaño del contenido.
Orientation Obtiene o establece un ScrollOrientation valor de enumeración que representa la dirección de
desplazamiento de la ScrollView .
ScrollX Obtiene un double que representa la actual posición de desplazamiento X.
ScrollY Obtiene un double que representa la posición de desplazamiento Y actual.
HorizontalScrollBarVisibility Obtiene o establece un ScrollBarVisibility valor que representa cuándo está
visible la barra de desplazamiento horizontal.
VerticalScrollBarVisibility Obtiene o establece un ScrollBarVisibility valor que representa el momento
de la barra de desplazamiento vertical está visible.

Métodos
ScrollView Proporciona un ScrollToAsync método, que se puede usar para desplazar la vista mediante
coordenadas o bien especificando una vista concreta que debe hacerse visible.
Cuando se usa coordenadas, especifique el x y y coordenadas, junto con un valor booleano que indica si el
desplazamiento debe animarse:

scroll.ScrollToAsync(0, 150, true); //scrolls so that the position at 150px from the top is visible

scroll.ScrollToAsync(label, ScrollToPosition.Start, true); //scrolls so that the label is at the start of the
list
Al desplazarse a un elemento determinado, el ScrollToPosition especifica enumeración donde en la vista del
elemento aparecerá:
Centro de – desplaza el elemento en el centro de la parte visible de la vista.
End – desplaza el elemento al final de la parte visible de la vista.
MakeVisible – desplaza el elemento para que sea visible dentro de la vista.
Iniciar – desplaza el elemento al principio de la parte visible de la vista.
El IsAnimated propiedad especifica cómo se desplazará la vista. Cuando establece en true, una animación suave,
se usará, en lugar de pasar al instante el contenido en la vista.

Eventos
ScrollView define un solo evento Scrolled . Scrolled se produce cuando ha finalizado la vista de
desplazamiento. El controlador de eventos Scrolled toma ScrolledEventArgs , que tiene el ScrollX y ScrollY
propiedades. El siguiente ejemplo muestra cómo actualizar una etiqueta con la posición de desplazamiento actual
de un ScrollView :

Label label = new Label { Text = "Position: " };


ScrollView scroll = new ScrollView();
scroll.Scrolled += (object sender, ScrolledEventArgs e) => {
label.Text = "Position: " + e.ScrollX + " x " + e.ScrollY;
};

Tenga en cuenta que las posiciones de desplazamiento pueden ser negativas, porque el efecto de rebote al
desplazarse al final de una lista.

Vínculos relacionados
Diseño (ejemplo)
Ejemplo de BusinessTumble (ejemplo)
Opciones de diseño de Xamarin.Forms
11/07/2019 • 10 minutes to read • Edit Online

descargar el ejemplo
Cada vista Xamarin.Forms tiene propiedades HorizontalOptions y VerticalOptions, del tipo LayoutOptions. En
este artículo se explica el efecto que tiene cada valor LayoutOptions en la alineación y la expansión de una vista.

Información general
El LayoutOptions estructura encapsula dos de las preferencias de diseño:
Alineación – preferido de la vista de la alineación, que determina su posición y tamaño en su diseño principal.
Expansión : se usa únicamente por un StackLayout e indica si la vista debe utilizar el espacio adicional, si está
disponible.
Estas preferencias de diseño se pueden aplicar a un View respecto a su elemento primario, estableciendo el
HorizontalOptions o VerticalOptions propiedad de la View a uno de los campos públicos de la LayoutOptions
estructura. Los campos públicos son los siguientes:
Start
Center
End
Fill
StartAndExpand
CenterAndExpand
EndAndExpand
FillAndExpand

El Start , Center , End ,y Fill campos se usan para definir la alineación de la vista del diseño del elemento
primario:
Para la alineación horizontal, Start posiciones el View en el lado izquierdo del diseño del elemento primario
y para la alineación vertical, coloca el View en la parte superior de la diseño del elemento primario.
Para la alineación horizontal y vertical, Center centra horizontalmente o verticalmente el View .
Para la alineación horizontal, End posiciones el View en el lado derecho del diseño del elemento primario y
para la alineación vertical, coloca el View en la parte inferior del diseño del elemento primario.
Para la alineación horizontal, Fill garantiza que el View rellena el ancho del diseño del elemento primario y
para la alineación vertical, garantiza que el View rellena el alto del diseño del elemento primario.
El StartAndExpand , CenterAndExpand , EndAndExpand , y FillAndExpand valores se usan para definir la preferencia de
alineación, y si la vista ocupan más espacio si está disponible dentro del elemento primario StackLayout .

NOTE
El valor predeterminado de una vista HorizontalOptions y VerticalOptions propiedades es LayoutOptions.Fill .

Alineación
Alineación controla cómo se coloca una vista en su diseño principal cuando el diseño del elemento primario
contiene espacio no utilizado (es decir, el diseño del elemento primario es mayor que el tamaño combinado de
todos sus elementos secundarios).
Un StackLayout solo respeta el Start , Center , End , y Fill LayoutOptions campos en las vistas secundarias
que se encuentran en la dirección opuesta para el StackLayout orientación. Por lo tanto, las vistas secundarias
dentro de una orientación vertical StackLayout puede establecer sus HorizontalOptions propiedades a uno de los
Start , Center , End , o Fill campos. De forma similar, las vistas secundarios dentro de una orientación
horizontal StackLayout puede establecer sus VerticalOptions propiedades a uno de los Start , Center , End , o
Fill campos.

Un StackLayout no respeta el Start , Center , End , y Fill LayoutOptions campos en las vistas secundarias que
se encuentran en la misma dirección que el StackLayout orientación. Por lo tanto, una orientación vertical
StackLayout omite el Start , Center , End , o Fill campos si están establecidos en el VerticalOptions
propiedades de vistas secundarias. De forma similar, una orientación horizontal StackLayout omite el Start ,
Center , End , o Fill campos si están establecidos en el HorizontalOptions propiedades de vistas secundarias.

NOTE
LayoutOptions.Fill por lo general invalidaciones cambiar el tamaño de las solicitudes que se especifican mediante el
HeightRequest y WidthRequest propiedades.

El ejemplo de código XAML siguiente muestra una orientación vertical StackLayout donde cada elemento
secundario Label establece su HorizontalOptions propiedad en uno de los campos de cuatro de alineación de la
LayoutOptions estructura:

<StackLayout Margin="0,20,0,0">
...
<Label Text="Start" BackgroundColor="Gray" HorizontalOptions="Start" />
<Label Text="Center" BackgroundColor="Gray" HorizontalOptions="Center" />
<Label Text="End" BackgroundColor="Gray" HorizontalOptions="End" />
<Label Text="Fill" BackgroundColor="Gray" HorizontalOptions="Fill" />
</StackLayout>

El código de C# equivalente se muestra a continuación:

Content = new StackLayout


{
Margin = new Thickness(0, 20, 0, 0),
Children = {
...
new Label { Text = "Start", BackgroundColor = Color.Gray, HorizontalOptions = LayoutOptions.Start },
new Label { Text = "Center", BackgroundColor = Color.Gray, HorizontalOptions = LayoutOptions.Center },
new Label { Text = "End", BackgroundColor = Color.Gray, HorizontalOptions = LayoutOptions.End },
new Label { Text = "Fill", BackgroundColor = Color.Gray, HorizontalOptions = LayoutOptions.Fill }
}
};

El código da como resultado el diseño que se muestra en las capturas de pantalla siguiente:
Expansión
Expansión controla si una vista ocupa más espacio, si está disponible dentro de un StackLayout . Si el
StackLayout contiene espacio no utilizado (es decir, el StackLayout es mayor que el tamaño combinado de todos
sus elementos secundarios), el espacio no utilizado se comparte de forma equitativa todas las vistas secundarias
que solicitan expansión estableciendo sus HorizontalOptions o VerticalOptions propiedades para un
LayoutOptions campo que utiliza el AndExpand sufijo. Tenga en cuenta que, cuando todo el espacio en el
StackLayout es utilizado, las opciones de expansión no tienen ningún efecto.

Un StackLayout sólo se puede expandir vistas secundarias en la dirección de su orientación. Por lo tanto, una
orientación vertical StackLayout puede expandir las vistas secundarias que establecen sus VerticalOptions
propiedades a uno de los StartAndExpand , CenterAndExpand , EndAndExpand , o FillAndExpand campos, si la
StackLayout contiene espacio no utilizado. De forma similar, una orientación horizontal StackLayout puede
expandir las vistas secundarias que establecen sus HorizontalOptions propiedades a uno de los StartAndExpand ,
CenterAndExpand , EndAndExpand , o FillAndExpand campos, si la StackLayout contiene espacio no utilizado.

Un StackLayout no se puede expandir vistas secundarias en la dirección opuesta a la orientación. Por lo tanto, en
orientación vertical StackLayout , estableciendo el HorizontalOptions propiedad en una vista secundaria a
StartAndExpand tiene el mismo efecto que establecer la propiedad en Start .

NOTE
Tenga en cuenta que habilitar la expansión no cambia el tamaño de una vista a menos que lo use
LayoutOptions.FillAndExpand .

El ejemplo de código XAML siguiente muestra una orientación vertical StackLayout donde cada elemento
secundario Label establece su VerticalOptions propiedad en uno de los campos de cuatro de expansión desde
la LayoutOptions estructura:

<StackLayout Margin="0,20,0,0">
...
<BoxView BackgroundColor="Red" HeightRequest="1" />
<Label Text="Start" BackgroundColor="Gray" VerticalOptions="StartAndExpand" />
<BoxView BackgroundColor="Red" HeightRequest="1" />
<Label Text="Center" BackgroundColor="Gray" VerticalOptions="CenterAndExpand" />
<BoxView BackgroundColor="Red" HeightRequest="1" />
<Label Text="End" BackgroundColor="Gray" VerticalOptions="EndAndExpand" />
<BoxView BackgroundColor="Red" HeightRequest="1" />
<Label Text="Fill" BackgroundColor="Gray" VerticalOptions="FillAndExpand" />
<BoxView BackgroundColor="Red" HeightRequest="1" />
</StackLayout>

El código de C# equivalente se muestra a continuación:

Content = new StackLayout


{
Margin = new Thickness(0, 20, 0, 0),
Children = {
...
new BoxView { BackgroundColor = Color.Red, HeightRequest = 1 },
new Label { Text = "StartAndExpand", BackgroundColor = Color.Gray, VerticalOptions =
LayoutOptions.StartAndExpand },
new BoxView { BackgroundColor = Color.Red, HeightRequest = 1 },
new Label { Text = "CenterAndExpand", BackgroundColor = Color.Gray, VerticalOptions =
LayoutOptions.CenterAndExpand },
new BoxView { BackgroundColor = Color.Red, HeightRequest = 1 },
new Label { Text = "EndAndExpand", BackgroundColor = Color.Gray, VerticalOptions =
LayoutOptions.EndAndExpand },
new BoxView { BackgroundColor = Color.Red, HeightRequest = 1 },
new Label { Text = "FillAndExpand", BackgroundColor = Color.Gray, VerticalOptions =
LayoutOptions.FillAndExpand },
new BoxView { BackgroundColor = Color.Red, HeightRequest = 1 }
}
};

El código da como resultado el diseño que se muestra en las capturas de pantalla siguiente:
Cada Label ocupa la misma cantidad de espacio en el StackLayout . Sin embargo, solo la última Label , que
establece su VerticalOptions propiedad FillAndExpand tiene un tamaño diferente. Además, cada Label
separados por una pequeña roja BoxView , lo que permite el espacio de la Label ocupa para verse fácilmente.

Resumen
En este artículo se explica el efecto que cada LayoutOptions tiene el valor de la estructura en la alineación y la
expansión de una vista, en relación con su elemento primario. El Start , Center , End , y Fill campos se usan
para definir la alineación de la vista del diseño del elemento primario y el StartAndExpand , CenterAndExpand ,
EndAndExpand , y FillAndExpand campos se usan para definir la preferencia de alineación y para determinar si la
vista ocupa más espacio, si está disponible dentro de un StackLayout .

Vínculos relacionados
LayoutOptions (ejemplo)
LayoutOptions
Margen y relleno
11/07/2019 • 3 minutes to read • Edit Online

Los márgenes y relleno de las propiedades de controlan el comportamiento de diseño cuando se procesa un
elemento en la interfaz de usuario. En este artículo se muestra la diferencia entre las dos propiedades y cómo
establecerlas.

Información general
Márgenes y relleno son conceptos de diseño relacionadas:
El Margin propiedad representa la distancia entre un elemento y sus elementos adyacentes y se usa para
controlar la posición del elemento representación y la posición de representación de sus vecinos. Margin se
pueden especificar valores en diseño y vista clases.
El Padding propiedad representa la distancia entre un elemento y sus elementos secundarios y se usa para
separar el control de su propio contenido. Padding se pueden especificar valores en diseño clases.

El siguiente diagrama ilustra los dos conceptos:

Tenga en cuenta que Margin los valores son aditivos. Por lo tanto, si dos elementos adyacentes especifican un
margen de 20 píxeles, la distancia entre los elementos será 40 píxeles. Además, márgenes y relleno son aditivos
cuando ambos se aplican, en que la distancia entre un elemento y cualquier contenido será el margen más
relleno.
Especifica un grosor
El Margin y Padding propiedades son de tipo Thickness . Existen tres posibilidades al crear un Thickness
estructura:
Crear un Thickness estructura definida por un único valor uniforme. El valor solo se aplica a la izquierda,
superior, derecha y lados de la parte inferior del elemento.
Crear un Thickness estructura definida por los valores horizontales y verticales. El valor horizontal
simétricamente se aplica a los lados izquierdo y derecho del elemento, con el valor vertical simétricamente
que se aplica a los lados superior e inferior del elemento.
Crear un Thickness estructura definida por cuatro valores distintos que se aplican a la izquierda, superior,
derecha y lados de la parte inferior del elemento.
El ejemplo de código XAML siguiente muestra las tres posibilidades:

<StackLayout Padding="0,20,0,0">
<Label Text="Xamarin.Forms" Margin="20" />
<Label Text="Xamarin.iOS" Margin="10, 15" />
<Label Text="Xamarin.Android" Margin="0, 20, 15, 5" />
</StackLayout>

El código de C# equivalente se muestra en el ejemplo de código siguiente:

var stackLayout = new StackLayout {


Padding = new Thickness(0,20,0,0),
Children = {
new Label { Text = "Xamarin.Forms", Margin = new Thickness (20) },
new Label { Text = "Xamarin.iOS", Margin = new Thickness (10, 25) },
new Label { Text = "Xamarin.Android", Margin = new Thickness (0, 20, 15, 5) }
}
};

NOTE
Thickness los valores pueden ser negativos, lo que normalmente recorta o el contenido de este.

Resumen
En este artículo se muestra la diferencia entre el Margin y Padding propiedades y cómo establecerlas. Las
propiedades controlan el comportamiento de diseño cuando se procesa un elemento en la interfaz de usuario.

Vínculos relacionados
Margen
Padding
Grosor
Orientación del dispositivo
11/07/2019 • 18 minutes to read • Edit Online

descargar el ejemplo
Es importante tener en cuenta cómo se utilizará la aplicación y cómo se puede incorporar la orientación horizontal
para mejorar la experiencia del usuario. Diseños individuales pueden diseñarse para dar cabida a varias
orientaciones y mejor usa el espacio disponible. En el nivel de aplicación, rotación puede habilitar o deshabilitar.

Controlar la orientación
Cuando se usa Xamarin.Forms, el método admitido para controlar la orientación del dispositivo es usar la
configuración para cada proyecto individual.
iOS
En iOS, la orientación del dispositivo está configurado para las aplicaciones que usan el Info.plist archivo. Este
archivo incluirá la configuración de orientación para iPhone y iPod, así como la configuración para iPad si la
aplicación incluye como un destino. Las siguientes son instrucciones específicas para su IDE. Use las opciones del
IDE en la parte superior de este documento para seleccionar qué instrucciones que gustaría ver:
Visual Studio
Visual Studio para Mac
En Visual Studio, abra el proyecto de iOS y abra Info.plist. El archivo se abrirá en un panel de configuración, a
partir de la pestaña de información de implementación de iPhone:

Para configurar la orientación de iPad, seleccione el iPad información sobre implementación ficha en la parte
superior izquierda del panel, a continuación, seleccione desde las orientaciones disponibles:
Android
Para controlar la orientación en Android, abra MainActivity.cs y establezca la orientación mediante el atributo
decorar el MainActivity clase:

namespace MyRotatingApp.Droid
{
[Activity (Label = "MyRotatingApp.Droid", Icon = "@drawable/icon", Theme = "@style/MainTheme",
MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
ScreenOrientation = ScreenOrientation.Landscape)] //This is what controls orientation
public class MainActivity : FormsAppCompatActivity
{
protected override void OnCreate (Bundle bundle)
...

Xamarin.Android admite varias opciones para especificar la orientación:


Panorama – fuerza la orientación de la aplicación a horizontal, independientemente de los datos del sensor.
Vertical – fuerza la orientación de la aplicación sea vertical, independientemente de los datos del sensor.
Usuario – hace que la aplicación que se presentará con orientación preferida del usuario.
Detrás de – hace que la orientación de la aplicación a la misma que la orientación de la actividad detrás de él.
Sensor – hace que la orientación de la aplicación se puede determinar el sensor, incluso si el usuario ha
deshabilitado la rotación automática.
SensorLandscape – hace que la aplicación utiliza orientación horizontal al uso de datos del sensor para
cambiar la dirección de la pantalla está orientado a (de modo que no se muestra la pantalla como boca abajo).
SensorPortrait – hace que la aplicación utiliza orientación vertical al uso de datos del sensor para cambiar la
dirección de la pantalla está orientado a (de modo que no se muestra la pantalla como boca abajo).
ReverseLandscape – hace que la aplicación utiliza orientación horizontal, orientado a la dirección contraria a
habitual, de forma que aparezca "boca abajo".
ReversePortrait – hace que la aplicación utiliza orientación vertical, orientado a la dirección contraria a
habitual, de forma que aparezca "boca abajo".
FullSensor – hace que la aplicación se basan en los datos del sensor para seleccionar la orientación correcta
(fuera de la posible 4).
FullUser – hace que la aplicación para usar las preferencias del usuario orientación. Si está habilitada la
rotación automática, pueden usar todas las 4 orientaciones.
UserLandscape – [no admite] hace que la aplicación utiliza orientación horizontal, a menos que el usuario
tiene la rotación automática está habilitada, en cuyo caso usará el sensor de orientación. Esta opción
interrumpirá la compilación.
UserPortrait – [no admite] hace que la aplicación utiliza orientación vertical, a menos que el usuario tiene la
rotación automática está habilitada, en cuyo caso usará el sensor de orientación. Esta opción interrumpirá la
compilación.
Bloqueado – [no admite] hace que la aplicación para usar la orientación de pantalla, lo que sea en el
lanzamiento, sin responder a los cambios en el dispositivo físico de la orientación. Esta opción interrumpirá la
compilación.
Tenga en cuenta que las API nativas de Android proporcionan un gran control sobre cómo se administra la
orientación, incluidas las opciones que contradicen explícitamente al usuario expresan las preferencias.
Plataforma universal de Windows
En la plataforma Universal de Windows (UWP ), orientaciones admitidas se establecen el Package.appxmanifest
archivo. Abriendo el manifiesto se mostrará un panel de configuración donde se pueden seleccionar orientaciones
admitidas.

Reaccionar a los cambios de orientación


Xamarin.Forms no ofrece los eventos nativos para notificar a la aplicación de los cambios de orientación en código
compartido. Sin embargo, el SizeChanged eventos de la Page se desencadena cuando el ancho o alto de la Page
cambios. Cuando el ancho de la Page es mayor que el alto, el dispositivo está en modo horizontal. Para obtener
más información, consulte mostrar una imagen basada en la orientación de pantalla.

NOTE
Hay un paquete de NuGet existente y gratuito para recibir notificaciones de cambios de orientación en código compartido.
Consulte la repositorio de GitHub para obtener más información.

Como alternativa, es posible invalidar el OnSizeAllocated método en un Page , insertar cualquier diseño cambie la
lógica no existe. El OnSizeAllocated se llama al método cada vez que un Page se asigna un nuevo tamaño, lo que
sucede cada vez que se gira el dispositivo. Tenga en cuenta que la implementación base de OnSizeAllocated
realiza funciones de diseño importante, por lo que es importante llamar a la implementación base en la
invalidación:

protected override void OnSizeAllocated(double width, double height)


{
base.OnSizeAllocated(width, height); //must be called
}

Error al dar ese paso dará como resultado una página que no funciona.
Tenga en cuenta que el OnSizeAllocated método puede llamarse varias veces cuando se gira un dispositivo.
Cambiar el diseño cada vez es un desperdicio de recursos y puede provocar parpadeos. Considere el uso de una
variable de instancia dentro de la página para seguir si es la orientación en horizontal o vertical y solo vuelve a
dibujar cuando se produce un cambio:

private double width = 0;


private double height = 0;

protected override void OnSizeAllocated(double width, double height)


{
base.OnSizeAllocated(width, height); //must be called
if (this.width != width || this.height != height)
{
this.width = width;
this.height = height;
//reconfigure layout
}
}
Una vez que se ha detectado un cambio en la orientación del dispositivo, es posible que desee agregar o quitar
vistas adicionales desde la interfaz de usuario para reaccionar ante el cambio en el espacio disponible. Por
ejemplo, considere la calculadora integrada en cada plataforma en vertical:

y horizontal:
Tenga en cuenta que las aplicaciones aprovechar el espacio disponible mediante la adición de más funcionalidad
en horizontal.

Diseño dinámico
Es posible a las interfaces de diseño mediante los diseños integrados para que hagan el cambio correctamente
cuando se gira el dispositivo. Al diseñar las interfaces que seguirán siendo atractivo al responder a los cambios de
orientación, tenga en cuenta las siguientes reglas generales:
Preste atención a proporciones – cambios en la orientación pueden causar problemas cuando se realizan
determinadas suposiciones con respecto a la relación. Por ejemplo, una vista que tendría una gran cantidad de
espacio en 1/3 del espacio vertical de una pantalla en posición vertical no cabrá en 1/3 del espacio vertical en
horizontal.
Debe tener cuidado con los valores absolutos – valores absolutos (píxeles) que tengan sentido en vertical
pueden no tener sentido en horizontal. Cuando se necesitan valores absolutos, utilice los diseños anidados
para aislar su impacto. Por ejemplo, sería razonable utilizar valores absolutos en un TableView ItemTemplate
cuando la plantilla de elemento tiene un alto uniforme garantizado.
Las reglas anteriores también se aplican al implementar las interfaces para varios tamaños de pantalla y son
generalmente considera mejores prácticas. El resto de esta guía explica ejemplos específicos de diseños con
capacidad de respuesta con cada uno de los diseños principales en Xamarin.Forms.
NOTE
Para mayor claridad, las siguientes secciones muestran cómo implementar diseños con capacidad de respuesta con un solo
tipo de Layout a la vez. En la práctica, a menudo resulta más sencillo mezclar Layout s para lograr un diseño deseado con
la más sencilla o más intuitivo Layout para cada componente.

StackLayout
Tenga en cuenta la siguiente aplicación, se muestra en vertical:

y horizontal:
Que se logra con el XAML siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveLayout.StackLayoutPageXaml"
Title="Stack Photo Editor - XAML">
<ContentPage.Content>
<StackLayout Spacing="10" Padding="5" Orientation="Vertical"
x:Name="outerStack"> <!-- can change orientation to make responsive -->
<ScrollView>
<StackLayout Spacing="5" HorizontalOptions="FillAndExpand"
WidthRequest="1000">
<StackLayout Orientation="Horizontal">
<Label Text="Name: " WidthRequest="75"
HorizontalOptions="Start" />
<Entry Text="deer.jpg"
HorizontalOptions="FillAndExpand" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Date: " WidthRequest="75"
HorizontalOptions="Start" />
<Entry Text="07/05/2015"
HorizontalOptions="FillAndExpand" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Tags:" WidthRequest="75"
HorizontalOptions="Start" />
<Entry Text="deer, tiger"
HorizontalOptions="FillAndExpand" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Button Text="Save" HorizontalOptions="FillAndExpand" />
</StackLayout>
</StackLayout>
</ScrollView>
<Image Source="deer.jpg" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

Algunos C# se utiliza para cambiar la orientación de outerStack según la orientación del dispositivo:

protected override void OnSizeAllocated (double width, double height){


base.OnSizeAllocated (width, height);
if (width != this.width || height != this.height) {
this.width = width;
this.height = height;
if (width > height) {
outerStack.Orientation = StackOrientation.Horizontal;
} else {
outerStack.Orientation = StackOrientation.Vertical;
}
}
}

Tenga en cuenta lo siguiente:


outerStack se ajusta para presentar los controles y la imagen como una pila horizontal o vertical según la
orientación, para aprovechar al máximo el espacio disponible.
AbsoluteLayout
Tenga en cuenta la siguiente aplicación, se muestra en vertical:
y horizontal:
Que se logra con el XAML siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveLayout.AbsoluteLayoutPageXaml"
Title="AbsoluteLayout - XAML" BackgroundImageSource="deer.jpg">
<ContentPage.Content>
<AbsoluteLayout>
<ScrollView AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="PositionProportional,SizeProportional">
<AbsoluteLayout>
<Image Source="deer.jpg"
AbsoluteLayout.LayoutBounds=".5,0,300,300"
AbsoluteLayout.LayoutFlags="PositionProportional" />
<BoxView Color="#CC1A7019" AbsoluteLayout.LayoutBounds=".5
300,.7,50" AbsoluteLayout.LayoutFlags="XProportional
WidthProportional" />
<Label Text="deer.jpg" AbsoluteLayout.LayoutBounds = ".5
310,1, 50" AbsoluteLayout.LayoutFlags="XProportional
WidthProportional" HorizontalTextAlignment="Center" TextColor="White" />
</AbsoluteLayout>
</ScrollView>
<Button Text="Previous" AbsoluteLayout.LayoutBounds="0,1,.5,60"
AbsoluteLayout.LayoutFlags="PositionProportional
WidthProportional"
BackgroundColor="White" TextColor="Green" BorderRadius="0" />
<Button Text="Next" AbsoluteLayout.LayoutBounds="1,1,.5,60"
AbsoluteLayout.LayoutFlags="PositionProportional
WidthProportional" BackgroundColor="White"
TextColor="Green" BorderRadius="0" />
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>

Tenga en cuenta lo siguiente:


Debido al modo en que se ha diseñado la página, no hay ninguna necesidad de código de procedimientos
introducir la capacidad de respuesta.
El ScrollView se usa para permitir que la etiqueta esté visible incluso cuando el alto de la pantalla es menor
que la suma de la altura fija de los botones y la imagen.
RelativeLayout
Tenga en cuenta la siguiente aplicación, se muestra en vertical:
y horizontal:
Que se logra con el XAML siguiente:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveLayout.RelativeLayoutPageXaml"
Title="RelativeLayout - XAML"
BackgroundImageSource="deer.jpg">
<ContentPage.Content>
<RelativeLayout x:Name="outerLayout">
<BoxView BackgroundColor="#AA1A7019"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=1}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=0,Constant=0}" />
<ScrollView
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=1}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1,Constant=-60}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=0,Constant=0}">
<RelativeLayout>
<Image Source="deer.jpg" x:Name="imageDeer"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.8}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.1}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=0,Constant=10}" />
<Label Text="deer.jpg" HorizontalTextAlignment="Center"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=1}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=0,Constant=75}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToView,ElementName=imageDeer,Property=Height,Factor=1,Constant=20}"
/>
</RelativeLayout>

</ScrollView>

<Button Text="Previous" BackgroundColor="White" TextColor="Green" BorderRadius="0"


RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1,Constant=-60}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=60}"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.5}"
/>
<Button Text="Next" BackgroundColor="White" TextColor="Green" BorderRadius="0"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.5}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=1,Constant=-60}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=0,Constant=60}"
RelativeLayout.WidthConstraint="{ConstraintExpression
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.5}"
/>
</RelativeLayout>
</ContentPage.Content>
</ContentPage>

Tenga en cuenta lo siguiente:


Debido al modo en que se ha diseñado la página, no hay ninguna necesidad de código de procedimientos
introducir la capacidad de respuesta.
El ScrollView se usa para permitir que la etiqueta esté visible incluso cuando el alto de la pantalla es menor
que la suma de la altura fija de los botones y la imagen.
Cuadrícula
Tenga en cuenta la siguiente aplicación, se muestra en vertical:

y horizontal:
Que se logra con el XAML siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResponsiveLayout.GridPageXaml"
Title="Grid - XAML">
<ContentPage.Content>
<Grid x:Name="outerGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<Grid x:Name="innerGrid" Grid.Row="0" Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="deer.jpg" Grid.Row="0" Grid.Column="0" HeightRequest="300" WidthRequest="300"
/>
<Grid x:Name="controlsGrid" Grid.Row="0" Grid.Column="1" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="Name:" Grid.Row="0" Grid.Column="0" />
<Label Text="Date:" Grid.Row="1" Grid.Column="0" />
<Label Text="Tags:" Grid.Row="2" Grid.Column="0" />
<Entry Grid.Row="0" Grid.Column="1" />
<Entry Grid.Row="1" Grid.Column="1" />
<Entry Grid.Row="2" Grid.Column="1" />
</Grid>
</Grid>
<Grid x:Name="buttonsGrid" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Previous" Grid.Column="0" />
<Button Text="Save" Grid.Column="1" />
<Button Text="Next" Grid.Column="2" />
</Grid>
</Grid>
</ContentPage.Content>
</ContentPage>

Junto con el siguiente código de procedimientos para controlar los cambios de rotación:
private double width;
private double height;

protected override void OnSizeAllocated (double width, double height){


base.OnSizeAllocated (width, height);
if (width != this.width || height != this.height) {
this.width = width;
this.height = height;
if (width > height) {
innerGrid.RowDefinitions.Clear();
innerGrid.ColumnDefinitions.Clear ();
innerGrid.RowDefinitions.Add (new RowDefinition{ Height = new GridLength (1, GridUnitType.Star)
});
innerGrid.ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (1,
GridUnitType.Star) });
innerGrid.ColumnDefinitions.Add (new ColumnDefinition { Width = new GridLength (1,
GridUnitType.Star) });
innerGrid.Children.Remove (controlsGrid);
innerGrid.Children.Add (controlsGrid, 1, 0);
} else {
innerGrid.RowDefinitions.Clear();
innerGrid.ColumnDefinitions.Clear ();
innerGrid.ColumnDefinitions.Add (new ColumnDefinition{ Width = new GridLength (1,
GridUnitType.Star) });
innerGrid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (1, GridUnitType.Auto)
});
innerGrid.RowDefinitions.Add (new RowDefinition { Height = new GridLength (1, GridUnitType.Star)
});
innerGrid.Children.Remove (controlsGrid);
innerGrid.Children.Add (controlsGrid, 0, 1);
}
}
}

Tenga en cuenta lo siguiente:


Debido al modo en que se ha diseñado la página, hay un método para cambiar la posición de cuadrícula de los
controles.

Vínculos relacionados
Diseño (ejemplo)
Ejemplo de BusinessTumble (ejemplo)
Diseño dinámico (ejemplo)
Mostrar una imagen basada en la orientación de pantalla
Diseño para aplicaciones de tableta y escritorio
11/07/2019 • 5 minutes to read • Edit Online

Xamarin.Forms es compatible con todos los tipos de dispositivo disponibles en las plataformas compatibles, por lo
que los teléfonos, además de las aplicaciones también pueden ejecutar:
iPad,
Tabletas Android,
Tabletas de Windows y equipos de escritorio (que ejecutan Windows 10).
Esta página se describe brevemente:
admitidos tipos de dispositivo, y
cómo optimizar diseños para tabletas y teléfonos.

Tipos de dispositivos
Dispositivos de pantalla de mayor tamaño están disponibles para todas las plataformas compatibles con
Xamarin.Forms.
iPad (iOS )
La plantilla de Xamarin.Forms automáticamente incluye soporte técnico de iPad configurando el Info.plist >
dispositivos si se establece en Universal (lo que significa que se admiten iPhone y iPad).
Para proporcionar una experiencia de inicio agradable y asegúrese de que se usa la resolución de pantalla
completa en todos los dispositivos, debe asegurarse de que un pantalla de inicio específicas de iPad (utilizar un
guión gráfico) se proporciona. Esto garantiza que la aplicación se representa correctamente en dispositivos iPad
con mini, iPad y iPad Pro.
Antes de iOS 9 ocupaba todas las aplicaciones de la pantalla completa en el dispositivo, pero ahora pueden
realizar algunas iPad dividir la pantalla multitarea. Esto significa que la aplicación puede tardar hasta simplemente
una columna delgada en el lateral de la pantalla, 50% del ancho de la pantalla, o toda la pantalla.
Funcionalidad de pantalla dividida significa que debe diseñar la aplicación para funcionar bien con tan solo 320
píxeles de anchos o tanto como 1366 píxeles de ancho.
Tabletas Android
El ecosistema de Android tiene una gran variedad de tamaños de pantalla compatibles, desde teléfonos pequeños
hasta grandes tabletas. Xamarin.Forms puede admitir todos los tamaños de pantalla, pero como con las otras
plataformas que es posible que desea ajustar la interfaz de usuario para dispositivos de mayor tamaño.
Cuando se admiten muchas diferentes resoluciones de pantalla, puede proporcionar los recursos de imagen
nativa en diferentes tamaños para optimizar la experiencia del usuario. Revise el recursos de Android
documentación (y, en particular crear recursos en varios tamaños de pantalla) para obtener más información
sobre cómo estructurar las carpetas y los nombres de archivo en su aplicación Android proyecto para incluir los
recursos de la imagen optimizada de la aplicación.
Escritorios y tabletas de Windows
Para admitir las tabletas y equipos de escritorio con Windows, deberá usar compatibilidad con Windows UWP,
que compila las aplicaciones universales que se ejecutan en Windows 10.
Aplicaciones que se ejecutan en equipos de escritorio y tabletas de Windows que pueden ajustarse dimensiones
arbitrarias además al funcionamiento de pantalla completa.
Optimizar para tableta y escritorio
Puede ajustar la interfaz de usuario de Xamarin.Forms en función de si un teléfono o se usa el dispositivo de
escritorio o tableta. Esto significa que puede optimizar la experiencia del usuario para dispositivos con pantallas
grandes, como tabletas y equipos de escritorio.
Device.Idiom
Puede usar el Device clase para cambiar el comportamiento de la interfaz de usuario o aplicación. Mediante el
Device.Idiom enumeración puede

if (Device.Idiom == TargetIdiom.Phone)
{
HeroImage.Source = ImageSource.FromFile("hero.jpg");
} else {
HeroImage.Source = ImageSource.FromFile("herotablet.jpg");
}

Este enfoque se puede expandir para realizar cambios significativos en los diseños de página individuales, o
incluso para procesar completamente diferentes páginas en pantallas más grandes.
Aprovechamiento de MasterDetailPage
El MasterDetailPage es ideal para pantallas más grandes, especialmente en el iPad que utiliza el
UISplitViewController para proporcionar una experiencia de iOS nativo.

Revisión esta entrada de blog de Xamarin para ver cómo se puede adaptar la interfaz de usuario para que los
teléfonos usan un diseño y pantallas más grandes pueden utilizar otra (con el MasterDetailPage ).

Vínculos relacionados
Blog de Xamarin
Ejemplo de MyShoppe
Diseños enlazables en Xamarin.Forms
11/07/2019 • 8 minutes to read • Edit Online

descargar el ejemplo
Los diseños enlazables permiten que cualquier clase de diseño que deriva de la clase Layout<T> genere su
contenido al enlazarse a una colección de elementos, con la opción de establecer la apariencia de cada elemento
con una DataTemplate . Los diseños enlazables los proporciona la clase BindableLayout , que expone las siguientes
propiedades adjuntas:
ItemsSource : especifica la colección de elementos IEnumerable que va a mostrar el diseño.
ItemTemplate : especifica la DataTemplate que se aplicará a cada elemento de la colección de elementos que
muestra el diseño.
ItemTemplateSelector : especifica el DataTemplateSelector que se usará para elegir una DataTemplate para un
elemento en tiempo de ejecución.
Estas propiedades se pueden conectar a las clases AbsoluteLayout , FlexLayout , Grid , RelativeLayout y
StackLayout , que derivan de la clase Layout<T> .

NOTE
La propiedad ItemTemplate tiene prioridad cuando están establecidas ambas propiedades ItemTemplate e
ItemTemplateSelector .

La clase Layout<T> expone una colección Children , a la que se agregan los elementos secundarios de un diseño.
Cuando se establece la propiedad BinableLayout.ItemsSource en una colección de elementos y se adjunta a una
clase derivada de Layout<T> , cada elemento de la colección se agrega a la colección Layout<T>.Children para que
el diseño lo muestre. Después, la clase derivada de Layout<T> actualizará sus vistas secundarias cuando cambie la
colección subyacente. Para obtener más información sobre el ciclo de diseño de Xamarin.Forms, consulte Crear un
diseño personalizado.
Los diseños enlazables solo deben usarse cuando la colección de elementos que se mostrará es pequeña y no se
necesiten el desplazamiento ni la selección. Aunque el desplazamiento puede proporcionarse al encapsular un
diseño enlazable en un ScrollView , no se recomienda, ya que los diseños enlazables carecen de virtualización de
interfaz de usuario. Cuando se requiere desplazamiento, una vista desplazable que incluye virtualización de
interfaz de usuario, como ListView o CollectionView , se debe usar. Ignorar esta recomendación puede provocar
problemas de rendimiento.
IMPORTANT
Aunque es técnicamente posible adjuntar un diseño se puede enlazar a cualquier clase de diseño que se deriva de la
Layout<T> (clase), no siempre resulta práctico volver a hacerlo, especialmente para la AbsoluteLayout , Grid , y
RelativeLayout clases. Por ejemplo, considere el escenario de que se desean mostrar una colección de datos en un Grid
mediante un diseño enlazable, donde cada elemento de la colección es un objeto que contiene varias propiedades. Cada fila
de la Grid debe mostrar un objeto de la colección, con cada columna de la Grid mostrar una de las propiedades del
objeto. Dado que el DataTemplate para el diseño enlazable solo puede contener un único objeto, es necesario para ese
objeto sea una clase de diseño que contiene varias vistas que muestran una de las propiedades del objeto en un
determinado Grid columna. Aunque este escenario puede evaluarse con diseños enlazables, resulta en un elemento
primario Grid que contiene un elemento secundario Grid para cada elemento de la colección enlazada, que es un uso
muy ineficaz y problemático de la Grid diseño.

Rellenar un diseño enlazable con datos


Un diseño enlazable se rellena con datos estableciendo la propiedad ItemsSource a cualquier colección que
implementa IEnumerable y adjuntarlo a una clase derivada de Layout<T> :

<Grid BindableLayout.ItemsSource="{Binding Items}" />

El código de C# equivalente es:

IEnumerable<string> items = ...;


var grid = new Grid();
BindableLayout.SetItemsSource(grid, items);

Cuando la propiedad adjunta BindableLayout.ItemsSource se establece en un diseño, pero la propiedad adjunta


BindableLayout.ItemTemplate no se ha establecido, todos los elementos en el IEnumerable colección serán
mostrados por un Label creado por la clase BindableLayout .

Definir la apariencia del elemento


Se puede definir la apariencia de cada elemento en el diseño enlazable estableciendo la propiedad adjunta
BindableLayout.ItemTemplate a un DataTemplate :

<StackLayout BindableLayout.ItemsSource="{Binding User.TopFollowers}"


Orientation="Horizontal"
...>
<BindableLayout.ItemTemplate>
<DataTemplate>
<controls:CircleImage Source="{Binding}"
Aspect="AspectFill"
WidthRequest="44"
HeightRequest="44"
... />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>

El código de C# equivalente es:


DataTemplate circleImageTemplate = ...;
var stackLayout = new StackLayout();
BindableLayout.SetItemsSource(stackLayout, viewModel.User.TopFollowers);
BindableLayout.SetItemTemplate(stackLayout, circleImageTemplate);

En este ejemplo, una vista CircleImage definida en la DataTemplate mostrará todos los elementos de la colección
TopFollowers :

Para obtener más información sobre las plantillas de datos, consulte Plantillas de datos de Xamarin.Forms.

Elegir la apariencia del elemento en tiempo de ejecución


Se puede elegir la apariencia de cada elemento en el diseño enlazable en tiempo de ejecución, según el valor del
elemento, estableciendo la propiedad adjunta BindableLayout.ItemTemplateSelector a un DataTemplateSelector :

<FlexLayout BindableLayout.ItemsSource="{Binding User.FavoriteTech}"


BindableLayout.ItemTemplateSelector="{StaticResource TechItemTemplateSelector}"
... />

El código de C# equivalente es:

DataTemplateSelector dataTemplateSelector = new TechItemTemplateSelector { ... };


var flexLayout = new FlexLayout();
BindableLayout.SetItemsSource(flexLayout, viewModel.User.FavoriteTech);
BindableLayout.SetItemTemplateSelector(flexLayout, dataTemplateSelector);

El DataTemplateSelector utilizado en el ejemplo de aplicación se muestra en el ejemplo siguiente:

public class TechItemTemplateSelector : DataTemplateSelector


{
public DataTemplate DefaultTemplate { get; set; }
public DataTemplate XamarinFormsTemplate { get; set; }

protected override DataTemplate OnSelectTemplate(object item, BindableObject container)


{
return (string)item == "Xamarin.Forms" ? XamarinFormsTemplate : DefaultTemplate;
}
}

La clase TechItemTemplateSelectordefine las propiedades DefaultTemplate y XamarinFormsTemplate de tipo


DataTemplate que se establecen para diferentes plantillas de datos. El método OnSelectTemplate devuelve el
XamarinFormsTemplate , que muestra un elemento en rojo oscuro con un corazón junto a él, cuando el elemento es
igual a "Xamarin.Forms". Cuando el elemento no es igual a "Xamarin.Forms", la OnSelectTemplate método
devuelve el DefaultTemplate , que muestra un elemento usando el color predeterminado de un Label :
Para obtener más información acerca de los selectores de plantilla de datos, vea creando un Xamarin.Forms
DataTemplateSelector.

Vínculos relacionados
Demostración de diseño enlazable (ejemplo)
Creación de un diseño personalizado
Plantillas de datos de Xamarin.Forms
Creación de un Xamarin.Forms DataTemplateSelector
Crear un diseño personalizado
11/07/2019 • 28 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms define cuatro clases de diseño: StackLayout, AbsoluteLayout, RelativeLayout y cuadrícula, y cada
uno de ellos organiza a sus elementos secundarios de forma diferente. Sin embargo, a veces es necesario
organizar el contenido de la página con un diseño que no se proporciona mediante Xamarin.Forms. En este
artículo se explica cómo escribir una clase de diseño personalizado y se muestra una clase de WrapLayout
minúsculas orientación que sus elementos secundarios se organiza horizontalmente en la página y, a
continuación, ajusta la presentación de los elementos secundarios subsiguientes a las filas adicionales.

Información general
En Xamarin.Forms, se derivan todas las clases de diseño de la Layout<T> clase y restringir el tipo genérico a
View y sus tipos derivados. A su vez, el Layout<T> clase se deriva de la Layout (clase), que proporciona el
mecanismo para colocar y secundarios de ajuste de tamaño los elementos.
Todos los elementos visuales es responsable de determinar su propio tamaño preferido, que se conoce como el
solicitado tamaño. Page , Layout , y Layout<View> tipos derivados son responsables de determinar la ubicación
y el tamaño de sus secundarios o elementos secundarios, con respecto a ellos mismos. Por lo tanto, el diseño
implica una relación de elementos primarios y secundarios, donde el elemento primario determina cuál debe ser
el tamaño de sus elementos secundarios, pero intentará adaptarse al tamaño solicitado del elemento secundario.
Se requiere un conocimiento exhaustivo de los ciclos de diseño y la invalidación de Xamarin.Forms para crear un
diseño personalizado. Ahora se tratarán estos ciclos.

Diseño
Diseño comienza en la parte superior del árbol visual con una página y pasa a través de todas las ramas del
árbol visual para abarcar todos los elementos visuales en una página. Los elementos que son elementos
primarios a otros elementos son responsables de ajuste de tamaño y la posición de sus elementos secundarios
en relación con ellos mismos.
El VisualElementclase define un Measure método que mide un elemento para las operaciones de diseño, y un
Layout método que especifica el área rectangular que se representa en el elemento. Cuando se inicia una
aplicación y se muestra la primera página, un ciclo de diseño primero que consta de Measure llamadas y, a
continuación, Layout llama, se inicia en el Page objeto:
1. Durante el ciclo de diseño, cada elemento primario es responsable de llamar a la Measure método en sus
elementos secundarios.
2. Después de haberse medidos los elementos secundarios, cada elemento primario es responsable de llamar a
la Layout método en sus elementos secundarios.
Este ciclo garantiza que todos los elementos visuales en la página recibe las llamadas a la Measure y Layout
métodos. El proceso se muestra en el diagrama siguiente:
NOTE
Tenga en cuenta que los ciclos de diseño también pueden producirse en un subconjunto del árbol visual si algo cambia
para afectar al diseño. Esto incluye los elementos que se agregan o quitan de una colección, como en un StackLayout ,
un cambio en el IsVisible propiedad de un elemento o un cambio en el tamaño de un elemento.

Todas las clases de Xamarin.Forms que tiene un Content o un Children propiedad tiene un reemplazable
LayoutChildren método. Las clases de diseño personalizado que se derivan de Layout<View> debe invalidar este
método y asegúrese de que el Measure y Layout son métodos se llama en todos los elementos secundarios del
elemento, para proporcionar el diseño personalizado deseado.
Además, cada clase que derive de Layout o Layout<View> debe invalidar el OnMeasure método, que es donde
una clase de diseño Determina el tamaño que deba estar realizando llamadas a la Measure métodos de sus
elementos secundarios.

NOTE
Elementos determinan su tamaño según restricciones, que indican cuánto espacio hay disponible para un elemento dentro
primario del elemento. Las restricciones se pasan a la Measure y OnMeasure métodos pueden oscilar entre 0 y
Double.PositiveInfinity . Es un elemento restringida, o completamente restringida, cuando recibe una llamada a su
Measure método con argumentos no infinita - está restringido el elemento un tamaño determinado. Es un elemento sin
restricciones, o parcialmente restringida, cuando recibe una llamada a su Measure método con igual que al menos un
argumento Double.PositiveInfinity : puede ser la restricción infinita considerar que indica el ajuste automático de
tamaño.

Invalidación
Invalidación es el proceso por el cual un cambio en un elemento de una página desencadena un nuevo ciclo de
diseño. Los elementos se consideran no válidos cuando ya no tienen el tamaño correcto o la posición. Por
ejemplo, si la FontSize propiedad de un Button cambios, el Button se dice que es válida porque ya no tiene el
tamaño correcto. Cambiar el tamaño de la Button , a continuación, puede tener un efecto dominó de cambios
de diseño en el resto de una página.
Invalidarán elementos propios invocando el InvalidateMeasure método, por lo general cuando cambia una
propiedad del elemento que podría dar lugar a un nuevo tamaño del elemento. Este método desencadena el
MeasureInvalidated evento, que controla el primario elemento para desencadenar un nuevo ciclo de diseño.
El Layout clase establece un controlador para el MeasureInvalidated eventos en cada elemento secundario que
se agrega a su Content propiedad o Children colección y desasocia el controlador cuando el se quita el
elemento secundario. Por lo tanto, todos los elementos en el árbol visual que tiene elementos secundarios es una
alerta cuando uno de sus elementos secundarios cambia de tamaño. El siguiente diagrama ilustra cómo un
cambio en el tamaño de un elemento en el árbol visual puede producir cambios que ripple el árbol:

Sin embargo, la Layout clase intenta restringir el impacto de un cambio de tamaño de un elemento secundario
en el diseño de una página. Si el diseño es limitada de tamaño, a continuación, un cambio de tamaño del
elemento secundario no afecta a algo mayor que el diseño del elemento primario en el árbol visual. Sin
embargo, normalmente un cambio en el tamaño de un diseño afecta a cómo el diseño organiza a sus elementos
secundarios. Por lo tanto, cualquier cambio en el tamaño de un diseño se iniciará un ciclo de diseño para el
diseño y el diseño recibirán llamadas a su OnMeasure y LayoutChildren métodos.
El Layoutclase define también un InvalidateLayout método que tiene una finalidad similar a la
InvalidateMeasure método. El InvalidateLayout debe invocarse el método cada vez que se realiza un cambio
que afecta a cómo el diseño de posiciones y tamaños de sus elementos secundarios. Por ejemplo, el Layout
clase invoca el InvalidateLayout método cada vez que se agrega o quita de un diseño de un elemento
secundario.
El InvalidateLayout se puede invalidar para implementar una memoria caché para minimizar las llamadas
repetitivas de la Measure métodos de elementos secundarios del diseño. Reemplazar el InvalidateLayout
método proporcionará una notificación cuando se agregan o se quita del diseño los elementos secundarios. De
forma similar, el OnChildMeasureInvalidated se puede invalidar el método para proporcionar una notificación
cuando uno de los elementos secundarios del diseño cambia de tamaño. Para los reemplazos de método debe
responder un diseño personalizado al borrar la caché. Para obtener más información, consulte calcular y
almacenar en caché datos.

Crear un diseño personalizado


El proceso para crear un diseño personalizado es como sigue:
1. Cree una clase que se derive de la clase Layout<View> . Para obtener más información, consulte creando un
WrapLayout.
2. [opcional] agregar propiedades, respaldadas por propiedades enlazables, para cualquier parámetro que se
debe establecer en la clase de diseño. Para obtener más información, consulte agregando propiedades
respaldadas por propiedades enlazables.
3. Invalidar el OnMeasure método para invocar el Measure método en todo el diseño los elementos secundarios
y devolver un tamaño solicitado para el diseño. Para obtener más información, consulte invalidación del
método OnMeasure.
4. Invalidar el LayoutChildren método para invocar el Layout método en elementos secundarios de la del
diseño. Error al invocar el Layout método en cada elemento secundario en un diseño dará como resultado el
elemento secundario nunca reciben un tamaño correcto o una posición y, por lo tanto, el elemento secundario
no estará visible en la página. Para obtener más información, consulte invalidación del método
LayoutChildren.

NOTE
Al enumerar los elementos secundarios en el OnMeasure y LayoutChildren invalidaciones, omitir cualquier elemento
secundario cuyo IsVisible propiedad está establecida en false . Esto garantizará que el diseño personalizado no deja
espacio para elementos secundarios visibles.

1. [opcional] invalidar el InvalidateLayout método para recibir notificaciones cuando los elementos
secundarios se agregan o se quita del diseño. Para obtener más información, consulte invalidación del
método InvalidateLayout.
2. [opcional] invalidar el OnChildMeasureInvalidated método para recibir una notificación cuando uno de los
elementos secundarios del diseño cambia de tamaño. Para obtener más información, consulte invalidación
del método OnChildMeasureInvalidated.

NOTE
Tenga en cuenta que el OnMeasure override no se invoca si el tamaño del diseño se rige por su elemento primario, en
lugar de sus elementos secundarios. Sin embargo, se invocará la invalidación si una o ambas de las restricciones son
infinitos, o bien, si la clase de diseño no predeterminado HorizontalOptions o VerticalOptions los valores de
propiedad. Por este motivo, el LayoutChildren invalidación no puede depender de tamaños secundarios obtenidos
durante el OnMeasure llamada al método. En su lugar, LayoutChildren debe invocar el Measure método en elementos
secundarios del diseño, antes de invocar el Layout método. Como alternativa, el tamaño de los elementos secundarios se
obtuvo en el OnMeasure invalidación puede almacenarse en caché para evitar más adelante Measure invocaciones en el
LayoutChildren invalidación, pero la clase de diseño deberá saber cuándo es necesario volver a obtener los tamaños.
Para obtener más información, consulte calcular y almacenar en caché datos de diseño.

La clase de diseño puede utilizarse, a continuación, agregándolo a un Page y mediante la adición de elementos
secundarios para el diseño. Para obtener más información, consulte consumiendo el WrapLayout.
Creación de un WrapLayout
La aplicación de ejemplo muestra una orientación distinción WrapLayout clase que sus elementos secundarios se
organiza horizontalmente en la página y, a continuación, ajusta la presentación de los elementos secundarios
subsiguientes a las filas adicionales.
El WrapLayout clase asigna la misma cantidad de espacio para cada elemento secundario, conocido como el
tamaño de la celda, según el tamaño máximo de los elementos secundarios. Los elementos secundarios más
pequeños que el tamaño de celda se puede colocar dentro de la celda según sus HorizontalOptions y
VerticalOptions los valores de propiedad.

El WrapLayout definición de clase se muestra en el ejemplo de código siguiente:

public class WrapLayout : Layout<View>


{
Dictionary<Size, LayoutData> layoutDataCache = new Dictionary<Size, LayoutData>();
...
}
Calcular y almacenar en caché datos de diseño
El LayoutData estructura almacena los datos de una colección de elementos secundarios en un número de
propiedades:
VisibleChildCount : el número de elementos secundarios que están visibles en el diseño.
CellSize – el tamaño máximo de todos los elementos secundarios, ajustado el tamaño del diseño.
Rows : el número de filas.
Columns : el número de columnas.

El layoutDataCache campo se utiliza para almacenar varios LayoutData valores. Cuando se inicia la aplicación,
dos LayoutData se almacenarán en caché de objetos en el layoutDataCache diccionario para la orientación
actual: uno para los argumentos de restricción a la OnMeasure invalidación y otra para el width y height
argumentos para el LayoutChildren invalidar. Al girar el dispositivo en orientación horizontal, el OnMeasure
invalidar y LayoutChildren se invocará nuevo reemplazo, lo que producirá dos otro LayoutData objetos que se
va a almacenar en caché en el diccionario. Sin embargo, cuando el dispositivo se vuelve a la orientación vertical,
ningún cálculo adicional es necesario porque el layoutDataCache ya tiene los datos necesarios.
El siguiente ejemplo de código muestra la GetLayoutData método, que calcula las propiedades de la LayoutData
estructurados según un tamaño determinado:

LayoutData GetLayoutData(double width, double height)


{
Size size = new Size(width, height);

// Check if cached information is available.


if (layoutDataCache.ContainsKey(size))
{
return layoutDataCache[size];
}

int visibleChildCount = 0;
Size maxChildSize = new Size();
int rows = 0;
int columns = 0;
LayoutData layoutData = new LayoutData();

// Enumerate through all the children.


foreach (View child in Children)
{
// Skip invisible children.
if (!child.IsVisible)
continue;

// Count the visible children.


visibleChildCount++;

// Get the child's requested size.


SizeRequest childSizeRequest = child.Measure(Double.PositiveInfinity, Double.PositiveInfinity);

// Accumulate the maximum child size.


maxChildSize.Width = Math.Max(maxChildSize.Width, childSizeRequest.Request.Width);
maxChildSize.Height = Math.Max(maxChildSize.Height, childSizeRequest.Request.Height);
}

if (visibleChildCount != 0)
{
// Calculate the number of rows and columns.
if (Double.IsPositiveInfinity(width))
{
columns = visibleChildCount;
rows = 1;
}
else
else
{
columns = (int)((width + ColumnSpacing) / (maxChildSize.Width + ColumnSpacing));
columns = Math.Max(1, columns);
rows = (visibleChildCount + columns - 1) / columns;
}

// Now maximize the cell size based on the layout size.


Size cellSize = new Size();

if (Double.IsPositiveInfinity(width))
cellSize.Width = maxChildSize.Width;
else
cellSize.Width = (width - ColumnSpacing * (columns - 1)) / columns;

if (Double.IsPositiveInfinity(height))
cellSize.Height = maxChildSize.Height;
else
cellSize.Height = (height - RowSpacing * (rows - 1)) / rows;

layoutData = new LayoutData(visibleChildCount, cellSize, rows, columns);


}

layoutDataCache.Add(size, layoutData);
return layoutData;
}

El GetLayoutData método realiza las siguientes operaciones:


Determina si un calculado LayoutData valor ya está en la memoria caché y lo devuelve si está disponible.
En caso contrario, enumera a través de todos los elementos secundarios, invocar el Measure método en cada
elemento secundario con un ancho infinito y el alto y determina el tamaño máximo de secundarios.
Siempre que hay al menos un elemento secundario visible, calcula el número de filas y columnas necesarios
y, a continuación, calcula un tamaño de celda para los elementos secundarios en función de las dimensiones
de la WrapLayout . Tenga en cuenta que el tamaño de celda normalmente es ligeramente más amplio que el
tamaño máximo de secundarios, sino que también podría ser más pequeño si la WrapLayout no es lo
suficientemente ancha para secundario más amplia o lo suficientemente alto para el elemento secundario
más alto.
Almacena el nuevo LayoutData valor en la memoria caché.
Agregar propiedades respaldadas por propiedades enlazables
El WrapLayout define la clase ColumnSpacing y RowSpacing propiedades cuyos valores se usan para separar las
filas y columnas en el diseño y que están respaldadas por propiedades enlazables. Las propiedades enlazables se
muestran en el ejemplo de código siguiente:
public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(
"ColumnSpacing",
typeof(double),
typeof(WrapLayout),
5.0,
propertyChanged: (bindable, oldvalue, newvalue) =>
{
((WrapLayout)bindable).InvalidateLayout();
});

public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(


"RowSpacing",
typeof(double),
typeof(WrapLayout),
5.0,
propertyChanged: (bindable, oldvalue, newvalue) =>
{
((WrapLayout)bindable).InvalidateLayout();
});

Invoca el controlador de cambio de propiedad de cada propiedad enlazable el InvalidateLayout invalidación del
método para desencadenar un nuevo diseño de pasar el WrapLayout . Para obtener más información, consulte
invalidación del método InvalidateLayout y invalidación del método OnChildMeasureInvalidated.
Invalidación del método OnMeasure
El OnMeasure invalidación se muestra en el ejemplo de código siguiente:

protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)


{
LayoutData layoutData = GetLayoutData(widthConstraint, heightConstraint);
if (layoutData.VisibleChildCount == 0)
{
return new SizeRequest();
}

Size totalSize = new Size(layoutData.CellSize.Width * layoutData.Columns + ColumnSpacing *


(layoutData.Columns - 1),
layoutData.CellSize.Height * layoutData.Rows + RowSpacing * (layoutData.Rows - 1));
return new SizeRequest(totalSize);
}

Invoca la invalidación del GetLayoutData método y construcciones un SizeRequest objeto a partir de los datos
devueltos, teniendo en cuenta también la RowSpacing y ColumnSpacing los valores de propiedad. Para obtener
más información sobre la GetLayoutData método, consulte calcular y almacenar en caché datos.

IMPORTANT
El Measure y OnMeasure métodos nunca deben solicitar una dimensión infinita devolviendo un SizeRequest valor con
una propiedad establecida en Double.PositiveInfinity . Sin embargo, al menos uno de los argumentos de restricción
OnMeasure puede ser Double.PositiveInfinity .

Invalidación del método LayoutChildren


El LayoutChildren invalidación se muestra en el ejemplo de código siguiente:
protected override void LayoutChildren(double x, double y, double width, double height)
{
LayoutData layoutData = GetLayoutData(width, height);

if (layoutData.VisibleChildCount == 0)
{
return;
}

double xChild = x;
double yChild = y;
int row = 0;
int column = 0;

foreach (View child in Children)


{
if (!child.IsVisible)
{
continue;
}

LayoutChildIntoBoundingRegion(child, new Rectangle(new Point(xChild, yChild), layoutData.CellSize));


if (++column == layoutData.Columns)
{
column = 0;
row++;
xChild = x;
yChild += RowSpacing + layoutData.CellSize.Height;
}
else
{
xChild += ColumnSpacing + layoutData.CellSize.Width;
}
}
}

La invalidación comienza con una llamada a la GetLayoutData método y, a continuación, enumera todos los
elementos secundarios para el tamaño y colocarlos dentro de la celda de cada elemento secundario. Esto se
logra invocando el LayoutChildIntoBoundingRegion método, que se usa para colocar un elemento secundario
dentro de un rectángulo en función de su HorizontalOptions y VerticalOptions los valores de propiedad. Esto
equivale a hacer una llamada a la secundaria Layout método.

NOTE
Tenga en cuenta que el rectángulo que se pasó a la LayoutChildIntoBoundingRegion método incluye el área completa
en el que puede residir el elemento secundario.

Para obtener más información sobre la GetLayoutData método, consulte calcular y almacenar en caché datos.
Invalidación del método InvalidateLayout
El InvalidateLayout invalidación se invoca cuando los elementos secundarios se agregan o quitan del diseño, o
cuando una de las WrapLayout Propiedades cambie el valor, como se muestra en el ejemplo de código siguiente:

protected override void InvalidateLayout()


{
base.InvalidateLayout();
layoutInfoCache.Clear();
}

La invalidación invalida el diseño y descarta toda la información de diseño almacenado en caché.


NOTE
Para detener el Layout clase invocar el InvalidateLayout invalidar el método cada vez que un elemento secundario se
agrega o quita de un diseño, el ShouldInvalidateOnChildAdded y ShouldInvalidateOnChildRemoved métodos y
devolver false . La clase de diseño, a continuación, puede implementar un proceso personalizado cuando se agregan o
quitan elementos secundarios.

Invalidación del método OnChildMeasureInvalidated


El OnChildMeasureInvalidated invalidación se invoca cuando cambia el tamaño de uno de los elementos
secundarios del diseño y se muestra en el ejemplo de código siguiente:

protected override void OnChildMeasureInvalidated()


{
base.OnChildMeasureInvalidated();
layoutInfoCache.Clear();
}

La invalidación invalida el diseño del elemento secundario y descarta toda la información de diseño almacenado
en caché.
Consumir el WrapLayout
El WrapLayout clase puede utilizarse colocando en un Page tipo derivado, como se muestra en el ejemplo de
código XAML siguiente:

<ContentPage ... xmlns:local="clr-namespace:ImageWrapLayout">


<ScrollView Margin="0,20,0,20">
<local:WrapLayout x:Name="wrapLayout" />
</ScrollView>
</ContentPage>

El código de C# equivalente se muestra a continuación:

public class ImageWrapLayoutPageCS : ContentPage


{
WrapLayout wrapLayout;

public ImageWrapLayoutPageCS()
{
wrapLayout = new WrapLayout();

Content = new ScrollView


{
Margin = new Thickness(0, 20, 0, 20),
Content = wrapLayout
};
}
...
}

Los elementos secundarios, a continuación, se pueden agregar a la WrapLayout según sea necesario. El siguiente
ejemplo de código muestra Image elementos que se agrega a la WrapLayout :
protected override async void OnAppearing()
{
base.OnAppearing();

var images = await GetImageListAsync();


if (images != null)
{
foreach (var photo in images.Photos)
{
var image = new Image
{
Source = ImageSource.FromUri(new Uri(photo))
};
wrapLayout.Children.Add(image);
}
}
}

async Task<ImageList> GetImageListAsync()


{
try
{
string requestUri = "https://raw.githubusercontent.com/xamarin/docs-
archive/master/Images/stock/small/stock.json";
string result = await _client.GetStringAsync(requestUri);
return JsonConvert.DeserializeObject<ImageList>(result);
}
catch (Exception ex)
{
Debug.WriteLine($"\tERROR: {ex.Message}");
}

return null;
}

Cuando la página que contiene el WrapLayout aparece, la aplicación de ejemplo de forma asincrónica obtiene
acceso a un archivo remoto de JSON que contiene una lista de fotos, crea un Image de fotografías para cada
elemento y lo agrega a la WrapLayout . El resultado es el aspecto que se muestra en las capturas de pantalla
siguiente:
Capturas de pantalla siguientes se muestra el WrapLayout después de se ha girado en orientación horizontal:
El número de columnas en cada fila depende del tamaño de fotografía, el ancho de pantalla y el número de
píxeles independientes del dispositivo unitario. El Image elementos cargar las fotos, de forma asincrónica y, por
tanto, el WrapLayout clase recibirán llamadas frecuentes a su LayoutChildren método cada Image elemento
recibe un nuevo tamaño en función de la fotografía cargada.

Vínculos relacionados
WrapLayout (ejemplo)
Diseños personalizados
Creación de los diseños personalizados en Xamarin.Forms (vídeo)
Diseño
Diseño
VisualElement
Compresión de diseño
11/07/2019 • 8 minutes to read • Edit Online

descargar el ejemplo
Compresión de diseño quita diseños especificados del árbol visual en un intento de mejorar el rendimiento de
representación de página. En este artículo se explica cómo habilitar la compresión de diseño y los beneficios que
puede ofrecer.

Información general
Xamarin.Forms se realiza mediante dos series de llamadas al método recursiva del diseño:
Diseño comienza en la parte superior del árbol visual con una página y pasa a través de todas las ramas del
árbol visual para abarcar todos los elementos visuales en una página. Los elementos que son elementos
primarios a otros elementos son responsables de ajuste de tamaño y la posición de sus elementos secundarios
en relación con ellos mismos.
Invalidación es el proceso por el cual un cambio en un elemento de una página desencadena un nuevo ciclo de
diseño. Los elementos se consideran no válidos cuando ya no tienen el tamaño correcto o la posición. Todos
los elementos en el árbol visual que tiene elementos secundarios es una alerta cuando cambia uno de sus
elementos secundarios tamaños. Por lo tanto, un cambio en el tamaño de un elemento en el árbol visual puede
provocar cambios que ripple el árbol.
Para obtener más información acerca de cómo realiza el diseño Xamarin.Forms, consulte crear un diseño
personalizado.
El resultado del proceso de diseño es una jerarquía de controles nativos. Sin embargo, esta jerarquía incluye los
representadores de contenedor adicional y contenedores de los representadores de plataforma, lo que infla aún
más la jerarquía de vistas de anidamiento. La profundidad del nivel de anidamiento, mayor será la cantidad de
trabajo que Xamarin.Forms tiene que realizar para mostrar una página. Para diseños complejos, la jerarquía de
vistas puede ser profunda y amplia, con varios niveles de anidamiento.
Por ejemplo, considere el siguiente botón desde la aplicación de ejemplo para iniciar sesión en Facebook:

Este botón se especifica como un control personalizado con la jerarquía de vistas XAML siguiente:
<ContentView ...>
<StackLayout>
<StackLayout ...>
<AbsoluteLayout ...>
<Button ... />
<Image ... />
<Image ... />
<BoxView ... />
<Label ... />
<Button ... />
</AbsoluteLayout>
</StackLayout>
<Label ... />
</StackLayout>
</ContentView>

Se puede examinar la jerarquía resultante de la vista anidada con Xamarin Inspector. En Android, la jerarquía de la
vista anidada contiene 17 vistas:

Compresión de diseño, que está disponible para las aplicaciones de Xamarin.Forms en las plataformas iOS y
Android, tiene como objetivo simplificar el anidamiento quitando diseños especificados del árbol visual, lo que
puede mejorar el rendimiento de la representación de la página de vistas. La ventaja de rendimiento que se
entrega varía según la complejidad de una página, la versión del sistema operativo que se va a usar y el
dispositivo en el que se ejecuta la aplicación. Sin embargo, las mejoras de rendimiento más importantes se
apreciarán en los dispositivos más antiguos.

NOTE
Aunque en este artículo se centra en los resultados de aplicar la compresión de diseño en Android, es igualmente aplicable a
iOS.

Compresión de diseño
En XAML, se puede habilitar la compresión de diseño estableciendo el CompressedLayout.IsHeadless propiedad
adjunta true en una clase de diseño:

<StackLayout CompressedLayout.IsHeadless="true">
...
</StackLayout>
Como alternativa, se puede habilitar en C# mediante la especificación de la instancia de diseño como el primer
argumento para el CompressedLayout.SetIsHeadless método:

CompressedLayout.SetIsHeadless(stackLayout, true);

IMPORTANT
Dado que la compresión de diseño quita un diseño el árbol visual, no es adecuado para los diseños que tienen una
apariencia visual, o que obtener la entrada táctil. Por lo tanto, los diseños que establezca VisualElement propiedades
(como BackgroundColor , IsVisible , Rotation , Scale , TranslationX y TranslationY o que aceptar
movimientos, no son candidatos para el diseño compresión. Sin embargo, habilitar la compresión de diseño en un diseño
que establece las propiedades de apariencia visual, o que acepta los gestos, no se producirá un error de compilación o en
tiempo de ejecución. En su lugar, se aplicará la compresión de diseño y las propiedades de apariencia visual y reconocimiento
de gestos, fallarán en modo silencioso.

Para el botón de Facebook, se puede habilitar la compresión de diseño en las clases de tres diseño:

<StackLayout CompressedLayout.IsHeadless="true">
<StackLayout CompressedLayout.IsHeadless="true" ...>
<AbsoluteLayout CompressedLayout.IsHeadless="true" ...>
...
</AbsoluteLayout>
</StackLayout>
...
</StackLayout>

En Android, el resultado en una jerarquía de vistas anidadas de 14 vistas:

En comparación con la jerarquía de vistas anidadas original de 17 vistas, esto representa una reducción en el
número de vistas de un 17%. Aunque esta reducción puede parecer insignificante, la reducción de la vista a través
de una página entera puede ser más importante.
Representadores rápidos
Los representadores rápidos reducen la inflación y los costos de representación de controles de Xamarin.Forms
en Android mediante la reducción de la jerarquía de vistas nativas resultante. Esto mejora aún más el rendimiento
porque crea menos objetos, lo que a su vez resulta en un árbol visual menos complejo y menos uso de memoria.
Para obtener más información sobre los representadores rápidos, consulte representadores rápidos.
Para el botón de Facebook en la aplicación de ejemplo, combinación de compresión de diseño y representadores
rápidos genera una jerarquía de vistas anidadas de 8 vistas:
En comparación con la jerarquía de vistas anidadas original de 17 vistas, esto representa una reducción de 52%.
La aplicación de ejemplo contiene una página que se extraen de una aplicación real. Sin compresión de diseño y
representadores rápidos, la página genera una jerarquía de vistas anidadas de 130 vistas en Android. Habilitación
de representadores rápidos y compresión de diseño en las clases de diseño apropiado, reduce la jerarquía de
vistas anidadas a 70 vistas, una reducción del 46%.

Resumen
Compresión de diseño quita diseños especificados del árbol visual en un intento de mejorar el rendimiento de
representación de página. La ventaja de rendimiento que esto ofrece varía según la complejidad de una página, la
versión del sistema operativo que se va a usar y el dispositivo en el que se ejecuta la aplicación. Sin embargo, las
mejoras de rendimiento más importantes se apreciarán en los dispositivos más antiguos.

Vínculos relacionados
Creación de un diseño personalizado
Representadores rápidos
LayoutCompression (ejemplo)
ListView de Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

descargar el ejemplo
ListView es una vista para presentar las listas de datos, especialmente las listas largas que requieren el
desplazamiento.

IMPORTANT
CollectionView es una vista para presentar las listas de datos mediante las especificaciones de diseño diferente. Su
objetivo es proporcionar una forma más flexible y una alternativa de alto rendimiento ListView . Para obtener más
información, consulte Xamarin.Forms CollectionView.

Casos de uso
Asegúrese de que ListView es el control adecuado para sus necesidades. ListView puede usarse en cualquier
situación donde se va a mostrar desplazables listas de datos. ListView admite acciones de contexto y enlace de
datos.
ListView no debe confundirse con TableView. El control de TableView es una mejor opción cada vez que tenga
una lista no enlazada de opciones o datos. Por ejemplo, la aplicación de configuración de iOS, que tiene un
conjunto de opciones predefinido en su mayoría, es más adecuada usar TableView que ListView.
También tenga en cuenta que es mejor un ListView adecuado para datos homogéneos – es decir, todos los
datos deben ser del mismo tipo. Esto es porque se puede usar un único tipo de celda para cada fila de la lista.
TableViews puede admitir varios tipos de celda, por lo que son una mejor opción cuando necesite combinar las
vistas.

Componentes
ListView tiene una serie de componentes disponibles para ejercer la funcionalidad nativa de cada plataforma.
Cada uno de estos componentes se describe a continuación:
Encabezados y pies de página – texto o vista para mostrar al principio y al final de una lista, separar de los
datos de la lista. Encabezados y pies de página se pueden enlazar a un origen de datos independientemente
del origen de datos del ListView.
Grupos – datos en un ListView se pueden agrupar para navegar más fácilmente. Normalmente, los grupos
están enlazados a datos:
Las celdas – datos en un ListView se presentan en las celdas. Cada celda corresponde a una fila de datos.
Hay celdas integradas que puede elegir, o puede definir su propia celda personalizada. Las celdas integradas
y personalizadas pueden ser usa/definido en XAML o código.
Integrada – integrada en las celdas, especialmente TextCell y ImageCell, puede ser bueno para el
rendimiento, puesto que corresponden a los controles nativos en cada plataforma.
TextCell – muestra una cadena de texto, opcionalmente con detalle. Texto de detalle se
representa como una segunda línea en una fuente menor con un color de énfasis.
ImageCell – muestra una imagen con texto. Aparece como un TextCell con una imagen de la
izquierda.
Las celdas personalizadas – celdas personalizado son excelentes cuando se necesita para presentar
datos complejos. Por ejemplo, se podría usar una vista personalizada para presentar una lista de
canciones, incluido el álbum y el intérprete:
Para más información acerca de cómo personalizar las celdas de un ListView, consulte personalizar la
apariencia de una celda ListView.

Funcionalidad
ListView es compatible con un número de estilos de interacción, incluidos:
Incorporación de cambios para actualizar – ListView es compatible con la incorporación de cambios
para actualizar en cada plataforma.
Acciones de contexto – ListView admite tomar las medidas en elementos individuales en una lista. Por
ejemplo, puede implementar la acción de pasar el dedo en iOS, o mantenga pulsado acciones en Android.
Selección – puede escuchar las selecciones y deselections para realizar una acción cuando se pulsa una fila.
Para obtener más información acerca de las características de interactividad de ListView, vea acciones &
interactividad con ListView.

Vínculos relacionados
Trabajar con ListView (ejemplo)
Enlaces bidireccionales (ejemplo)
Integrado en las celdas (ejemplo)
Celdas personalizadas (ejemplo)
GROUPING (ejemplo)
Vista de representador personalizado (ejemplo)
Interactividad de ListView (ejemplo)
Orígenes de datos de ListView
11/07/2019 • 5 minutes to read • Edit Online

descargar el ejemplo
Un ListView se utiliza para mostrar listas de datos. Aprenderemos acerca de cómo rellenar un ListView con datos
y cómo podemos enlazar al elemento seleccionado.

ItemsSource
Un ListView se rellena con datos mediante el ItemsSource propiedad, que puede aceptar cualquier colección que
implementa IEnumerable . La manera más sencilla para rellenar un ListView implica el uso de una matriz de
cadenas:

<ListView>
<ListView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>mono</x:String>
<x:String>monodroid</x:String>
<x:String>monotouch</x:String>
<x:String>monorail</x:String>
<x:String>monodevelop</x:String>
<x:String>monotone</x:String>
<x:String>monopoly</x:String>
<x:String>monomodal</x:String>
<x:String>mononucleosis</x:String>
</x:Array>
</ListView.ItemsSource>
</ListView>

El código de C# equivalente es:

var listView = new ListView();


listView.ItemsSource = new string[]
{
"mono",
"monodroid",
"monotouch",
"monorail",
"monodevelop",
"monotone",
"monopoly",
"monomodal",
"mononucleosis"
};

//monochrome will not appear in the list because it was added


//after the list was populated.
listView.ItemsSource.Add("monochrome");
El enfoque anterior se rellenará la ListView con una lista de cadenas. De forma predeterminada, ListView llamará
ToString y mostrar el resultado en un TextCell para cada fila. Para personalizar cómo se muestran los datos, vea
aspecto de la celda.
Dado que ItemsSource se ha enviado a una matriz, no se actualizará el contenido como los cambios de lista o
matriz subyacentes. Si desea que el ListView para actualizarse automáticamente a medida que los elementos se
agregan, quitan y puede cambiar en la lista subyacente, deberá usar un ObservableCollection .
ObservableCollection se define en System.Collections.ObjectModel y es exactamente igual que List , excepto en
que se puede notificar ListView de los cambios:

ObservableCollection<Employee> employees = new ObservableCollection<Employee>();


listView.ItemsSource = employees;

//Mr. Mono will be added to the ListView because it uses an ObservableCollection


employees.Add(new Employee(){ DisplayName="Mr. Mono"});

Enlace de datos
Enlace de datos es la "adherencia" que enlaza las propiedades de un objeto de interfaz de usuario a las propiedades
de un objeto CLR, como una clase en ViewModel. Enlace de datos es útil porque simplifica el desarrollo de
interfaces de usuario mediante la sustitución mucha aburrido código reutilizable.
Enlace de datos funciona manteniendo los objetos sincronizados a medida que cambian sus valores enlazados. En
lugar de tener que escribir controladores de eventos para cada vez que cambia el valor de un control, establecer el
enlace y habilitar el enlace en ViewModel.
Para obtener más información sobre el enlace de datos, vea conceptos básicos del enlace de datos que es la cuarta
parte de la serie de artículos de conceptos básicos de XAML de Xamarin.Forms.
Enlazar celdas
Propiedades de celdas (y elementos secundarios de las celdas) se pueden enlazar a propiedades de objetos en el
ItemsSource . Por ejemplo, podría usarse un ListView para presentar una lista de empleados.

La clase employee:

public class Employee


{
public string DisplayName {get; set;}
}

ObservableCollection<Employee> se crea y se establece como el ListView del ItemsSource :

ObservableCollection<Employee> employees = new ObservableCollection<Employee>();


public EmployeeListPage()
{
//defined in XAML to follow
EmployeeView.ItemsSource = employees;
...
}

La lista se rellena con datos:

public EmployeeListPage()
{
...
employees.Add(new Employee{ DisplayName="Rob Finnerty"});
employees.Add(new Employee{ DisplayName="Bill Wrestler"});
employees.Add(new Employee{ DisplayName="Dr. Geri-Beth Hooper"});
employees.Add(new Employee{ DisplayName="Dr. Keith Joyce-Purdy"});
employees.Add(new Employee{ DisplayName="Sheri Spruce"});
employees.Add(new Employee{ DisplayName="Burt Indybrick"});
}

El siguiente fragmento muestra un ListView enlazado a una lista de empleados:

<?xml version="1.0" encoding="utf-8" ?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:constants="clr-namespace:XamarinFormsSample;assembly=XamarinFormsXamlSample"
x:Class="XamarinFormsXamlSample.Views.EmployeeListPage"
Title="Employee List">
<ListView x:Name="EmployeeView">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding DisplayName}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>

Tenga en cuenta que el enlace es el programa de instalación en el código por motivos de simplicidad, aunque
podría haber enlazado en XAML.
El bit de XAML anterior define un ContentPage que contiene un ListView . El origen de datos de la ListView se
establece a través de la ItemsSource atributo. El diseño de cada fila de la ItemsSource se define dentro el
ListView.ItemTemplate elemento.

Éste es el resultado:
Enlace SelectedItem
A menudo conveniente enlazar con el elemento seleccionado de un ListView , en lugar de usar un controlador de
eventos para responder a cambios. Para hacer esto en XAML, enlazar la SelectedItem propiedad:

<ListView x:Name="listView"
SelectedItem="{Binding Source={x:Reference SomeLabel},
Path=Text}">

</ListView>

Suponiendo que listView del ItemsSource es una lista de cadenas, SomeLabel tendrán su propiedad text enlazada
a la SelectedItem .

Vínculos relacionados
Enlaces bidireccionales (ejemplo)
Personalizar la apariencia de una celda de ListView
11/07/2019 • 10 minutes to read • Edit Online

descargar el ejemplo
ListView presenta listas desplazables, que se pueden personalizar mediante el uso de ViewCell s. ViewCells
puede usarse para mostrar texto e imágenes, que indica un estado verdadero/falso y recibir datos de entrada del
usuario.

Integrado en celdas
Xamarin.Forms incluye celdas integradas que funcionan para muchas aplicaciones simples:
TextCell – para mostrar texto
ImageCell – para mostrar una imagen con texto.
Las otras dos celdas SwitchCell y EntryCell están disponibles, pero no se usan habitualmente con ListView .
Consulte TableView para obtener más información acerca de estas celdas.
TextCell
TextCell es una celda para mostrar texto, opcionalmente con una segunda línea como texto de detalle.
TextCells se representan como controles nativos en tiempo de ejecución, por lo que es muy buen rendimiento en
comparación con una personalizada ViewCell . TextCells son personalizables, lo que permite establecer:
Text – el texto que se muestra en la primera línea, en el tamaño de fuente.
Detail – el texto que se muestra debajo de la primera línea, en una fuente menor.
TextColor – el color del texto.
DetailColor – el color del texto de detalle
ImageCell
ImageCell , como TextCell , se puede usar para mostrar texto y texto de detalles secundario y ofrece un
excelente rendimiento mediante el uso de controles nativos de cada plataforma. ImageCell difiere de TextCell
que muestra una imagen a la izquierda del texto.
ImageCell es útil cuando se necesita mostrar una lista de los datos con un aspecto visual, como una lista de
contactos o películas. ImageCells son personalizables, lo que permite establecer:
Text – el texto que se muestra en la primera línea, en el tamaño de fuente
Detail – el texto que se muestra debajo de la primera línea, en una fuente menor
TextColor – el color del texto
DetailColor – el color del texto de detalle
ImageSource – la imagen que se muestra al lado del texto
Celdas personalizadas
Cuando las celdas integradas no proporcionan el diseño necesarias, celdas personalizadas implementan el
diseño necesarias. Por ejemplo, desea presentar una celda con dos etiquetas que tengan el mismo peso. Un
TextCell sería suficiente porque la TextCell tiene una etiqueta que sea más pequeña. La mayoría de las
personalizaciones de celda agregan datos adicionales de solo lectura (por ejemplo, etiquetas adicionales,
imágenes u otra información de presentación).
Todas las celdas personalizadas deben derivarse de ViewCell , la misma clase base que todos los de la celda
integrada tipos de uso.
Xamarin.Forms 2 presentó un nuevo comportamiento de almacenamiento en caché en el ListView control que
se puede establecer para mejorar el rendimiento de desplazamiento para algunos tipos de celdas
personalizadas.
Este es un ejemplo de una celda personalizada:
XAML
El XAML para crear el diseño anterior está por debajo:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="demoListView.ImageCellPage">
<ContentPage.Content>
<ListView x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="#eee"
Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<Image Source="{Binding image}" />
<Label Text="{Binding title}"
TextColor="#f35e20" />
<Label Text="{Binding subtitle}"
HorizontalOptions="EndAndExpand"
TextColor="#503026" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>

El XAML anterior hace mucho. Permítame desglosarlo:


La celda personalizada está anidada dentro de un DataTemplate , que está dentro de ListView.ItemTemplate .
Este es el mismo proceso que con cualquier otra celda.
ViewCell es el tipo de la celda personalizada. El elemento secundario de la DataTemplate debe ser de
elemento o se derivan del tipo ViewCell .
Tenga en cuenta que dentro del ViewCell , diseño está administrado por un StackLayout . Este diseño
permite personalizar el color de fondo. Tenga en cuenta que cualquier propiedad de StackLayout es
enlazable pueden ser enlazadas dentro de una celda personalizada, aunque no se muestra aquí.
Dentro de la ViewCell , diseño puede administrarse mediante cualquier diseño de Xamarin.Forms.
C#
Especificación de una celda personalizada en C# es un poco más detallado que el equivalente XAML.
Observemos lo siguiente:
En primer lugar, defina una clase de celda personalizada, con ViewCell como clase base:

public class CustomCell : ViewCell


{
public CustomCell()
{
//instantiate each of our views
var image = new Image ();
StackLayout cellWrapper = new StackLayout ();
StackLayout horizontalLayout = new StackLayout ();
Label left = new Label ();
Label right = new Label ();

//set bindings
left.SetBinding (Label.TextProperty, "title");
right.SetBinding (Label.TextProperty, "subtitle");
image.SetBinding (Image.SourceProperty, "image");

//Set properties for desired design


cellWrapper.BackgroundColor = Color.FromHex ("#eee");
horizontalLayout.Orientation = StackOrientation.Horizontal;
right.HorizontalOptions = LayoutOptions.EndAndExpand;
left.TextColor = Color.FromHex ("#f35e20");
right.TextColor = Color.FromHex ("503026");

//add views to the view hierarchy


horizontalLayout.Children.Add (image);
horizontalLayout.Children.Add (left);
horizontalLayout.Children.Add (right);
cellWrapper.Children.Add (horizontalLayout);
View = cellWrapper;
}
}

En el constructor de la página con el ListView , establecer el ListView ItemTemplate propiedad a un nuevo


DataTemplate :

public partial class ImageCellPage : ContentPage


{
public ImageCellPage ()
{
InitializeComponent ();
listView.ItemTemplate = new DataTemplate (typeof(CustomCell));
}
}

Tenga en cuenta que el constructor de DataTemplate toma un tipo. El operador typeof Obtiene el tipo CLR para
CustomCell .
Cambios de contexto de enlace
Al enlazar a un tipo de celda personalizado BindableProperty instancias, los controles de interfaz de usuario
mostrar el BindableProperty valores deben usar el OnBindingContextChanged invalide para establecer los datos
que se mostrará en cada celda, en lugar de al constructor de la celda, como se muestra en el ejemplo de código
siguiente:

public class CustomCell : ViewCell


{
Label nameLabel, ageLabel, locationLabel;

public static readonly BindableProperty NameProperty =


BindableProperty.Create ("Name", typeof(string), typeof(CustomCell), "Name");
public static readonly BindableProperty AgeProperty =
BindableProperty.Create ("Age", typeof(int), typeof(CustomCell), 0);
public static readonly BindableProperty LocationProperty =
BindableProperty.Create ("Location", typeof(string), typeof(CustomCell), "Location");

public string Name {


get { return(string)GetValue (NameProperty); }
set { SetValue (NameProperty, value); }
}

public int Age {


get { return(int)GetValue (AgeProperty); }
set { SetValue (AgeProperty, value); }
}

public string Location {


get { return(string)GetValue (LocationProperty); }
set { SetValue (LocationProperty, value); }
}
...

protected override void OnBindingContextChanged ()


{
base.OnBindingContextChanged ();

if (BindingContext != null) {
nameLabel.Text = Name;
ageLabel.Text = Age.ToString ();
locationLabel.Text = Location;
}
}
}

El OnBindingContextChanged invalidación llamará cuando el BindingContextChanged se activa el evento, en


respuesta al valor de la BindingContext cambio de propiedad. Por lo tanto, cuando el BindingContext cambia,
los controles de interfaz de usuario muestra el BindableProperty valores deben establecer sus datos. Tenga en
cuenta que el BindingContext debe comprobarse una null valor, tal como se puede establecer mediante
Xamarin.Forms para la recolección, lo que resultará en la OnBindingContextChanged invalidación que se llama.
Como alternativa, pueden enlazar controles de interfaz de usuario a la BindableProperty instancias para mostrar
sus valores, lo que elimina la necesidad de reemplazar el OnBindingContextChanged método.

NOTE
Cuando se reemplaza OnBindingContextChanged , asegúrese de que la clase base OnBindingContextChanged se llama al
método para que los delegados registrados reciban el BindingContextChanged eventos.

En XAML, el tipo de celda personalizado de enlace a datos puede lograrse tal como se muestra en el ejemplo de
código siguiente:

<ListView x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<local:CustomCell Name="{Binding Name}" Age="{Binding Age}" Location="{Binding Location}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Esto enlaza el Name , Age , y Location propiedades enlazables en el CustomCell instancia, el Name , Age ,y
Location las propiedades de cada objeto de la colección subyacente.

El enlace equivalente en C# se muestra en el ejemplo de código siguiente:

var customCell = new DataTemplate (typeof(CustomCell));


customCell.SetBinding (CustomCell.NameProperty, "Name");
customCell.SetBinding (CustomCell.AgeProperty, "Age");
customCell.SetBinding (CustomCell.LocationProperty, "Location");

var listView = new ListView {


ItemsSource = people,
ItemTemplate = customCell
};

En iOS y Android, si la ListView se recicla de elementos y la celda personalizada usa un representador


personalizado, el representador personalizado debe implementar correctamente la notificación de cambio de
propiedad. Cuando se reutilizan las celdas sus valores de propiedad cambiará cuando se actualiza el contexto de
enlace a la de una celda disponible, con PropertyChanged eventos provocados. Para obtener más información,
consulte personalizar ViewCell. Para obtener más información acerca de la celda de reciclaje, consulte estrategia
de almacenamiento en caché.

Vínculos relacionados
Integrado en las celdas (ejemplo)
Celdas personalizadas (ejemplo)
Cambiar de contexto de enlace (ejemplo)
Personalizar la apariencia de ListView
11/07/2019 • 11 minutes to read • Edit Online

descargar el ejemplo
ListView tiene la capacidad para controlar la presentación de la lista, además del ViewCell instancias para cada
fila de la lista.

Agrupar
A menudo, grandes conjuntos de datos pueden ser difícil de manejar cuando se presentan en una lista desplazable
continuamente. Habilitar agrupación puede mejorar la experiencia del usuario en estos casos organizar mejor el
contenido y activando controles específicos de la plataforma que facilitan la navegación de datos.
Cuando se activa la agrupación por una ListView , se agrega una fila de encabezado para cada grupo.
Para habilitar la agrupación:
Crear una lista de listas (una lista de grupos, cada grupo que se va a obtener una lista de elementos).
Establecer el ListView del ItemsSource a dicha lista.
Establecer IsGroupingEnabled en true.
Establecer GroupDisplayBinding para enlazar a la propiedad de los grupos que se usa como el título del grupo.
[Opcional] Establecer GroupShortNameBinding para enlazar a la propiedad de los grupos que se usa como el
nombre corto para el grupo. El nombre corto se utiliza para las listas de salto (columna derecha en iOS ).
Empiece por crear una clase para los grupos:

public class PageTypeGroup : List<PageModel>


{
public string Title { get; set; }
public string ShortName { get; set; } //will be used for jump lists
public string Subtitle { get; set; }
private PageTypeGroup(string title, string shortName)
{
Title = title;
ShortName = shortName;
}

public static IList<PageTypeGroup> All { private set; get; }


}

En el código anterior, All es la lista que se asignará a nuestra ListView como origen de enlace. Title y
ShortName son las propiedades que se usará para los encabezados de grupo.

En esta fase, All es una lista vacía. Agregue un constructor estático para que se rellenará la lista en el inicio del
programa:
static PageTypeGroup()
{
List<PageTypeGroup> Groups = new List<PageTypeGroup> {
new PageTypeGroup ("Alfa", "A"){
new PageModel("Amelia", "Cedar", new switchCellPage(),""),
new PageModel("Alfie", "Spruce", new switchCellPage(), "grapefruit.jpg"),
new PageModel("Ava", "Pine", new switchCellPage(), "grapefruit.jpg"),
new PageModel("Archie", "Maple", new switchCellPage(), "grapefruit.jpg")
},
new PageTypeGroup ("Bravo", "B"){
new PageModel("Brooke", "Lumia", new switchCellPage(),""),
new PageModel("Bobby", "Xperia", new switchCellPage(), "grapefruit.jpg"),
new PageModel("Bella", "Desire", new switchCellPage(), "grapefruit.jpg"),
new PageModel("Ben", "Chocolate", new switchCellPage(), "grapefruit.jpg")
}
}
All = Groups; //set the publicly accessible list
}

En el código anterior, también lo llamemos Add en elementos de groups , que son instancias del tipo
PageTypeGroup . Esto es posible porque PageTypeGroup hereda de List<PageModel> . Este es un ejemplo de la lista de
patrón de listas que se ha indicado anteriormente.
Este es el XAML para mostrar la lista agrupada:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DemoListView.GroupingViewPage"
<ContentPage.Content>
<ListView x:Name="GroupedView"
GroupDisplayBinding="{Binding Title}"
GroupShortNameBinding="{Binding ShortName}"
IsGroupingEnabled="true">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Title}"
Detail="{Binding Subtitle}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>

Esto da como resultado lo siguiente:


Tenga en cuenta que tenemos:
Establecer GroupShortNameBinding a la ShortName propiedad definido en nuestra clase de grupo
Establecer GroupDisplayBinding a la Title propiedad definido en nuestra clase de grupo
Establecer IsGroupingEnabled en true
Puede cambiar el ListView del ItemsSource a la lista agrupada
Personalizar la agrupación
Si se ha habilitado la agrupación en la lista, también se puede personalizar el encabezado de grupo.
Similar a cómo el ListView tiene un ItemTemplate para definir cómo se muestran las filas, ListView tiene un
GroupHeaderTemplate .
Aquí se muestra un ejemplo de personalizar el encabezado de grupo en XAML:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DemoListView.GroupingViewPage">
<ContentPage.Content>
<ListView x:Name="GroupedView"
GroupDisplayBinding="{Binding Title}"
GroupShortNameBinding="{Binding ShortName}"
IsGroupingEnabled="true">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Title}"
Detail="{Binding Subtitle}"
TextColor="#f35e20"
DetailColor="#503026" />
</DataTemplate>
</ListView.ItemTemplate>
<!-- Group Header Customization-->
<ListView.GroupHeaderTemplate>
<DataTemplate>
<TextCell Text="{Binding Title}"
Detail="{Binding ShortName}"
TextColor="#f35e20"
DetailColor="#503026" />
</DataTemplate>
</ListView.GroupHeaderTemplate>
<!-- End Group Header Customization -->
</ListView>
</ContentPage.Content>
</ContentPage>

Encabezados y pies de página


Es posible que un ListView presentar un encabezado y pie de página que se desplazan con los elementos de la lista.
El encabezado y pie de página pueden ser cadenas de texto o un diseño más complicado. Tenga en cuenta que esto
es independiente de grupos de sección.
Puede establecer el Header o Footer en una cadena simple valor, o bien puede configurarlos en un diseño más
complejo. También hay HeaderTemplate y FooterTemplate propiedades que le permiten creación diseños más
complejos para el encabezado y pie de página que admitan el enlace de datos.
Para crear un encabezado o pie de página simple, simplemente establezca las propiedades de encabezado o pie de
página en el texto que desea mostrar. Mediante código:

ListView HeaderList = new ListView() {


Header = "Header",
Footer = "Footer"
};

En XAML:

<ListView x:Name="HeaderList" Header="Header" Footer="Footer"></ListView>


Para crear un encabezado personalizado y un pie de página, definir las vistas de encabezado y pie de página:

<ListView.Header>
<StackLayout Orientation="Horizontal">
<Label Text="Header"
TextColor="Olive"
BackgroundColor="Red" />
</StackLayout>
</ListView.Header>
<ListView.Footer>
<StackLayout Orientation="Horizontal">
<Label Text="Footer"
TextColor="Gray"
BackgroundColor="Blue" />
</StackLayout>
</ListView.Footer>
Visibilidad de la barra de desplazamiento
ListView tiene HorizontalScrollBarVisibility y VerticalScrollBarVisibility propiedades, que obtención o
establece un ScrollBarVisibility valor que representa el momento de la barra de desplazamiento horizontal o
vertical, está visible. Ambas propiedades se pueden establecer en los siguientes valores:
Default indica el comportamiento de barra de desplazamiento predeterminado para la plataforma, y es el valor
predeterminado para el HorizontalScrollBarVisibility y VerticalScrollBarVisibility propiedades.
Always indica que las barras de desplazamiento serán visibles, incluso cuando el contenido se ajusta en la vista.
Never indica que las barras de desplazamiento no serán visibles, incluso si el contenido no se ajusta en la vista.

Separadores de fila
Se muestran las líneas de separación entre ListView elementos de forma predeterminada en iOS y Android. Si
prefiere ocultar las líneas de separación en iOS y Android, establezca el SeparatorVisibility propiedad en el
ListView. Las opciones para SeparatorVisibility son:
Default -muestra una línea de separación en iOS y Android.
Ninguno -oculta el separador en todas las plataformas.
Visibilidad predeterminada:
C#:

SepratorDemoListView.SeparatorVisibility = SeparatorVisibility.Default;

XAML:
<ListView x:Name="SeparatorDemoListView" SeparatorVisibility="Default" />

Ninguno:
C#:

SepratorDemoListView.SeparatorVisibility = SeparatorVisibility.None;

XAML:

<ListView x:Name="SeparatorDemoListView" SeparatorVisibility="None" />


También puede establecer el color de la línea de separación a través de la SeparatorColor propiedad:
C#:

SepratorDemoListView.SeparatorColor = Color.Green;

XAML:

<ListView x:Name="SeparatorDemoListView" SeparatorColor="Green" />


NOTE
Configuración de cualquiera de estas propiedades en Android después de cargar el ListView conlleva una reducción del
rendimiento.

Alto de fila
Todas las filas en una ListView tienen el mismo alto de forma predeterminada. ListView tiene dos propiedades que
pueden usarse para cambiar este comportamiento:
HasUnevenRows – true / false valor, las filas tienen diferentes alturas si establece en true . Tiene como valor
predeterminado false .
RowHeight – establece el alto de cada fila cuando HasUnevenRows es false .

Puede establecer el alto de todas las filas estableciendo el RowHeight propiedad en el ListView .
Alto de fila fijo personalizado
C#:

RowHeightDemoListView.RowHeight = 100;

XAML:

<ListView x:Name="RowHeightDemoListView" RowHeight="100" />


Filas desiguales
Si desea que las filas individuales tengan distintas alturas, puede establecer el HasUnevenRows propiedad true .
Tenga en cuenta que el alto de las filas no tiene que configurar manualmente una vez HasUnevenRows se ha
establecido en true , ya que el alto se calculará automáticamente mediante Xamarin.Forms.
C#:

RowHeightDemoListView.HasUnevenRows = true;

XAML:

<ListView x:Name="RowHeightDemoListView" HasUnevenRows="true" />


El cambio de tamaño en tiempo de ejecución de filas
Individuales ListView filas pueden cambiarse mediante programación en tiempo de ejecución, siempre que el
HasUnevenRows propiedad está establecida en true . El Cell.ForceUpdateSize método actualiza el tamaño de una
celda, incluso cuando no está actualmente visible, como se muestra en el ejemplo de código siguiente:

void OnImageTapped (object sender, EventArgs args)


{
var image = sender as Image;
var viewCell = image.Parent.Parent as ViewCell;

if (image.HeightRequest < 250) {


image.HeightRequest = image.Height + 100;
viewCell.ForceUpdateSize ();
}
}

El OnImageTapped controlador de eventos se ejecuta en respuesta a una Image en una celda que se pulsa y
aumenta el tamaño de la Image mostrada en la celda para que lo esté viendo fácilmente.
Tenga en cuenta que hay grandes posibilidades de degradación del rendimiento si esta característica está
sobreutilizada.

Vínculos relacionados
GROUPING (ejemplo)
Vista de representador personalizado (ejemplo)
Cambiar el tamaño de las filas dinámicas (ejemplo)
notas de la versión 1.4
notas de la versión 1.3
Interactividad de ListView
11/07/2019 • 9 minutes to read • Edit Online

descargar el ejemplo
ListView admite la interacción con los datos que presenta.

Derivaciones de & selección


El modo de selección se controla estableciendo la
ListView ListView.SelectionMode propiedad con un valor de la
ListViewSelectionMode enumeración:

Single indica que se puede seleccionar un solo elemento con el elemento seleccionado se resalta. Este es el
valor predeterminado.
None indica que no se pueden seleccionar elementos.

Cuando un usuario puntea un elemento, se activan dos eventos:


ItemSelected se desencadena cuando se selecciona un elemento nuevo.
ItemTapped se desencadena cuando se pulsa un elemento.
Pulsar dos veces el mismo elemento se desencadenarán dos ItemTapped eventos, pero tendrá solo desencadena
una sola ItemSelected eventos.

NOTE
El ItemTappedEventArgs (clase), que contiene los argumentos de evento para el ItemTapped evento tiene Group y
Item propiedades y un ItemIndex propiedad cuyo valor representa el índice de la ListView del elemento derivado. De
forma similar, el SelectedItemChangedEventArgs (clase), que contiene los argumentos de evento para el ItemSelected
evento, tiene un SelectedItem propiedad y un SelectedItemIndex propiedad cuyo valor representa el índice de la
ListView del elemento seleccionado.

Cuando el SelectionMode propiedad está establecida en Single , los elementos de la ListView puede
seleccionarse el ItemSelected y ItemTapped se desencadena eventos y el SelectedItem propiedad se establecerá
en el valor del elemento seleccionado.
Cuando el SelectionMode propiedad está establecida en None , los elementos de la ListView no puede
seleccionarse el ItemSelected no se desencadenará el evento y el SelectedItem propiedad permanecerá null .
Sin embargo, ItemTapped todavía se desencadena eventos y el elemento punteado aparecerán resaltado
brevemente durante la derivación.
Cuando se selecciona un elemento y el SelectionMode se cambia la propiedad de Single a None , SelectedItem
propiedad se establecerá en null y ItemSelected se desencadenará el evento con un null elemento.
El siguientes capturas de pantalla se muestra un ListView con el modo de selección predeterminado:
Deshabilitar selección
Para deshabilitar ListView selección conjunto el SelectionMode propiedad None :

<ListView ... SelectionMode="None" />

var listView = new ListView { ... SelectionMode = ListViewSelectionMode.None };

Acciones de contexto
A menudo, los usuarios deseen realizar acciones en un elemento en un ListView . Por ejemplo, considere la
posibilidad de obtener una lista de mensajes de correo electrónico en la aplicación de correo. En iOS, puede
deslizar para eliminar un mensaje::

Acciones de contexto se pueden implementar en C# y XAML. A continuación encontrará guías específicas para
ambos, pero primero vamos a Eche un vistazo a algunos detalles de implementación clave para ambos.
Acciones de contexto se crean mediante MenuItem s. Los eventos TAP para MenuItems generados por el
MenuItem, no el ListView. Esto es diferente de cómo se controlan los eventos tap para las celdas, donde el
ListView provoca el evento en lugar de la celda. Dado que el ListView está provocando el evento, su controlador
de eventos tiene información de clave, al igual que el elemento se ha seleccionado o pulsa.
De forma predeterminada, un elemento de menú no tiene ninguna manera de saber qué celda pertenece.
CommandParameter está disponible en MenuItem para almacenar objetos, como el objeto subyacente ViewCell de
MenuItem. CommandParameter se puede establecer en XAML y C#.
C#
Acciones de contexto se pueden implementar en cualquier Cell subclase (siempre y cuando no se use como un
encabezado de grupo) mediante la creación de MenuItem s y agregarlas a la ContextActions colección para la
celda. Tiene las siguientes propiedades se pueden configurar para la acción de contexto:
Texto – la cadena que aparece en el elemento de menú.
Hacer clic en – el evento cuando se hace clic en el elemento.
IsDestructive – (opcional) cuando sea true el elemento se representa de forma diferente, en iOS.
Se pueden agregar varias acciones de contexto a una celda, pero solo uno debe tener IsDestructive establecido
en true . El código siguiente muestra cómo las acciones de contexto se agregarían a un ViewCell :

var moreAction = new MenuItem { Text = "More" };


moreAction.SetBinding (MenuItem.CommandParameterProperty, new Binding ("."));
moreAction.Clicked += async (sender, e) => {
var mi = ((MenuItem)sender);
Debug.WriteLine("More Context Action clicked: " + mi.CommandParameter);
};

var deleteAction = new MenuItem { Text = "Delete", IsDestructive = true }; // red background
deleteAction.SetBinding (MenuItem.CommandParameterProperty, new Binding ("."));
deleteAction.Clicked += async (sender, e) => {
var mi = ((MenuItem)sender);
Debug.WriteLine("Delete Context Action clicked: " + mi.CommandParameter);
};
// add to the ViewCell's ContextActions property
ContextActions.Add (moreAction);
ContextActions.Add (deleteAction);

XAML
MenuItem s también se pueden crear mediante declaración en una colección de XAML. El XAML siguiente muestra
una celda personalizada con dos acciones de contexto implementadas:

<ListView x:Name="ContextDemoList">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Clicked="OnMore" CommandParameter="{Binding .}"
Text="More" />
<MenuItem Clicked="OnDelete" CommandParameter="{Binding .}"
Text="Delete" IsDestructive="True" />
</ViewCell.ContextActions>
<StackLayout Padding="15,0">
<Label Text="{Binding title}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

En el archivo de código subyacente, asegúrese de que el Clicked se implementan los métodos:


public void OnMore (object sender, EventArgs e) {
var mi = ((MenuItem)sender);
DisplayAlert("More Context Action", mi.CommandParameter + " more context action", "OK");
}

public void OnDelete (object sender, EventArgs e) {


var mi = ((MenuItem)sender);
DisplayAlert("Delete Context Action", mi.CommandParameter + " delete context action", "OK");
}

NOTE
El NavigationPageRenderer para Android tiene una reemplazable UpdateMenuItemIcon método que se puede usar para
cargar los iconos de un personalizado Drawable . Esta invalidación permite usar las imágenes SVG como iconos en
MenuItem instancias en Android.

Deslizar para actualizar


Los usuarios han llegado a esperar que extraer en una lista de datos se actualizará esa lista. ListView es
compatible con esta-de-fábrica. Para habilitar la funcionalidad de deslizar para actualizar, establezca
IsPullToRefreshEnabled a true :

<ListView ...
IsPullToRefreshEnabled="true" />

El código de C# equivalente es:

listView.IsPullToRefreshEnabled = true;

Aparece un indicador giratorio durante la actualización, que es el negra de forma predeterminada. Sin embargo, se
puede cambiar el color del indicador giratorio en iOS y Android estableciendo el RefreshControlColor propiedad a
un Color :

<ListView ...
IsPullToRefreshEnabled="true"
RefreshControlColor="Red" />

El código de C# equivalente es:

listView.RefreshControlColor = Color.Red;

Las capturas de pantalla siguientes muestran Deslizar para actualizar tal y como se extrae el usuario:
Las capturas de pantalla siguientes muestran Deslizar para actualizar después de que el usuario ha soltado la
incorporación de cambios, con el control de número que se muestra mientras el ListView se está actualizando:

ListView se desencadena la Refreshing eventos para iniciar la actualización y el IsRefreshing propiedad se


establecerá en true . No se requiere para actualizar el contenido de cualquier código el ListView , a continuación,
se debe ejecutar el controlador de eventos para el Refreshing evento, o mediante el método ejecutado por el
RefreshCommand . Una vez el ListView se actualiza, el IsRefreshing propiedad debe establecerse en false , o el
EndRefresh método debe llamarse, para indicar que la actualización ha finalizado.

NOTE
Al definir un RefreshCommand , el CanExecute se puede especificar el método del comando para habilitar o deshabilitar el
comando.

Vínculos relacionados
Interactividad de ListView (ejemplo)
Rendimiento de ListView
11/07/2019 • 15 minutes to read • Edit Online

descargar el ejemplo
Al escribir aplicaciones móviles, es importante el rendimiento. Los usuarios han llegado a esperar el
desplazamiento suave y tiempos de carga rápida. No se puede satisfacer las expectativas de los usuarios de costo
que las clasificaciones en el almacén de aplicaciones o en el caso de una aplicación de línea de negocio, costo de su
organización tiempo y dinero.
Aunque ListView es una eficaz vista para mostrar los datos, tiene algunas limitaciones. El rendimiento del
desplazamiento puede verse afectado cuando se usa celdas personalizadas, especialmente cuando contienen
jerarquías de vista profundamente anidadas o utilicen algunos diseños que requieren una gran cantidad de
medida. Afortunadamente, hay técnicas que puede usar para evitar un rendimiento deficiente.

Estrategia de almacenamiento en caché


ListView a menudo se usa para mostrar muchos más datos que puede ajustar en la pantalla. Considere la
posibilidad de una aplicación de música, por ejemplo. Una biblioteca de canciones puede tener miles de entradas.
El enfoque simple, que sería crear una fila por cada canción, tendría un rendimiento deficiente. Ese enfoque
desperdicia memoria valiosa y puede ralentizar el desplazamiento a un rastreo. Otro enfoque consiste en crear y
destruir las filas, como los datos se desplazan en la vista. Esto requiere la creación de instancias constante y
limpieza de los objetos de vista, que puede ser muy lento.
Para ahorrar memoria, nativo ListView equivalentes para cada plataforma tienen características integradas para
la reutilización de las filas. Solo las celdas visibles en pantalla se cargan en memoria y el contenido se carga en
las celdas existentes. Esto evita que la aplicación de la necesidad de crear instancias de miles de objetos, ahorra
tiempo y memoria.
Xamarin.Forms permite ListView celda volver a usar a través de la ListViewCachingStrategy enumeración, que
tiene los siguientes valores:

public enum ListViewCachingStrategy


{
RetainElement, // the default value
RecycleElement,
RecycleElementAndDataTemplate
}

NOTE
La plataforma Universal de Windows (UWP) pasa por alto el RetainElement almacenamiento en caché de estrategia,
porque siempre usa almacenamiento en caché para mejorar el rendimiento. Por lo tanto, de forma predeterminada se
comporta como si la RecycleElement se aplica la estrategia de almacenamiento en caché.

RetainElement
El RetainElement estrategia de almacenamiento en caché especifica que el ListView generará una celda para cada
elemento en la lista y el valor predeterminado es ListView comportamiento. Por lo general debe utilizarse en las
siguientes circunstancias:
Cuando cada celda tiene un gran número de enlaces (20-30 +).
Cuando la plantilla de celda cambia con frecuencia.
Cuando las pruebas revelan que el RecycleElement almacenamiento en caché de resultados de la estrategia
una velocidad de ejecución reducida.
Es importante tener en cuenta las consecuencias de la RetainElement estrategia de almacenamiento en caché
cuando se trabaja con celdas personalizadas. Cualquier código de inicialización de la celda tendrá que ejecutar
para la creación de cada celda, que puede ser varias veces por segundo. En este caso, las técnicas de diseño que
eran aceptables en una página, como uso de varios anidados StackLayout instancias, se convierten en los cuellos
de botella de rendimiento cuando se programa de instalación y se destruye en tiempo real como el usuario se
desplaza.
RecycleElement
El RecycleElement estrategia de almacenamiento en caché especifica que el ListView intentará minimizar su
velocidad de consumo y la ejecución de memoria mediante el reciclado de celdas de la lista. Este modo no siempre
ofrece una mejora del rendimiento y las pruebas deben realizarse para determinar las mejoras. Sin embargo, suele
ser la opción preferida y debe usarse en las siguientes circunstancias:
Cuando cada celda tiene un pequeño o un número moderado de enlaces.
Cuando cada celda BindingContext define todos los datos de celda.
Cuando cada celda es similar en gran medida, con la plantilla de celda que no cambian.
Durante la virtualización de la celda tendrá su contexto de enlace que se actualiza y, por lo que si una aplicación
utiliza este modo debe asegurarse de que las actualizaciones de contexto de enlace se controlan apropiadamente.
Todos los datos sobre la celda deben proceder del contexto de enlace o se pueden producir errores de coherencia.
Esto puede realizarse mediante el uso de enlace de datos para mostrar datos de la celda. Como alternativa, se
deben establecer los datos de la celda la OnBindingContextChanged invalidar, en lugar de en el constructor de la
celda personalizada, como se muestra en el ejemplo de código siguiente:

public class CustomCell : ViewCell


{
Image image = null;

public CustomCell ()
{
image = new Image();
View = image;
}

protected override void OnBindingContextChanged ()


{
base.OnBindingContextChanged ();

var item = BindingContext as ImageItem;


if (item != null) {
image.Source = item.ImageUrl;
}
}
}

Para obtener más información, consulte cambios de contexto de enlace.


En iOS y Android, si las celdas usar a representadores personalizados, debe asegurarse de que se haya
implementado correctamente la notificación de cambio de propiedad. Cuando se reutilizan las celdas sus valores
de propiedad cambiará cuando se actualiza el contexto de enlace a la de una celda disponible, con
PropertyChanged eventos provocados. Para obtener más información, consulte personalizar ViewCell.

RecycleElement con un DataTemplateSelector


Cuando un ListView usa un DataTemplateSelector para seleccionar un DataTemplate , RecycleElement
almacenamiento en caché estrategia no almacena en caché DataTemplate s. En su lugar, un DataTemplate está
seleccionado para cada elemento de datos en la lista.

NOTE
El RecycleElement estrategia de almacenamiento tiene un requisito previo, introducido en 2.4 de Xamarin.Forms, que,
cuando un DataTemplateSelector se le pide que seleccione un DataTemplate que cada DataTemplate debe devolver el
mismo ViewCell tipo. Por ejemplo, dada una ListView con un DataTemplateSelector que puede devolver
MyDataTemplateA (donde MyDataTemplateA devuelve un ViewCell typu MyViewCellA ), o MyDataTemplateB (donde
MyDataTemplateB devuelve un ViewCell typu MyViewCellB ), cuando MyDataTemplateA devuelto debe devolver
MyViewCellA o se producirá una excepción.

RecycleElementAndDataTemplate
El RecycleElementAndDataTemplate estrategia de almacenamiento en caché se basa en el RecycleElement estrategia
de almacenamiento en caché asegurándose además que, cuando un ListView usa un DataTemplateSelector para
seleccionar un DataTemplate , DataTemplate s se almacenan en caché por el tipo de elemento de la lista. Por lo
tanto, DataTemplate s se seleccionan una vez por cada tipo de elemento, en lugar de una vez por cada instancia del
elemento.

NOTE
El RecycleElementAndDataTemplate estrategia de almacenamiento tiene un requisito previo que la DataTemplate s
devuelto por la DataTemplateSelector debe utilizar el DataTemplate constructor que toma un Type .

Establecimiento de la estrategia de almacenamiento en caché


El ListViewCachingStrategy se especifica el valor de enumeración con un ListView sobrecarga del constructor, tal
como se muestra en el ejemplo de código siguiente:

var listView = new ListView(ListViewCachingStrategy.RecycleElement);

En XAML, establezca el CachingStrategy atributo tal como se muestra en el código siguiente:

<ListView CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
...
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Esto tiene el mismo efecto que establecer el argumento de estrategia de almacenamiento en caché en el
constructor en C# Tenga en cuenta que no hay ningún CachingStrategy propiedad ListView .
Establecimiento de la estrategia de almacenamiento en caché en un ListView crean Subclase
Establecer el CachingStrategy atributo de XAML en una subclase ListView no producirá el comportamiento
deseado, porque no hay ningún CachingStrategy propiedad ListView . Además, si XAMLC está habilitado, se
producirá el siguiente mensaje de error: Ninguna propiedad, propiedad enlazable o evento encontrado
para 'CachingStrategy'
La solución a este problema consiste en especificar un constructor de subclases ListView que acepta un
ListViewCachingStrategy parámetro y lo pasa a la clase base:
public class CustomListView : ListView
{
public CustomListView (ListViewCachingStrategy strategy) : base (strategy)
{
}
...
}

El ListViewCachingStrategy se puede especificar el valor de enumeración de XAML mediante el uso de la


x:Arguments sintaxis:

<local:CustomListView>
<x:Arguments>
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
</x:Arguments>
</local:CustomListView>

Mejorar el rendimiento de ListView


Existen muchas técnicas para mejorar el rendimiento de un ListView :
Enlazar el ItemsSource propiedad a un IList<T> colección en lugar de un IEnumerable<T> colección, porque
IEnumerable<T> colecciones no admiten el acceso aleatorio.
Utilice las celdas integradas (como TextCell / SwitchCell ) en lugar de ViewCell siempre puede.
Use menos elementos. Por ejemplo, considere el uso de una sola FormattedString etiqueta en lugar de varias
etiquetas.
Reemplace el ListView con un TableView al mostrar los datos no homogéneos, es decir, los datos de
diferentes tipos.
Limitar el uso de la Cell.ForceUpdateSize método. Si se sobreutiliza, degradará el rendimiento.
En Android, evite establecer un ListView de visibilidad del separador de fila o de color después de que se ha
creado una instancia, como resultado una reducción del rendimiento.
Evite cambiar el diseño de la celda según la BindingContext . Esto provoca costos de inicialización y de diseño
grandes.
Evite las jerarquías de diseño profundamente anidado. Use AbsoluteLayout o Grid para ayudar a reducir el
anidamiento.
Evitar específico LayoutOptions distinto Fill (relleno es el más barato de proceso).
Evite colocar un ListView dentro de un ScrollView por las razones siguientes:
El ListView implementa su propio desplazamiento.
El ListView no recibirán los gestos, como se controlarán por el elemento primario ScrollView .
El ListView puede presentar un encabezado personalizado y un pie de página que se desplaza por los
elementos de la lista, ofreciendo la funcionalidad que el ScrollView utilizó. Para obtener más
información, consulte encabezados y pies de página.
Considere la posibilidad de un representador personalizado si necesita un diseño muy específico y complejo
presentan en sus celdas.
AbsoluteLayout tiene la posibilidad de realizar diseños sin una llamada única medida. Esto resulta muy eficaz para
el rendimiento. Si AbsoluteLayout no se puede usar, considere la posibilidad de RelativeLayout . Si usa
RelativeLayout , pasar directamente restricciones será considerablemente más rápido que utilizar la API de la
expresión. Eso es porque la expresión de API usa JIT, y en iOS el árbol debe interpretarse, que es más lento. La
expresión de API es adecuada para los diseños de página donde solo requería en el diseño inicial y la rotación,
pero en ListView , donde se ejecuta constantemente durante el desplazamiento, repercute en el rendimiento.
Creación de un representador personalizado para un ListView o sus celdas es un enfoque para reducir el efecto
de los cálculos de diseño en el rendimiento del desplazamiento. Para obtener más información, consulte
personalizar un ListView y personalizar ViewCell.

Vínculos relacionados
Vista de representador personalizado (ejemplo)
ViewCell de representador personalizado (ejemplo)
ListViewCachingStrategy
Mapa de Xamarin.Forms
11/07/2019 • 16 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms utiliza la asignación nativa API en cada plataforma.
Xamarin.Forms.Maps para utiliza la asignación nativa API en cada plataforma. Esto proporciona una
experiencia rápida, familiar mapas para los usuarios, pero significa que algunos pasos de configuración son
necesarias para cumplir con requisitos de cada API de plataformas. Una vez configurado, el Map funciona igual
que cualquier otro elemento de Xamarin.Forms en el código común de control.
Se ha utilizado el control de mapa en el MapsSample ejemplo, que se muestra a continuación.

Funcionalidad de asignación se puede mejorar aún más mediante la creación de un asignar un representador
personalizado.

Inicialización del mapa


Al agregar mapas a una aplicación de Xamarin.Forms, xamarin.Forms.Maps para es un paquete NuGet
independiente que se debe agregar a cada proyecto en la solución. En Android, esto también tiene una
dependencia en GooglePlayServices (NuGet otro) que se descarga automáticamente cuando se agrega
xamarin.Forms.Maps para.
Después de instalar el paquete de NuGet, es necesario algún código de inicialización en cada proyecto de
aplicación, después el Xamarin.Forms.Forms.Init llamada al método. Para iOS, use el código siguiente:

Xamarin.FormsMaps.Init();

En Android debe pasar los mismos parámetros que Forms.Init :

Xamarin.FormsMaps.Init(this, bundle);

Para la plataforma Universal de Windows (UWP ) use el código siguiente:

Xamarin.FormsMaps.Init("INSERT_AUTHENTICATION_TOKEN_HERE");
Agregue esta llamada en los siguientes archivos para cada plataforma:
iOS -archivo AppDelegate.cs en el FinishedLaunching método.
Android -archivo MainActivity.cs, en el OnCreate método.
UWP -archivo MainPage.xaml.cs, en el MainPage constructor.
Una vez que se ha agregado el paquete de NuGet y llama al método de inicialización dentro de cada aplicación,
Xamarin.Forms.Maps API pueden usarse en el proyecto de biblioteca estándar de .NET común o el código de
proyecto compartido.

Configuración de la plataforma
Se requieren pasos de configuración adicional en algunas plataformas antes de que se mostrará el mapa.
iOS
Para obtener acceso a servicios de ubicación en iOS, debe establecer las siguientes claves Info.plist:
iOS 11
NSLocationWhenInUseUsageDescription : para usar los servicios de ubicación cuando la aplicación está
en uso
NSLocationAlwaysAndWhenInUseUsageDescription : para usar los servicios de ubicación en todo
momento
iOS 10 y versiones anteriores
NSLocationWhenInUseUsageDescription : para usar los servicios de ubicación cuando la aplicación está
en uso
NSLocationAlwaysUsageDescription : para usar los servicios de ubicación en todo momento

Para admitir iOS 11 y versiones anteriores, puede incluir estas tres claves:
NSLocationWhenInUseUsageDescription , NSLocationAlwaysAndWhenInUseUsageDescription , y
NSLocationAlwaysUsageDescription .

La representación XML de estas claves en Info.plist se muestra a continuación. Debe actualizar el string
valores para que reflejen cómo la aplicación está utilizando la información de ubicación:

<key>NSLocationAlwaysUsageDescription</key>
<string>Can we use your location at all times?</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Can we use your location when your app is being used?</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Can we use your location at all times?</string>

El Info.plist también se pueden agregar entradas en origen vista mientras edita el Info.plist archivo:

Android
Para usar el v2 de la API de Google Maps en Android debe generar una clave de API y agregarlo al proyecto
Android. Siga las instrucciones de la documentación de Xamarin obtención de una clave de API de Google
Maps v2. Después de seguir estas instrucciones, pegue la clave de API en el
Properties/Androidmanifest.XML archivo (ver código fuente y buscar o actualizar el elemento siguiente):
<application ...>
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOUR_API_KEY" />
</application>

Sin una clave de API válida, el control de mapas se mostrará como un cuadro gris en Android.

NOTE
Tenga en cuenta que, en orden para el APK tener acceso a Google Maps, debe incluir las huellas digitales de SHA-1 y
empaquetar los nombres para cada almacén de claves (debug y release) que usó para firmar el APK. Por ejemplo, si usa
un equipo para depuración y otro equipo para generar el APK de lanzamiento, debe incluir la huella digital de certificado
de SHA-1 desde el almacén de claves de depuración del primer equipo y la huella digital de certificado de SHA-1 desde el
almacén de claves de la versión de el segundo equipo. Recuerde también que modificar las credenciales de clave si la
aplicación nombre del paquete cambios. Consulte obtención de una clave de API de Google Maps v2.

También deberá habilitar los permisos adecuados, con el botón secundario en el proyecto de Android y
seleccione Opciones > compilar > aplicación de Android y funciona a la perfección lo siguiente:
AccessCoarseLocation
AccessFineLocation
AccessLocationExtraCommands
AccessMockLocation
AccessNetworkState
AccessWifiState
Internet

Algunos de ellos se muestran en la captura de pantalla siguiente:

Los dos últimos son necesarios porque las aplicaciones requieren una conexión de red para descargar datos
del mapa. Obtenga información sobre Android permisos para obtener más información.
Además, 9 Android quitó la biblioteca de cliente Apache HTTP desde el bootclasspath, y por lo que no está
disponible para las aplicaciones destinadas a API 28 o superior. Se debe agregar la siguiente línea a la
application nodo de su AndroidManifest.xml archivo seguir usando el cliente HTTP de Apache en las
aplicaciones destinadas a API 28 o superior:

<application ...>
...
<uses-library android:name="org.apache.http.legacy" android:required="false" />
</application>

Plataforma universal de Windows


Para utilizar asignaciones en la plataforma Universal de Windows debe generar un token de autorización. Para
obtener más información, consulte solicitar una clave de autenticación de asignaciones en MSDN.
El token de autenticación, a continuación, se debe especificar en el FormsMaps.Init("AUTHORIZATION_TOKEN")
llamada al método, para autenticar la aplicación con Bing Maps.
Configuración de asignación
Consulte la MapPage.cs en el ejemplo MobileCRM para obtener un ejemplo de cómo se puede usar el control
de mapa en el código. Un simple MapPage clase podría parecerse a este: tenga en cuenta que un nuevo
MapSpan se crea para colocar la vista del mapa:

public class MapPage : ContentPage {


public MapPage() {
var map = new Map(
MapSpan.FromCenterAndRadius(
new Position(37,-122), Distance.FromMiles(0.3))) {
IsShowingUser = true,
HeightRequest = 100,
WidthRequest = 960,
VerticalOptions = LayoutOptions.FillAndExpand
};
var stack = new StackLayout { Spacing = 0 };
stack.Children.Add(map);
Content = stack;
}
}

Tipo de mapa
También se puede cambiar el contenido del mapa estableciendo el MapType propiedad para mostrar un mapa
de calle normal (predeterminado), imágenes de satélite o una combinación de ambos.

map.MapType == MapType.Street;

Válido MapType los valores son:


Híbrido
Satélite
Calle (predeterminado)
Región del mapa y MapSpan
Como se muestra en el fragmento de código anterior, proporcionando un MapSpan instancia a un constructor
de asignación establece la vista inicial (punto central y el nivel de zoom) de la asignación cuando se cargue. El
MoveToRegion método en la clase map, a continuación, puede utilizarse para cambiar el nivel de posición o
zoom del mapa. Hay dos maneras de crear un nuevo MapSpan instancia:
MapSpan.FromCenterAndRadius() -método estático para crear un intervalo de un Position y
especificando un Distance .
New () de MapSpan -constructor que usa un Position y los grados de latitud y longitud para mostrar.
Para cambiar el nivel de zoom del mapa sin modificar la ubicación, cree un nuevo MapSpan utilizando la
ubicación actual de la VisibleRegion.Center propiedad del control de mapa. Un Slider podría usarse para
controlar el zoom del mapa similar al siguiente (sin embargo, hacer zoom directamente en el control de mapa,
actualmente no puede actualizar el valor del control deslizante):

var slider = new Slider (1, 18, 1);


slider.ValueChanged += (sender, e) => {
var zoomLevel = e.NewValue; // between 1 and 18
var latlongdegrees = 360 / (Math.Pow(2, zoomLevel));
map.MoveToRegion(new MapSpan (map.VisibleRegion.Center, latlongdegrees, latlongdegrees));
};
Asignar PIN
Se pueden marcar las ubicaciones en el mapa con Pin objetos.

var position = new Position(37,-122); // Latitude, Longitude


var pin = new Pin {
Type = PinType.Place,
Position = position,
Label = "custom pin",
Address = "custom detail info"
};
map.Pins.Add(pin);

PinType puede establecerse en uno de los valores siguientes, que pueden afectar a la manera en que se
procesa el pin (según la plataforma):
Genérico
Lugar
SavedPin
SearchResult
Clics de mapa
Map define un MapClicked evento que se desencadena cuando se pulsa el mapa. El MapClickedEventArgs
objeto que acompaña a la MapClicked el evento tiene una propiedad única denominada Position , del tipo
Position . Cuando se desencadena el evento, el valor de la Position propiedad está establecida en la
ubicación del mapa que se ha punteado.
En el ejemplo de código siguiente se muestra un controlador de eventos para el MapClicked eventos:

map.MapClicked += OnMapClicked;

void OnMapClicked(object sender, MapClickedEventArgs e)


{
System.Diagnostics.Debug.WriteLine($"MapClick: {e.Position.Latitude}, {e.Position.Longitude}");
}

En este ejemplo, el OnMapClicked controlador de eventos genera la latitud y longitud que representa la
ubicación del mapa derivados.
Crear un mapa en XAML
MAPS también se pueden crear en XAML, como se muestra en este ejemplo:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"
x:Class="MapDemo.MapPage">
<StackLayout VerticalOptions="StartAndExpand" Padding="30">
<maps:Map x:Name="MyMap"
Clicked="OnMapClicked"
WidthRequest="320"
HeightRequest="200"
IsShowingUser="true"
MapType="Hybrid" />
</StackLayout>
</ContentPage>

NOTE
Más xmlns definición de espacio de nombres es necesario para hacer referencia a los controles de xamarin.Forms.Maps
para.

El MapRegion y Pins se puede establecer en el código mediante la referencia con nombre para el Map :

MyMap.MoveToRegion(
MapSpan.FromCenterAndRadius(
new Position(37,-122), Distance.FromMiles(1)));

Rellenar un mapa con datos mediante el enlace de datos


El Map clase también expone las siguientes propiedades:
ItemsSource : especifica la colección de IEnumerable elementos para mostrar.
ItemTemplate : especifica el DataTemplate para aplicar a cada elemento de la colección de elementos
mostrados.
ItemTemplateSelector : especifica el DataTemplateSelector que se usará para elegir una DataTemplate para
un elemento en tiempo de ejecución.

NOTE
La propiedad ItemTemplate tiene prioridad cuando están establecidas ambas propiedades ItemTemplate e
ItemTemplateSelector .

Un Map se pueden rellenar con datos mediante el uso de enlace de datos para enlazar su ItemsSource
propiedad a un IEnumerable colección:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"
x:Class="WorkingWithMaps.PinItemsSourcePage">
<Grid>
...
<maps:Map x:Name="map"
ItemsSource="{Binding Locations}">
<maps:Map.ItemTemplate>
<DataTemplate>
<maps:Pin Position="{Binding Position}"
Address="{Binding Address}"
Label="{Binding Description}" />
</DataTemplate>
</maps:Map.ItemTemplate>
</maps:Map>
...
</Grid>
</ContentPage>

El ItemsSource datos de la propiedad se enlaza a la Locations propiedad del modelo de vista conectada, que
devuelve un ObservableCollection de Location objetos, que es un tipo personalizado. Cada Location objeto
define Address y Description propiedades de tipo string y un Position propiedad de tipo Position .
La apariencia de cada elemento de la IEnumerable colección se define estableciendo la ItemTemplate
propiedad a un DataTemplate que contiene un Pin objeto que se enlaza a datos propiedades adecuadas.
El siguientes capturas de pantalla se muestra un Map mostrando un Pin colección utilizando el enlace de
datos:

Elija la apariencia del elemento en tiempo de ejecución


La apariencia de cada elemento en el IEnumerable se puede elegir la colección en tiempo de ejecución, según
el valor del elemento, estableciendo el ItemTemplateSelector propiedad a un DataTemplateSelector :
<ContentPage ...
xmlns:local="clr-namespace:WorkingWithMaps"
xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps">
<ContentPage.Resources>
<local:MapItemTemplateSelector x:Key="MapItemTemplateSelector">
<local:MapItemTemplateSelector.DefaultTemplate>
<DataTemplate>
<maps:Pin Position="{Binding Position}"
Address="{Binding Address}"
Label="{Binding Description}" />
</DataTemplate>
</local:MapItemTemplateSelector.DefaultTemplate>
<local:MapItemTemplateSelector.XamarinTemplate>
<DataTemplate>
<maps:Pin Position="{Binding Position}"
Address="{Binding Address}"
Label="Xamarin!" />
</DataTemplate>
</local:MapItemTemplateSelector.XamarinTemplate>
</local:MapItemTemplateSelector>
</ContentPage.Resources>

<Grid>
...
<maps:Map x:Name="map"
ItemsSource="{Binding Locations}"
ItemTemplateSelector="{StaticResource MapItemTemplateSelector}" />
...
</Grid>
</ContentPage>

El ejemplo siguiente se muestra la MapItemTemplateSelector clase:

public class MapItemTemplateSelector : DataTemplateSelector


{
public DataTemplate DefaultTemplate { get; set; }
public DataTemplate XamarinTemplate { get; set; }

protected override DataTemplate OnSelectTemplate(object item, BindableObject container)


{
return ((Location)item).Address.Contains("San Francisco") ? XamarinTemplate : DefaultTemplate;
}
}

La clase MapItemTemplateSelectordefine las propiedades DefaultTemplate y XamarinTemplate de tipo


DataTemplate que se establecen para diferentes plantillas de datos. El OnSelectTemplate método devuelve el
XamarinTemplate , que muestra "Xamarin" como una etiqueta cuando un Pin se pulsa, cuando el elemento
tiene una dirección que contenga "San Francisco". Cuando el elemento no tiene una dirección que contenga
"San Francisco", la OnSelectTemplate método devuelve el DefaultTemplate .
Para obtener más información acerca de los selectores de plantilla de datos, vea creando un Xamarin.Forms
DataTemplateSelector.

Vínculos relacionados
MapsSample
Asignar a un representador personalizado
Xamarin.Forms Samples (Ejemplos de Xamarin.Forms)
Creación de un Xamarin.Forms DataTemplateSelector
Selector de Xamarin.Forms
11/07/2019 • 3 minutes to read • Edit Online

La vista de selector es un control para seleccionar un elemento de texto en una lista de datos.
Xamarin.Forms Picker muestra una breve lista de elementos, desde el que el usuario puede seleccionar un
elemento. Picker define las siguientes propiedades:
Title de tipo string , cuyo valor predeterminado es null .
TitleColor de tipo Color , el color utilizado para mostrar el Title texto.
ItemsSource de tipo IList , la lista de origen de elementos para mostrar, cuyo valor predeterminado es null .
SelectedIndex de tipo int , el índice del elemento seleccionado, cuyo valor predeterminado es -1.
SelectedItem de tipo object , el elemento seleccionado, cuyo valor predeterminado es null .
TextColor de tipo Color , el color utilizado para mostrar el texto, cuyo valor predeterminado es
Color.Default .
FontAttributes de tipo FontAttributes , cuyo valor predeterminado es FontAtributes.None .
FontFamily de tipo string , cuyo valor predeterminado es null .
FontSize de tipo double , que de forma predeterminada va de -1,0.

Todas las propiedades están respaldados por BindableProperty objetos, lo que significa que puede cambiar el
estilo y las propiedades pueden ser destinos de enlaces de datos. El SelectedIndex y SelectedItem propiedades
tienen un modo de enlace predeterminada de BindingMode.TwoWay , lo que significa que pueden ser destinos de
enlaces de datos en una aplicación que utiliza el Model-View -ViewModel (MVVM ) arquitectura. Para obtener
información sobre cómo establecer las propiedades de fuente, consulte fuentes.
Un Picker no muestra ningún dato al que se muestra por primera vez. En su lugar, el valor de su Title
propiedad se muestra como un marcador de posición en las plataformas iOS y Android:

Cuando el Picker se muestra el enfoque de ganancias, sus datos y el usuario puede seleccionar un elemento:
El Picker se activa un SelectedIndexChanged eventos cuando el usuario selecciona un elemento. Después de
selección, se muestra el elemento seleccionado por el Picker :

Hay dos técnicas para rellenar un Picker con datos:


Establecer el ItemsSource propiedad a los datos que se mostrará. Esta es la técnica recomendada. Para
obtener más información, consulte establecer la propiedad ItemsSource de un selector.
Agregar los datos que se mostrará a los Items colección. Esta técnica era el proceso original para rellenar un
Picker con datos. Para obtener más información, consulte agregar datos a la colección de elementos de un
selector.

Vínculos relacionados
Selector
Establecer la propiedad ItemsSource de un selector
11/07/2019 • 7 minutes to read • Edit Online

descargar el ejemplo
La vista de selector es un control para seleccionar un elemento de texto en una lista de datos. En este artículo se
explica cómo rellenar un selector de datos estableciendo la propiedad ItemsSource y cómo responder a la
selección de elementos por el usuario.
Xamarin.Forms 2.3.4 mejoró la Picker vista agregando la capacidad para rellenarlo con datos estableciendo sus
ItemsSource propiedad y para recuperar el elemento seleccionado de la SelectedItem propiedad. Además, se
puede cambiar el color del texto del elemento seleccionado estableciendo el TextColor propiedad a un Color .

Rellenar un selector de datos


Un Picker se pueden rellenar con datos estableciendo sus ItemsSource propiedad a un IList colección. Debe
ser de cada elemento de la colección, o derivado de, escriba object . Se pueden agregar elementos en XAML,
inicialice el ItemsSource propiedad desde una matriz de elementos:

<Picker x:Name="picker"
Title="Select a monkey"
TitleColor="Red">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Baboon</x:String>
<x:String>Capuchin Monkey</x:String>
<x:String>Blue Monkey</x:String>
<x:String>Squirrel Monkey</x:String>
<x:String>Golden Lion Tamarin</x:String>
<x:String>Howler Monkey</x:String>
<x:String>Japanese Macaque</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>

NOTE
Tenga en cuenta que el x:Array elemento requiere un Type atributo que indica el tipo de los elementos de la matriz.

El código de C# equivalente se muestra a continuación:

var monkeyList = new List<string>();


monkeyList.Add("Baboon");
monkeyList.Add("Capuchin Monkey");
monkeyList.Add("Blue Monkey");
monkeyList.Add("Squirrel Monkey");
monkeyList.Add("Golden Lion Tamarin");
monkeyList.Add("Howler Monkey");
monkeyList.Add("Japanese Macaque");

var picker = new Picker { Title = "Select a monkey", TitleColor = Color.Red };


picker.ItemsSource = monkeyList;
Responder a la selección de elemento
Un admite la selección de un elemento a la vez. Cuando un usuario selecciona un elemento, el
Picker
SelectedIndexChanged desencadena el evento, el SelectedIndex se actualiza la propiedad en un entero que
representa el índice del elemento seleccionado en la lista y el SelectedItem propiedad se actualiza a la object que
representa el elemento seleccionado. El SelectedIndex propiedad es un número de base cero que indica el
elemento del usuario seleccionado. Si se selecciona ningún elemento, que es el caso cuando la Picker en primer
lugar se crea y se inicializa, SelectedIndex será -1.

NOTE
Elemento de comportamiento de la selección en un Picker puede personalizarse en iOS con una plataforma específica.
Para obtener más información, consulte selección de elementos de control de selector de.

El ejemplo de código siguiente muestra cómo recuperar el SelectedItem valor de propiedad de la Picker en
XAML:

<Label Text="{Binding Source={x:Reference picker}, Path=SelectedItem}" />

El código de C# equivalente se muestra a continuación:

var monkeyNameLabel = new Label();


monkeyNameLabel.SetBinding(Label.TextProperty, new Binding("SelectedItem", source: picker));

Además, puede ser un controlador de eventos se ejecuta cuando el SelectedIndexChanged desencadena el evento:

void OnPickerSelectedIndexChanged(object sender, EventArgs e)


{
var picker = (Picker)sender;
int selectedIndex = picker.SelectedIndex;

if (selectedIndex != -1)
{
monkeyNameLabel.Text = (string)picker.ItemsSource[selectedIndex];
}
}

Este método obtiene la SelectedIndex valor de propiedad y el valor se utiliza para recuperar el elemento
seleccionado de la ItemsSource colección. Esto es funcionalmente equivalente a recuperar el elemento
seleccionado de la SelectedItem propiedad. Tenga en cuenta que cada elemento de la ItemsSource colección es de
tipo object por lo que debe convertirse a un string para su presentación.

NOTE
Un Picker puede inicializarse para mostrar un elemento específico mediante el establecimiento del SelectedIndex o
SelectedItem propiedades. Sin embargo, estas propiedades deben establecerse después de inicializar el ItemsSource
colección.

Rellenar un selector de datos mediante el enlace de datos


Un Picker puede también se rellena con datos mediante el uso de enlace de datos para enlazar su ItemsSource
propiedad a un IList colección. En XAML Esto se logra con la Binding extensión de marcado:
<Picker Title="Select a monkey"
TitleColor="Red"
ItemsSource="{Binding Monkeys}"
ItemDisplayBinding="{Binding Name}" />

El código de C# equivalente se muestra a continuación:

var picker = new Picker { Title = "Select a monkey", TitleColor = Color.Red };


picker.SetBinding(Picker.ItemsSourceProperty, "Monkeys");
picker.ItemDisplayBinding = new Binding("Name");

El ItemsSource datos de la propiedad se enlaza a la Monkeys propiedad del modelo de vista conectada, que
devuelve un IList<Monkey> colección. El siguiente ejemplo de código muestra la Monkey (clase), que contiene
cuatro propiedades:

public class Monkey


{
public string Name { get; set; }
public string Location { get; set; }
public string Details { get; set; }
public string ImageUrl { get; set; }
}

Cuando se enlaza a una lista de objetos, el Picker se le debe indicar qué propiedad para mostrar de cada objeto.
Esto se consigue estableciendo la ItemDisplayBinding propiedad a la propiedad necesaria de cada objeto. En los
ejemplos de código anteriores, el Picker está establecido para mostrar cada Monkey.Name valor de propiedad.
Responder a la selección de elemento
Enlace de datos puede usarse para establecer un objeto con el SelectedItem valor de propiedad cuando cambia:

<Picker Title="Select a monkey"


TitleColor="Red"
ItemsSource="{Binding Monkeys}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding SelectedMonkey}" />
<Label Text="{Binding SelectedMonkey.Name}" ... />
<Label Text="{Binding SelectedMonkey.Location}" ... />
<Image Source="{Binding SelectedMonkey.ImageUrl}" ... />
<Label Text="{Binding SelectedMonkey.Details}" ... />

El código de C# equivalente se muestra a continuación:


var picker = new Picker { Title = "Select a monkey", TitleColor = Color.Red };
picker.SetBinding(Picker.ItemsSourceProperty, "Monkeys");
picker.SetBinding(Picker.SelectedItemProperty, "SelectedMonkey");
picker.ItemDisplayBinding = new Binding("Name");

var nameLabel = new Label { ... };


nameLabel.SetBinding(Label.TextProperty, "SelectedMonkey.Name");

var locationLabel = new Label { ... };


locationLabel.SetBinding(Label.TextProperty, "SelectedMonkey.Location");

var image = new Image { ... };


image.SetBinding(Image.SourceProperty, "SelectedMonkey.ImageUrl");

var detailsLabel = new Label();


detailsLabel.SetBinding(Label.TextProperty, "SelectedMonkey.Details");

El SelectedItem datos de la propiedad se enlaza a la SelectedMonkey propiedad del modelo de vista conectada,
que es de tipo Monkey . Por lo tanto, cuando el usuario selecciona un elemento en el Picker , SelectedMonkey se
establecerá la propiedad seleccionada Monkey objeto. El SelectedMonkey datos de objeto se muestran en la
interfaz de usuario Label y Image vistas:

NOTE
Tenga en cuenta que el SelectedItem y SelectedIndex ambas propiedades admiten enlaces bidireccionales de forma
predeterminada.

Vínculos relacionados
Demostración de selector (ejemplo)
Monkey App (ejemplo)
Selector de enlazable (ejemplo)
Selector de API
Adición de datos a la colección de elementos de un
selector
11/07/2019 • 3 minutes to read • Edit Online

descargar el ejemplo
La vista de selector es un control para seleccionar un elemento de texto en una lista de datos. En este artículo se
explica cómo rellenar un selector de datos, éste se agrega a la colección de elementos y cómo responder a la
selección de elementos por el usuario.

Rellenar un selector de datos


Antes de Xamarin.Forms 2.3.4, el proceso para rellenar un Picker con datos consistió en agregar los datos que se
mostrará como de solo lectura Items colección, que es de tipo IList<string> . Cada elemento de la colección
debe ser de tipo string . Se pueden agregar elementos en XAML, inicialice la Items propiedad con una lista de
x:String elementos:

<Picker Title="Select a monkey"


TitleColor="Red">
<Picker.Items>
<x:String>Baboon</x:String>
<x:String>Capuchin Monkey</x:String>
<x:String>Blue Monkey</x:String>
<x:String>Squirrel Monkey</x:String>
<x:String>Golden Lion Tamarin</x:String>
<x:String>Howler Monkey</x:String>
<x:String>Japanese Macaque</x:String>
</Picker.Items>
</Picker>

El código de C# equivalente se muestra a continuación:

var picker = new Picker { Title = "Select a monkey", TitleColor = Color.Red };


picker.Items.Add("Baboon");
picker.Items.Add("Capuchin Monkey");
picker.Items.Add("Blue Monkey");
picker.Items.Add("Squirrel Monkey");
picker.Items.Add("Golden Lion Tamarin");
picker.Items.Add("Howler Monkey");
picker.Items.Add("Japanese Macaque");

Además de agregar datos utilizando el Items.Add método, datos también se pueden insertar en la colección
utilizando el Items.Insert método.

Responder a la selección de elemento


Un Pickeradmite la selección de un elemento a la vez. Cuando un usuario selecciona un elemento, el
SelectedIndexChanged desencadena el evento y el SelectedIndex se actualiza la propiedad en un entero que
representa el índice del elemento seleccionado en la lista. El SelectedIndex propiedad es un número de base cero
que indica el elemento que el usuario seleccionado. Si se selecciona ningún elemento, que es el caso cuando la
Picker en primer lugar se crea y se inicializa, SelectedIndex será -1.
NOTE
Elemento de comportamiento de la selección en un Picker puede personalizarse en iOS con una plataforma específica.
Para obtener más información, consulte selección de elementos de control de selector de.

El siguiente ejemplo de código muestra la OnPickerSelectedIndexChanged método de controlador de eventos, que


es ejecutado cuando la SelectedIndexChanged desencadena el evento:

void OnPickerSelectedIndexChanged(object sender, EventArgs e)


{
var picker = (Picker)sender;
int selectedIndex = picker.SelectedIndex;

if (selectedIndex != -1)
{
monkeyNameLabel.Text = picker.Items[selectedIndex];
}
}

Este método obtiene la SelectedIndex valor de propiedad y el valor se utiliza para recuperar el elemento
seleccionado de la Items colección. Dado que cada elemento de la Items colección es un string , se pueden
mostrar mediante un Label sin necesidad de realizar una conversión.

NOTE
Un Picker puede inicializarse para mostrar un elemento específico mediante el establecimiento del SelectedIndex
propiedad. Sin embargo, el SelectedIndex propiedad debe establecerse después de inicializar el Items colección.

Vínculos relacionados
Demostración de selector (ejemplo)
Selector
Xamarin.Forms ProgressBar
12/07/2019 • 4 minutes to read • Edit Online

Descargar el ejemplo
Xamarin.Forms ProgressBar es un control que se representa visualmente el progreso como una barra horizontal
que se rellena a un porcentaje representado por un float valor. El ProgressBar clase hereda de View .
La siguiente captura de pantalla muestra un ProgressBar en iOS y Android:

El ProgressBar control define dos propiedades:


Progresses un float valor que representa el progreso actual como un valor de 0 a 1. Progress los valores
menores que 0 se unirá a 0, los valores mayores que 1 se unirá a 1.
ProgressColor es un Color que afecta el interior que representa el progreso actual de color de la barra.

Estas propiedades están respaldadas por BindableProperty objetos, lo que significa que el ProgressBar puede
cambiar el estilo y ser el destino de los enlaces de datos.
El ProgressBar control también define un ProgressTo método que anima la barra de su valor actual en un valor
especificado. Para obtener más información, consulte animar un ProgressBar.

NOTE
El ProgressBar no acepta manipulación del usuario, por lo que se ha omitido al usar la tecla Tab para seleccionar los
controles.

Crear una barra de progreso


Un ProgressBar se pueden crear instancias en XAML. Su Progress se puede establecer la propiedad para
determinar el porcentaje de relleno de la barra de color interno. Si el Progress no está establecida la propiedad, el
valor predeterminado es 0. El ejemplo siguiente muestra cómo crear una instancia de un ProgressBar en XAML
con el elemento opcional Progress conjunto de propiedades:

<ProgressBar Progress="0.5" />

Un ProgressBar también se pueden crear en el código:

ProgressBar progressBar = new ProgressBar { Progress = 0.5f };


WARNING
No utilice las opciones de diseño horizontal sin restricciones, como Center , Start , o End con ProgressBar . En UWP,
el ProgressBar se contrae en una barra de ancho cero. Mantenga el valor predeterminado HorizontalOptions valor
Fill y no use un ancho de Auto al poner un ProgressBar en un Grid diseño.

Propiedades de apariencia de la barra de progreso


El ProgressColor propiedad puede establecerse para definir la barra interna de color cuando el Progress
propiedad es mayor que cero. El ejemplo siguiente muestra cómo crear una instancia de un ProgressBar en
XAML con el ProgressColor conjunto de propiedades:

<ProgressBar OnColor="Orange" />

El ProgressColor propiedad también se puede establecer al crear un ProgressBar en el código:

ProgressBar progressBar = new ProgressBar { ProgressColor = Color.Orange };

La siguiente captura de pantalla muestra la ProgressBar con el ProgressColor propiedad establecida en


Color.Orange en iOS y Android:

Animar un ProgressBar
El ProgressTo método anima la ProgressBar desde su actual Progress valor a un valor proporcionado con el
tiempo. El método acepta un float progreso valor, un uint duración en milisegundos, un Easing valor enum y
devuelve un Task<bool> . El código siguiente muestra cómo animar una ProgressBar :

// animate to 75% progress over 500 milliseconds with linear easing


await progressBar.ProgressTo(0.75, 500, Easing.Linear);

Para obtener más información sobre la Easing enumeración, consulte funciones de aceleración en
Xamarin.Forms.

Vínculos relacionados
Demostraciones de ProgressBar
Control deslizante de Xamarin.Forms
11/07/2019 • 22 minutes to read • Edit Online

descargar el ejemplo
Usar un control deslizante para seleccionar un intervalo de valores continuos.
Xamarin.Forms Slider es una barra horizontal que se puede manipular por el usuario para seleccionar un
double valor desde un intervalo continuo.

El Slider define tres propiedades de tipo double :


Minimum es el mínimo del intervalo, con un valor predeterminado de 0.
Maximum es el máximo del intervalo, con un valor predeterminado de 1.
Value es el valor del control deslizante, que puede oscilar entre Minimum y Maximum y tiene un valor
predeterminado de 0.
Las tres propiedades están respaldadas por BindableProperty objetos. El Value propiedad tiene un modo de
enlace predeterminada de BindingMode.TwoWay , lo que significa que es adecuado como origen de enlace en una
aplicación que utiliza el Model-View -ViewModel (MVVM ) arquitectura.

WARNING
Internamente, el Slider garantiza que Minimum es menor que Maximum . Si Minimum o Maximum nunca se establecen
para que Minimum es menos Maximum , se produce una excepción. Consulte la precauciones sección para obtener más
información sobre cómo el Minimum y Maximum propiedades.

El Slider convierte la Value propiedad para que esté entre Minimum y Maximum , ambos inclusive. Si el Minimum
propiedad se establece en un valor mayor que el Value propiedad, el Slider establece la Value propiedad
Minimum . De forma similar, si Maximum está establecido en un valor menor que Value , a continuación, Slider
establece la Value propiedad a Maximum .
Slider define un ValueChanged evento que se desencadena cuando el Value cambios, ya sea a través de la
manipulación del usuario de la Slider o cuando el programa establece la Value propiedad directamente. Un
ValueChanged evento se desencadena cuando el Value propiedad se convierte como se describe en el párrafo
anterior.
El ValueChangedEventArgs objeto que acompaña a la ValueChanged eventos tiene dos propiedades, ambos de tipo
double : OldValue y NewValue . En el momento en el evento se desencadena, el valor de NewValue es el mismo
que el Value propiedad de la Slider objeto.
Slider También define DragStarted y DragCompleted eventos, que se activan al principio y al final de la acción de
arrastrar. A diferencia de la ValueChanged eventos, el DragStarted y DragCompleted solo se desencadenan los
eventos mediante la manipulación de usuario de la Slider . Cuando el DragStarted desencadena el evento, el
DragStartedCommand , del tipo ICommand , se ejecuta. De forma similar, cuando el DragCompleted desencadena el
evento, el DragCompletedCommand , del tipo ICommand , se ejecuta.
WARNING
No utilice opciones de diseño horizontal sin restricciones de Center , Start , o End con Slider . En Android y UWP, la
Slider contrae a una barra de longitud cero y en iOS, la barra es muy breve. Mantenga el valor predeterminado
HorizontalOptions de Fill y no use un ancho de Auto al poner Slider en un Grid diseño.

El Slider también define varias propiedades que afectan a su apariencia:


MinimumTrackColor es la barra de color en el lado izquierdo del control.
MaximumTrackColor es la barra de color en el lado derecho del control.
ThumbColor es el color del control de posición.
ThumbImageSource es la imagen que se utilizará para el control de posición de tipo ImageSource .

NOTE
El ThumbColor y ThumbImageSource propiedades son mutuamente excluyentes. Si se establecen ambas propiedades, la
ThumbImageSource propiedad tendrá prioridad.

Marcado y código básico de control deslizante


El SliderDemos ejemplo comienza con tres páginas que son funcionalmente idénticos, pero se implementan de
maneras diferentes. La primera página usa solo código de C#, la segunda usa XAML con un controlador de
eventos en el código y el tercero es para evitar el controlador de eventos mediante el uso de enlace de datos en el
archivo XAML.
Creación de un control deslizante en el código
El código básico de control deslizante página en el SliderDemos ejemplo muestra cómo crear un Slider y
dos Label objetos en el código:
public class BasicSliderCodePage : ContentPage
{
public BasicSliderCodePage()
{
Label rotationLabel = new Label
{
Text = "ROTATING TEXT",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};

Label displayLabel = new Label


{
Text = "(uninitialized)",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};

Slider slider = new Slider


{
Maximum = 360
};
slider.ValueChanged += (sender, args) =>
{
rotationLabel.Rotation = slider.Value;
displayLabel.Text = String.Format("The Slider value is {0}", args.NewValue);
};

Title = "Basic Slider Code";


Padding = new Thickness(10, 0);
Content = new StackLayout
{
Children =
{
rotationLabel,
slider,
displayLabel
}
};
}
}

El Sliderse inicializa para tener un Maximum propiedad de 360. El ValueChanged controlador de la Slider usa el
Value propiedad de la slider objeto para establecer el Rotation propiedad de la primera Label y usa el
String.Format método con el NewValue propiedad de la argumentos de evento para establecer el Text
propiedad del segundo Label . Estos dos enfoques para obtener el valor actual de la Slider son intercambiables.
Este es el programa que se ejecutan en iOS, Android y plataforma Universal de Windows (UWP ) dispositivos:
El segundo Label muestra el texto "(no inicializado)" hasta que el Slider se manipula, lo que hace que la
primera ValueChanged eventos se activen. Tenga en cuenta que el número de posiciones decimales que se
muestran es diferente para cada plataforma. Estas diferencias están relacionadas con las implementaciones de la
plataforma de la Slider y se describen más adelante en este artículo en la sección diferencias de implementación
de la plataforma.
Creación de un control deslizante en XAML
El XAML básica de control deslizante página funcionalmente es igual a código básico de control deslizante
pero se implementan principalmente en XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SliderDemos.BasicSliderXamlPage"
Title="Basic Slider XAML"
Padding="10, 0">
<StackLayout>
<Label x:Name="rotatingLabel"
Text="ROTATING TEXT"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

<Slider Maximum="360"
ValueChanged="OnSliderValueChanged" />

<Label x:Name="displayLabel"
Text="(uninitialized)"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

El archivo de código subyacente contiene el controlador para el ValueChanged eventos:


public partial class BasicSliderXamlPage : ContentPage
{
public BasicSliderXamlPage()
{
InitializeComponent();
}

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)


{
double value = args.NewValue;
rotatingLabel.Rotation = value;
displayLabel.Text = String.Format("The Slider value is {0}", value);
}
}

También es posible que el controlador de eventos obtener el Slider que está desencadenando el evento a través
de la sender argumento. El Value propiedad contiene el valor actual:

double value = ((Slider)sender).Value;

Si el Slider objeto se asignó un nombre en el archivo XAML con un x:Name atributo (por ejemplo, "slider") y, a
continuación, el controlador de eventos podría hacer referencia a ese objeto directamente:

double value = slider.Value;

El control deslizante de enlace de datos


El enlaces básicos de control deslizante página muestra cómo escribir un programa casi equivalente que
elimina la Value controlador de eventos mediante el uso de enlace de datos:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SliderDemos.BasicSliderBindingsPage"
Title="Basic Slider Bindings"
Padding="10, 0">
<StackLayout>
<Label Text="ROTATING TEXT"
Rotation="{Binding Source={x:Reference slider},
Path=Value}"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />

<Slider x:Name="slider"
Maximum="360" />

<Label x:Name="displayLabel"
Text="{Binding Source={x:Reference slider},
Path=Value,
StringFormat='The Slider value is {0:F0}'}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

El Rotation propiedad de la primera Label está enlazado a la Value propiedad de la Slider , ya que es el Text
propiedad del segundo Label con un StringFormat especificación. El enlaces básicos de control deslizante
página funciones un poco diferente de las dos páginas anteriores: Cuando aparece la página por primera vez, el
segundo Label muestra la cadena de texto con el valor. Esta es una ventaja del uso de enlace de datos. Para
mostrar texto sin el enlace de datos, deberá inicializar específicamente el Text propiedad de la Label o simular
una activación de la ValueChanged eventos llamando al controlador de eventos desde el constructor de clase.

Precauciones
El valor de la Minimum propiedad siempre debe ser menor que el valor de la Maximum propiedad. Causas de
fragmento de código siguiente el Slider para generar una excepción:

// Throws an exception!
Slider slider = new Slider
{
Minimum = 10,
Maximum = 20
};

El compilador de C# genera código que establece estas dos propiedades en la secuencia, y cuándo el Minimum
propiedad se establece en 10, es mayor que el valor predeterminado Maximum valor 1. Puede evitar la excepción
en este caso, establezca el Maximum propiedad primera:

Slider slider = new Slider


{
Maximum = 20,
Minimum = 10
};

Establecer Maximum a 20 no es un problema porque es mayor que el valor predeterminado Minimum el valor 0.
Cuando Minimum está establecido, el valor es menor que el Maximum valor de 20.
El mismo problema existe en XAML. Establecer las propiedades en un orden que garantiza que Maximum siempre
es mayor que Minimum :

<Slider Maximum="20"
Minimum="10" ... />

Puede establecer el Minimum y Maximum valores para números negativos, pero solo en un orden donde Minimum
es siempre menor que Maximum :

<Slider Minimum="-20"
Maximum="-10" ... />

El Value propiedad siempre es mayor o igual que el Minimum valor y menor o igual que Maximum . Si Value se
establece en un valor fuera de ese intervalo, el valor se convertirán para que se encuentran dentro del intervalo,
pero no se produce ninguna excepción. Por ejemplo, este código le no genere una excepción:

Slider slider = new Slider


{
Value = 10
};

En su lugar, el Value propiedad se convierte en el Maximum valor 1.


Este es un fragmento de código mostrado anteriormente:
Slider slider = new Slider
{
Maximum = 20,
Minimum = 10
};

Cuando Minimum se establece en 10, a continuación, Value también se establece en 10.


Si un ValueChanged se ha adjuntado el controlador de eventos en el momento en que el Value propiedad se
convierte en algo distinto de su valor predeterminado de 0, entonces un ValueChanged desencadena el evento.
Este es un fragmento de XAML:

<Slider ValueChanged="OnSliderValueChanged"
Maximum="20"
Minimum="10" />

Cuando Minimum se establece en 10, Value también se establece en 10 y el ValueChanged desencadena el evento.
Esto puede ocurrir antes de que se ha construido el resto de la página y el controlador puede intentar hacer
referencia a otros elementos en la página que todavía no se han creado. Es posible que desea agregar algún
código para el ValueChanged controlador que busca null valores de otros elementos en la página. O bien, puede
establecer el ValueChanged controlador de eventos después de la Slider valores se hayan inicializado.

Diferencias de implementación de la plataforma


Las capturas de pantalla anterior muestran el valor de la Slider con un número diferente de cifras decimales.
Esto se relaciona con cómo el Slider se implementa en las plataformas Android y UWP.
La implementación de Android
La implementación de Android Slider se basa en el Android SeekBar y siempre establece la Max propiedad a
1000. Esto significa que el Slider en Android tiene solo 1.001 valores discretos. Si establece el Slider para
tener un Minimum 0 y un Maximum de 5.000, posteriormente, como el Slider se manipula el Value propiedad
tiene valores de 0, 5, 10, 15 y así sucesivamente.
La implementación de UWP
La implementación de UWP de Slider se basa en la UWP Slider control. El StepFrequency propiedad de la
UWP Slider se establece en la diferencia entre el Maximum y Minimum propiedades se dividen por 10, pero no
mayor que 1.
Por ejemplo, para el intervalo predeterminado de 0 a 1, el StepFrequency propiedad está establecida en 0,1.
Como el Slider se manipula el Value propiedad está restringida a 0, 0.1, 0.2, 0.3, 0,4, 0,5, 0.6, 0,7, 0,8, 0,9 y 1.0.
(Esto es evidente en la última página en el SliderDemos ejemplo.) Cuando la diferencia entre el Maximum y
Minimum propiedades es 10 o superior, a continuación, StepFrequency está establecido en 1 y el Value propiedad
tiene valores enteros.
La solución StepSlider
Más versátil StepSlider se describe en capítulo 27. Los representadores personalizados del libro Creating Mobile
Apps with Xamarin.Forms. El StepSlider es similar a Slider pero agrega un Steps propiedad para especificar
el número de valores entre Minimum y Maximum .

Controles deslizantes para selección de color


El último dos páginas en el SliderDemos ejemplo usan tres Slider instancias para la selección de color. La
primera página controla todas las interacciones en el archivo de código subyacente, mientras que la segunda
página muestra cómo usar el enlace de datos con un modelo de vista.
Control de los controles deslizantes en el archivo de código subyacente
El reguladores de Color RGB crea una instancia de la página de un BoxView para mostrar un color, tres Slider
instancias para seleccionar los componentes rojos, verde y azules del color y tres Label elementos para mostrar
esos colores valores:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SliderDemos.RgbColorSlidersPage"
Title="RGB Color Sliders">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Slider">
<Setter Property="Maximum" Value="255" />
</Style>

<Style TargetType="Label">
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<StackLayout Margin="10">
<BoxView x:Name="boxView"
Color="Black"
VerticalOptions="FillAndExpand" />

<Slider x:Name="redSlider"
ValueChanged="OnSliderValueChanged" />

<Label x:Name="redLabel" />

<Slider x:Name="greenSlider"
ValueChanged="OnSliderValueChanged" />

<Label x:Name="greenLabel" />

<Slider x:Name="blueSlider"
ValueChanged="OnSliderValueChanged" />

<Label x:Name="blueLabel" />


</StackLayout>
</ContentPage>

Un ofrece tres Slider elementos de un intervalo de 0 a 255. El Slider elementos comparten el mismo
Style
ValueChanged controlador, que se implementa en el archivo de código subyacente:
public partial class RgbColorSlidersPage : ContentPage
{
public RgbColorSlidersPage()
{
InitializeComponent();
}

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)


{
if (sender == redSlider)
{
redLabel.Text = String.Format("Red = {0:X2}", (int)args.NewValue);
}
else if (sender == greenSlider)
{
greenLabel.Text = String.Format("Green = {0:X2}", (int)args.NewValue);
}
else if (sender == blueSlider)
{
blueLabel.Text = String.Format("Blue = {0:X2}", (int)args.NewValue);
}

boxView.Color = Color.FromRgb((int)redSlider.Value,
(int)greenSlider.Value,
(int)blueSlider.Value);
}
}

Los primeros conjuntos de sección la Text propiedad de uno de los Label instancias en una cadena de texto
breve que indica el valor de la Slider en formato hexadecimal. A continuación, las tres Slider se tiene acceso a
las instancias para crear un Color valor de los componentes RGB:

Enlazar el control deslizante a una clase ViewModel


El reguladores de Color HSL página muestra cómo usar un modelo de vista para realizar los cálculos utilizados
para crear un Color valor a partir de los valores de matiz, saturación y luminosidad. Al igual que todos los
modelos de vista, el HSLColorViewModel la clase implementa la INotifyPropertyChanged interfaz y se activa un
PropertyChanged cada vez que cambia una de las propiedades de evento:

public class HslColorViewModel : INotifyPropertyChanged


{
Color color;

public event PropertyChangedEventHandler PropertyChanged;


public event PropertyChangedEventHandler PropertyChanged;

public double Hue


{
set
{
if (color.Hue != value)
{
Color = Color.FromHsla(value, color.Saturation, color.Luminosity);
}
}
get
{
return color.Hue;
}
}

public double Saturation


{
set
{
if (color.Saturation != value)
{
Color = Color.FromHsla(color.Hue, value, color.Luminosity);
}
}
get
{
return color.Saturation;
}
}

public double Luminosity


{
set
{
if (color.Luminosity != value)
{
Color = Color.FromHsla(color.Hue, color.Saturation, value);
}
}
get
{
return color.Luminosity;
}
}

public Color Color


{
set
{
if (color != value)
{
color = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Hue"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Saturation"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Luminosity"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));
}
}
get
{
return color;
}
}
}

ViewModels y INotifyPropertyChanged interfaz se tratan en el artículo enlace de datos.


El HslColorSlidersPage.xaml crea una instancia de archivo del HslColorViewModel y lo establece en la página
BindingContext propiedad. Esto permite que todos los elementos en el archivo XAML para enlazar a propiedades
en ViewModel:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SliderDemos"
x:Class="SliderDemos.HslColorSlidersPage"
Title="HSL Color Sliders">

<ContentPage.BindingContext>
<local:HslColorViewModel Color="Chocolate" />
</ContentPage.BindingContext>

<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<StackLayout Margin="10">
<BoxView Color="{Binding Color}"
VerticalOptions="FillAndExpand" />

<Slider Value="{Binding Hue}" />


<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />

<Slider Value="{Binding Saturation}" />


<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />

<Slider Value="{Binding Luminosity}" />


<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</ContentPage>

Como el Slider se manipulan elementos, el BoxView y Label elementos se actualizan desde el ViewModel:

El StringFormat componente de la Binding extensión de marcado está establecida para un formato de "F2" para
mostrar dos posiciones decimales. (Cadena de formato en los enlaces de datos se describe en el artículo aplicarles
un formato.) Sin embargo, la versión de UWP del programa se limita a los valores de 0, 0.1, 0.2... 0.9 y 1.0. Se
trata de un resultado directo de la implementación de la UWP Slider como se describió anteriormente en la
sección diferencias de implementación de la plataforma.

Vínculos relacionados
Ejemplo de demostraciones de control deslizante
API de control deslizante
Motor Xamarin.Forms paso a paso
11/07/2019 • 13 minutes to read • Edit Online

descargar el ejemplo
Use un motor paso a paso para seleccionar un valor numérico de un intervalo de valores.
Xamarin.Forms Stepper consta de dos botones etiquetados con menos y signos más. Estos botones pueden ser
manipulados por el usuario para seleccionar de forma incremental un double valor desde un intervalo de
valores.
El Stepper define cuatro propiedades de tipo double :
Increment es la cantidad para cambiar el valor seleccionado, su valor predeterminado de 1.
Minimum es el mínimo del intervalo, con un valor predeterminado de 0.
Maximum es el máximo del intervalo, con un valor predeterminado de 100.
Value es el valor del componente, que puede oscilar entre Minimum y Maximum y tiene un valor
predeterminado de 0.
Todas estas propiedades están respaldados por BindableProperty objetos. El Value propiedad tiene un modo de
enlace predeterminada de BindingMode.TwoWay , lo que significa que es adecuado como origen de enlace en una
aplicación que utiliza el Model-View -ViewModel (MVVM ) arquitectura.

WARNING
Internamente, el Stepper garantiza que Minimum es menor que Maximum . Si Minimum o Maximum nunca se
establecen para que Minimum es menos Maximum , se produce una excepción. Para obtener más información sobre cómo
el Minimum y Maximum propiedades, consulte precauciones sección.

El convierte la Value propiedad para que esté entre Minimum y Maximum , ambos inclusive. Si el
Stepper
Minimum propiedad se establece en un valor mayor que el Value propiedad, el Stepper establece la Value
propiedad Minimum . De forma similar, si Maximum está establecido en un valor menor que Value , a continuación,
Stepper establece la Value propiedad a Maximum .

Stepper define un ValueChanged evento que se desencadena cuando el Value cambios, ya sea a través de la
manipulación del usuario de la Stepper o cuando la aplicación establece el Value propiedad directamente. Un
ValueChanged evento se desencadena cuando el Value propiedad se convierte como se describe en el párrafo
anterior.
El objeto que acompaña a la ValueChanged eventos tiene dos propiedades, ambos de tipo
ValueChangedEventArgs
double : OldValue y NewValue . En el momento en el evento se desencadena, el valor de NewValue es el mismo
que el Value propiedad de la Stepper objeto.

Marcado y código básico de motor paso a paso


El StepperDemos ejemplo contiene tres páginas que son funcionalmente idénticos, pero se implementan de
maneras diferentes. La primera página se usa solo C# código, el segundo usa XAML con un controlador de
eventos en código y la tercera es capaz de evitar el controlador de eventos mediante el uso de enlace de datos en
el archivo XAML.
Creación de un motor paso a paso en el código
El motor paso a paso de código básico página en el StepperDemos ejemplo muestra cómo crear un Stepper
y dos Label objetos en el código:

public class BasicStepperCodePage : ContentPage


{
public BasicStepperCodePage()
{
Label rotationLabel = new Label
{
Text = "ROTATING TEXT",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};

Label displayLabel = new Label


{
Text = "(uninitialized)",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};

Stepper stepper = new Stepper


{
Maximum = 360,
Increment = 30,
HorizontalOptions = LayoutOptions.Center
};
stepper.ValueChanged += (sender, e) =>
{
rotationLabel.Rotation = stepper.Value;
displayLabel.Text = string.Format("The Stepper value is {0}", e.NewValue);
};

Title = "Basic Stepper Code";


Content = new StackLayout
{
Margin = new Thickness(20),
Children = { rotationLabel, stepper, displayLabel }
};
}
}

El Stepperse inicializa para tener un Maximum propiedad de 360 y un Increment propiedad de 30. Manipular el
Stepper cambia el valor seleccionado de forma incremental entre Minimum a Maximum según el valor de la
Increment propiedad. El ValueChanged controlador de la Stepper usa el Value propiedad de la stepper objeto
para establecer el Rotation propiedad de la primera Label y usa el string.Format método con el NewValue
propiedad de los argumentos de evento para establecer el Text propiedad de la segundo Label . Estos dos
enfoques para obtener el valor actual de la Stepper son intercambiables.
Capturas de pantalla siguientes se muestra el motor paso a paso de código básico página:
El segundo Label muestra el texto "(no inicializado)" hasta que el Stepper se manipula, lo que hace que la
primera ValueChanged eventos en activarse.
Creación de un motor paso a paso en XAML
El XAML básica de motor paso a paso página funcionalmente es igual a motor paso a paso de código
básico pero se implementan principalmente en XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="StepperDemo.BasicStepperXAMLPage"
Title="Basic Stepper XAML">
<StackLayout Margin="20">
<Label x:Name="_rotatingLabel"
Text="ROTATING TEXT"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Stepper Maximum="360"
Increment="30"
HorizontalOptions="Center"
ValueChanged="OnStepperValueChanged" />
<Label x:Name="_displayLabel"
Text="(uninitialized)"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

El archivo de código subyacente contiene el controlador para el ValueChanged eventos:


public partial class BasicStepperXAMLPage : ContentPage
{
public BasicStepperXAMLPage()
{
InitializeComponent();
}

void OnStepperValueChanged(object sender, ValueChangedEventArgs e)


{
double value = e.NewValue;
_rotatingLabel.Rotation = value;
_displayLabel.Text = string.Format("The Stepper value is {0}", value);
}
}

También es posible que el controlador de eventos obtener el Stepper que está desencadenando el evento a
través de la sender argumento. El Value propiedad contiene el valor actual:

double value = ((Stepper)sender).Value;

Si el Stepper objeto se asignó un nombre en el archivo XAML con un x:Name atributo (por ejemplo,
"componente") y, a continuación, el controlador de eventos podría hacer referencia a ese objeto directamente:

double value = stepper.Value;

El motor paso a paso de enlace de datos


El enlaces básicos de motor paso a paso página muestra cómo escribir una aplicación casi equivalente que
elimina la Value controlador de eventos mediante el uso de enlace de datos:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="StepperDemo.BasicStepperBindingsPage"
Title="Basic Stepper Bindings">
<StackLayout Margin="20">
<Label Text="ROTATING TEXT"
Rotation="{Binding Source={x:Reference _stepper}, Path=Value}"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Stepper x:Name="_stepper"
Maximum="360"
Increment="30"
HorizontalOptions="Center" />
<Label Text="{Binding Source={x:Reference _stepper}, Path=Value, StringFormat='The Stepper value is
{0:F0}'}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>

El Rotation propiedad de la primera Label está enlazado a la Value propiedad de la Stepper , ya que es el
Text propiedad del segundo Label con un StringFormat especificación. El enlaces básicos de motor paso a
paso página funciones un poco diferente de las dos páginas anteriores: Cuando aparece la página por primera
vez, el segundo Label muestra la cadena de texto con el valor. Esta es una ventaja del uso de enlace de datos.
Para mostrar texto sin el enlace de datos, deberá inicializar específicamente el Text propiedad de la Label o
simular una activación de la ValueChanged eventos llamando al controlador de eventos desde el constructor de
clase .
Precauciones
El valor de la Minimum propiedad siempre debe ser menor que el valor de la Maximum propiedad. Causas de
fragmento de código siguiente el Stepper para generar una excepción:

// Throws an exception!
Stepper stepper = new Stepper
{
Minimum = 180,
Maximum = 360
};

El C# compilador genera código que establece estas dos propiedades en la secuencia, y cuándo el Minimum
propiedad está establecida en 180, es mayor que el valor predeterminado Maximum valor de 100. Puede evitar la
excepción en este caso, establezca el Maximum propiedad primera:

Stepper stepper = new Stepper


{
Maximum = 360,
Minimum = 180
};

Establecer Maximum a 360 no es un problema porque es mayor que el valor predeterminado Minimum el valor 0.
Cuando Minimum está establecido, el valor es menor que el Maximum valor de 360.
El mismo problema existe en XAML. Establecer las propiedades en un orden que garantiza que Maximum siempre
es mayor que Minimum :

<Stepper Maximum="360"
Minimum="180" ... />

Puede establecer el Minimum y Maximum valores para números negativos, pero solo en un orden donde Minimum
es siempre menor que Maximum :

<Stepper Minimum="-360"
Maximum="-180" ... />

El Value propiedad siempre es mayor o igual que el Minimum valor y menor o igual a Maximum . Si Value se
establece en un valor fuera de ese intervalo, el valor se convertirán para que se encuentran dentro del intervalo,
pero no se produce ninguna excepción. Por ejemplo, este código le no genere una excepción:

Stepper stepper = new Stepper


{
Value = 180
};

En su lugar, el Value propiedad se convierte en el Maximum valor de 100.


Este es un fragmento de código mostrado anteriormente:
Stepper stepper = new Stepper
{
Maximum = 360,
Minimum = 180
};

Cuando Minimum está establecido en 180, a continuación, Value también está establecido en 180.
Si un ValueChanged se ha adjuntado el controlador de eventos en el momento en que el Value propiedad se
convierte en algo distinto de su valor predeterminado de 0, entonces un ValueChanged desencadena el evento.
Este es un fragmento de XAML:

<Stepper ValueChanged="OnStepperValueChanged"
Maximum="360"
Minimum="180" />

Cuando Minimum está establecido en 180, Value también está establecido en 180 y el ValueChanged
desencadena el evento. Esto puede ocurrir antes de que se ha construido el resto de la página y el controlador
puede intentar hacer referencia a otros elementos en la página que todavía no se han creado. Es posible que
desea agregar algún código para el ValueChanged controlador que busca null valores de otros elementos en la
página. O bien, puede establecer el ValueChanged controlador de eventos después de la Stepper valores se hayan
inicializado.

Vínculos relacionados
Ejemplo de demostraciones de motor paso a paso
API de motor paso a paso
Aplicaciones de Xamarin.Forms de estilo
11/07/2019 • 2 minutes to read • Edit Online

Aplicación de estilos para aplicaciones Xamarin.Forms con estilos


XAML
Aplicar un estilo a una aplicación de Xamarin.Forms tradicionalmente se logra mediante el Style clase para
agrupar una colección de valores de propiedad en un objeto que, a continuación, se puede aplicar a varias
instancias del elemento visual. Esto ayuda a reducir el marcado repetitivo y permite una apariencia de las
aplicaciones a cambiarse más fácilmente.

Aplicación de estilos para aplicaciones Xamarin.Forms con hojas de


estilo CSS
Xamarin.Forms es compatible con los elementos de estilo visual con hojas de estilos en cascada (CSS ). Una
hoja de estilos consta de una lista de reglas, con cada regla que consta de uno o varios de los selectores y un
bloque de declaración.
Aplicación de estilos para aplicaciones
Xamarin.Forms con estilos XAML
11/07/2019 • 2 minutes to read • Edit Online

Introducción
A menudo, las aplicaciones de Xamarin.Forms contienen varios controles que tienen una apariencia idéntica.
Establecer la apariencia de cada control individual puede ser repetitiva y propensas a errores. En su lugar, se
pueden crear estilos que personalizar la apariencia del control mediante la agrupación y establecer las
propiedades disponibles en el tipo de control.

Estilos explícitos
Un explícita estilo es aquel que se aplica de manera selectiva a los controles estableciendo sus Style
propiedades.

Estilos implícitos
Un implícita estilo es aquella que se usa por todos los controles del mismo TargetType , sin necesidad de cada
control para hacer referencia al estilo.

Estilos globales
Los estilos pueden estar disponibles globalmente agregándolos a la aplicación ResourceDictionary . Esto ayuda a
evitar la duplicación de estilos a través de las páginas o controles.

Herencia de estilo
Los estilos pueden heredar de otros estilos para reducir la duplicación y habilitar la reutilización.

Estilos dinámicos
Los estilos no responder a los cambios de propiedad y permanecen sin cambios para la duración de una
aplicación. Sin embargo, las aplicaciones pueden responder a cambios de estilo dinámicamente en tiempo de
ejecución mediante el uso de recursos dinámicos.

Estilos de dispositivo
Xamarin.Forms incluye seis dinámica estilos, conocidos como dispositivo estilos, en el Devices.Styles clase.
Todos los seis estilos pueden aplicarse a Label solo instancias.

Clases de estilo
Las clases de estilo de Xamarin.Forms permiten varios estilos para aplicarse a un control, sin tener que recurrir a
la herencia de estilo.
Introducción a los estilos de Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

Los estilos permiten la apariencia de los elementos visuales para personalizarse. Los estilos se definen para un tipo
específico y contienen valores para las propiedades disponibles en ese tipo.
A menudo, las aplicaciones de Xamarin.Forms contienen varios controles que tienen una apariencia idéntica. Por
ejemplo, una aplicación puede tener varios Label instancias que tienen las mismas opciones de fuente y las
opciones de diseño, como se muestra en el ejemplo de código XAML siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Styles.NoStylesPage"
Title="No Styles"
IconImageSource="xaml.png">
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<Label Text="These labels"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
FontSize="Large" />
<Label Text="are not"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
FontSize="Large" />
<Label Text="using styles"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
FontSize="Large" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

En el ejemplo de código siguiente se muestra la página equivalente creada en C#:


public class NoStylesPageCS : ContentPage
{
public NoStylesPageCS ()
{
Title = "No Styles";
IconImageSource = "csharp.png";
Padding = new Thickness (0, 20, 0, 0);

Content = new StackLayout {


Children = {
new Label {
Text = "These labels",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand,
FontSize = Device.GetNamedSize (NamedSize.Large, typeof(Label))
},
new Label {
Text = "are not",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand,
FontSize = Device.GetNamedSize (NamedSize.Large, typeof(Label))
},
new Label {
Text = "using styles",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand,
FontSize = Device.GetNamedSize (NamedSize.Large, typeof(Label))
}
}
};
}
}

Cada Label instancia tiene valores idénticos de propiedad para controlar la apariencia del texto mostrado por el
Label . El resultado es el aspecto que se muestra en las capturas de pantalla siguientes:

Establecer la apariencia de cada control individual puede ser repetitiva y propensas a errores. En su lugar, puede
crearse un estilo que define la apariencia y, a continuación, se aplica a los controles necesarios.

Crear un estilo
El Style clase agrupa una colección de valores de propiedad en un objeto que, a continuación, se puede aplicar a
varias instancias del elemento visual. Esto ayuda a reducir el marcado repetitivo y permite una apariencia de las
aplicaciones a cambiarse más fácilmente.
Aunque los estilos se han diseñado principalmente para aplicaciones basadas en XAML, también se pueden crear
en C#:
Style las instancias creadas en XAML se definen normalmente en un ResourceDictionary que se asigna a la
Resources colección de un control, página, o a la Resources recolección de la aplicación.
Style las instancias creadas en C# normalmente se definen en la clase de página o en una clase que se puede
acceder de forma global.
La elección de dónde se puede definir una instancia de Style afecta a dónde se puede usar:
Style las instancias definidas en el nivel de control solo pueden aplicarse al control y a sus elementos
secundarios.
Style las instancias definidas en el nivel de página sólo pueden aplicarse a la página y a sus elementos
secundarios.
Style las instancias definidas en el nivel de aplicación se pueden aplicar a lo largo de la aplicación.

Cada Style instancia contiene una colección de uno o varios Setter objetos, con cada Setter tener un
Property y un Value . El Property es el nombre de la propiedad enlazable del elemento que se aplica el estilo, y el
Value es el valor que se aplica a la propiedad.

Cada Style instancia puede ser explícita, o implícita:


Un explícita Style instancia se define mediante la especificación de un TargetType y un x:Key valor y
establezca el elemento de destino Style propiedad a la x:Key referencia. Para obtener más información
acerca de explícita estilos, consulte estilos explícitos.
Un implícita Style instancia se define especificando solo un TargetType . El Style instancia, a continuación,
automáticamente se aplicará a todos los elementos de ese tipo. Tenga en cuenta que las subclases de la
TargetType no tienen automáticamente la Style aplicado. Para obtener más información acerca de implícita
estilos, consulte los estilos implícitos.
Al crear un Style , TargetType propiedad siempre es necesaria. El siguiente ejemplo de código muestra un
explícita estilo (tenga en cuenta la x:Key ) creado en XAML:

<Style x:Key="labelStyle" TargetType="Label">


<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
<Setter Property="FontSize" Value="Large" />
</Style>

Para aplicar un Style , el objeto de destino debe ser un VisualElement que coincide con el TargetType valor de
propiedad de la Style , tal y como se muestra en el ejemplo de código XAML siguiente:

<Label Text="Demonstrating an explicit style" Style="{StaticResource labelStyle}" />

Estilos de más abajo en la jerarquía de vistas tienen prioridad sobre las define mayor seguridad. Por ejemplo, si se
establece un Style que establece Label.TextColor a Red en la aplicación de nivel serán reemplazado por un
estilo de nivel de página establece Label.TextColor a Green . De forma similar, un estilo de nivel de página se
reemplazará por un estilo de nivel de control. Además, si Label.TextColor se establece directamente en una
propiedad de control, esto tiene prioridad sobre los estilos.
Los artículos de esta sección se muestran y se explica cómo crear y aplicar explícita y implícita estilos, cómo crear
estilos globales, aplicar el estilo de herencia, cómo responder a los cambios de estilo en tiempo de ejecución y
cómo usar los estilos integrados incluidos en Xamarin.Forms.
NOTE
¿Qué es StyleId?
Anteriores a Xamarin.Forms 2.2, el StyleId propiedad se utiliza para identificar los elementos individuales de una aplicación
para la identificación en las pruebas de interfaz de usuario y en los motores de temas como Pixate. Sin embargo,
Xamarin.Forms 2.2 introdujo el AutomationId propiedad, que reemplazó el StyleId propiedad.

Vínculos relacionados
Extensiones de marcado XAML
Estilo
Establecedor
Estilos explícitos en Xamarin.Forms
11/07/2019 • 6 minutes to read • Edit Online

descargar el ejemplo
Un estilo explícito es aquella que se aplica de manera selectiva a los controles estableciendo sus propiedades de
estilo.

Crear un estilo explícito en XAML


Para declarar un Style en el nivel de página, un ResourceDictionary debe agregarse a la página y, a continuación,
uno o varios Style declaraciones pueden incluirse en el ResourceDictionary . Un Style estará explícita
proporcionando su declaración un x:Key atributo, que proporciona una clave descriptiva en el
ResourceDictionary . Explícita estilos, a continuación, se deben aplicar a los elementos visuales específicos
estableciendo sus Style propiedades.
El siguiente ejemplo de código muestra explícita estilos declaran en XAML en una página ResourceDictionary y se
aplica a la página Label instancias:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.ExplicitStylesPage" Title="Explicit"
IconImageSource="xaml.png">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="labelRedStyle" TargetType="Label">
<Setter Property="HorizontalOptions"
Value="Center" />
<Setter Property="VerticalOptions"
Value="CenterAndExpand" />
<Setter Property="FontSize" Value="Large" />
<Setter Property="TextColor" Value="Red" />
</Style>
<Style x:Key="labelGreenStyle" TargetType="Label">
...
<Setter Property="TextColor" Value="Green" />
</Style>
<Style x:Key="labelBlueStyle" TargetType="Label">
...
<Setter Property="TextColor" Value="Blue" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<Label Text="These labels"
Style="{StaticResource labelRedStyle}" />
<Label Text="are demonstrating"
Style="{StaticResource labelGreenStyle}" />
<Label Text="explicit styles,"
Style="{StaticResource labelBlueStyle}" />
<Label Text="and an explicit style override"
Style="{StaticResource labelBlueStyle}"
TextColor="Teal" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
El ResourceDictionary define tres explícita estilos que se aplican a la página Label instancias. Cada Style se
utiliza para mostrar texto en un color diferente, y también establece la fuente de las opciones de diseño de tamaño
y horizontales y verticales. Cada Style se aplica a otro Label estableciendo su Style propiedades mediante el
StaticResource extensión de marcado. El resultado es el aspecto que se muestra en las capturas de pantalla
siguiente:

Además, el último Label tiene un Style aplicado a él, pero también invalida la TextColor propiedad a una
diferente Color valor.
Crear un estilo explícito en el nivel de control
Además de crear explícita estilos en el nivel de página, también pueden crearse en el nivel de control, tal como se
muestra en el ejemplo de código siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.ExplicitStylesPage" Title="Explicit"
IconImageSource="xaml.png">
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<StackLayout.Resources>
<ResourceDictionary>
<Style x:Key="labelRedStyle" TargetType="Label">
...
</Style>
...
</ResourceDictionary>
</StackLayout.Resources>
<Label Text="These labels" Style="{StaticResource labelRedStyle}" />
...
</StackLayout>
</ContentPage.Content>
</ContentPage>

En este ejemplo, el explícita Style instancias se asignan a la Resources colección de la StackLayout control. Los
estilos, a continuación, se aplican al control y sus elementos secundarios.
Para obtener información sobre cómo crear estilos en una aplicación ResourceDictionary , consulte estilos
globales.

Crear un estilo explícito en C#


Style las instancias se pueden agregar a una página Resources colección en C# mediante la creación de un
nuevo ResourceDictionary y, a continuación, agregando el Style instancias para el ResourceDictionary , tal y
como se muestra en el ejemplo de código siguiente:

public class ExplicitStylesPageCS : ContentPage


{
public ExplicitStylesPageCS ()
{
var labelRedStyle = new Style (typeof(Label)) {
Setters = {
...
new Setter { Property = Label.TextColorProperty, Value = Color.Red }
}
};
var labelGreenStyle = new Style (typeof(Label)) {
Setters = {
...
new Setter { Property = Label.TextColorProperty, Value = Color.Green }
}
};
var labelBlueStyle = new Style (typeof(Label)) {
Setters = {
...
new Setter { Property = Label.TextColorProperty, Value = Color.Blue }
}
};

Resources = new ResourceDictionary ();


Resources.Add ("labelRedStyle", labelRedStyle);
Resources.Add ("labelGreenStyle", labelGreenStyle);
Resources.Add ("labelBlueStyle", labelBlueStyle);
...

Content = new StackLayout {


Children = {
new Label { Text = "These labels",
Style = (Style)Resources ["labelRedStyle"] },
new Label { Text = "are demonstrating",
Style = (Style)Resources ["labelGreenStyle"] },
new Label { Text = "explicit styles,",
Style = (Style)Resources ["labelBlueStyle"] },
new Label { Text = "and an explicit style override",
Style = (Style)Resources ["labelBlueStyle"], TextColor = Color.Teal }
}
};
}
}

El constructor define tres explícita estilos que se aplican a la página Label instancias. Cada explícita Style se
agrega a la ResourceDictionary utilizando el Add método, especifica un key cadena para hacer referencia a la
Style instancia. Cada Style se aplica a otro Label estableciendo sus Style propiedades.

Sin embargo, no hay ninguna ventaja en utilizar un ResourceDictionary aquí. En su lugar, Style instancias pueden
asignarse directamente a la Style las propiedades de los elementos visuales necesarios y el ResourceDictionary
puede quitarse, como se muestra en la siguiente ejemplo de código:
public class ExplicitStylesPageCS : ContentPage
{
public ExplicitStylesPageCS ()
{
var labelRedStyle = new Style (typeof(Label)) {
...
};
var labelGreenStyle = new Style (typeof(Label)) {
...
};
var labelBlueStyle = new Style (typeof(Label)) {
...
};
...
Content = new StackLayout {
Children = {
new Label { Text = "These labels", Style = labelRedStyle },
new Label { Text = "are demonstrating", Style = labelGreenStyle },
new Label { Text = "explicit styles,", Style = labelBlueStyle },
new Label { Text = "and an explicit style override", Style = labelBlueStyle,
TextColor = Color.Teal }
}
};
}
}

El constructor define tres explícita estilos que se aplican a la página Label instancias. Cada Style se utiliza para
mostrar texto en un color diferente, y también establece la fuente de las opciones de diseño de tamaño y
horizontales y verticales. Cada Style se aplica a otro Label estableciendo su Style propiedades. Además, el
último Label tiene un Style aplicado a él, pero también invalida la TextColor propiedad en otro Color valor.

Vínculos relacionados
Extensiones de marcado XAML
Estilos básicos (ejemplo)
Trabajar con estilos (ejemplo)
ResourceDictionary
Estilo
Establecedor
Estilos implícitos en Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

descargar el ejemplo
Un estilo implícito es aquella que se usa por todos los controles de la mismo TargetType, sin necesidad de cada
control para hacer referencia al estilo.

Crear un estilo implícito en XAML


Para declarar un Style en el nivel de página, un ResourceDictionary debe agregarse a la página y, a continuación,
uno o varios Style declaraciones pueden incluirse en el ResourceDictionary . Un Style estará implícita al no
especificar un x:Key atributo. A continuación, se aplicará el estilo a los elementos visuales que coinciden con el
TargetType exactamente, pero no a los elementos que se derivan de la TargetType valor.

El siguiente ejemplo de código muestra un implícita estilo declarado en XAML en una página ResourceDictionary y
se aplica a la página Entry instancias:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Styles;assembly=Styles"
x:Class="Styles.ImplicitStylesPage" Title="Implicit" IconImageSource="xaml.png">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Entry">
<Setter Property="HorizontalOptions" Value="Fill" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
<Setter Property="BackgroundColor" Value="Yellow" />
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="TextColor" Value="Blue" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<Entry Text="These entries" />
<Entry Text="are demonstrating" />
<Entry Text="implicit styles," />
<Entry Text="and an implicit style override" BackgroundColor="Lime" TextColor="Red" />
<local:CustomEntry Text="Subclassed Entry is not receiving the style" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

El ResourceDictionary define una sola implícita estilo que se aplica a la página Entry instancias. El Style se usa
para mostrar el texto azul sobre un fondo amarillo, y también establece otras opciones de apariencia. El Style se
agrega a la página ResourceDictionary sin especificar un x:Key atributo. Por lo tanto, el Style se aplica a todas la
Entry implícitamente instancias que coinciden con el TargetType propiedad de la Style exactamente. Sin
embargo, el Style no se aplica a la CustomEntry instancia, que es una subclase Entry . El resultado es el aspecto
que se muestra en las capturas de pantalla siguiente:
Además, la cuarta Entry invalida la BackgroundColor y TextColor propiedades del estilo implícito a diferentes
Color valores.

Crear un estilo implícito en el nivel de control


Además de crear implícita estilos en el nivel de página, también pueden crearse en el nivel de control, tal como se
muestra en el ejemplo de código siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Styles;assembly=Styles"
x:Class="Styles.ImplicitStylesPage" Title="Implicit" IconImageSource="xaml.png">
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<StackLayout.Resources>
<ResourceDictionary>
<Style TargetType="Entry">
<Setter Property="HorizontalOptions" Value="Fill" />
...
</Style>
</ResourceDictionary>
</StackLayout.Resources>
<Entry Text="These entries" />
...
</StackLayout>
</ContentPage.Content>
</ContentPage>

En este ejemplo, el implícita Style se asigna a la Resources colección de la StackLayout control. El implícita , a
continuación, se puede aplicar estilo al control y sus elementos secundarios.
Para obtener información sobre cómo crear estilos en una aplicación ResourceDictionary , consulte estilos
globales.

Crear un estilo implícito en C#


Style las instancias se pueden agregar a una página Resources colección en C# mediante la creación de un
nuevo ResourceDictionary y, a continuación, agregando el Style instancias para el ResourceDictionary , tal y
como se muestra en el ejemplo de código siguiente:
public class ImplicitStylesPageCS : ContentPage
{
public ImplicitStylesPageCS ()
{
var entryStyle = new Style (typeof(Entry)) {
Setters = {
...
new Setter { Property = Entry.TextColorProperty, Value = Color.Blue }
}
};

...
Resources = new ResourceDictionary ();
Resources.Add (entryStyle);

Content = new StackLayout {


Children = {
new Entry { Text = "These entries" },
new Entry { Text = "are demonstrating" },
new Entry { Text = "implicit styles," },
new Entry { Text = "and an implicit style override", BackgroundColor = Color.Lime, TextColor =
Color.Red },
new CustomEntry { Text = "Subclassed Entry is not receiving the style" }
}
};
}
}

El constructor define una sola implícita estilo que se aplica a la página Entry instancias. El Style se usa para
mostrar el texto azul sobre un fondo amarillo, y también establece otras opciones de apariencia. El Style se
agrega a la página ResourceDictionary sin especificar un key cadena. Por lo tanto, el Style se aplica a todas la
Entry implícitamente instancias que coinciden con el TargetType propiedad de la Style exactamente. Sin
embargo, el Style no se aplica a la CustomEntry instancia, que es una subclase Entry .

Aplicar un estilo a los tipos derivados


El Style.ApplyToDerivedTypes propiedad permite que un estilo que se aplicará a los controles que se derivan del
tipo base al que hace referencia el TargetType propiedad. Por lo tanto, establecer esta propiedad en true permite
un único estilo como destino varios tipos, siempre que los tipos se derivan del tipo base especificado en el
TargetType propiedad.

El ejemplo siguiente muestra un estilo implícito que establece el color de fondo Button instancias a rojo:

<Style TargetType="Button"
ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor"
Value="Red" />
</Style>

Colocación de este estilo en un nivel de página ResourceDictionary dará como resultado lo que se aplica a todos
los Button instancias en la página así como todos los controles que derivan de Button . Sin embargo, si la
ApplyToDerivedTypes permaneció sin establecer la propiedad, el estilo se aplicaría solo a Button instancias.

El código de C# equivalente es:


var buttonStyle = new Style(typeof(Button))
{
ApplyToDerivedTypes = true,
Setters =
{
new Setter
{
Property = VisualElement.BackgroundColorProperty,
Value = Color.Red
}
}
};

Resources = new ResourceDictionary { buttonStyle };

Vínculos relacionados
Extensiones de marcado XAML
Estilos básicos (ejemplo)
Trabajar con estilos (ejemplo)
ResourceDictionary
Estilo
Establecedor
Estilos globales en Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

descargar el ejemplo
Los estilos pueden estar disponibles globalmente agregándolas al diccionario de recursos de la aplicación. Esto
ayuda a evitar la duplicación de estilos a través de las páginas o controles.

Crear un estilo global en XAML


De forma predeterminada, en todas las aplicaciones de Xamarin.Forms creadas a partir de una plantilla se usa la
clase App para implementar la subclase Application . Para declarar un Style en el nivel de aplicación, en la
aplicación ResourceDictionary mediante XAML, el valor predeterminado aplicación clase debe reemplazarse por
un XAML Aplicación clase y código subyacente asociado. Para obtener más información, consulte trabajar con la
clase App.
El siguiente ejemplo de código muestra un Style declarado en el nivel de aplicación:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.App">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="buttonStyle" TargetType="Button">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
<Setter Property="BorderColor" Value="Lime" />
<Setter Property="BorderRadius" Value="5" />
<Setter Property="BorderWidth" Value="5" />
<Setter Property="WidthRequest" Value="200" />
<Setter Property="TextColor" Value="Teal" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

Esto ResourceDictionary define una sola explícita estilo, buttonStyle , que se usará para establecer el aspecto de
Button instancias. Sin embargo, pueden ser estilos globales explícita o implícita.
En el ejemplo de código siguiente se muestra una aplicación de página XAML el buttonStyle a la página Button
instancias:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.ApplicationStylesPage"
Title="Application" IconImageSource="xaml.png">
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<Button Text="These buttons" Style="{StaticResource buttonStyle}" />
<Button Text="are demonstrating" Style="{StaticResource buttonStyle}" />
<Button Text="application style overrides" Style="{StaticResource buttonStyle}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

El resultado es el aspecto que se muestra en las capturas de pantalla siguiente:


Para obtener información sobre cómo crear estilos en una página ResourceDictionary , consulte estilos explícitos y
los estilos implícitos.
Invalidar los estilos
Estilos de más abajo en la jerarquía de vistas tienen prioridad sobre las define mayor seguridad. Por ejemplo, si se
establece un Style que establece Button.TextColor a Red en la aplicación de nivel serán reemplazado por un
estilo de nivel de página establece Button.TextColor a Green . De forma similar, un estilo de nivel de página se
reemplazará por un estilo de nivel de control. Además, si Button.TextColor se establece directamente en una
propiedad de control, esto tendrá prioridad sobre los estilos. Esta prioridad se muestra en el ejemplo de código
siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.ApplicationStylesPage"
Title="Application" IconImageSource="xaml.png">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="buttonStyle" TargetType="Button">
...
<Setter Property="TextColor" Value="Red" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<StackLayout.Resources>
<ResourceDictionary>
<Style x:Key="buttonStyle" TargetType="Button">
...
<Setter Property="TextColor" Value="Blue" />
</Style>
</ResourceDictionary>
</StackLayout.Resources>
<Button Text="These buttons" Style="{StaticResource buttonStyle}" />
<Button Text="are demonstrating" Style="{StaticResource buttonStyle}" />
<Button Text="application style overrides" Style="{StaticResource buttonStyle}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

La versión original buttonStyle , definido en el nivel de aplicación, se reemplaza por la buttonStyle instancia
definida en el nivel de página. Además, el estilo de nivel de página se haya reemplazado por el nivel de control
buttonStyle . Por lo tanto, el Button se muestran las instancias con texto azul, como se muestra en las capturas de
pantalla siguiente:

Crear un estilo global en C#


Style las instancias se pueden agregar a la aplicación Resources colección en C# mediante la creación de un
nuevo ResourceDictionary y, a continuación, agregando el Style instancias para el ResourceDictionary , como se
muestra en el ejemplo de código siguiente:

public class App : Application


{
public App ()
{
var buttonStyle = new Style (typeof(Button)) {
Setters = {
...
new Setter { Property = Button.TextColorProperty, Value = Color.Teal }
}
};

Resources = new ResourceDictionary ();


Resources.Add ("buttonStyle", buttonStyle);
...
}
...
}

El constructor define una sola explícita estilo para aplicar a Button instancias a lo largo de la aplicación. Explícita
Style instancias se agregan a la ResourceDictionary utilizando el Add método, especificando un key cadena
para hacer referencia a la Style instancia. El Style instancia, a continuación, se puede aplicar a todos los
controles del tipo correcto en la aplicación. Sin embargo, pueden ser estilos globales explícita o implícita.
El ejemplo de código siguiente muestra un C# página aplicando el buttonStyle a la página Button instancias:
public class ApplicationStylesPageCS : ContentPage
{
public ApplicationStylesPageCS ()
{
...
Content = new StackLayout {
Children = {
new Button { Text = "These buttons", Style = (Style)Application.Current.Resources
["buttonStyle"] },
new Button { Text = "are demonstrating", Style = (Style)Application.Current.Resources
["buttonStyle"] },
new Button { Text = "application styles", Style = (Style)Application.Current.Resources
["buttonStyle"]
}
}
};
}
}

El buttonStyle se aplica a la Button instancias estableciendo sus Style propiedades y controla la apariencia de la
Button instancias.

Vínculos relacionados
Extensiones de marcado XAML
Estilos básicos (ejemplo)
Trabajar con estilos (ejemplo)
ResourceDictionary
Estilo
Establecedor
Herencia de estilo en Xamarin.Forms
11/07/2019 • 6 minutes to read • Edit Online

descargar el ejemplo
Los estilos pueden heredar de otros estilos para reducir la duplicación y habilitar la reutilización.

Herencia de estilo en XAML


Herencia de estilo se realiza estableciendo la Style.BasedOn propiedad a una existente Style . En XAML, esto se
logra estableciendo el BasedOn propiedad a un StaticResource extensión de marcado que se hace referencia a una
previamente creada Style . En C#, esto se logra estableciendo el BasedOn propiedad a un Style instancia.
Pueden incluir los estilos que se heredan de un estilo base Setter instancias para las nuevas propiedades, o
usarlos para invalidar los estilos al estilo de base. Además, los estilos que se heredan de un estilo base deben tener
como destino el mismo tipo o un tipo que deriva el tipo de destino en el estilo de base. Por ejemplo, si el destino de
una base de estilo View instancias, los estilos que se basan en el estilo base pueden tener como destino View
instancias o tipos que derivan de la View clase, como Label y Button instancias.
El siguiente código muestra explícita herencia de estilo en una página XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.StyleInheritancePage"
Title="Inheritance" IconImageSource="xaml.png">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="baseStyle" TargetType="View">
<Setter Property="HorizontalOptions"
Value="Center" />
<Setter Property="VerticalOptions"
Value="CenterAndExpand" />
</Style>
<Style x:Key="labelStyle" TargetType="Label"
BasedOn="{StaticResource baseStyle}">
...
<Setter Property="TextColor" Value="Teal" />
</Style>
<Style x:Key="buttonStyle" TargetType="Button"
BasedOn="{StaticResource baseStyle}">
<Setter Property="BorderColor" Value="Lime" />
...
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<Label Text="These labels"
Style="{StaticResource labelStyle}" />
...
<Button Text="So is the button"
Style="{StaticResource buttonStyle}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

El baseStyle destinos View instancias y establece el HorizontalOptions y VerticalOptions propiedades. El


baseStyle no se establece directamente en los controles. En su lugar, labelStyle y buttonStyle heredan de él,
establecer valores de propiedad enlazable adicionales. El labelStyle y buttonStyle , a continuación, se aplican a
la Label instancias y Button instancia estableciendo sus Style propiedades. El resultado es el aspecto que se
muestra en las capturas de pantalla siguiente:

NOTE
Un estilo implícito se puede derivar de un estilo explícito, pero no puede derivar un estilo explícito de un estilo implícito.

Respetar la cadena de herencia


Solo puede heredar un estilo de estilos en el mismo nivel o superior, en la jerarquía de vistas. Esto significa que:
Un recurso de nivel de aplicación solo puede heredar de otros recursos de nivel de aplicación.
Un recurso de nivel de página puede heredar de los recursos de nivel de aplicación y otros recursos de nivel de
página.
Un recurso de nivel de control puede heredar de los recursos de nivel de aplicación, los recursos de nivel de
página y otros recursos de nivel de control.
Esta cadena de herencia se muestra en el ejemplo de código siguiente:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.StyleInheritancePage"
Title="Inheritance" IconImageSource="xaml.png">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="baseStyle" TargetType="View">
...
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<StackLayout.Resources>
<ResourceDictionary>
<Style x:Key="labelStyle" TargetType="Label" BasedOn="{StaticResource baseStyle}">
...
</Style>
<Style x:Key="buttonStyle" TargetType="Button" BasedOn="{StaticResource baseStyle}">
...
</Style>
</ResourceDictionary>
</StackLayout.Resources>
...
</StackLayout>
</ContentPage.Content>
</ContentPage>

En este ejemplo, labelStyle y buttonStyle son los recursos de nivel de control mientras baseStyle es un recurso
de nivel de página. Sin embargo, mientras labelStyle y buttonStyle heredar baseStyle , no es posible que
baseStyle va a heredar labelStyle o buttonStyle , debido a sus respectivas ubicaciones en la jerarquía de vistas.

Herencia de estilo de C#
La página C# equivalente, donde Style instancias se asignan directamente a la Style propiedades de los
controles necesarios, se muestra en el ejemplo de código siguiente:
public class StyleInheritancePageCS : ContentPage
{
public StyleInheritancePageCS ()
{
var baseStyle = new Style (typeof(View)) {
Setters = {
new Setter {
Property = View.HorizontalOptionsProperty, Value = LayoutOptions.Center },
...
}
};

var labelStyle = new Style (typeof(Label)) {


BasedOn = baseStyle,
Setters = {
...
new Setter { Property = Label.TextColorProperty, Value = Color.Teal }
}
};

var buttonStyle = new Style (typeof(Button)) {


BasedOn = baseStyle,
Setters = {
new Setter { Property = Button.BorderColorProperty, Value = Color.Lime },
...
}
};
...

Content = new StackLayout {


Children = {
new Label { Text = "These labels", Style = labelStyle },
...
new Button { Text = "So is the button", Style = buttonStyle }
}
};
}
}

El baseStyledestinos View instancias y establece el HorizontalOptions y VerticalOptions propiedades. El


baseStyle no se establece directamente en los controles. En su lugar, labelStyle y buttonStyle heredan de él,
establecer valores de propiedad enlazable adicionales. El labelStyle y buttonStyle , a continuación, se aplican a
la Label instancias y Button instancia estableciendo sus Style propiedades.

Vínculos relacionados
Extensiones de marcado XAML
Estilos básicos (ejemplo)
Trabajar con estilos (ejemplo)
ResourceDictionary
Estilo
Establecedor
Estilos dinámicos en Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

descargar el ejemplo
Los estilos no responder a los cambios de propiedad y permanecen sin cambios para la duración de una
aplicación. Por ejemplo, después de asignar un estilo a un elemento visual, si se modifica una de las instancias de
establecedor, quitar, o una nueva instancia de establecedor agregado, los cambios no se aplicará al elemento
visual. Sin embargo, las aplicaciones pueden responder a cambios de estilo dinámicamente en tiempo de ejecución
mediante el uso de recursos dinámicos.
El DynamicResource extensión de marcado es similar a la StaticResource extensión de marcado en ambos utilizan
una clave de diccionario para capturar un valor desde un ResourceDictionary . Sin embargo, mientras el
StaticResource realiza una búsqueda de diccionario único, el DynamicResource mantiene un vínculo a la clave del
diccionario. Por lo tanto, si se reemplaza la entrada del diccionario asociada con la clave, el cambio se aplica al
elemento visual. Esto permite que los cambios de estilo en tiempo de ejecución que se realiza en una aplicación.
En el ejemplo de código siguiente se muestra dinámica estilos en una página XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.DynamicStylesPage" Title="Dynamic"
IconImageSource="xaml.png">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="baseStyle" TargetType="View">
...
</Style>
<Style x:Key="blueSearchBarStyle"
TargetType="SearchBar"
BasedOn="{StaticResource baseStyle}">
...
</Style>
<Style x:Key="greenSearchBarStyle"
TargetType="SearchBar">
...
</Style>
...
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<SearchBar Placeholder="These SearchBar controls"
Style="{DynamicResource searchBarStyle}" />
...
</StackLayout>
</ContentPage.Content>
</ContentPage>

El SearchBar instancias uso el DynamicResource extensión de marcado para hacer referencia a un Style
denominado searchBarStyle , que no está definido en el XAML. Sin embargo, dado que el Style propiedades de
la SearchBar instancias se establecen mediante un DynamicResource , falta la clave del diccionario no se produce
una excepción.
En su lugar, en el archivo de código subyacente, el constructor crea un ResourceDictionary entrada con la clave
searchBarStyle , tal y como se muestra en el ejemplo de código siguiente:
public partial class DynamicStylesPage : ContentPage
{
bool originalStyle = true;

public DynamicStylesPage ()
{
InitializeComponent ();
Resources ["searchBarStyle"] = Resources ["blueSearchBarStyle"];
}

void OnButtonClicked (object sender, EventArgs e)


{
if (originalStyle) {
Resources ["searchBarStyle"] = Resources ["greenSearchBarStyle"];
originalStyle = false;
} else {
Resources ["searchBarStyle"] = Resources ["blueSearchBarStyle"];
originalStyle = true;
}
}
}

Cuando el OnButtonClickedse ejecuta el controlador de eventos, searchBarStyle cambiará entre


blueSearchBarStyle y greenSearchBarStyle . El resultado es el aspecto que se muestra en las capturas de pantalla
siguiente:

[
] (dynamic-images/dynamic-style-green-
large.png#lightbox "Verde ejemplo estilo dinámico")
En el ejemplo de código siguiente se muestra la página equivalente en C#:

public class DynamicStylesPageCS : ContentPage


{
bool originalStyle = true;

public DynamicStylesPageCS ()
{
...
var baseStyle = new Style (typeof(View)) {
...
};
var blueSearchBarStyle = new Style (typeof(SearchBar)) {
...
};
var greenSearchBarStyle = new Style (typeof(SearchBar)) {
...
};
...
var searchBar1 = new SearchBar { Placeholder = "These SearchBar controls" };
searchBar1.SetDynamicResource (VisualElement.StyleProperty, "searchBarStyle");
...
Resources = new ResourceDictionary ();
Resources.Add ("blueSearchBarStyle", blueSearchBarStyle);
Resources.Add ("greenSearchBarStyle", greenSearchBarStyle);
Resources ["searchBarStyle"] = Resources ["blueSearchBarStyle"];

Content = new StackLayout {


Children = { searchBar1, searchBar2, searchBar3, searchBar4, button }
};
}
...
}

En C#, la instancias uso el SetDynamicResource método para hacer referencia a searchBarStyle . El


SearchBar
OnButtonClicked es idéntico del ejemplo XAML y cuando se ejecuta, el código del controlador de eventos
searchBarStyle cambiará entre blueSearchBarStyle y greenSearchBarStyle .

Herencia de estilo dinámico


Derivar un estilo de un estilo dinámico no pueden lograrse mediante el Style.BasedOn propiedad. En su lugar, el
Style clase incluye el BaseResourceKey propiedad, que se puede establecer en una clave de diccionario cuyo valor
puede cambiar dinámicamente.
En el ejemplo de código siguiente se muestra dinámica herencia de estilo en una página XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.DynamicStylesInheritancePage"
Title="Dynamic Inheritance" IconImageSource="xaml.png">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="baseStyle" TargetType="View">
...
</Style>
<Style x:Key="blueSearchBarStyle" TargetType="SearchBar" BasedOn="{StaticResource baseStyle}">
...
</Style>
<Style x:Key="greenSearchBarStyle" TargetType="SearchBar">
...
</Style>
<Style x:Key="tealSearchBarStyle" TargetType="SearchBar" BaseResourceKey="searchBarStyle">
...
</Style>
...
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<SearchBar Text="These SearchBar controls" Style="{StaticResource tealSearchBarStyle}" />
...
</StackLayout>
</ContentPage.Content>
</ContentPage>

El SearchBar instancias uso el StaticResource extensión de marcado para hacer referencia a un Style
denominado tealSearchBarStyle . Esto Style establece algunas propiedades adicionales y utiliza el
BaseResourceKey propiedad para hacer referencia a searchBarStyle . El DynamicResource extensión de marcado no
es necesaria porque tealSearchBarStyle no cambiará, excepto para el Style deriva de. Por lo tanto,
tealSearchBarStyle mantiene un vínculo a searchBarStyle y se modifica cuando cambia el estilo de base.

En el archivo de código subyacente, el constructor crea un ResourceDictionary entrada con la clave


searchBarStyle según el ejemplo anterior que se muestra estilos dinámicos. Cuando el OnButtonClicked se ejecuta
el controlador de eventos, searchBarStyle cambiará entre blueSearchBarStyle y greenSearchBarStyle . El resultado
es el aspecto que se muestra en las capturas de pantalla siguiente:
En el ejemplo de código siguiente se muestra la página equivalente en C#:
public class DynamicStylesInheritancePageCS : ContentPage
{
bool originalStyle = true;

public DynamicStylesInheritancePageCS ()
{
...
var baseStyle = new Style (typeof(View)) {
...
};
var blueSearchBarStyle = new Style (typeof(SearchBar)) {
...
};
var greenSearchBarStyle = new Style (typeof(SearchBar)) {
...
};
var tealSearchBarStyle = new Style (typeof(SearchBar)) {
BaseResourceKey = "searchBarStyle",
...
};
...
Resources = new ResourceDictionary ();
Resources.Add ("blueSearchBarStyle", blueSearchBarStyle);
Resources.Add ("greenSearchBarStyle", greenSearchBarStyle);
Resources ["searchBarStyle"] = Resources ["blueSearchBarStyle"];

Content = new StackLayout {


Children = {
new SearchBar { Text = "These SearchBar controls", Style = tealSearchBarStyle },
...
}
};
}
...
}

El tealSearchBarStyle se asigna directamente a la Style propiedad de la SearchBar instancias. Esto Style


establece algunas propiedades adicionales y utiliza el BaseResourceKey propiedad para hacer referencia a
searchBarStyle . El SetDynamicResource método no es necesario aquí porque tealSearchBarStyle no cambiará,
excepto para el Style deriva de. Por lo tanto, tealSearchBarStyle mantiene un vínculo a searchBarStyle y se
modifica cuando cambia el estilo de base.

Vínculos relacionados
Extensiones de marcado XAML
Estilos dinámicos (ejemplo)
Trabajar con estilos (ejemplo)
ResourceDictionary
Estilo
Establecedor

Vídeo relacionado
Encuentre más vídeos de Xamarin en Channel 9 y YouTube.
Estilos de dispositivo en Xamarin.Forms
11/07/2019 • 4 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms incluye seis estilos dinámicos, conocidos como estilos de dispositivo, en la clase Device.Styles.
El dispositivo estilos son:
BodyStyle
CaptionStyle
ListItemDetailTextStyle
ListItemTextStyle
SubtitleStyle
TitleStyle

Solo pueden aplicarse a todos los seis estilos Label instancias. Por ejemplo, un Label que muestra el cuerpo de
un párrafo puede establecer su Style propiedad BodyStyle .
En el ejemplo de código siguiente se muestra cómo utilizar el dispositivo estilos en una página XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.DeviceStylesPage" Title="Device"
IconImageSource="xaml.png">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="myBodyStyle" TargetType="Label"
BaseResourceKey="BodyStyle">
<Setter Property="TextColor" Value="Accent" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<Label Text="Title style"
Style="{DynamicResource TitleStyle}" />
<Label Text="Subtitle text style"
Style="{DynamicResource SubtitleStyle}" />
<Label Text="Body style"
Style="{DynamicResource BodyStyle}" />
<Label Text="Caption style"
Style="{DynamicResource CaptionStyle}" />
<Label Text="List item detail text style"
Style="{DynamicResource ListItemDetailTextStyle}" />
<Label Text="List item text style"
Style="{DynamicResource ListItemTextStyle}" />
<Label Text="No style" />
<Label Text="My body style"
Style="{StaticResource myBodyStyle}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

Los estilos de dispositivo están enlazados a usar el DynamicResource extensión de marcado. La naturaleza dinámica
de los estilos se puede ver en iOS cambiando el accesibilidad configuración para el tamaño del texto. La
apariencia de la dispositivo estilos es diferente en cada plataforma, como se muestra en las capturas de pantalla
siguiente:

Dispositivo estilos también se pueden derivar de estableciendo el BaseResourceKey propiedad para el nombre de
clave para el estilo del dispositivo. En el ejemplo de código anterior, myBodyStyle hereda BodyStyle y establece un
color de texto acentuados. Para obtener más información sobre la herencia de estilo dinámico, consulte herencia de
estilo dinámico.
En el ejemplo de código siguiente se muestra la página equivalente en C#:

public class DeviceStylesPageCS : ContentPage


{
public DeviceStylesPageCS ()
{
var myBodyStyle = new Style (typeof(Label)) {
BaseResourceKey = Device.Styles.BodyStyleKey,
Setters = {
new Setter {
Property = Label.TextColorProperty,
Value = Color.Accent
}
}
};

Title = "Device";
IconImageSource = "csharp.png";
Padding = new Thickness (0, 20, 0, 0);

Content = new StackLayout {


Children = {
new Label { Text = "Title style", Style = Device.Styles.TitleStyle },
new Label { Text = "Subtitle style", Style = Device.Styles.SubtitleStyle },
new Label { Text = "Body style", Style = Device.Styles.BodyStyle },
new Label { Text = "Caption style", Style = Device.Styles.CaptionStyle },
new Label { Text = "List item detail text style",
Style = Device.Styles.ListItemDetailTextStyle },
new Label { Text = "List item text style", Style = Device.Styles.ListItemTextStyle },
new Label { Text = "No style" },
new Label { Text = "My body style", Style = myBodyStyle }
}
};
}
}

El Style propiedad de cada uno Label instancia se establece en la propiedad adecuada de la Devices.Styles
clase.

Accesibilidad
El dispositivo estilos respetan las preferencias de accesibilidad, por lo que cambiarán los tamaños de fuente que se
modifiquen las preferencias de accesibilidad en cada plataforma. Por lo tanto, para admitir texto accesible,
asegúrese de que el dispositivo estilos se usan como base para los estilos de texto dentro de la aplicación.
Las capturas de pantalla siguientes muestran los estilos de dispositivo en cada plataforma, con el menor tamaño
de fuente accesible:

Las capturas de pantalla siguientes muestran los estilos de dispositivo en cada plataforma, con el mayor tamaño de
fuente accesible:

Vínculos relacionados
Estilos de texto
Extensiones de marcado XAML
Estilos dinámicos (ejemplo)
Trabajar con estilos (ejemplo)
Device.Styles
ResourceDictionary
Estilo
Establecedor
Clases de estilo de Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

descargar el ejemplo
Las clases de estilo de Xamarin.Forms permiten varios estilos para aplicarse a un control, sin tener que recurrir a la
herencia de estilo.

Crear clases de estilo


Se puede crear una clase de estilo estableciendo el Class propiedad en un Style a un string que representa el
nombre de clase. La ventaja de esto ofrece, sobre la definición de un estilo explícito utilizando la x:Key atributo, es
que se pueden aplicar varias clases de estilo para un VisualElement .

IMPORTANT
Varios estilos pueden compartir el mismo nombre de clase, siempre se dirigen a distintos tipos. Esto permite que varias clases
de estilo, que son con el mismo nombre, a distintos tipos de destino.

El ejemplo siguiente muestra tres BoxView de estilo de las clases y un VisualElement clase de estilo:
<ContentPage ...>
<ContentPage.Resources>
<Style TargetType="BoxView"
Class="Separator">
<Setter Property="BackgroundColor"
Value="#CCCCCC" />
<Setter Property="HeightRequest"
Value="1" />
</Style>

<Style TargetType="BoxView"
Class="Rounded">
<Setter Property="BackgroundColor"
Value="#1FAECE" />
<Setter Property="HorizontalOptions"
Value="Start" />
<Setter Property="CornerRadius"
Value="10" />
</Style>

<Style TargetType="BoxView"
Class="Circle">
<Setter Property="BackgroundColor"
Value="#1FAECE" />
<Setter Property="WidthRequest"
Value="100" />
<Setter Property="HeightRequest"
Value="100" />
<Setter Property="HorizontalOptions"
Value="Start" />
<Setter Property="CornerRadius"
Value="50" />
</Style>

<Style TargetType="VisualElement"
Class="Rotated"
ApplyToDerivedTypes="true">
<Setter Property="Rotation"
Value="45" />
</Style>
</ContentPage.Resources>
</ContentPage>

El Separator , Rounded ,y Circle cada conjunto de clases de estilo BoxView propiedades con valores específicos.
El Rotatedestilo clase tiene un TargetType de VisualElement , lo que significa que sólo puede aplicarse a
VisualElement instancias. Sin embargo, su ApplyToDerivedTypes propiedad está establecida en true , lo que
garantiza que se puede aplicar a todos los controles que derivan de VisualElement , tales como BoxView . Para
obtener más información sobre cómo aplicar un estilo a un tipo derivado, consulte aplicar un estilo a los tipos
derivados.
El código de C# equivalente es:

var separatorBoxViewStyle = new Style(typeof(BoxView))


{
Class = "Separator",
Setters =
{
new Setter
{
Property = VisualElement.BackgroundColorProperty,
Value = Color.FromHex("#CCCCCC")
},
new Setter
{
{
Property = VisualElement.HeightRequestProperty,
Value = 1
}
}
};

var roundedBoxViewStyle = new Style(typeof(BoxView))


{
Class = "Rounded",
Setters =
{
new Setter
{
Property = VisualElement.BackgroundColorProperty,
Value = Color.FromHex("#1FAECE")
},
new Setter
{
Property = View.HorizontalOptionsProperty,
Value = LayoutOptions.Start
},
new Setter
{
Property = BoxView.CornerRadiusProperty,
Value = 10
}
}
};

var circleBoxViewStyle = new Style(typeof(BoxView))


{
Class = "Circle",
Setters =
{
new Setter
{
Property = VisualElement.BackgroundColorProperty,
Value = Color.FromHex("#1FAECE")
},
new Setter
{
Property = VisualElement.WidthRequestProperty,
Value = 100
},
new Setter
{
Property = VisualElement.HeightRequestProperty,
Value = 100
},
new Setter
{
Property = View.HorizontalOptionsProperty,
Value = LayoutOptions.Start
},
new Setter
{
Property = BoxView.CornerRadiusProperty,
Value = 50
}
}
};

var rotatedVisualElementStyle = new Style(typeof(VisualElement))


{
Class = "Rotated",
ApplyToDerivedTypes = true,
Setters =
{
new Setter
new Setter
{
Property = VisualElement.RotationProperty,
Value = 45
}
}
};

Resources = new ResourceDictionary


{
separatorBoxViewStyle,
roundedBoxViewStyle,
circleBoxViewStyle,
rotatedVisualElementStyle
};

Utilizar las clases de estilo


Las clases de estilo que pueden utilizarse al establecer el StyleClass propiedad del control, que es de tipo
IList<string> , a una lista de nombres de clase de estilo. Las clases de estilo se aplicará siempre que el tipo del
control coincide con el TargetType de las clases de estilo.
El ejemplo siguiente muestra tres BoxView instancias, cada conjunto a las clases de estilo diferente:

<ContentPage ...>
<ContentPage.Resources>
...
</ContentPage.Resources>
<StackLayout Margin="20">
<BoxView StyleClass="Separator" />
<BoxView WidthRequest="100"
HeightRequest="100"
HorizontalOptions="Center"
StyleClass="Rounded, Rotated" />
<BoxView HorizontalOptions="Center"
StyleClass="Circle" />
</StackLayout>
</ContentPage>

En este ejemplo, la primera BoxView se ha adaptado para que sea un separador de línea, mientras que el tercer
BoxView es circular. El segundo BoxView dos clases de estilo aplicado a él, que asigne a TI redondeada esquinas y
giremoslo en 45 grados:
IMPORTANT
Varias clases de estilo se pueden aplicar a un control porque el StyleClass propiedad es de tipo IList<string> . Cuando
esto ocurre, las clases de estilo se aplican de forma ascendente de la lista. Por lo tanto, cuando varias clases de estilo
establecen unas propiedades idénticas, la propiedad en la clase de estilo que se encuentra en la posición de la lista más alta
tendrá prioridad.

El código de C# equivalente es:

...
Content = new StackLayout
{
Children =
{
new BoxView { StyleClass = new [] { "Separator" } },
new BoxView { WidthRequest = 100, HeightRequest = 100, HorizontalOptions = LayoutOptions.Center,
StyleClass = new [] { "Rounded", "Rotated" } },
new BoxView { HorizontalOptions = LayoutOptions.Center, StyleClass = new [] { "Circle" } }
}
};

Vínculos relacionados
Estilos básicos (ejemplo)
Aplicar estilos a las aplicaciones de Xamarin.Forms
con hojas de estilos en cascada (CSS)
12/07/2019 • 24 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms es compatible con los elementos de estilo visual con hojas de estilos en cascada (CSS ).
Las aplicaciones de Xamarin.Forms pueden cambiar el estilo mediante CSS. Una hoja de estilos consta de una
lista de reglas, con cada regla que consta de uno o varios de los selectores y un bloque de declaración. Un bloque
de declaración consta de una lista de declaraciones de llaves, con cada declaración que consta de una propiedad,
un signo de dos puntos y un valor. Cuando hay varias declaraciones en un bloque, se inserta un punto y coma
como separador. El ejemplo de código siguiente muestra algunas CSS compatibles con Xamarin.Forms:
navigationpage {
-xf-bar-background-color: lightgray;
}

^contentpage {
background-color: lightgray;
}

#listView {
background-color: lightgray;
}

stacklayout {
margin: 20;
}

.mainPageTitle {
font-style: bold;
font-size: medium;
}

.mainPageSubtitle {
margin-top: 15;
}

.detailPageTitle {
font-style: bold;
font-size: medium;
text-align: center;
}

.detailPageSubtitle {
text-align: center;
font-style: italic;
}

listview image {
height: 60;
width: 60;
}

stacklayout>image {
height: 200;
width: 200;
}

En Xamarin.Forms, hojas de estilo CSS se analizan y se evalúan en tiempo de ejecución, en lugar de tiempo de
compilación y las hojas de estilo son volver a analizar en uso.

NOTE
Actualmente, todos los estilos que es posible con estilos XAML no se puede realizar con CSS. Sin embargo, estilos XAML se
pueden utilizar para complementar CSS para las propiedades que actualmente no son compatibles con Xamarin.Forms. Para
obtener más información sobre los estilos XAML, vea aplicar estilos a las aplicaciones de Xamarin.Forms con estilos XAML.

El MonkeyAppCSS ejemplo muestra cómo utilizar CSS para aplicar estilo a una aplicación simple y se muestra en
las capturas de pantalla siguiente:
Consumo de una hoja de estilos
El proceso para agregar una hoja de estilos a una solución es como sigue:
1. Agregue un archivo CSS vacío al proyecto de biblioteca .NET Standard.
2. Establezca la acción de compilación del archivo CSS para EmbeddedResource.
Cargar una hoja de estilos
Hay varios enfoques que puede utilizarse para cargar una hoja de estilos.
XAML
Una hoja de estilos se puede cargar y analizar con el StyleSheet clase antes de que se agrega a un
ResourceDictionary :

<Application ...>
<Application.Resources>
<StyleSheet Source="/Assets/styles.css" />
</Application.Resources>
</Application>

El StyleSheet.Source propiedad especifica la hoja de estilos como un URI relativo a la ubicación del archivo
XAML envolvente o en relación con la raíz del proyecto si el URI comienza con un / .
WARNING
El archivo CSS no se cargará si su acción de compilación no está establecido en EmbeddedResource.

Como alternativa, puede cargar y analizar con una hoja de estilos el StyleSheet (clase), antes de que se agrega a
un ResourceDictionary , por inserción en un CDATA sección:

<ContentPage ...>
<ContentPage.Resources>
<StyleSheet>
<![CDATA[
^contentpage {
background-color: lightgray;
}
]]>
</StyleSheet>
</ContentPage.Resources>
...
</ContentPage>

Para obtener más información acerca de los diccionarios de recursos, consulte los diccionarios de recursos.
C#
En C#, una hoja de estilos puede cargarse como recurso incrustado y agregarse a un ResourceDictionary :

public partial class MyPage : ContentPage


{
public MyPage()
{
InitializeComponent();

this.Resources.Add(StyleSheet.FromAssemblyResource(
IntrospectionExtensions.GetTypeInfo(typeof(MyPage)).Assembly,
"MyProject.Assets.styles.css"));
}
}

El primer argumento para el StyleSheet.FromAssemblyResource método es el ensamblado que contiene la hoja de


estilos, mientras que el segundo argumento es un string que representa el identificador de recurso. Se puede
obtener el identificador de recursos de la propiedades ventana cuando se selecciona el archivo CSS.
Como alternativa, una hoja de estilos puede cargarse desde un StringReader y se agrega a un
ResourceDictionary :

public partial class MyPage : ContentPage


{
public MyPage()
{
InitializeComponent();

using (var reader = new StringReader("^contentpage { background-color: lightgray; }"))


{
this.Resources.Add(StyleSheet.FromReader(reader));
}
}
}

El argumento para el StyleSheet.FromReader método es el TextReader que leyó la hoja de estilos.


Selección de elementos y aplicación de propiedades
CSS usa los selectores para determinar qué elementos de destino. Estilos con coincidencia de los selectores se
aplican de forma consecutiva, en orden de definición. Los estilos definidos en un elemento específico siempre se
aplican en último lugar. Para obtener más información acerca de los selectores compatibles, consulte Selector de
referencia.
CSS usa las propiedades para definir el estilo de un elemento seleccionado. Cada propiedad tiene un conjunto de
valores posibles, y algunas propiedades pueden afectar a cualquier tipo de elemento, mientras que otras se
aplican a los grupos de elementos. Para obtener más información acerca de las propiedades admitidas, consulte
referencia de propiedad.
Selección de elementos por tipo
Los elementos en el árbol visual pueden seleccionarse por tipo con las mayúsculas y minúsculas element
selector:

stacklayout {
margin: 20;
}

Este selector identifica cualquier StackLayout elementos en las páginas que consumen la hoja de estilos y
establecen sus márgenes para un grosor uniforme de 20.

NOTE
El element selector no identifica las subclases del tipo especificado.

Selección de elementos mediante la clase base


Se pueden seleccionar elementos en el árbol visual mediante la clase base con las mayúsculas y minúsculas
^base selector:

^contentpage {
background-color: lightgray;
}

Este selector identifica cualquier ContentPage elementos que consumen la hoja de estilos y establece su fondo de
color a lightgray .

NOTE
El ^base selector es específico de Xamarin.Forms y no forma parte de la especificación de CSS.

Seleccionar un elemento por nombre


Se pueden seleccionar elementos individuales en el árbol visual con las mayúsculas y minúsculas #id selector:

#listView {
background-color: lightgray;
}

Este selector identifica el elemento cuyo StyleId propiedad está establecida en listView . Sin embargo, si la
StyleId no está establecida la propiedad, el selector se vuelven a usar el x:Name del elemento. Por lo tanto, en el
siguiente ejemplo XAML, el #listView selector identificará la ListView cuyo x:Name atributo está establecido en
listView y establece su color de fondo en lightgray .

<ContentPage ...>
<ContentPage.Resources>
<StyleSheet Source="/Assets/styles.css" />
</ContentPage.Resources>
<StackLayout>
<ListView x:Name="listView" ...>
...
</ListView>
</StackLayout>
</ContentPage>

Selección de elementos con un atributo de clase específica


Se pueden seleccionar elementos con un atributo de clase específica con las mayúsculas y minúsculas .class
selector:

.detailPageTitle {
font-style: bold;
font-size: medium;
text-align: center;
}

.detailPageSubtitle {
text-align: center;
font-style: italic;
}

Una clase CSS que puede asignarse a un elemento XAML estableciendo el StyleClass propiedad del elemento
en el nombre de clase CSS. Por lo tanto, en el siguiente ejemplo XAML, los estilos definidos por el
.detailPageTitle clase se asignan a la primera Label , mientras que los estilos definidos por el
.detailPageSubtitle clase se asignan a la segunda Label .

<ContentPage ...>
<ContentPage.Resources>
<StyleSheet Source="/Assets/styles.css" />
</ContentPage.Resources>
<ScrollView>
<StackLayout>
<Label ... StyleClass="detailPageTitle" />
<Label ... StyleClass="detailPageSubtitle"/>
...
</StackLayout>
</ScrollView>
</ContentPage>

Selección de elementos secundarios


Se pueden seleccionar los elementos secundarios en el árbol visual con las mayúsculas y minúsculas
element element selector:

listview image {
height: 60;
width: 60;
}

Este selector identifica cualquier Image elementos secundarios de ListView elementos y su alto y ancho se
establece en 60. Por lo tanto, en el siguiente ejemplo XAML, el listview image selector identificará la Image que
es un elemento secundario de la ListView y su alto y ancho se establece en 60.

<ContentPage ...>
<ContentPage.Resources>
<StyleSheet Source="/Assets/styles.css" />
</ContentPage.Resources>
<StackLayout>
<ListView ...>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
...
<Image ... />
...
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>

NOTE
El element element selector no requiere el elemento secundario sea un directo secundario del miembro primario: el
elemento secundario puede tener un elemento primario diferente. Selección se produce siempre que un nodo primario es el
primer elemento especificado.

Selección de elementos secundarios directos


Dirigir los elementos secundarios en el árbol visual se pueden seleccionar con las mayúsculas y minúsculas
element>element selector:

stacklayout>image {
height: 200;
width: 200;
}

Este selector identifica cualquier Image directa de los elementos que son elementos secundarios de StackLayout
elementos y su alto y ancho se establece en 200. Por lo tanto, en el siguiente ejemplo XAML, el
stacklayout>image selector identificará la Image que es un elemento secundario directo de la StackLayout y
establece el alto y ancho en 200.

<ContentPage ...>
<ContentPage.Resources>
<StyleSheet Source="/Assets/styles.css" />
</ContentPage.Resources>
<ScrollView>
<StackLayout>
...
<Image ... />
...
</StackLayout>
</ScrollView>
</ContentPage>
NOTE
El element>element selector requiere que el elemento secundario es un directo secundario del miembro primario.

Referencia de selector
Xamarin.Forms admite los siguientes selectores de CSS:

SELECTOR EJEMPLO DESCRIPCIÓN

.class .header Selecciona todos los elementos con el


StyleClass propiedad que contiene
"header". Tenga en cuenta que este
selector distingue mayúsculas de
minúsculas.

#id #email Selecciona todos los elementos con


StyleId establecido en email . Si
StyleId no se establece, el retroceso
a x:Name . Cuando se usa XAML,
x:Name es preferible StyleId . Tenga
en cuenta que este selector distingue
mayúsculas de minúsculas.

* * Selecciona todos los elementos.

element label Selecciona todos los elementos de tipo


Label , pero no las subclases. Tenga
en cuenta que este selector distingue
mayúsculas de minúsculas.

^base ^contentpage Selecciona todos los elementos con


ContentPage como clase base,
incluyendo ContentPage propio.
Tenga en cuenta que este selector
distingue mayúsculas de minúsculas y
no forma parte de la especificación de
CSS.

element,element label,button Todos los selecciona Button


elementos y todos Label elementos.
Tenga en cuenta que este selector
distingue mayúsculas de minúsculas.

element element stacklayout label Todos los selecciona Label elementos


dentro de un StackLayout . Tenga en
cuenta que este selector distingue
mayúsculas de minúsculas.

element>element stacklayout>label Todos los selecciona Label elementos


con StackLayout como elemento
primario directo. Tenga en cuenta que
este selector distingue mayúsculas de
minúsculas.
SELECTOR EJEMPLO DESCRIPCIÓN

element+element label+entry Todos los selecciona Entry elementos


directamente detrás de un Label .
Tenga en cuenta que este selector
distingue mayúsculas de minúsculas.

element~element label~entry Todos los selecciona Entry elementos


precedido por un Label . Tenga en
cuenta que este selector distingue
mayúsculas de minúsculas.

Estilos con coincidencia de los selectores se aplican de forma consecutiva, en orden de definición. Los estilos
definidos en un elemento específico siempre se aplican en último lugar.

TIP
Los selectores se pueden combinar sin limitación, tales como StackLayout>ContentView>label.email .

Se admite actualmente los siguientes selectores:


[attribute]
@media y @supports
: y ::

NOTE
No se admiten especificidad e invalidaciones de especificidad.

Referencia de propiedad
Las siguientes propiedades CSS son compatibles con Xamarin.Forms (en el valores son de tipos de columna,
cursiva, mientras que los literales de cadena son gray ):

PROPERTY SE APLICA A VALORES EJEMPLO

align-content FlexLayout stretch | center | align-content: space-


start | end | between;

spacebetween |
spacearound |
spaceevenly |
flex-start | flex-end |
space-between |
space-around | initial

align-items FlexLayout stretch | center | align-items: flex-


start | end | start;

flex-start | flex-end |
initial
PROPERTY SE APLICA A VALORES EJEMPLO

align-self VisualElement auto | stretch | align-self: flex-end;


center | start | end |
flex-start | flex-end |
initial

background-color VisualElement Color | initial background-color:


springgreen;

background-image Page Cadena | initial background-image:


bg.png;

border-color Button , Frame , Color | initial border-color: #9acd32;


ImageButton

border-radius BoxView , Button , Doble | initial border-radius: 10;


Frame , ImageButton

border-width Button , ImageButton Doble | initial border-width: .5;

color ActivityIndicator , Color | initial color: rgba(255, 0, 0,


0.3);
BoxView , Button ,
CheckBox , DatePicker ,
Editor , Entry , Label ,
Picker , ProgressBar ,
SearchBar , Switch ,
TimePicker

column-gap Grid Doble | initial column-gap: 9;

direction VisualElement ltr | rtl | inherit | direction: rtl;


initial

flex-direction FlexLayout column | columnreverse | flex-direction: column-


row | rowreverse | reverse;

row-reverse |
column-reverse |
initial

flex-basis VisualElement float | auto | initial . flex-basis: 25%;


Además, se puede
especificar un porcentaje en
el intervalo de 0 a 100% con
el % inicio de sesión.

flex-grow VisualElement Float | initial flex-grow: 1.5;

flex-shrink VisualElement Float | initial flex-shrink: 1;

flex-wrap VisualElement nowrap | wrap | flex-wrap: wrap-


reverse | wrap-reverse | reverse;

initial
PROPERTY SE APLICA A VALORES EJEMPLO

font-family Button , DatePicker , Cadena | initial font-family: Consolas;


Editor , Entry , Label ,
Picker , SearchBar ,
TimePicker , Span

font-size Button , DatePicker , Double | namedsize | font-size: 12;


Editor , Entry , Label , initial
Picker , SearchBar ,
TimePicker , Span

font-style Button , DatePicker , bold | italic | font-style: bold;


Editor , Entry , Label , initial
Picker , SearchBar ,
TimePicker , Span

height VisualElement Doble | initial min-height: 250;

justify-content FlexLayout start | center | end | justify-content: flex-


spacebetween | end;

spacearound |
spaceevenly |
flex-start | flex-end |
space-between |
space-around | initial

line-height Label , Span Doble | initial line-height: 1.8;

margin View Grosor | initial margin: 6 12;

margin-left View Grosor | initial margin-left: 3;

margin-top View Grosor | initial margin-top: 2;

margin-right View Grosor | initial margin-right: 1;

margin-bottom View Grosor | initial margin-bottom: 6;

max-lines Label Int | initial max-lines: 2;

min-height VisualElement Doble | initial min-height: 50;

min-width VisualElement Doble | initial min-width: 112;

opacity VisualElement Doble | initial opacity: .3;

order VisualElement Int | initial order: -1;

padding Button , ImageButton , Grosor | initial padding: 6 12 12;


Layout , Page
PROPERTY SE APLICA A VALORES EJEMPLO

padding-left Button , ImageButton , Doble | initial padding-left: 3;


Layout , Page

padding-top Button , ImageButton , Doble | initial padding-top: 4;


Layout , Page

padding-right Button , ImageButton , Doble | initial padding-right: 2;


Layout , Page

padding-bottom Button , ImageButton , Doble | initial padding-bottom: 6;


Layout , Page

position FlexLayout relative | absolute | position: absolute;


initial

row-gap Grid Doble | initial row-gap: 12;

text-align Entry , EntryCell , left |


| right |
top text-align: right;
Label , SearchBar | start |
bottom
center | middle | end |
initial . left y right
deben evitarse en entornos
de derecha a izquierda.

text-decoration Label , Span none | underline | text-decoration:


strikethrough | underline, line-
through;
line-through | initial

transform VisualElement none , rotate , rotateX , transform: rotate(180),


rotateY , scale , scaleX(2.5);

scaleX , scaleY ,
translate , translateX ,
translateY , initial

transform-origin VisualElement Double, dobles | initial transform-origin: 7.5,


12.5;

vertical-align Label left | top | right | vertical-align: bottom;


bottom | start |
center | middle | end |
initial

visibility VisualElement true | visible | false visibility: hidden;


| hidden | collapse |
initial

width VisualElement Doble | initial min-width: 320;


NOTE
initial es un valor válido para todas las propiedades. Borra el valor (restablece en el valor predeterminado) que se ha
establecido de otro estilo.

Las siguientes propiedades no se admiten actualmente:


all: initial.
Propiedades de diseño (cuadro o cuadrícula).
Propiedades de la forma abreviada, como font , y border .

Además, no hay ningún inherit herencia del valor de modo que no se admite. Por lo tanto, por ejemplo, no es
posible, establezca el font-size propiedad en un diseño y esperar que todos los Label instancias en el diseño
para heredar el valor. La única excepción es el direction propiedad, que tiene un valor predeterminado de
inherit .

Propiedades específicas de Xamarin.Forms


También se admiten las siguientes propiedades CSS específicas de Xamarin.Forms (en el valores son de tipos de
columna, cursiva, mientras que los literales de cadena son gray ):

PROPERTY SE APLICA A VALORES EJEMPLO

-xf-placeholder Entry , Editor , texto entre comillas | -xf-placeholder: Enter


initial name;
SearchBar

-xf-placeholder-color Entry , Editor , Color | initial -xf-placeholder-color:


SearchBar green;

-xf-max-length Entry , Editor Int | initial -xf-max-length: 20;

-xf-bar-background- NavigationPage , Color | initial -xf-bar-background-


color TabbedPage color: teal;

-xf-bar-text-color NavigationPage , Color | initial -xf-bar-text-color:


TabbedPage gray

-xf-orientation ScrollView , horizontal | vertical | -xf-orientation:


StackLayout both | initial . both horizontal;

solo se admite en un
ScrollView .

-xf-horizontal-scroll- ScrollView default | always | -xf-horizontal-scroll-


bar-visibility never | initial bar-visibility: never;

-xf-vertical-scroll- ScrollView default | always | -xf-vertical-scroll-


bar-visibility never | initial bar-visibility: always;

-xf-min-track-color Slider Color | initial -xf-min-track-color:


yellow;

-xf-max-track-color Slider Color | initial -xf-max-track-color:


red;
PROPERTY SE APLICA A VALORES EJEMPLO

-xf-thumb-color Slider Color | initial -xf-thumb-color:


limegreen;

-xf-spacing StackLayout Doble | initial -xf-spacing: 8;

Propiedades específicas del Xamarin.Forms Shell


También se admiten las siguientes propiedades CSS específicas de Xamarin.Forms Shell (en el valores son de
tipos de columna, cursiva, mientras que los literales de cadena son gray ):

PROPIEDAD SE APLICA A VALORES EJEMPLO

-xf-flyout-background Shell Color | initial -xf-flyout-background:


red;

-xf-shell-background Element Color | initial -xf-shell-background:


green;

-xf-shell-disabled Element Color | initial -xf-shell-disabled:


blue;

-xf-shell-foreground Element Color | initial -xf-shell-foreground:


yellow;

-xf-shell-tabbar- Element Color | initial -xf-shell-tabbar-


background background: white;

-xf-shell-tabbar- Element Color | initial -xf-shell-tabbar-


disabled disabled: black;

-xf-shell-tabbar- Element Color | initial -xf-shell-tabbar-


foreground foreground: gray;

-xf-shell-tabbar-title Element Color | initial -xf-shell-tabbar-title:


lightgray;

-xf-shell-tabbar- Element Color | initial -xf-shell-tabbar-


unselected unselected: cyan;

-xf-shell-title Element Color | initial -xf-shell-title: teal;

-xf-shell-unselected Element Color | initial -xf-shell-unselected:


limegreen;

Color
La siguiente color se admiten valores:
X11 colores, que coincide con los colores CSS, los colores predefinidos de UWP y Xamarin.Forms colores.
Tenga en cuenta que estos valores de color distinguen mayúsculas de minúsculas.
hex colores: #rgb , #argb , #rrggbb , #aarrggbb
los colores RGB: rgb(255,0,0) , rgb(100%,0%,0%) . Los valores son en el intervalo 0-255, o 0-100%.
los colores RGBA: rgba(255, 0, 0, 0.8) , rgba(100%, 0%, 0%, 0.8) . Es el valor de opacidad en el intervalo de
0,0 a 1,0.
colores HSL: hsl(120, 100%, 50%) . El valor h está en el intervalo 0-360, mientras s y l están en el intervalo 0-
100%.
colores de HSLA: hsla(120, 100%, 50%, .8) . Es el valor de opacidad en el intervalo de 0,0 a 1,0.
Thickness
Uno, dos, tres o cuatro thickness se admiten valores, separados por espacios en blanco:
Un único valor indica el grosor uniforme.
Dos valores indican grosor vertical y horizontal.
Estos tres valores indican la parte superior, horizontal (izquierda y derecha) y, luego, grosor de la parte inferior.
Cuatro valores indican la parte superior, derecha y, a continuación, inferior y, luego, grosor de la izquierda.

NOTE
CSS thickness valores difieren XAML Thickness valores. Por ejemplo, en XAML, un valor de dos Thickness indica el
grosor horizontal y vertical, mientras que un valor de cuatro Thickness indica izquierda, superior y, luego, derecha, abajo,
a continuación, grosor. Además, XAML Thickness valores están delimitados por comas.

NamedSize
Lo siguiente entre mayúsculas y minúsculas namedsize se admiten valores:
default
micro
small
medium
large

El significado exacto de cada namedsize valor es dependiente de la plataforma y dependientes de la vista.

CSS en Xamarin.Forms con Xamarin.University


Vídeo CSS 3.0 de Xamarin.Forms

Vínculos relacionados
MonkeyAppCSS (ejemplo)
Diccionarios de recursos
Aplicación de estilos para aplicaciones Xamarin.Forms con estilos XAML
Conmutador de Xamarin.Forms
11/07/2019 • 6 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms Switch es un botón de alternancia horizontal que se puede manipular por el usuario para
alternar entre activar y desactivar los Estados, que se representan mediante un boolean valor. El Switch clase
hereda de View .
La siguiente captura de pantalla muestra un Switch en controlar su en y desactivar alternar los Estados en iOS
y Android:

El Switch control define dos propiedades:


OnColor es un Color que afecta a cómo el Switch está representado en el estado de alternancia, o en,
estado.
IsToggled es un boolean valor que indica si el Switch es en.
Estas propiedades están respaldadas por un BindableProperty objeto, lo que significa el Switch puede cambiar el
estilo y ser el destino de los enlaces de datos.
El Switch control define un Toggled evento que se desencadena cuando el IsToggled los cambios de propiedad,
mediante la manipulación de usuario o cuando una aplicación establece el IsToggled propiedad. El
ToggledEventArgs objeto que acompaña a la Toggled el evento tiene una propiedad única denominada Value ,
del tipo bool . Cuando se desencadena el evento, el valor de la Value propiedad refleja el nuevo valor de la
IsToggled propiedad.

Crear un conmutador
Un Switch se pueden crear instancias en XAML. Su IsToggled propiedad puede establecerse para activar o
desactivar el Switch . De forma predeterminada, el IsToggled propiedad es false . El ejemplo siguiente muestra
cómo crear una instancia de un Switch en XAML con el elemento opcional IsToggled conjunto de propiedades:

<Switch IsToggled="true"/>

Un Switch también se pueden crear en el código:

Switch switch = new Switch { IsToggled = true };

Cambiar las propiedades de estilo


El OnColor propiedad puede establecerse para definir el Switch de color cuando se cambia a su en estado. El
ejemplo siguiente muestra cómo crear una instancia de un Switch en XAML con el OnColor conjunto de
propiedades:
<Switch OnColor="Orange" />

El OnColor propiedad también se puede establecer al crear un Switch en el código:

Switch switch = new Switch { OnColor = Color.Orange };

La siguiente captura de pantalla muestra la Switch en su en y desactivar alternar los Estados, con el OnColor
propiedad establecida en Color.Orange en iOS y Android:

Responder a un cambio de estado del conmutador


Cuando el IsToggled los cambios de propiedad, mediante la manipulación de usuario o cuando una aplicación
establece el IsToggled propiedad, el Toggled desencadena el evento. Puede registrar un controlador de eventos
para este evento para responder al cambio:

<Switch Toggled="OnToggled" />

El archivo de código subyacente contiene el controlador para el Toggled eventos:

void OnToggled(object sender, ToggledEventArgs e)


{
// Perform an action after examining e.Value
}

El sender argumento en el controlador de eventos es el Switch responsable de activar este evento. Puede usar
el sender propiedad para tener acceso a la Switch objeto, o para distinguir entre varias Switch objetos que
comparten el mismo Toggled controlador de eventos.
El Toggled también se puede asignar el controlador de eventos en el código:

Switch switch = new Switch {...};


switch.Toggled += (sender, e) =>
{
// Perform an action after examining e.Value
}

Enlazar datos de un conmutador


El Toggled controlador de eventos se puede eliminar mediante el uso de enlace de datos y desencadenadores
para responder a un Switch cambiar los Estados de alternancia.
<Switch x:Name="styleSwitch" />
<Label Text="Lorem ipsum dolor sit amet, elit rutrum, enim hendrerit augue vitae praesent sed non, lorem
aenean quis praesent pede.">
<Label.Triggers>
<DataTrigger TargetType="Label"
Binding="{Binding Source={x:Reference styleSwitch}, Path=IsToggled}"
Value="true">
<Setter Property="FontAttributes"
Value="Italic, Bold" />
<Setter Property="FontSize"
Value="Large" />
</DataTrigger>
</Label.Triggers>
</Label>

En este ejemplo, el Label utiliza una expresión de enlace en un DataTrigger para supervisar la IsToggled
propiedad de la Switch denominado styleSwitch . Cuando esta propiedad se convierte en true ,
FontAttributes y FontSize propiedades de la Label cambian. Cuando el IsToggled propiedad devuelve al
false , el FontAttributes y FontSize propiedades de la Label se restablecen a su estado inicial.

Para obtener información acerca de los desencadenadores, vea Xamarin.Forms desencadenadores.

Deshabilitar un conmutador
Una aplicación puede entrar en un estado donde el Switch no es una operación válida que se va a activar o
desactivar. En tales casos, el Switch puede deshabilitarse estableciendo su IsEnabled propiedad false . Esto
impedirá que los usuarios puedan manipular la Switch .

Vínculos relacionados
Demostraciones de conmutador
Desencadenadores de Xamarin.Forms
Xamarin.Forms TableView
11/07/2019 • 10 minutes to read • Edit Online

descargar el ejemplo
TableView es una vista para mostrar listas desplazables de datos o las opciones que hay filas que no comparten
la misma plantilla. A diferencia de ListView, TableView no tiene el concepto de un ItemsSource , por lo que se
deben agregar manualmente elementos como elementos secundarios.

Casos de uso
TableView resulta útil cuando:
presentar una lista de configuración,
recopilación de datos en un formulario, o
mostrando datos que se presentan diferente de la fila a fila (por ejemplo, números, porcentajes e imágenes).
TableView Controla el desplazamiento y el diseño de filas en las secciones atractivas, una necesidad común
para los escenarios anteriores. El TableView utiliza control de la plataforma subyacente de vista equivalente
cuando esté disponible para crear una apariencia nativa para cada plataforma.

Estructura
Elementos de un TableView se organiza en secciones. En la raíz de la TableView es el TableRoot , que es
primario a uno o varios TableSection instancias. Cada TableSection consta de un encabezado y uno o más
ViewCell instancias:

<TableView Intent="Settings">
<TableRoot>
<TableSection Title="Ring">
<SwitchCell Text="New Voice Mail" />
<SwitchCell Text="New Mail" On="true" />
</TableSection>
</TableRoot>
</TableView>
El código de C# equivalente es:

Content = new TableView


{
Root = new TableRoot
{
new TableSection("Ring")
{
// TableSection constructor takes title as an optional parameter
new SwitchCell { Text = "New Voice Mail" },
new SwitchCell { Text = "New Mail", On = true }
}
},
Intent = TableIntent.Settings
};

Apariencia
TableView expone el Intent propiedad, que se puede establecer en cualquiera de los TableIntent miembros
de enumeración:
Data : para su uso al mostrar las entradas de datos. Tenga en cuenta que ListView puede ser una mejor
opción para el desplazamiento de listas de datos.
Form : para su uso cuando la TableView está actuando como un formulario.
Menu : para su uso cuando se presenta un menú de las selecciones.
Settings : para su uso cuando se muestra una lista de valores de configuración.

El TableIntent valor que elija puede afectar a cómo el TableView aparece en cada plataforma. Incluso si hay no
borrar las diferencias, es una práctica recomendada para seleccionar el TableIntent que más acerque cómo
piensa usar la tabla.
Además, se muestra el color del texto para cada TableSection se puede cambiar estableciendo la TextColor
propiedad a un Color .

Celdas integradas
Xamarin.Forms incluye celdas integradas para recopilar y mostrar información. Aunque ListView y TableView
puede usar todas las celdas de la mismas, SwitchCell y EntryCell son las más relevantes para un TableView
escenario.
Consulte aspecto de la celda de ListView para obtener una descripción detallada de TextCell y ImageCell.
SwitchCell
SwitchCell es el control que se utilizará para presentar y capturar un activado/desactivado o true / false
estado. Define las siguientes propiedades:
Text : texto que se muestra al lado del conmutador.
On : determina si se muestra el conmutador como activado o desactivado.
OnColor – el Color del conmutador cuando se encuentra en la posición de encendido.

Todas estas propiedades son enlazables.


SwitchCell También expone el OnChanged eventos, que le permite responder a los cambios de estado de la
celda.
EntryCell
EntryCell es útil cuando se necesita mostrar los datos de texto que el usuario puede editar. Define las
siguientes propiedades:
Keyboard : El teclado para mostrar mientras se edita. Hay opciones para cosas como valores numéricos,
correo electrónico, números de teléfono, etcetera. Consulte la documentación de API.
Label : El texto de etiqueta para mostrar a la izquierda del campo de entrada de texto.
LabelColor : El color del texto de la etiqueta.
Placeholder : Texto que se muestra en el campo de entrada cuando es nulo o está vacío. Este texto
desaparece cuando comienza la entrada de texto.
Text : El texto en el campo de entrada.
HorizontalTextAlignment : La alineación horizontal del texto. Puede ser, izquierda o derecha centrado.
Consulte la documentación de API.
EntryCell También expone el Completed evento, que se desencadena cuando el usuario presiona el botón
'listo' en el teclado mientras edita el texto.
Celdas personalizadas
Cuando las celdas integradas no son suficientes, celdas personalizadas pueden utilizarse para presentar y
capturar datos de la manera que tenga sentido para su aplicación. Por ejemplo, es posible que desee presentar
un control deslizante para permitir que un usuario puede elegir la opacidad de una imagen.
Todas las celdas personalizadas deben derivarse de ViewCell , la misma clase base que todos los de la celda
integrada tipos de uso.
Este es un ejemplo de una celda personalizada:
El ejemplo siguiente muestra el XAML usado para crear el TableView en las capturas de pantalla anterior:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DemoTableView.TablePage"
Title="TableView">
<TableView Intent="Settings">
<TableRoot>
<TableSection Title="Getting Started">
<ViewCell>
<StackLayout Orientation="Horizontal">
<Image Source="bulb.png" />
<Label Text="left"
TextColor="#f35e20" />
<Label Text="right"
HorizontalOptions="EndAndExpand"
TextColor="#503026" />
</StackLayout>
</ViewCell>
</TableSection>
</TableRoot>
</TableView>
</ContentPage>

El código de C# equivalente es:


var table = new TableView();
table.Intent = TableIntent.Settings;
var layout = new StackLayout() { Orientation = StackOrientation.Horizontal };
layout.Children.Add (new Image() { Source = "bulb.png"});
layout.Children.Add (new Label()
{
Text = "left",
TextColor = Color.FromHex("#f35e20"),
VerticalOptions = LayoutOptions.Center
});
layout.Children.Add (new Label ()
{
Text = "right",
TextColor = Color.FromHex ("#503026"),
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.EndAndExpand
});
table.Root = new TableRoot ()
{
new TableSection("Getting Started")
{
new ViewCell() {View = layout}
}
};
Content = table;

El elemento raíz en el TableView es el TableRoot y hay un TableSection inmediatamente debajo de la


TableRoot . El ViewCell se define directamente bajo el TableSection y un StackLayout se usa para administrar
el diseño de la celda personalizada, aunque cualquier diseño podría usarse aquí.

NOTE
A diferencia de ListView , TableView no requiere ese personalizado (o ninguno) se definen las celdas en una
ItemTemplate .

Alto de fila
El TableView clase tiene dos propiedades que pueden usarse para cambiar el alto de fila de celdas:
RowHeight : establece el alto de cada fila a una int .
HasUnevenRows : las filas tienen diferentes alturas si establece en true . Tenga en cuenta que al establecer
esta propiedad en true , automáticamente se calcula y aplicada por Xamarin.Forms alto de las filas.

Cuando el alto del contenido en una celda de un TableView se cambia la fila se actualiza implícitamente alto en
la plataforma Universal de Windows (UWP ) y Android. Sin embargo, en iOS debe obligársele a actualizar
mediante el establecimiento la HasUnevenRows propiedad true y mediante una llamada a la
Cell.ForceUpdateSize método.

El siguiente ejemplo XAML se muestra un TableView que contiene un ViewCell :


<ContentPage ...>
<TableView ...
HasUnevenRows="true">
<TableRoot>
...
<TableSection ...>
...
<ViewCell x:Name="_viewCell"
Tapped="OnViewCellTapped">
<Grid Margin="15,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Text="Tap this cell." />
<Label x:Name="_target"
Grid.Row="1"
Text="The cell has changed size."
IsVisible="false" />
</Grid>
</ViewCell>
</TableSection>
</TableRoot>
</TableView>
</ContentPage>

Cuando el ViewCell se pulsa, el OnViewCellTapped se ejecuta el controlador de eventos:

void OnViewCellTapped(object sender, EventArgs e)


{
_target.IsVisible = !_target.IsVisible;
_viewCell.ForceUpdateSize();
}

El OnViewCellTapped controlador de eventos muestra u oculta el segundo Label en el ViewCell y actualiza


explícitamente el tamaño de la celda mediante una llamada a la Cell.ForceUpdateSize método.
Las capturas de pantalla siguientes muestran la celda antes de que se puntea en:

Las capturas de pantalla siguientes muestran la celda después de que se puntea en:

IMPORTANT
Si esta característica está sobreutilizada, hay grandes posibilidades de degradación del rendimiento.

Vínculos relacionados
TableView (ejemplo)
Texto en Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Uso de Xamarin.Forms para escribir o mostrar texto.
Xamarin.Forms tiene tres vistas principales para trabajar con texto:
Etiqueta — para presentar un o varias líneas de texto. Puede mostrar texto con varias opciones de formato en
la misma línea.
Entrada — para escribir texto que es solo una línea. Entrada tiene un modo de contraseña.
Editor — para escribir texto que podría tardar más de una línea.
Apariencia del texto se puede cambiar mediante integradas o personalizadas estilos y algunos controles admiten
personalizado fuentes.

Label
El Label vista se utiliza para mostrar texto. Puede mostrar varias líneas de texto o una sola línea de texto. Label
puede presentar el texto con varias opciones de formato usadas en línea. La vista de la etiqueta puede ajustar o
truncar el texto cuando no caben en una sola línea.

Consulte la etiqueta artículo para obtener más información.


Para obtener información acerca de cómo personalizar la fuente utilizada en una etiqueta, vea fuentes.
Entrada
Entry se usa para aceptar la entrada de texto de una línea. Entry ofrece el control sobre los colores y fuentes.
Entry tiene un modo de contraseña y puede mostrar texto de marcador de posición hasta que se escribe texto.

Consulte la entrada artículo para obtener más información.


Tenga en cuenta que, a diferencia de Label , Entry no puede tener la configuración de fuente personalizados.

Editor
Editor se usa para aceptar la entrada de texto de varias líneas. Editor ofrece el control sobre los colores y
fuentes.
Consulte la Editor artículo para obtener más información.

Fuentes
Muchos controles admiten la configuración de fuente diferente utilizando las fuentes integradas en cada
plataforma, o fuentes personalizadas que se incluye con la aplicación. Consulte la fuentes artículo para obtener
más información.

Estilos
Consulte trabajar con estilos para obtener información sobre cómo configurar la fuente, colory otras propiedades
de presentación que se aplican a través de varios controles.

Vínculos relacionados
Texto (ejemplo)
Etiqueta de Xamarin.Forms
11/07/2019 • 19 minutes to read • Edit Online

descargar el ejemplo
Mostrar texto en Xamarin.Forms
El Label vista se utiliza para mostrar texto, único y varias líneas. Las etiquetas pueden tener decoraciones de
texto, texto de color y usar las fuentes personalizadas (familias de tamaños y opciones).

Decoraciones de texto
Se pueden aplicar las decoraciones de texto subrayado y tachado a Label instancias estableciendo el
Label.TextDecorations propiedad a uno o varios TextDecorations miembros de enumeración:

None
Underline
Strikethrough

En el siguiente ejemplo XAML se muestra cómo establecer el Label.TextDecorations propiedad:

<Label Text="This is underlined text." TextDecorations="Underline" />


<Label Text="This is text with strikethrough." TextDecorations="Strikethrough" />
<Label Text="This is underlined text with strikethrough." TextDecorations="Underline, Strikethrough" />

El código de C# equivalente es:

var underlineLabel = new Label { Text = "This is underlined text.", TextDecorations =


TextDecorations.Underline };
var strikethroughLabel = new Label { Text = "This is text with strikethrough.", TextDecorations =
TextDecorations.Strikethrough };
var bothLabel = new Label { Text = "This is underlined text with strikethrough.", TextDecorations =
TextDecorations.Underline | TextDecorations.Strikethrough };

Capturas de pantalla siguientes se muestra el TextDecorations aplicados a miembros de la enumeración Label


instancias:

NOTE
También se pueden aplicar las decoraciones de texto a Span instancias. Para obtener más información sobre la Span de
clases, vea texto con formato.

Colores
Las etiquetas se pueden establecer para usar un color de texto personalizado a través de la enlazable TextColor
propiedad.
Atención especial es necesaria para asegurarse de que los colores se podrán usar en cada plataforma. Dado que
cada plataforma tiene distintos valores predeterminados para los colores de texto y fondo, deberá tener cuidado
al elegir un valor predeterminado que funciona en cada uno.
En el siguiente ejemplo XAML establece el color del texto de un Label :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TextSample.LabelPage"
Title="Label Demo">
<StackLayout Padding="5,10">
<Label TextColor="#77d065" FontSize = "20" Text="This is a green label." />
</StackLayout>
</ContentPage>

El código de C# equivalente es:

public partial class LabelPage : ContentPage


{
public LabelPage ()
{
InitializeComponent ();

var layout = new StackLayout { Padding = new Thickness(5,10) };


var label = new Label { Text="This is a green label.", TextColor = Color.FromHex("#77d065"), FontSize
= 20 };
layout.Children.Add(label);
this.Content = layout;
}
}

Las capturas de pantalla siguientes muestran el resultado de establecer el TextColor propiedad:

Para obtener más información acerca de los colores, consulte colores.

Fuentes
Para obtener más información acerca de cómo especificar las fuentes en un Label , consulte fuentes.

Truncamiento y ajuste
Se pueden establecer etiquetas para controlar el texto que no cabe en una sola línea en una de varias maneras,
expuestos por la LineBreakMode propiedad. LineBreakMode es una enumeración con los valores siguientes:
HeadTruncation – trunca el encabezado del texto, que muestra el extremo.
CharacterWrap – ajusta el texto en una nueva línea en un límite de caracteres.
MiddleTruncation – muestra el principio y al final del texto, con el reemplazo intermedio mediante puntos
suspensivos.
NoWrap – no ajusta el texto, mostrar solo mayor cantidad de texto que puede cabe en una sola línea.
TailTruncation – muestra el principio del texto, truncar final.
WordWrap – ajusta el texto en el límite de palabras.
Mostrar un número específico de líneas
El número de líneas muestra un Label se puede especificar estableciendo el Label.MaxLines propiedad a un
int valor:

Cuando MaxLines es 0, el Label respeta el valor de la LineBreakMode propiedad que se va a mostrar ya sea
una sola línea, posiblemente truncada, o todas las líneas con todo el texto.
Cuando MaxLines es 1, el resultado es idéntico al valor de la LineBreakMode propiedad NoWrap ,
HeadTruncation , MiddleTruncation , o TailTruncation . Sin embargo, el Label respetará el valor de la
LineBreakMode propiedad con respecto a la selección de ubicación de puntos suspensivos, si procede.
Cuando MaxLines es mayor que 1, el Label mostrará hasta el número especificado de líneas, respetando el
valor de la LineBreakMode propiedad con respecto a la selección de ubicación de puntos suspensivos, si
procede. Sin embargo, establecer el MaxLines propiedad a un valor mayor que 1 no tiene ningún efecto si la
LineBreakMode propiedad está establecida en NoWrap .

En el siguiente ejemplo XAML se muestra cómo establecer el MaxLines propiedad en un Label :

<Label Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. In facilisis nulla eu felis fringilla
vulputate. Nullam porta eleifend lacinia. Donec at iaculis tellus."
LineBreakMode="WordWrap"
MaxLines="2" />

El código de C# equivalente es:

var label =
{
Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In facilisis nulla eu felis fringilla
vulputate. Nullam porta eleifend lacinia. Donec at iaculis tellus.", LineBreakMode = LineBreakMode.WordWrap,
MaxLines = 2
};

Las capturas de pantalla siguientes muestran el resultado de establecer el MaxLines propiedad en 2, cuando el
texto es lo suficientemente largo para ocupar más de 2 líneas:

Texto con formato


Las etiquetas de exponen un FormattedText propiedad que permite la presentación de texto con varias fuentes y
colores en la misma vista.
El FormattedText propiedad es de tipo FormattedString , que consta de uno o varios Span instancias, se
establece a través de la Spans propiedad . La siguiente Span propiedades pueden usarse para establecer la
apariencia visual:
BackgroundColor : el color del fondo del intervalo.
Font : la fuente para el texto del intervalo.
FontAttributes : los atributos de fuente para el texto del intervalo.
FontFamily – la familia de fuentes a la que pertenece la fuente para el texto del intervalo.
FontSize – el tamaño de la fuente para el texto del intervalo.
ForegroundColor : el color del texto en el intervalo. Esta propiedad está obsoleta y se ha reemplazado por el
TextColor propiedad.
LineHeight -el multiplicador que se aplican en el alto de línea predeterminado del intervalo. Para obtener
más información, consulte alto de línea.
Style : el estilo que se aplican al intervalo.
Text : el texto del intervalo.
TextColor : el color del texto en el intervalo.
TextDecorations -las decoraciones para aplicar al texto en el intervalo. Para obtener más información,
consulte decoraciones de texto.

NOTE
El BackgroundColor , Text , y Text propiedades enlazables tienen un modo de enlace predeterminada de OneWay .
Para obtener más información acerca de este modo de enlace, consulte el modo de enlace predeterminada en el modo de
enlace guía.

Además, el GestureRecognizers propiedad puede usarse para definir una colección de los reconocedores de
gestos que responderá a los movimientos en el Span .
En el ejemplo de XAML siguiente se muestra un FormattedText propiedad que consta de tres Span instancias:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TextSample.LabelPage"
Title="Label Demo - XAML">
<StackLayout Padding="5,10">
...
<Label LineBreakMode="WordWrap">
<Label.FormattedText>
<FormattedString>
<Span Text="Red Bold, " TextColor="Red" FontAttributes="Bold" />
<Span Text="default, " Style="{DynamicResource BodyStyle}">
<Span.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}" />
</Span.GestureRecognizers>
</Span>
<Span Text="italic small." FontAttributes="Italic" FontSize="Small" />
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</ContentPage>

El código de C# equivalente es:


public class LabelPageCode : ContentPage
{
public LabelPageCode ()
{
var layout = new StackLayout{ Padding = new Thickness (5, 10) };
...
var formattedString = new FormattedString ();
formattedString.Spans.Add (new Span{ Text = "Red bold, ", ForegroundColor = Color.Red, FontAttributes
= FontAttributes.Bold });

var span = new Span { Text = "default, " };


span.GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(async () => await
DisplayAlert("Tapped", "This is a tapped Span.", "OK")) });
formattedString.Spans.Add(span);
formattedString.Spans.Add (new Span { Text = "italic small.", FontAttributes = FontAttributes.Italic,
FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)) });

layout.Children.Add (new Label { FormattedText = formattedString });


this.Content = layout;
}
}

IMPORTANT
El Text propiedad de un Span puede establecerse mediante enlace de datos. Para obtener más información, consulte
enlace de datos.

Tenga en cuenta que un Span también puede responder a los gestos que se agregan en el intervalo
GestureRecognizers colección. Por ejemplo, un TapGestureRecognizer se ha agregado a la segunda Span en los
ejemplos de código anteriores. Por lo tanto, cuando esto Span se pulsa la TapGestureRecognizer responderá
mediante la ejecución de la ICommand definido por el Command propiedad. Para obtener más información acerca
de los reconocedores de gestos, vea Xamarin.Forms gestos.
Las capturas de pantalla siguientes muestran el resultado de la configuración de la FormattedString propiedad a
tres Span instancias:

Alto de línea
La altura vertical de un Label y un Span puede personalizarse configurando el Label.LineHeight propiedad o
Span.LineHeight a un double valor. En iOS y Android, estos valores son multiplicadores del alto de línea original
y en la plataforma Universal de Windows (UWP ) la Label.LineHeight valor de propiedad es un multiplicador del
tamaño de fuente de etiqueta.
NOTE
En iOS, el Label.LineHeight y Span.LineHeight propiedades cambian el alto de línea de texto que encaja en una
sola línea y el texto que se ajusta en varias líneas.
En Android, el Label.LineHeight y Span.LineHeight propiedades sólo cambian el alto de línea de texto que se
ajusta en varias líneas.
En UWP, la Label.LineHeight propiedad cambia el alto de línea de texto que se ajusta en varias líneas, y el
Span.LineHeight propiedad no tiene ningún efecto.

En el siguiente ejemplo XAML se muestra cómo establecer el LineHeight propiedad en un Label :

<Label Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. In facilisis nulla eu felis fringilla
vulputate. Nullam porta eleifend lacinia. Donec at iaculis tellus."
LineBreakMode="WordWrap"
LineHeight="1.8" />

El código de C# equivalente es:

var label =
{
Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In facilisis nulla eu felis fringilla
vulputate. Nullam porta eleifend lacinia. Donec at iaculis tellus.", LineBreakMode = LineBreakMode.WordWrap,
LineHeight = 1.8
};

Las capturas de pantalla siguientes muestran el resultado de la configuración de la Label.LineHeight propiedad a


1.8:

En el siguiente ejemplo XAML se muestra cómo establecer el LineHeight propiedad en un Span :

<Label LineBreakMode="WordWrap">
<Label.FormattedText>
<FormattedString>
<Span Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. In a tincidunt sem.
Phasellus mollis sit amet turpis in rutrum. Sed aliquam ac urna id scelerisque. "
LineHeight="1.8"/>
<Span Text="Nullam feugiat sodales elit, et maximus nibh vulputate id."
LineHeight="1.8" />
</FormattedString>
</Label.FormattedText>
</Label>

El código de C# equivalente es:


var formattedString = new FormattedString();
formattedString.Spans.Add(new Span
{
Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In a tincidunt sem. Phasellus mollis sit
amet turpis in rutrum. Sed aliquam ac urna id scelerisque. ",
LineHeight = 1.8
});
formattedString.Spans.Add(new Span
{
Text = "Nullam feugiat sodales elit, et maximus nibh vulputate id.",
LineHeight = 1.8
});
var label = new Label
{
FormattedText = formattedString,
LineBreakMode = LineBreakMode.WordWrap
};

Las capturas de pantalla siguientes muestran el resultado de la configuración de la Span.LineHeight propiedad a


1.8:

Hipervínculos
El texto mostrado por Label y Span instancias se pueden convertir en hipervínculos con el siguiente enfoque:
1. Establecer el TextColor y TextDecoration propiedades de la Label o Span .
2. Agregar un TapGestureRecognizer a la GestureRecognizers colección de la Label o Span , cuya Command
propiedad se enlaza a un ICommand y cuyo CommandParameter propiedad contiene la dirección URL para abrir.
3. Definir la ICommand que ejecutará el TapGestureRecognizer .
4. Escribir el código que se ejecutará por la ICommand .

El ejemplo de código siguiente, tomado de la demostraciones de hipervínculo ejemplo, se muestra un Label


cuyo contenido se establece desde varios Span instancias:
<Label>
<Label.FormattedText>
<FormattedString>
<Span Text="Alternatively, click " />
<Span Text="here"
TextColor="Blue"
TextDecorations="Underline">
<Span.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}"
CommandParameter="https://docs.microsoft.com/xamarin/" />
</Span.GestureRecognizers>
</Span>
<Span Text=" to view Xamarin documentation." />
</FormattedString>
</Label.FormattedText>
</Label>

En este ejemplo, la primera y tercera Span instancias comprenden el texto, mientras que el segundo Span
representa un hipervínculo tappable. Tiene el color del texto establecido en azul, y tiene una decoración de texto
subrayado. Esto crea la apariencia de un hipervínculo, como se muestra en las capturas de pantalla siguiente:

Cuando se pulsa el hipervínculo, el TapGestureRecognizer responderá mediante la ejecución de la ICommand


definido por su Command propiedad. Además, la dirección URL especificada por el CommandParameter propiedad
se pasará a la ICommand como un parámetro.
El código subyacente para la página XAML contiene el TapCommand implementación:

public partial class MainPage : ContentPage


{
public ICommand TapCommand => new Command<string>(OpenBrowser);

public MainPage()
{
InitializeComponent();
BindingContext = this;
}

void OpenBrowser(string url)


{
Device.OpenUri(new Uri(url));
}
}
El TapCommand ejecuta el OpenBrowser método, pasando el TapGestureRecognizer.CommandParameter valor de
propiedad como un parámetro. A su vez, llama este método la Device.OpenUri método para abrir la dirección
URL en un explorador web. Por lo tanto, el efecto general es que cuando se pulsa el hipervínculo en la página,
aparece un explorador web y la dirección URL asociada con el hipervínculo se navega.
Creación de una clase reutilizable de hipervínculo
El enfoque anterior para crear un hipervínculo requiere escribir código repetitivo cada vez que requieren un
hipervínculo en la aplicación. Sin embargo, tanto el Label y Span pueden crear subclases de clases para crear
HyperlinkLabel y HyperlinkSpan clases, con el reconocedor de gestos y el código de formato de texto ha
agregado ahí.
El ejemplo de código siguiente, tomado de la demostraciones de hipervínculo ejemplo, se muestra un
HyperlinkSpan clase:

public class HyperlinkSpan : Span


{
public static readonly BindableProperty UrlProperty =
BindableProperty.Create(nameof(Url), typeof(string), typeof(HyperlinkSpan), null);

public string Url


{
get { return (string)GetValue(UrlProperty); }
set { SetValue(UrlProperty, value); }
}

public HyperlinkSpan()
{
TextDecorations = TextDecorations.Underline;
TextColor = Color.Blue;
GestureRecognizers.Add(new TapGestureRecognizer
{
Command = new Command(() => Device.OpenUri(new Uri(Url)))
});
}
}

El HyperlinkSpan clase define un Url propiedad y asociados BindableProperty , y el constructor establece el


aspecto de los hipervínculos y el TapGestureRecognizer que responderá Cuando se pulsa el hipervínculo. Cuando
un HyperlinkSpan se pulsa, el TapGestureRecognizer responderá mediante la ejecución de la Device.OpenUri
método para abrir la dirección URL, especificada por el Url propiedad en un explorador web.
La HyperlinkSpan clase puede utilizarse mediante la adición de una instancia de la clase para el XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:HyperlinkDemo"
x:Class="HyperlinkDemo.MainPage">
<StackLayout>
...
<Label>
<Label.FormattedText>
<FormattedString>
<Span Text="Alternatively, click " />
<local:HyperlinkSpan Text="here"
Url="https://docs.microsoft.com/appcenter/" />
<Span Text=" to view AppCenter documentation." />
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</ContentPage>

Etiquetas de estilo
Las secciones anteriores tratan la configuración Label y Span las propiedades de por instancia. Sin embargo,
los conjuntos de propiedades se pueden agrupar en un estilo que se aplica de manera coherente a una o varias
vistas. Esto puede mejorar la legibilidad del código y realizar cambios de diseño sea más fácil de implementar.
Para obtener más información, consulte estilos.

Vínculos relacionados
Texto (ejemplo)
Hipervínculos (ejemplo)
Creación de aplicaciones móviles con Xamarin.Forms, capítulo 3
API de etiqueta
Intervalo de API
Entrada de Xamarin.Forms
11/07/2019 • 17 minutes to read • Edit Online

descargar el ejemplo
Texto de una línea o la contraseña de entrada
Xamarin.Forms Entry se usa para la entrada de texto de una línea. El Entry , como el Editor ver, admite varios
tipos de teclado. Además, el Entry puede usarse como un campo de contraseña.

Personalización de la presentación
Establecimiento y lectura de texto
El Entry , al igual que otras vistas de presentación de texto, expone el Text propiedad. Esta propiedad se puede
utilizar para establecer y leer el texto presentado por el Entry . El ejemplo siguiente se muestra cómo establecer
el Text propiedad en XAML:

<Entry Text="I am an Entry" />

En C#:

var MyEntry = new Entry { Text = "I am an Entry" };

Para leer el texto, obtener acceso a la Text propiedad en C#:

var text = MyEntry.Text;

Establecer el texto de marcador de posición


El Entry puede establecerse para mostrar texto de marcador de posición cuando no está almacenando
proporcionados por el usuario. Esto se consigue estableciendo la Placeholder propiedad a un string y a
menudo se usa para indicar el tipo de contenido que sea adecuada para el Entry . Además, el color del texto de
marcador de posición puede controlarse estableciendo la PlaceholderColor propiedad a un Color :

<Entry Placeholder="Username" PlaceholderColor="Olive" />

var entry = new Entry { Placeholder = "Username", PlaceholderColor = Color.Olive };

NOTE
El ancho de un Entry puede definirse estableciendo su WidthRequest propiedad. No dependen del ancho de un Entry
que se define en función del valor de su Text propiedad.

Impedir la entrada de texto


Pueden ser impide que los usuarios modificar el texto en un Entry estableciendo el IsReadOnly propiedad, que
tiene un valor predeterminado de false a true :
<Entry Text="This is a read-only Entry"
IsReadOnly="true" />

var entry = new Entry { Text = "This is a read-only Entry", IsReadOnly = true });

NOTE
El IsReadonly propiedad no modifica la apariencia visual de un Entry , a diferencia el IsEnabled propiedad que
también cambia la apariencia visual de la Entry a gris.

Limitar la longitud de entrada


El MaxLength propiedad puede utilizarse para limitar la longitud de entrada que sea aceptable para el Entry .
Esta propiedad debe establecerse en un entero positivo:

<Entry ... MaxLength="10" />

var entry = new Entry { ... MaxLength = 10 };

Un MaxLength valor de propiedad de 0 indica que no se permitirá ninguna entrada y un valor de int.MaxValue ,
que es el valor predeterminado para un Entry , indica que no hay ninguna límite real del número de caracteres
que pueden especificarse.
Campos de contraseña
Entry proporciona el IsPassword propiedad. Cuando IsPassword es true , se mostrará el contenido del campo
como círculos negros:
En XAML:

<Entry IsPassword="true" />

En C#:

var MyEntry = new Entry { IsPassword = true };


Marcadores de posición se pueden usar con instancias de Entry que están configurados como campos de
contraseña:
En XAML:

<Entry IsPassword="true" Placeholder="Password" />

En C#:

var MyEntry = new Entry { IsPassword = true, Placeholder = "Password" };


Establecer la posición del Cursor y la longitud de la selección de texto
El CursorPosition propiedad puede usarse para devolver o establecer la posición en la que se insertará el
siguiente carácter en la cadena almacenada en el Text propiedad:

<Entry Text="Cursor position set" CursorPosition="5" />

var entry = new Entry { Text = "Cursor position set", CursorPosition = 5 };

El valor predeterminado de la CursorPosition propiedad es 0, lo que indica que se insertará el texto al principio
de la Entry .
Además, el SelectionLength propiedad puede usarse para devolver o establecer la longitud de selección de texto
dentro de la Entry :

<Entry Text="Cursor position and selection length set" CursorPosition="2" SelectionLength="10" />

var entry = new Entry { Text = "Cursor position and selection length set", CursorPosition = 2,
SelectionLength = 10 };

El valor predeterminado de la SelectionLength propiedad es 0, lo que indica que no hay texto seleccionado.
Personalizar el teclado
El teclado que aparece cuando los usuarios interactúan con un Entry se puede establecer mediante
programación a través de la Keyboard propiedad a una de las siguientes propiedades desde el Keyboard clase:
Chat : se usa para el texto y los lugares donde emoji son útiles.
Default : el teclado predeterminado.
Email : se usa al especificar direcciones de correo electrónico.
Numeric : se usa al escribir números.
Plain : se usa al escribir texto, sin ningún KeyboardFlags especificado.
Telephone : se usa al escribir números de teléfono.
Text : se usa al escribir texto.
Url : se usa para especificar las rutas de acceso de archivo y direcciones web.

Esto puede realizarse en XAML como sigue:

<Entry Keyboard="Chat" />

El código de C# equivalente es:

var entry = new Entry { Keyboard = Keyboard.Chat };

Ejemplos de cada teclado pueden encontrarse en nuestra recetas repositorio.


El Keyboard clase también tiene un Create método generador que puede usarse para personalizar un teclado
mediante la especificación del comportamiento de mayúsculas y minúsculas, corrector ortográfico y sugerencias.
Los valores de enumeración KeyboardFlags se especifican como argumentos para el método, con la devolución
de un Keyboard personalizado. La enumeración KeyboardFlags contiene los valores siguientes:
None : no se agregan características al teclado.
CapitalizeSentence : indica que la primera letra de la primera palabra de cada frase se escribirá
automáticamente en mayúsculas.
Spellcheck : indica que se pasará el corrector ortográfico al texto especificado.
Suggestions : indica que se ofrecerán finalizaciones de palabra para el texto especificado.
CapitalizeWord : indica que las primeras letras de todas las palabras se escribirán automáticamente en
mayúsculas.
CapitalizeCharacter : indica que todos los caracteres se escribirán automáticamente en mayúsculas.
CapitalizeNone : indica que no se producirá ningún uso automático de mayúsculas.
All : indica que se pasará el corrector automático, se ofrecerán finalizaciones de palabras y las frases
empezarán en mayúsculas en el texto especificado.
El ejemplo de código XAML siguiente muestra cómo personalizar el Keyboard predeterminado para ofrecer
finalizaciones de palabras y poner en mayúsculas todos los caracteres especificados:

<Entry Placeholder="Enter text here">


<Entry.Keyboard>
<Keyboard x:FactoryMethod="Create">
<x:Arguments>
<KeyboardFlags>Suggestions,CapitalizeCharacter</KeyboardFlags>
</x:Arguments>
</Keyboard>
</Entry.Keyboard>
</Entry>

El código de C# equivalente es:


var entry = new Entry { Placeholder = "Enter text here" };
entry.Keyboard = Keyboard.Create(KeyboardFlags.Suggestions | KeyboardFlags.CapitalizeCharacter);

Personalización de la tecla ENTRAR


La apariencia de la tecla ENTRAR en el teclado en pantalla, que es que se muestra cuando un Entry tiene el
foco, se pueden personalizar mediante el establecimiento del ReturnType propiedad con un valor de la
ReturnType enumeración:

Default : indica que se requiere ninguna clave de valor devuelta concreta y que se usará el valor
predeterminado de la plataforma.
Done : indica una clave de valor devuelta "Done".
Go : indica una clave de valor devuelta "Ir".
Next : indica una clave de valor devuelta "Siguiente".
Search : indica una clave de valor devuelta de "Búsqueda".
Send : indica una tecla de retorno del "Envío".

En el siguiente ejemplo XAML se muestra cómo establecer la tecla ENTRAR:

<Entry ReturnType="Send" />

El código de C# equivalente es:

var entry = new Entry { ReturnType = ReturnType.Send };

NOTE
El aspecto exacto de la clave de valor devuelto depende de la plataforma. En iOS, la clave de valor devuelta es un botón
basado en texto. Sin embargo, en plataformas de Windows Universal y Android, la clave devuelta es un botón de icono.

Cuando se presiona la tecla ENTRAR, el Completed desencadena el evento y cualquier ICommand especificado
por el ReturnCommand se ejecuta la propiedad. Además, cualquier object especificado por el
ReturnCommandParameter propiedad se pasará a la ICommand como un parámetro. Para obtener más información
acerca de los comandos, consulte la interfaz de comandos.
Habilitar y deshabilitar el corrector ortográfico
El IsSpellCheckEnabled propiedad controla si corrector ortográfico está habilitado. De forma predeterminada, la
propiedad se establece en true . Cuando el usuario escriba texto, se indican los errores de ortografía.
Sin embargo, para algunos escenarios de entrada de texto, como escribir un nombre de usuario, corrector
ortográfico proporciona una experiencia negativa y debe deshabilitarse estableciendo el IsSpellCheckEnabled
propiedad false :

<Entry ... IsSpellCheckEnabled="false" />

var entry = new Entry { ... IsSpellCheckEnabled = false };


NOTE
Cuando el IsSpellCheckEnabled propiedad está establecida en false y no se usa un teclado personalizado, se
deshabilitará el corrector ortográfico nativo. Sin embargo, si un Keyboard ha sido conjunto que deshabilita el corrector
comprobación, como Keyboard.Chat , el IsSpellCheckEnabled se omite. Por lo tanto, no se puede usar la propiedad
para habilitar el corrector ortográfico para un Keyboard que deshabilita de forma explícita.

Habilitación y deshabilitación de la predicción de texto


El IsTextPredictionEnabled propiedad controla si la predicción de texto y automática está habilitada la corrección
de texto. De forma predeterminada, la propiedad se establece en true . Cuando el usuario escriba texto, se
presentan las predicciones de word.
Sin embargo, para algunos escenarios de entrada de texto, como escribir un nombre de usuario, la predicción de
texto y texto automático corrección proporciona una experiencia negativa y debe deshabilitarse estableciendo el
IsTextPredictionEnabled propiedad false :

<Entry ... IsTextPredictionEnabled="false" />

var entry = new Entry { ... IsTextPredictionEnabled = false };

NOTE
Cuando el IsTextPredictionEnabled propiedad está establecida en false , y un teclado personalizado no es que se va
a usar, la predicción de texto y automática se deshabilita la corrección de texto. Sin embargo, si un Keyboard se ha
establecido que deshabilita la predicción de texto, el IsTextPredictionEnabled se omite. Por lo tanto, no se puede usar la
propiedad para habilitar la predicción de texto para un Keyboard que deshabilita de forma explícita.

Colores
Puede establecerse una entrada para usar un fondo personalizado y los colores de texto a través de las
propiedades enlazables siguientes:
TextColor – establece el color del texto.
BackgroundColor – establece el color que aparece detrás del texto.
Atención especial es necesaria para asegurarse de que los colores se podrán usar en cada plataforma. Dado que
cada plataforma tiene distintos valores predeterminados para los colores de texto y fondo, a menudo deberá
establecer ambos si establece uno.
Use el código siguiente para establecer el color del texto de una entrada:
En XAML:

<Entry TextColor="Green" />

En C#:

var entry = new Entry();


entry.TextColor = Color.Green;
Tenga en cuenta que el marcador de posición no está afectado por el TextColor .
Para establecer el color de fondo en XAML:

<Entry BackgroundColor="#2c3e50" />

En C#:

var entry = new Entry();


entry.BackgroundColor = Color.FromHex("#2c3e50");
Tenga cuidado para asegurarse de que los colores de fondo y de texto que elija puede utilizar en cada plataforma
y no interferir con cualquier texto de marcador de posición.

Eventos e interactividad
Entrada expone dos eventos:
TextChanged – se genera cuando cambia el texto en la entrada. Proporciona el texto antes y después del
cambio.
Completed – se genera cuando el usuario ha finalizado la entrada presionando la tecla ENTRAR del teclado.

NOTE
El VisualElement (clase), desde el que Entry hereda, también tiene Focused y Unfocused eventos.

Completada
El Completed evento sirve para reaccionar a la finalización de una interacción con una entrada. Completed se
produce cuando el usuario finaliza la entrada con un campo presionando la tecla ENTRAR del teclado (o
presionando la tecla Tab en UWP ). El controlador para el evento es un controlador de eventos genéricos,
tomando el remitente y EventArgs :

void Entry_Completed (object sender, EventArgs e)


{
var text = ((Entry)sender).Text; //cast sender to access the properties of the Entry
}

El evento completado se puede suscribir en XAML:


<Entry Completed="Entry_Completed" />

y C#:

var entry = new Entry ();


entry.Completed += Entry_Completed;

Después de la Completed evento se activa, cualquier ICommand especificado por el ReturnCommand se ejecuta la
propiedad, con el object especificado por el ReturnCommandParameter propiedad que se pasan a la ICommand .
TextChanged
El TextChanged evento sirve para reaccionar ante un cambio en el contenido de un campo.
TextChanged se produce cada vez que el Text de la Entry cambios. El controlador para el evento toma una
instancia de TextChangedEventArgs . TextChangedEventArgs proporciona acceso a los valores antiguos y nuevos de
la Entry Text a través de la OldTextValue y NewTextValue propiedades:

void Entry_TextChanged (object sender, TextChangedEventArgs e)


{
var oldText = e.OldTextValue;
var newText = e.NewTextValue;
}

El TextChanged se pueden suscribir a eventos en XAML:

<Entry TextChanged="Entry_TextChanged" />

y C#:

var entry = new Entry ();


entry.TextChanged += Entry_TextChanged;

Vínculos relacionados
Texto (ejemplo)
Entrada de API
Editor de Xamarin.Forms
11/07/2019 • 14 minutes to read • Edit Online

descargar el ejemplo
Entrada de texto multilínea
El Editor control se usa para aceptar la entrada de varias líneas. En este artículo se tratan los aspectos
siguientes:
Personalización – opciones de color y el teclado.
Interactividad – eventos que se pueden escuchar para proporcionar interactividad.

Personalización
Establecimiento y lectura de texto
El Editor , al igual que otras vistas de presentación de texto, expone el Text propiedad. Esta propiedad se
puede utilizar para establecer y leer el texto presentado por el Editor . El ejemplo siguiente se muestra cómo
establecer el Text propiedad en XAML:

<Editor Text="I am an Editor" />

En C#:

var MyEditor = new Editor { Text = "I am an Editor" };

Para leer el texto, obtener acceso a la Text propiedad en C#:

var text = MyEditor.Text;

Establecer el texto de marcador de posición


El Editor puede establecerse para mostrar texto de marcador de posición cuando no está almacenando
proporcionados por el usuario. Esto se consigue estableciendo la Placeholder propiedad a un string y a
menudo se usa para indicar el tipo de contenido que sea adecuada para el Editor . Además, el color del texto de
marcador de posición puede controlarse estableciendo la PlaceholderColor propiedad a un Color :

<Editor Placeholder="Enter text here" PlaceholderColor="Olive" />

var editor = new Editor { Placeholder = "Enter text here", PlaceholderColor = Color.Olive };

Impedir la entrada de texto


Pueden ser impide que los usuarios modificar el texto en un Editor estableciendo el IsReadOnly propiedad, que
tiene un valor predeterminado de false a true :
<Editor Text="This is a read-only Editor"
IsReadOnly="true" />

var editor = new Editor { Text = "This is a read-only Editor", IsReadOnly = true });

NOTE
El IsReadonly propiedad no modifica la apariencia visual de un Editor , a diferencia el IsEnabled propiedad que
también cambia la apariencia visual de la Editor a gris.

Limitar la longitud de entrada


El MaxLength propiedad puede utilizarse para limitar la longitud de entrada que sea aceptable para el Editor .
Esta propiedad debe establecerse en un entero positivo:

<Editor ... MaxLength="10" />

var editor = new Editor { ... MaxLength = 10 };

Un MaxLength valor de propiedad de 0 indica que no se permitirá ninguna entrada y un valor de int.MaxValue ,
que es el valor predeterminado para un Editor , indica que no hay ninguna límite real del número de caracteres
que pueden especificarse.
Un Editor de tamaño automático
Un Editor pueden realizarse en el ajuste de tamaño automático a su contenido estableciendo el
Editor.AutoSize propiedad TextChanges , que es un valor de la EditoAutoSizeOption enumeración. Esta
enumeración tiene dos valores:
Disabled indica que el cambio de tamaño automático está deshabilitado y es el valor predeterminado.
TextChanges indica que el cambio de tamaño automático está habilitado.

Esto puede realizarse en el código siguiente:

<Editor Text="Enter text here" AutoSize="TextChanges" />

var editor = new Editor { Text = "Enter text here", AutoSize = EditorAutoSizeOption.TextChanges };

Cuando el cambio de tamaño automático está habilitado, el alto de la Editor aumentará cuando el usuario lo
rellena con texto y se reducirá el alto como el usuario elimina el texto.

NOTE
Un Editor le if no redimensionar el HeightRequest se estableció la propiedad.

Personalizar el teclado
El teclado que aparece cuando los usuarios interactúan con un Editor se puede establecer mediante
programación a través de la Keyboard propiedad a una de las siguientes propiedades desde el Keyboard clase:
Chat : se usa para el texto y los lugares donde emoji son útiles.
Default : el teclado predeterminado.
Email : se usa al especificar direcciones de correo electrónico.
Numeric : se usa al escribir números.
Plain : se usa al escribir texto, sin ningún KeyboardFlags especificado.
Telephone : se usa al escribir números de teléfono.
Text : se usa al escribir texto.
Url : se usa para especificar las rutas de acceso de archivo y direcciones web.

Esto puede realizarse en XAML como sigue:

<Editor Keyboard="Chat" />

El código de C# equivalente es:

var editor = new Editor { Keyboard = Keyboard.Chat };

Ejemplos de cada teclado pueden encontrarse en nuestra recetas repositorio.


El Keyboard clase también tiene un Create método generador que puede usarse para personalizar un teclado
mediante la especificación del comportamiento de mayúsculas y minúsculas, corrector ortográfico y sugerencias.
Los valores de enumeración KeyboardFlags se especifican como argumentos para el método, con la devolución
de un Keyboard personalizado. La enumeración KeyboardFlags contiene los valores siguientes:
None : no se agregan características al teclado.
CapitalizeSentence : indica que la primera letra de la primera palabra de cada frase se escribirá
automáticamente en mayúsculas.
Spellcheck : indica que se pasará el corrector ortográfico al texto especificado.
Suggestions : indica que se ofrecerán finalizaciones de palabra para el texto especificado.
CapitalizeWord : indica que las primeras letras de todas las palabras se escribirán automáticamente en
mayúsculas.
CapitalizeCharacter : indica que todos los caracteres se escribirán automáticamente en mayúsculas.
CapitalizeNone : indica que no se producirá ningún uso automático de mayúsculas.
All : indica que se pasará el corrector automático, se ofrecerán finalizaciones de palabras y las frases
empezarán en mayúsculas en el texto especificado.
El ejemplo de código XAML siguiente muestra cómo personalizar el Keyboard predeterminado para ofrecer
finalizaciones de palabras y poner en mayúsculas todos los caracteres especificados:

<Editor>
<Editor.Keyboard>
<Keyboard x:FactoryMethod="Create">
<x:Arguments>
<KeyboardFlags>Suggestions,CapitalizeCharacter</KeyboardFlags>
</x:Arguments>
</Keyboard>
</Editor.Keyboard>
</Editor>

El código de C# equivalente es:


var editor = new Editor();
editor.Keyboard = Keyboard.Create(KeyboardFlags.Suggestions | KeyboardFlags.CapitalizeCharacter);

Habilitar y deshabilitar el corrector ortográfico


El IsSpellCheckEnabled propiedad controla si corrector ortográfico está habilitado. De forma predeterminada, la
propiedad se establece en true . Cuando el usuario escriba texto, se indican los errores de ortografía.
Sin embargo, algunos escenarios de entrada de texto, como escribir un nombre de usuario, corrector ortográfico
ofrece una experiencia negativa de modo que deben deshabilitarse estableciendo el IsSpellCheckEnabled
propiedad false :

<Editor ... IsSpellCheckEnabled="false" />

var editor = new Editor { ... IsSpellCheckEnabled = false };

NOTE
Cuando el IsSpellCheckEnabled propiedad está establecida en false y no se usa un teclado personalizado, se
deshabilitará el corrector ortográfico nativo. Sin embargo, si un Keyboard ha sido conjunto que deshabilita el corrector
comprobación, como Keyboard.Chat , el IsSpellCheckEnabled se omite. Por lo tanto, no se puede usar la propiedad
para habilitar el corrector ortográfico para un Keyboard que deshabilita de forma explícita.

Habilitación y deshabilitación de la predicción de texto


El IsTextPredictionEnabled propiedad controla si la predicción de texto y automática está habilitada la corrección
de texto. De forma predeterminada, la propiedad se establece en true . Cuando el usuario escriba texto, se
presentan las predicciones de word.
Sin embargo, para algunos escenarios de entrada de texto, como escribir un nombre de usuario, la predicción de
texto y texto automático corrección proporciona una experiencia negativa y debe deshabilitarse estableciendo el
IsTextPredictionEnabled propiedad false :

<Editor ... IsTextPredictionEnabled="false" />

var editor = new Editor { ... IsTextPredictionEnabled = false };

NOTE
Cuando el IsTextPredictionEnabled propiedad está establecida en false , y un teclado personalizado no es que se va
a usar, la predicción de texto y automática se deshabilita la corrección de texto. Sin embargo, si un Keyboard se ha
establecido que deshabilita la predicción de texto, el IsTextPredictionEnabled se omite. Por lo tanto, no se puede usar la
propiedad para habilitar la predicción de texto para un Keyboard que deshabilita de forma explícita.

Colores
Editor puede establecerse para usar un color de fondo a través de la BackgroundColor propiedad. Atención
especial es necesaria para asegurarse de que los colores se podrán usar en cada plataforma. Dado que cada
plataforma tiene distintos valores predeterminados para el color de texto, deberá establecer un color de fondo
personalizado para cada plataforma. Consulte trabajar con ajustes de la plataforma para obtener más
información sobre la optimización de la interfaz de usuario para cada plataforma.
En C#:

public partial class EditorPage : ContentPage


{
public EditorPage ()
{
InitializeComponent ();
var layout = new StackLayout { Padding = new Thickness(5,10) };
this.Content = layout;
//dark blue on UWP & Android, light blue on iOS
var editor = new Editor { BackgroundColor = Device.RuntimePlatform == Device.iOS ?
Color.FromHex("#A4EAFF") : Color.FromHex("#2c3e50") };
layout.Children.Add(editor);
}
}

En XAML:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TextSample.EditorPage"
Title="Editor Demo">
<ContentPage.Content>
<StackLayout Padding="5,10">
<Editor>
<Editor.BackgroundColor>
<OnPlatform x:TypeArguments="x:Color">
<On Platform="iOS" Value="#a4eaff" />
<On Platform="Android, UWP" Value="#2c3e50" />
</OnPlatform>
</Editor.BackgroundColor>
</Editor>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Asegúrese de que los colores de fondo y de texto que elija puede utilizar en cada plataforma y no interferir con
cualquier texto de marcador de posición.

Interactividad
Editor expone dos eventos:
TextChanged – se genera cuando cambia el texto en el editor. Proporciona el texto antes y después del cambio.
Completado – se genera cuando el usuario ha finalizado la entrada presionando la tecla ENTRAR del teclado.

NOTE
El VisualElement (clase), desde el que Entry hereda, también tiene Focused y Unfocused eventos.

Completada
El Completed evento sirve para reaccionar a la finalización de una interacción con un Editor . Completed se
produce cuando el usuario finaliza la entrada con un campo especificando la tecla ENTRAR del teclado (o
presionando la tecla Tab en UWP ). El controlador para el evento es un controlador de eventos genéricos,
tomando el remitente y EventArgs :

void EditorCompleted (object sender, EventArgs e)


{
var text = ((Editor)sender).Text; // sender is cast to an Editor to enable reading the `Text` property of
the view.
}

En el código y XAML, se puede suscribir el evento completado para:


En C#:

public partial class EditorPage : ContentPage


{
public EditorPage ()
{
InitializeComponent ();
var layout = new StackLayout { Padding = new Thickness(5,10) };
this.Content = layout;
var editor = new Editor ();
editor.Completed += EditorCompleted;
layout.Children.Add(editor);
}
}

En XAML:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TextSample.EditorPage"
Title="Editor Demo">
<ContentPage.Content>
<StackLayout Padding="5,10">
<Editor Completed="EditorCompleted" />
</StackLayout>
</ContentPage.Content>
</Contentpage>

TextChanged
El TextChanged evento sirve para reaccionar ante un cambio en el contenido de un campo.
TextChanged se produce cada vez que el Text de la Editor cambios. El controlador para el evento toma una
instancia de TextChangedEventArgs . TextChangedEventArgs proporciona acceso a los valores antiguos y nuevos de
la Editor Text a través de la OldTextValue y NewTextValue propiedades:

void EditorTextChanged (object sender, TextChangedEventArgs e)


{
var oldText = e.OldTextValue;
var newText = e.NewTextValue;
}

En el código y XAML, se puede suscribir el evento completado para:


Mediante código:

public partial class EditorPage : ContentPage


{
public EditorPage ()
{
InitializeComponent ();
var layout = new StackLayout { Padding = new Thickness(5,10) };
this.Content = layout;
var editor = new Editor ();
editor.TextChanged += EditorTextChanged;
layout.Children.Add(editor);
}
}
En XAML:

<?xml version="1.0" encoding="UTF-8"?>


<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TextSample.EditorPage"
Title="Editor Demo">
<ContentPage.Content>
<StackLayout Padding="5,10">
<Editor TextChanged="EditorTextChanged" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

Vínculos relacionados
Texto (ejemplo)
Editor de API
Fuentes de Xamarin.Forms
11/07/2019 • 12 minutes to read • Edit Online

descargar el ejemplo
Este artículo describe cómo Xamarin.Forms le permite especificar atributos de fuente (incluidas peso y
tamaño) en los controles que muestran texto. Puede ser información de fuente especificado en el código o
especificado en XAML. Tiene ' también es posible utilizar un fuente personalizada, y mostrar iconos de fuente.

Establecer la fuente en el código


Use las tres propiedades relacionadas con la fuente de todos los controles que muestran texto:
FontFamily – el string nombre de la fuente.
FontSize – el tamaño de fuente como un double .
Atributos de fuente – una cadena que especifica la información de estilo como cursiva y negrita
(mediante el FontAttributes enumeración en C#).

Este código muestra cómo crear una etiqueta y especifique el tamaño de fuente y el peso para mostrar:

var about = new Label {


FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
FontAttributes = FontAttributes.Bold,
Text = "Medium Bold Font"
};

Tamaño de fuente
El FontSize propiedad puede establecerse en un valor double, por ejemplo:

label.FontSize = 24;

Xamarin.Forms también define los campos de la NamedSize enumeración que representan los tamaños de
fuente específico. Para obtener más información acerca de los tamaños de fuente con nombre, vea
denominado tamaños de fuente.
Atributos de fuente
Estilos de fuente como negrita y cursiva se pueden establecer en el FontAttributes propiedad. Actualmente
se admiten los siguientes valores:
Ninguno
Negrita
Cursiva
El FontAttribute enumeración se puede usar como sigue (se puede especificar un atributo único o OR
juntarlas):

label.FontAttributes = FontAttributes.Bold | FontAttributes.Italic;

Conjunto de información de fuente por plataforma


Como alternativa, el Device.RuntimePlatform propiedad puede usarse para establecer los nombres de fuente
diferente en cada plataforma, como se muestra en este código:

label.FontFamily = Device.RuntimePlatform == Device.iOS ? "Lobster-Regular" :


Device.RuntimePlatform == Device.Android ? "Lobster-Regular.ttf#Lobster-Regular" :
"Assets/Fonts/Lobster-Regular.ttf#Lobster",
label.FontSize = Device.RuntimePlatform == Device.iOS ? 24 :
Device.RuntimePlatform == Device.Android ? Device.GetNamedSize(NamedSize.Medium, label) :
Device.GetNamedSize(NamedSize.Large, label);

Es una buena fuente de información de fuentes para iOS iosfonts.com.

Establezca la fuente en XAML


Xamarin.Forms controla ese texto para mostrar todos tienen un FontSize propiedad que se puede establecer
en XAML. La manera más sencilla para establecer la fuente en XAML es usar los valores de enumeración de
tamaño con nombre, como se muestra en este ejemplo:

<Label Text="Login" FontSize="Large"/>


<Label Text="Instructions" FontSize="Small"/>

Hay un convertidor de tipos integrado para el FontSize propiedad que permite que todos los valores de
fuente se exprese como un valor de cadena en XAML. Además, el FontAttributes propiedad puede utilizarse
para especificar atributos de fuente:

<Label Text="Italics are supported" FontAttributes="Italic" />


<Label Text="Biggest NamedSize" FontSize="Large" />
<Label Text="Use size 72" FontSize="72" />

Device.RuntimePlatform También se puede utilizar en XAML para representar una fuente diferente en cada
plataforma. El ejemplo siguiente utiliza un nombre de fuente personalizados en iOS (MarkerFelt Thin) y
especifica solo tamaño y atributos en las otras plataformas:

<Label Text="Hello Forms with XAML">


<Label.FontFamily>
<OnPlatform x:TypeArguments="x:String">
<On Platform="iOS" Value="MarkerFelt-Thin" />
<On Platform="Android" Value="Lobster-Regular.ttf#Lobster-Regular" />
<On Platform="UWP" Value="Assets/Fonts/Lobster-Regular.ttf#Lobster" />
</OnPlatform>
</Label.FontFamily>
</Label>

Al especificar un nombre de fuente personalizada, siempre es una buena idea usar OnPlatform , ya que es
difícil encontrar una fuente que está disponible en todas las plataformas.

Tamaños de fuente con nombre


Xamarin.Forms define los campos de la NamedSize enumeración que representan los tamaños de fuente
específico. La tabla siguiente muestra el NamedSize miembros y sus tamaños predeterminados en iOS,
Android y la plataforma Universal de Windows (UWP ):
MIEMBRO IOS ANDROID UWP

Default 16 14 14

Micro 11 10 15.667

Small 13 14 18.667

Medium 16 17 22.667

Large 20 22 32

Body 17 16 14

Header 17 96 46

Title 28 24 24

Subtitle 22 16 20

Caption 12 12 12

Los tamaños de fuente con nombre se pueden establecer mediante XAML y código. Además, el
Device.GetNamedSize se puede llamar el método para devolver un double que representa el tamaño de fuente
con nombre:

label.FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label));

NOTE
En iOS y Android, tamaños de fuente con nombre realizará el escalado automático en función de las opciones de
accesibilidad del sistema operativo. Este comportamiento se puede deshabilitar en iOS con una plataforma específica.
Para obtener más información, consulte accesibilidad escalado denominada tamaños de fuente en iOS.

Usar una fuente personalizada


El uso de una fuente que no sea de los tipos de letra integrados requiere algún código específico de la
plataforma. Esta captura de pantalla muestra la fuente personalizada Lobster desde fuentes de código abierto
de Google representar mediante Xamarin.Forms.

A continuación, se describen los pasos necesarios para cada plataforma. Cuando se incluyen archivos de
fuente personalizada con una aplicación, asegúrese de comprobar que permite la licencia de la fuente de
distribución.
iOS
Es posible mostrar una fuente personalizada en primer lugar, lo que garantiza que se carga y, luego, que hace
referencia a él por su nombre mediante Xamarin.Forms Font métodos. Siga las instrucciones de esta entrada
de blog:
1. Agregar el archivo de fuentes con acción de compilación: BundleResource, y
2. Actualización de la Info.plist archivo (fuentes proporcionadas por la aplicación, o UIAppFonts , key), a
continuación,
3. Hacer referencia al mismo nombre siempre que defina una fuente de Xamarin.Forms.

new Label
{
Text = "Hello, Forms!",
FontFamily = Device.RuntimePlatform == Device.iOS ? "Lobster-Regular" : null // set only for iOS
}

Android
Xamarin.Forms para Android puede hacer referencia a una fuente personalizada que se ha agregado al
proyecto siguiendo un estándar de nomenclatura. Primero agregue el archivo de fuente a la activos carpeta
en el proyecto de aplicación y establezca acción de compilación: AndroidAsset. A continuación, utilice la ruta de
acceso completa y nombre de la fuente separados por una almohadilla (#) como el nombre de fuente en
Xamarin.Forms, como se muestra en el siguiente fragmento de código:

new Label
{
Text = "Hello, Forms!",
FontFamily = Device.RuntimePlatform == Device.Android ? "Lobster-Regular.ttf#Lobster-Regular" : null //
set only for Android
}

Windows
Xamarin.Forms para plataformas de Windows puede hacer referencia a una fuente personalizada que se ha
agregado al proyecto siguiendo un estándar de nomenclatura. Primero agregue el archivo de fuente a la
/Assets/fuentes/ carpeta en el proyecto de aplicación y establezca el compilar: contenido de la acción. A
continuación, utilice la fuente y la ruta de acceso de nombre de archivo completo, seguido por una almohadilla
(#) y el nombre de la fuente, tal y como se muestra el siguiente fragmento de código:

new Label
{
Text = "Hello, Forms!",
FontFamily = Device.RuntimePlatform == Device.UWP ? "Assets/Fonts/Lobster-Regular.ttf#Lobster" : null
// set only for UWP apps
}

NOTE
Tenga en cuenta que el nombre de archivo de fuente y el nombre de fuente pueden ser diferentes. Para conocer el
nombre de fuente en Windows, haga clic en el archivo .ttf y seleccione Preview. A continuación, se puede determinar el
nombre de fuente desde la ventana Vista previa.

El código común de la aplicación ya está completo. El código del marcador de teléfono específico de la
plataforma se implementará como DependencyService.
XAML
También puede usar Device.RuntimePlatform en XAML para representar una fuente personalizada:
<Label Text="Hello Forms with XAML">
<Label.FontFamily>
<OnPlatform x:TypeArguments="x:String">
<On Platform="iOS" Value="Lobster-Regular" />
<On Platform="Android" Value="Lobster-Regular.ttf#Lobster-Regular" />
<On Platform="UWP" Value="Assets/Fonts/Lobster-Regular.ttf#Lobster" />
</OnPlatform>
</Label.FontFamily>
</Label>

Mostrar iconos de fuente


Iconos de la fuente pueden mostrar las aplicaciones de Xamarin.Forms mediante la especificación de los datos
del icono de fuente en un FontImageSource objeto. Esta clase, que se deriva de la ImageSource class, tiene las
siguientes propiedades:
Glyph : el valor de carácter unicode del icono de fuente, especificado como un string .
Size : un double valor que indica el tamaño, en unidades independientes del dispositivo, del icono de la
fuente representada. El valor predeterminado es 30.
FontFamily : un string que representa la familia de fuentes a la que pertenece el icono de fuente.
Color – opcional Color valor que se usará al mostrar el icono de fuente.

Estos datos se usan para crear un archivo PNG, que puede mostrarse en cualquier vista que puede mostrar un
ImageSource . Este enfoque permite que los iconos de la fuente, como los emojis, que va a mostrar varias
vistas, en lugar de limitar la presentación de iconos de fuente para un solo texto, ya que presenta la vista, como
un Label .

IMPORTANT
Actualmente solo se pueden especificar iconos de la fuente por su representación de caracteres unicode.

En el siguiente ejemplo XAML tiene un icono de única fuente mostrado por un Image vista:

<Image BackgroundColor="#D1D1D1">
<Image.Source>
<FontImageSource Glyph="&#xf30c;"
FontFamily="{OnPlatform iOS=Ionicons, Android=ionicons.ttf#}"
Size="44" />
</Image.Source>
</Image>

Este código muestra un icono de XBox, desde la familia de fuentes Ionicons, en un Image vista. Tenga en
cuenta que mientras el unicode de caracteres de este icono es \uf30c , tiene que ser caracteres de escape en
XAML y por lo que se convierte en &#xf30c; . El código de C# equivalente es:

Image image = new Image { BackgroundColor = Color.FromHex("#D1D1D1") };


image.Source = new FontImageSource
{
Glyph = "\uf30c",
FontFamily = Device.RuntimePlatform == Device.iOS ? "Ionicons" : "ionicons.ttf#",
Size = 44
};

Las siguientes capturas de pantalla, desde el diseños enlazable de ejemplo, se muestran varios iconos de
fuente mostrados por un diseño enlazable:
Vínculos relacionados
FontsSample
Texto (ejemplo)
Diseños enlazables (ejemplo)
Diseños enlazables
Estilos de texto de Xamarin.Forms
11/07/2019 • 4 minutes to read • Edit Online

descargar el ejemplo
Estilos de texto en Xamarin.Forms
Los estilos se pueden usar para ajustar la apariencia de las etiquetas, las entradas y editores. Estilos pueden
definirse una vez y utilizados por muchas vistas, pero solo puede usarse un estilo con vistas de un tipo. Los estilos
se pueden proporcionar un Key y aplicar selectivamente mediante un control específico Style propiedad.

Estilos integrados
Xamarin.Forms incluye varios integrada estilos para escenarios comunes:
BodyStyle
CaptionStyle
ListItemDetailTextStyle
ListItemTextStyle
SubtitleStyle
TitleStyle

Para aplicar uno de los estilos integrados, use el DynamicResource extensión de marcado para especificar el estilo:

<Label Text="I'm a Title" Style="{DynamicResource TitleStyle}"/>

En C#, se seleccionan los estilos integrados de Device.Styles :

label.Style = Device.Styles.TitleStyle;
Estilos personalizados
Estilos constan de establecedores y constan de establecedores de propiedades y los valores de las propiedades se
establecerá en.
En C#, un estilo personalizado para una etiqueta con texto rojo de tamaño 30 se definiría como sigue:

var LabelStyle = new Style (typeof(Label)) {


Setters = {
new Setter {Property = Label.TextColorProperty, Value = Color.Red},
new Setter {Property = Label.FontSizeProperty, Value = 30}
}
};

var label = new Label { Text = "Check out my style.", Style = LabelStyle };

En XAML:
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="TextColor" Value="Red"/>
<Setter Property="FontSize" Value="30"/>
</Style>
</ResourceDictionary>
</ContentPage.Resources>

<ContentPage.Content>
<StackLayout>
<Label Text="Check out my style." Style="{StaticResource LabelStyle}" />
</StackLayout>
</ContentPage.Content>

Tenga en cuenta que los recursos (incluidos todos los estilos) se definen dentro de ContentPage.Resources , que es
un elemento relacionado de cuanto más se familiarice ContentPage.Content elemento.

Aplicar estilos
Una vez que se ha creado un estilo, se puede aplicar a cualquier coincidencia de vista su TargetType .
En XAML, los estilos personalizados se aplican a vistas proporcionando sus Style propiedad con un
StaticResource el estilo deseado de referencia de extensión de marcado:

<Label Text="Check out my style." Style="{StaticResource LabelStyle}" />

En C#, estilos pueden se aplican directamente a una vista o agrega a y recuperarse de una página
ResourceDictionary . Para agregar directamente:
var label = new Label { Text = "Check out my style.", Style = LabelStyle };

Para agregar y recuperar desde la página ResourceDictionary :

this.Resources.Add ("LabelStyle", LabelStyle);


label.Style = (Style)Resources["LabelStyle"];

Estilos integrados se aplican de forma diferente, ya que se deben responder a la configuración de accesibilidad.
Para aplicar los estilos integrados en XAML, el DynamicResource se utiliza la extensión de marcado:

<Label Text="I'm a Title" Style="{DynamicResource TitleStyle}"/>

En C#, se seleccionan los estilos integrados de Device.Styles :

label.Style = Device.Styles.TitleStyle;

Accesibilidad
Los estilos integrados existen para que sea más fácil respetar las preferencias de accesibilidad. Al utilizar
cualquiera de los estilos integrados, los tamaños de fuente aumentará automáticamente si un usuario establece
las preferencias de accesibilidad en consecuencia.
Considere el siguiente ejemplo de la misma página de vistas de un estilo con los estilos integrados con la
configuración de accesibilidad habilitados y deshabilitados:
Deshabilitado:
Habilitado:

Para garantizar la accesibilidad, asegúrese de que los estilos integrados se utilizan como base para cualquier estilo
relacionada con el texto dentro de la aplicación y que está utilizando estilos de forma coherente. Consulte estilos
para obtener más detalles sobre la extensión y trabajar con estilos en general.

Vínculos relacionados
Creación de aplicaciones móviles con Xamarin.Forms, capítulo 12
Estilos
Texto (ejemplo)
Estilo
Temas de Xamarin.Forms
12/07/2019 • 3 minutes to read • Edit Online

descargar el ejemplo

Los temas de Xamarin.Forms se anunciaron en Evolve 2016 y están disponibles como una vista previa para
clientes probar y proporcionar comentarios.
Un tema se agrega a una aplicación de Xamarin.Forms mediante la inclusión de la
Xamarin.Forms.Theme.Base de paquetes de Nuget, además de un paquete adicional que define un tema
concreto (p ej. Xamarin.Forms.Theme.Light) o de lo contrario, se puede definir un tema local para la aplicación.
Hacer referencia a la el tema claro y tema oscuro páginas para obtener instrucciones sobre cómo agregarlos a
una aplicación, o visite el tema personalizado de ejemplo.

IMPORTANT
También debe seguir los pasos necesarios para cargar ensamblados de tema (abajo) agregando código estereotipado a
iOS AppDelegate y Android MainActivity . Se mejorará en versión preliminar futura.

Apariencia del control


El luz y oscuro temas ambos definen una apariencia visual específica para los controles estándar. Una vez que
agregue un tema para el diccionario de recursos de la aplicación, cambiará la apariencia de los controles
estándar.
El marcado XAML siguiente muestra algunos controles comunes:

<StackLayout Padding="40">
<Label Text="Regular label" />
<Entry Placeholder="type here" />
<Button Text="OK" />
<BoxView Color="Yellow" />
<Switch />
</StackLayout>

Estas capturas de pantalla muestran estos controles con:


Ningún tema aplicado
Tema claro (solo diferencias sutiles a no tener tema)
Tema oscuro
StyleClass
El StyleClass propiedad permite la apariencia de una vista que se puede cambiar según una definición de un
tema.
El luz y oscuro temas ambos definen tres diferentes aspectos de un BoxView : HorizontalRule , Circle , y
Rounded . Este marcado muestra tres diferentes BoxView s con clases diferentes de estilo aplicado:

<StackLayout Padding="40">
<BoxView StyleClass="HorizontalRule" />
<BoxView StyleClass="Circle" />
<BoxView StyleClass="Rounded" />
</StackLayout>

Esto se representa con claro y oscuro como sigue:

Clases integradas
Además de aplicar un estilo a automáticamente común controla la luz y los temas oscuros actualmente admiten
las siguientes clases que se pueden aplicar mediante el establecimiento del StyleClass en estos controles:
BoxView
HorizontalRule
Círculo
Redondeado
Image
Círculo
Redondeado
Vista en miniatura
Button
Default
Principal
Correcto
Info
Advertencia
Peligro
Vínculo
Pequeño
Grandes
Label
Header
Subencabezado
Cuerpo
Vínculo
Inverso

Solución de problemas
No se pudo cargar el archivo o ensamblado 'Xamarin.Forms.Theme.Light' o uno de sus dependencias
En la versión preliminar, los temas no pueda cargar en tiempo de ejecución. Agregue el código que se muestra
a continuación, en los proyectos correspondientes para corregir este error.
iOS
En el AppDelegate.cs agregue las líneas siguientes después de LoadApplication

var x = typeof(Xamarin.Forms.Themes.DarkThemeResources);
x = typeof(Xamarin.Forms.Themes.LightThemeResources);
x = typeof(Xamarin.Forms.Themes.iOS.UnderlineEffect);

Android
En el MainActivity.cs agregue las líneas siguientes después de LoadApplication

var x = typeof(Xamarin.Forms.Themes.DarkThemeResources);
x = typeof(Xamarin.Forms.Themes.LightThemeResources);
x = typeof(Xamarin.Forms.Themes.Android.UnderlineEffect);

Vínculos relacionados
Ejemplo de ThemesDemo
Tema claro de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

NOTE
Los temas requieren la versión preliminar de Xamarin.Forms 2.3. Compruebe el sugerencias para solucionar problemas si se
producen errores.

Para utilizar el tema claro:

1. Agregar paquetes de Nuget


Xamarin.Forms.Theme.Base
Xamarin.Forms.Theme.Light

2. Agregar al diccionario de recursos


En el App.xaml archivo agregar un nuevo personalizado xmlns para el tema y, a continuación, asegúrese de los
recursos del tema se combinan con el diccionario de recursos de la aplicación. Un ejemplo del archivo XAML se
muestra a continuación:

<?xml version="1.0" encoding="utf-8"?>


<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="EvolveApp.App"
xmlns:light="clr-namespace:Xamarin.Forms.Themes;assembly=Xamarin.Forms.Theme.Light">
<Application.Resources>
<ResourceDictionary MergedWith="light:LightThemeResources" />
</Application.Resources>
</Application>

3. Carga las clases de tema


Siga este solución de problemas paso y agregue el código necesario en los proyectos de aplicación de Android y
iOS.

4. Usar StyleClass
Este es un ejemplo de botones y etiquetas en el tema claro, junto con el marcado que los genera.
<StackLayout Padding="20">
<Button Text="Button Default" />
<Button Text="Button Class Default" StyleClass="Default" />
<Button Text="Button Class Primary" StyleClass="Primary" />
<Button Text="Button Class Success" StyleClass="Success" />
<Button Text="Button Class Info" StyleClass="Info" />
<Button Text="Button Class Warning" StyleClass="Warning" />
<Button Text="Button Class Danger" StyleClass="Danger" />
<Button Text="Button Class Link" StyleClass="Link" />
<Button Text="Button Class Default Small" StyleClass="Small" />
<Button Text="Button Class Default Large" StyleClass="Large" />
</StackLayout>

El lista completa de las clases integradas muestra qué estilos están disponibles para algunos controles comunes.
Tema oscuro de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

NOTE
Los temas requieren la versión preliminar de Xamarin.Forms 2.3. Compruebe el sugerencias para solucionar problemas si se
producen errores.

Para usar el tema oscuro:

1. Agregar paquetes de Nuget


Xamarin.Forms.Theme.Base
Xamarin.Forms.Theme.Dark

2. Agregar al diccionario de recursos


En el App.xaml archivo agregar un nuevo personalizado xmlns para el tema y, a continuación, asegúrese de los
recursos del tema se combinan con el diccionario de recursos de la aplicación. Un ejemplo del archivo XAML se
muestra a continuación:

<?xml version="1.0" encoding="utf-8"?>


<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="EvolveApp.App"
xmlns:dark="clr-namespace:Xamarin.Forms.Themes;assembly=Xamarin.Forms.Theme.Dark">
<Application.Resources>
<ResourceDictionary MergedWith="dark:DarkThemeResources" />
</Application.Resources>
</Application>

3. Carga las clases de tema


Siga este solución de problemas paso y agregue el código necesario en los proyectos de aplicación de Android y
iOS.

4. Usar StyleClass
Este es un ejemplo de botones y etiquetas en el tema oscuro, junto con el marcado que los genera.
<StackLayout Padding="20">
<Button Text="Button Default" />
<Button Text="Button Class Default" StyleClass="Default" />
<Button Text="Button Class Primary" StyleClass="Primary" />
<Button Text="Button Class Success" StyleClass="Success" />
<Button Text="Button Class Info" StyleClass="Info" />
<Button Text="Button Class Warning" StyleClass="Warning" />
<Button Text="Button Class Danger" StyleClass="Danger" />
<Button Text="Button Class Link" StyleClass="Link" />

<Button Text="Button Class Default Small" StyleClass="Small" />


<Button Text="Button Class Default Large" StyleClass="Large" />
</StackLayout>

El lista completa de las clases integradas muestra qué estilos están disponibles para algunos controles comunes.
Crear un tema personalizado de Xamarin.Forms
11/07/2019 • 4 minutes to read • Edit Online

Además de agregar un tema de un paquete Nuget (como el luz y oscuro temas), puede crear su propio recurso de
temas de diccionario que se pueden hacer referencia en la aplicación.

Ejemplo
Los tres BoxView s que se muestra en el página temas tienen un estilo según tres clases definidas en los dos temas
descargables.
Para entender cómo funcionan, el marcado siguiente crea un estilo equivalente que se puede agregar directamente
a su App.xaml.
Tenga en cuenta la Class atributo Style (en contraposición a la x:Key disponible en versiones anteriores de
Xamarin.Forms (atributo).

<ResourceDictionary>
<!-- DEFINE ANY CONSTANTS -->
<Color x:Key="SeparatorLineColor">#CCCCCC</Color>
<Color x:Key="iOSDefaultTintColor">#007aff</Color>
<Color x:Key="AndroidDefaultAccentColorColor">#1FAECE</Color>
<OnPlatform x:TypeArguments="Color" x:Key="AccentColor">
<On Platform="iOS" Value="{StaticResource iOSDefaultTintColor}" />
<On Platform="Android" Value="{StaticResource AndroidDefaultAccentColorColor}" />
</OnPlatform>
<!-- BOXVIEW CLASSES -->
<Style TargetType="BoxView" Class="HorizontalRule">
<Setter Property="BackgroundColor" Value="{ StaticResource SeparatorLineColor }" />
<Setter Property="HeightRequest" Value="1" />
</Style>

<Style TargetType="BoxView" Class="Circle">


<Setter Property="BackgroundColor" Value="{ StaticResource AccentColor }" />
<Setter Property="WidthRequest" Value="34"/>
<Setter Property="HeightRequest" Value="34"/>
<Setter Property="HorizontalOptions" Value="Start" />

<Setter Property="local:ThemeEffects.Circle" Value="True" />


</Style>

<Style TargetType="BoxView" Class="Rounded">


<Setter Property="BackgroundColor" Value="{ StaticResource AccentColor }" />
<Setter Property="HorizontalOptions" Value="Start" />
<Setter Property="BackgroundColor" Value="{ StaticResource AccentColor }" />

<Setter Property="local:ThemeEffects.CornerRadius" Value="4" />


</Style>
</ResourceDictionary>

Observará que el Rounded clase hace referencia a un efecto personalizado CornerRadius . El código para este
efecto se indica a continuación: hacer referencia a él correctamente un personalizado xmlns deben agregarse a la
App.xamldel elemento raíz:

xmlns:local="clr-namespace:ThemesDemo;assembly=ThemesDemo"

Código C# en el proyecto de biblioteca estándar de .NET o un proyecto compartido


El código para la creación de una esquina round BoxView usa efectos. El radio de redondeo se aplica mediante un
BindableProperty y se implementa aplicando un efecto. El efecto requiere código específico de plataforma en la
iOS y Android proyectos (se muestra a continuación).

namespace ThemesDemo
{
public static class ThemeEffects
{
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.CreateAttached("CornerRadius", typeof(double), typeof(ThemeEffects), 0.0,
propertyChanged: OnChanged<CornerRadiusEffect, double>);
private static void OnChanged<TEffect, TProp>(BindableObject bindable, object oldValue, object newValue)
where TEffect : Effect, new()
{
if (!(bindable is View view))
{
return;
}

if (EqualityComparer<TProp>.Equals(newValue, default(TProp)))
{
var toRemove = view.Effects.FirstOrDefault(e => e is TEffect);
if (toRemove != null)
{
view.Effects.Remove(toRemove);
}
}
else
{
view.Effects.Add(new TEffect());
}

}
public static void SetCornerRadius(BindableObject view, double radius)
{
view.SetValue(CornerRadiusProperty, radius);
}

public static double GetCornerRadius(BindableObject view)


{
return (double)view.GetValue(CornerRadiusProperty);
}

private class CornerRadiusEffect : RoutingEffect


{
public CornerRadiusEffect()
: base("Xamarin.CornerRadiusEffect")
{
}
}
}
}

Código C# en el proyecto de iOS


using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using CoreGraphics;
using Foundation;
using XFThemes;

namespace ThemesDemo.iOS
{
public class CornerRadiusEffect : PlatformEffect
{
private nfloat _originalRadius;

protected override void OnAttached()


{
if (Container != null)
{
_originalRadius = Container.Layer.CornerRadius;
Container.ClipsToBounds = true;

UpdateCorner();
}
}

protected override void OnDetached()


{
if (Container != null)
{
Container.Layer.CornerRadius = _originalRadius;
Container.ClipsToBounds = false;
}
}

protected override void OnElementPropertyChanged(System.ComponentModel.PropertyChangedEventArgs args)


{
base.OnElementPropertyChanged(args);

if (args.PropertyName == ThemeEffects.CornerRadiusProperty.PropertyName)
{
UpdateCorner();
}
}

private void UpdateCorner()


{
Container.Layer.CornerRadius = (nfloat)ThemeEffects.GetCornerRadius(Element);
}
}
}

Código C# en el proyecto de Android


using System;
using Xamarin.Forms.Platform;
using Xamarin.Forms.Platform.Android;
using Android.Views;
using Android.Graphics;

namespace ThemesDemo.Droid
{
public class CornerRadiusEffect : BaseEffect
{
private ViewOutlineProvider _originalProvider;

protected override bool CanBeApplied()


{
return Container != null && Android.OS.Build.VERSION.SdkInt >=
Android.OS.BuildVersionCodes.Lollipop;
}

protected override void OnAttachedInternal()


{
_originalProvider = Container.OutlineProvider;
Container.OutlineProvider = new CornerRadiusOutlineProvider(Element);
Container.ClipToOutline = true;
}

protected override void OnDetachedInternal()


{
Container.OutlineProvider = _originalProvider;
Container.ClipToOutline = false;
}

protected override void OnElementPropertyChanged(System.ComponentModel.PropertyChangedEventArgs args)


{
base.OnElementPropertyChanged(args);

if (!Attached)
{
return;
}

if (args.PropertyName == ThemeEffects.CornerRadiusProperty.PropertyName)
{
Container.Invalidate();
}
}

private class CornerRadiusOutlineProvider : ViewOutlineProvider


{
private Xamarin.Forms.Element _element;

public CornerRadiusOutlineProvider(Xamarin.Forms.Element element)


{
_element = element;
}

public override void GetOutline(Android.Views.View view, Outline outline)


{
var pixels =
(float)ThemeEffects.GetCornerRadius(_element) *
view.Resources.DisplayMetrics.Density;

outline.SetRoundRect(new Rect(0, 0, view.Width, view.Height), (int)pixels);


}
}
}
}
Resumen
Mediante la definición de estilos para cada control que requiere una apariencia personalizada se puede crear un
tema personalizado. Varios estilos de un control se deben distinguir las diferentes Class atributos en el
diccionario de recursos y, a continuación, aplica estableciendo el StyleClass atributo en el control.
También puede utilizar un estilo efectos para personalizar aún más la apariencia de un control.
Los estilos implícitos (pero no un x:Key o Style atributo) seguirá aplicando a todos los controles que coinciden
con la TargetType .
Xamarin.Forms TimePicker
11/07/2019 • 9 minutes to read • Edit Online

descargar el ejemplo
Una vista de Xamarin.Forms que permite al usuario seleccionar una hora.
Xamarin.Forms TimePicker invoca el control de selector de hora de la plataforma y permite al usuario seleccionar
una hora. TimePicker define las siguientes propiedades:
Time de tipo TimeSpan , la hora seleccionada, cuyo valor predeterminado es un TimeSpan de 0. El TimeSpan
tipo indica una duración de tiempo desde la medianoche.
Format de tipo string , un estándar o personalizado .NET formato de cadena, cuyo valor predeterminado es
"t", el patrón de hora corta.
TextColor de tipo Color , el color utilizado para mostrar la hora seleccionada, cuyo valor predeterminado es
Color.Default .
FontAttributes de tipo FontAttributes , cuyo valor predeterminado es FontAtributes.None .
FontFamily de tipo string , cuyo valor predeterminado es null .
FontSize de tipo double , que de forma predeterminada va de -1,0.

Todas estas propiedades están respaldados por BindableProperty objetos, lo que significa que puede cambiar el
estilo y las propiedades pueden ser destinos de enlaces de datos. El Time propiedad tiene un modo de enlace
predeterminada de BindingMode.TwoWay , lo que significa que puede ser un destino de enlace de datos en una
aplicación que utiliza el Model-View -ViewModel (MVVM ) arquitectura.
El TimePicker no incluye un evento para indicar un nuevo seleccionado Time valor. Si necesita recibir una
notificación de esto, puede agregar un controlador para el PropertyChanged eventos.

Inicializando la propiedad de tiempo


En el código, se puede inicializar el Time propiedad con un valor de tipo TimeSpan :

TimePicker timePicker = new TimePicker


{
Time = new TimeSpan(4, 15, 26) // Time set to "04:15:26"
};

Cuando el Time se especifica la propiedad en XAML, el valor se convierte en un TimeSpan y validado para
asegurarse de que el número de milisegundos es mayor o igual que 0 y que el número de horas es menor que 24.
Los componentes de tiempo deben estar separados por signos de dos puntos:

<TimePicker Time="4:15:26" />

Si el BindingContext propiedad de TimePicker está establecida en una instancia de un modelo de vista que
contiene una propiedad de tipo TimeSpan denominado SelectedTime (por ejemplo), puede crear una instancia el
TimePicker similar al siguiente:

<TimePicker Time="{Binding SelectedTime}" />


En este ejemplo, el Time propiedad se inicializa en el SelectedTime propiedad en el modelo de vista. Dado que el
Time propiedad tiene un modo de enlace de TwoWay y en cualquier momento nuevo que el usuario selecciona se
propagará automáticamente al ViewModel.
Si el TimePicker no contiene un enlace en su Time propiedad, una aplicación debe adjuntar un controlador para
el PropertyChanged eventos a Manténgase informado cuando el usuario selecciona uno nuevo.
Para obtener información sobre cómo establecer las propiedades de fuente, consulte fuentes.

TimePicker y diseño
Es posible usar una opción de diseño horizontal sin restricciones, como Center , Start ,o End con TimePicker :

<TimePicker ···
HorizontalOptions="Center"
··· />

Sin embargo, esto no se recomienda. Según la configuración de la Format propiedad, selecciona veces pueden
requerir los anchos de pantalla diferente. Por ejemplo, la cadena de formato "T" hace que el TimePicker vista para
mostrar veces en un formato largo y "4:15:26 A.M." requiere un mayor ancho de pantalla que el formato de hora
corta ("t") de "4:15 A.M.". Según la plataforma, esta diferencia puede provocar la TimePicker vista para cambiar el
ancho de diseño o en la presentación se trunque.

TIP
Es mejor usar el valor predeterminado HorizontalOptions de Fill con TimePicker y no se debe utilizar un ancho de
Auto al poner TimePicker en un Grid celda.

TimePicker en una aplicación


El SetTimer ejemplo incluye TimePicker , Entry , y Switch vistas en su página. El TimePicker se puede usar
para seleccionar una hora y, al tiempo que se produce se muestra un cuadro de diálogo de alerta que avisa al
usuario del texto en el Entry , que proporciona el Switch se alterna. Este es el archivo XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SetTimer"
x:Class="SetTimer.MainPage">
<StackLayout>
...
<Entry x:Name="_entry"
Placeholder="Enter event to be reminded of" />
<Label Text="Select the time below to be reminded at." />
<TimePicker x:Name="_timePicker"
Time="11:00:00"
Format="T"
PropertyChanged="OnTimePickerPropertyChanged" />
<StackLayout Orientation="Horizontal">
<Label Text="Enable timer:" />
<Switch x:Name="_switch"
HorizontalOptions="EndAndExpand"
Toggled="OnSwitchToggled" />
</StackLayout>
</StackLayout>
</ContentPage>

El Entry permite escribir el texto de recordatorio que se mostrará cuando se produce el tiempo seleccionado. El
TimePicker se asigna un Format propiedad de "T" para el formato de hora larga. Tiene un controlador de
eventos asociado a la PropertyChanged eventos y el Switch ha adjuntado un controlador a su Toggled eventos.
Estos controladores de eventos están en el archivo de código subyacente y llamar a la SetTriggerTime método:

public partial class MainPage : ContentPage


{
DateTime _triggerTime;

public MainPage()
{
InitializeComponent();

Device.StartTimer(TimeSpan.FromSeconds(1), OnTimerTick);
}

bool OnTimerTick()
{
if (_switch.IsToggled && DateTime.Now >= _triggerTime)
{
_switch.IsToggled = false;
DisplayAlert("Timer Alert", "The '" + _entry.Text + "' timer has elapsed", "OK");
}
return true;
}

void OnTimePickerPropertyChanged(object sender, PropertyChangedEventArgs args)


{
if (args.PropertyName == "Time")
{
SetTriggerTime();
}
}

void OnSwitchToggled(object sender, ToggledEventArgs args)


{
SetTriggerTime();
}

void SetTriggerTime()
{
if (_switch.IsToggled)
{
_triggerTime = DateTime.Today + _timePicker.Time;
if (_triggerTime < DateTime.Now)
{
_triggerTime += TimeSpan.FromDays(1);
}
}
}
}

El SetTriggerTime método calcula un tiempo de temporizador en función de la DateTime.Today valor de


propiedad y el TimeSpan valor devuelto desde el TimePicker . Esto es necesario porque el DateTime.Today
propiedad devuelve un DateTime que indica la fecha actual, pero con una hora de medianoche. Si ya ha
transcurrido el tiempo de temporizador de hoy en día, se supone que mañana.
Tics del temporizador cada segundo, ejecuta el OnTimerTick método que comprueba si el Switch es on y si la
hora actual es mayor que o igual que el tiempo del temporizador. Cuando se produce el tiempo del temporizador,
el DisplayAlert método presenta un cuadro de diálogo de alerta al usuario como un recordatorio.
Cuando el ejemplo se ejecuta en primer lugar, el TimePicker vista se inicializa en 11 am. Pulsar el TimePicker
invoca el selector de hora de la plataforma. Las plataformas de implementan el selector de hora de maneras muy
diferentes, pero cada enfoque es familiar para los usuarios de esa plataforma:
TIP
En Android, el TimePicker cuadro de diálogo se puede personalizar invalidando el CreateTimePickerDialog método en
un representador personalizado. Esto permite, por ejemplo, botones adicionales que se agregan al cuadro de diálogo.

Después de seleccionar una hora, se muestra la hora seleccionada en el TimePicker :

Siempre que el Switch se alterna en la posición, la aplicación muestra un cuadro de diálogo de alerta recuerda al
usuario del texto en el Entry cuando se produce la hora seleccionada:
Tan pronto como se muestra el cuadro de diálogo de alerta, el Switch cambia a la posición de apagado.

Vínculos relacionados
Ejemplo de SetTimer
TimePicker API
Xamarin.Forms Visual
11/07/2019 • 2 minutes to read • Edit Online

Material de Xamarin.Forms Visual


Xamarin.Forms Material Visual puede utilizarse para crear aplicaciones de Xamarin.Forms que tienen un aspecto
idénticos o idéntica en gran medida en iOS y Android.

Crear a un representador Xamarin.Forms Visual


Xamarin.Forms Visual permite que los representadores de forma selectiva se aplique a VisualElement objetos, sin
tener que las vistas de Xamarin.Forms subclase.
Material de Xamarin.Forms Visual
11/07/2019 • 8 minutes to read • Edit Online

descargar el ejemplo
Diseño de materiales es un sistema de diseño bien fundamentadas creado por Google, que establece el tamaño,
color, espaciado y otros aspectos de cómo las vistas y diseños deben aspecto y comportamiento.
Xamarin.Forms Material Visual puede utilizarse para aplicar las reglas de Material Design a las aplicaciones de
Xamarin.Forms, crear aplicaciones que tienen un aspecto idénticos o idéntica en gran medida en iOS y Android.
Cuando se habilita Material Visual, vistas admitidas adoptarán el mismo diseño multiplataforma, crear una
apariencia unificada. Esto se logra con los representadores de material, que se aplican las reglas de diseño de
materiales.
Es el proceso para habilitar Visual del Material de Xamarin.Forms en la aplicación:
1. Agregar el Xamarin.Forms.Visual.Material paquete NuGet a los proyectos de plataforma Android y iOS. Este
paquete de NuGet entrega optimizada de los representadores de Material Design en iOS y Android. En iOS, el
paquete proporciona la dependencia transitiva al Xamarin.iOS.MaterialComponents, que es un C# enlazar a
Google componentes de Material para iOS. En Android, el paquete proporciona destinos de compilación para
asegurarse de que su TargetFramework está configurado correctamente.
2. Inicializar a los representadores de material en cada proyecto de la plataforma. Para obtener más información,
consulte inicializar representadores materiales.
3. Consumir los representadores de material estableciendo el Visual propiedad Material en todas las páginas
que deben adoptar las reglas de diseño de materiales. Para obtener más información, consulte consumir los
representadores de material.
4. [opcional] Personalizar a los representadores de material. Para obtener más información, consulte personalizar
representadores materiales.

IMPORTANT
En Android, los representadores de material requieren una versión mínima de 5.0 (API 21) o superior y un TargetFramework
de la versión 9.0 (28 de API). Además, el proyecto de plataforma requiere compatibilidad con Android bibliotecas 28.0.0 o
superior, y su tema debe heredar de un tema de los componentes de Material o continuar heredar de un tema de
AppCompat. Para obtener más información, consulte Introducción a los componentes de Material para Android.

Actualmente se incluyen los representadores de material en el Xamarin.Forms.Visual.Material paquete NuGet


para las vistas siguientes:
Button
CheckBox
Entry
Frame
ProgressBar
DatePicker
TimePicker
Picker
ActivityIndicator
Editor
Slider
Stepper

Funcionalmente, los representadores de material no son distintos a los representadores de forma predeterminada.

Inicializar a los representadores de material


Después de instalar el Xamarin.Forms.Visual.Material de paquetes de NuGet, el material de representadores
deben inicializarse en cada proyecto de la plataforma.
En iOS, esto debe ocurrir en AppDelegate.cs invocando el FormsMaterial.Init método después el
Xamarin.Forms.Forms.Init método:

global::Xamarin.Forms.Forms.Init();
FormsMaterial.Init();

En Android, esto debe ocurrir en MainActivity.cs invocando el FormsMaterial.Init método después el


Xamarin.Forms.Forms.Init método:

global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
FormsMaterial.Init(this, savedInstanceState);

Consumir a los representadores de material


Las aplicaciones pueden participar en usando los representadores de material estableciendo el
VisualElement.Visual propiedad en una página, diseño o vista, para Material :

<ContentPage Visual="Material"
...>
...
</ContentPage>

El código de C# equivalente es:

ContentPage contentPage = new ContentPage();


contentPage.Visual = VisualMarker.Material;

El Visual propiedad puede establecerse en cualquier tipo que implemente IVisual , con el VisualMarker clase
que proporciona la siguiente IVisual propiedades:
Default : indica que la vista debe presentarse mediante el representador predeterminado.
MatchParent : indica que la vista debe utilizar el representador mismo como su elemento primario directo.
Material : indica que la vista debe representar con un procesador de material.

IMPORTANT
El Visual propiedad está definida en el VisualElement (clase), con vistas al heredar el Visual valor de propiedad de sus
elementos primarios. Por consiguiente, establecer el Visual propiedad en un ContentPage garantiza que todas las vistas
admitidas en la página usará ese objeto Visual. Además, el Visual propiedad se puede invalidar en una vista.

Las capturas de pantalla siguientes muestran una interfaz de usuario que se va a representar utilizando a los
representadores de forma predeterminada:

Las capturas de pantalla siguientes muestran la misma interfaz de usuario que se va a representar utilizando a los
representadores de materiales:

Las principales diferencias visibles entre los representadores de material, se muestra aquí y de los representadores
de forma predeterminada son que aproveche los representadores de material Button texto y redondear las
esquinas del Frame bordes. Sin embargo, los representadores de material usar controles nativos y, por tanto,
puede haber diferencias de la interfaz de usuario entre plataformas para áreas como fuentes, colores, sombras y
elevación.

NOTE
Componentes de diseño de materiales cumplen rigurosamente las directrices de Google. Como resultado, los
representadores de Material Design están orientados a ese cambio de tamaño y el comportamiento. Cuando necesite mayor
control del comportamiento o los estilos, puede crear sus propios efecto, comportamiento, o Custom Renderer para lograr el
detalle que se requiera.

Personalizar a los representadores de material


Los representadores de material, opcionalmente, se pueden personalizar, al igual que los representadores de
forma predeterminada, a través de las siguientes clases base:
MaterialButtonRenderer
MaterialCheckBoxRenderer
MaterialEntryRenderer
MaterialFrameRenderer
MaterialProgressBarRenderer
MaterialDatePickerRenderer
MaterialTimePickerRenderer
MaterialPickerRenderer
MaterialActivityIndicatorRenderer
MaterialEditorRenderer
MaterialSliderRenderer
MaterialStepperRenderer

El código siguiente muestra un ejemplo de personalización de la MaterialProgressBarRenderer clase:

using Xamarin.Forms.Material.Android;

[assembly: ExportRenderer(typeof(ProgressBar), typeof(CustomMaterialProgressBarRenderer), new[] {


typeof(VisualMarker.MaterialVisual) })]
namespace MyApp.Android
{
public class CustomMaterialProgressBarRenderer : MaterialProgressBarRenderer
{
...
}
}

En este ejemplo, el ExportRendererAttribute especifica que el CustomMaterialProgressBarRenderer clase se usará


para representar el ProgressBar vista, con el IVisual tipo registrado como el tercer argumento.

NOTE
Un representador que especifica un IVisual tipo, como parte de su ExportRendererAttribute , se usará para
representar participa en las vistas, en lugar de con el representador predeterminado. En tiempo de selección del
representador, el Visual propiedad de la vista es inspeccionar y se incluye en el proceso de selección del representador.

Para obtener más información acerca de los representadores personalizados, consulte representadores
personalizados.

Vínculos relacionados
Material Visual (ejemplo)
Crear a un representador Xamarin.Forms Visual
Representadores personalizados
Crear a un representador Xamarin.Forms Visual
11/07/2019 • 6 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms Visual permite crear y aplicar de forma selectiva a los representadores de VisualElement objetos,
sin tener que las vistas de Xamarin.Forms subclase. Un representador que especifica un IVisual tipo, como parte
de su ExportRendererAttribute , se usará para representar participa en las vistas, en lugar de con el representador
predeterminado. En tiempo de selección del representador, el Visual propiedad de la vista es inspeccionar y se
incluye en el proceso de selección del representador.

IMPORTANT
Actualmente la Visual propiedad no se puede cambiar después de que se ha representado la vista, pero esta operación
cambiará en futuras versiones.

El proceso para crear y consumir a un representador Xamarin.Forms Visual es:


1. Cree a los representadores de plataforma para la vista necesaria. Para obtener más información, consulte crear
representadores.
2. Crear un tipo que se deriva de IVisual . Para obtener más información, consulte crear un tipo IVisual.
3. Registrar el IVisual tipo como parte de la ExportRendererAttribute que decora los representadores. Para
obtener más información, consulte registrar el tipo IVisual.
4. Usar el representador Visual estableciendo el Visual propiedad en la vista para el IVisual nombre. Para
obtener más información, consulte consumir el representador Visual.
5. [opcional] Registrar un nombre para el IVisual tipo. Para obtener más información, consulte registrar un
nombre para el tipo de IVisual.

Crear a los representadores de plataforma


Para obtener información acerca de cómo crear una clase de procesador, vea representadores personalizados. Sin
embargo, tenga en cuenta que un representador Xamarin.Forms Visual se aplica a una vista sin necesidad de
subclase de la vista.
Las clases de representador que se describen aquí implementan personalizada Button que muestra su texto con
sombreado.
iOS
El ejemplo de código siguiente muestra a la representación de botón para iOS:
public class CustomButtonRenderer : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// Cleanup
}

if (e.NewElement != null)
{
Control.TitleShadowOffset = new CoreGraphics.CGSize(1, 1);
Control.SetTitleShadowColor(Color.Black.ToUIColor(), UIKit.UIControlState.Normal);
}
}
}

Android
El ejemplo de código siguiente muestra a la representación de botón para Android:

public class CustomButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer


{
public CustomButtonRenderer(Context context) : base(context)
{
}

protected override void OnElementChanged(ElementChangedEventArgs<Button> e)


{
base.OnElementChanged(e);

if (e.OldElement != null)
{
// Cleanup
}

if (e.NewElement != null)
{
Control.SetShadowLayer(5, 3, 3, Color.Black.ToAndroid());
}
}
}

Crear un tipo IVisual


En la biblioteca multiplataforma, creación de un tipo que derive de IVisual :

public class CustomVisual : IVisual


{
}

El CustomVisual tipo, a continuación, se puede registrar en las clases de representador que permita Button
objetos que opten por usar los representadores.

Registrar el tipo IVisual


En los proyectos de plataforma, decore las clases del representador con la ExportRendererAttribute :
[assembly: ExportRenderer(typeof(Xamarin.Forms.Button), typeof(CustomButtonRenderer), new[] {
typeof(CustomVisual) })]
public class CustomButtonRenderer : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
...
}
}

En este ejemplo, el ExportRendererAttribute especifica que el CustomButtonRenderer clase se usará para


representar consumiendo Button objetos, con el IVisual tipo registrado como el tercer argumento. Un
representador que especifica un IVisual tipo, como parte de su ExportRendererAttribute , se usará para
representar participa en las vistas, en lugar de con el representador predeterminado.

Usar al representador Visual


Un Button objeto puede optar por usar las clases del representador estableciendo su Visual propiedad Custom :

<Button Visual="Custom"
Text="CUSTOM BUTTON"
BackgroundColor="{StaticResource PrimaryColor}"
TextColor="{StaticResource SecondaryTextColor}"
HorizontalOptions="FillAndExpand" />

NOTE
En XAML, un convertidor de tipos elimina la necesidad de incluir el sufijo "Visual" en el Visual valor de propiedad. Sin
embargo, también se puede especificar el nombre de tipo completo.

El código de C# equivalente es:

Button button = new Button { Text = "CUSTOM BUTTON", ... };


button.Visual = new CustomVisual();

En tiempo de selección del representador, el Visual propiedad de la Button es inspeccionar y puede incluir en el
proceso de selección del representador. Si no encuentra un representador, se usará el representador
predeterminado de Xamarin.Forms.
Las capturas de pantalla siguientes muestran el representado Button , que muestra su texto con sombreado:

Registrar un nombre para el tipo de IVisual


El VisualAttribute se puede usar para registrar, opcionalmente, un nombre diferente para el IVisual tipo. Este
enfoque puede utilizarse para resolver conflictos de nombres entre diferentes bibliotecas Visual, o en situaciones
donde desea hacer referencia a un objeto Visual mediante un nombre distinto al nombre de su tipo.
El VisualAttribute debe definirse en el nivel de ensamblado en la biblioteca de multiplataforma, o en el proyecto
de plataforma:

[assembly: Visual("MyVisual", typeof(CustomVisual))]

El IVisual , a continuación, se puede utilizar el tipo en su nombre registrado:

<Button Visual="MyVisual"
... />

NOTE
Al consumir un objeto Visual a través de su nombre registrado, se debe incluir cualquier sufijo "Visual".

Vínculos relacionados
Material Visual (ejemplo)
Material de Xamarin.Forms Visual
Representadores personalizados
El Administrador de estado Visual de
Xamarin.Forms
11/07/2019 • 28 minutes to read • Edit Online

descargar el ejemplo
Utilice Visual State Manager para realizar cambios en los elementos XAML basados en los estados visuales
establecer desde el código.
El Administrador de estado Visual (VSM ) es nuevo en Xamarin.Forms 3.0. VSM proporciona una manera
estructurada para realizar cambios visuales en la interfaz de usuario desde el código. En la mayoría de los
casos, la interfaz de usuario de la aplicación se define en XAML, y este XAML incluye marcado que describe
cómo Visual State Manager afecta a los objetos visuales de la interfaz de usuario.
VSM introduce el concepto de estados visuales. Una vista de Xamarin.Forms, como un Button puede tener
varios una apariencia visual distinta según su estado subyacente — si está deshabilitado, o presiona o, tiene
foco de entrada. Estos son los Estados del botón.
Estados visuales se recopilan en grupos de estado visual. Todos los estados visuales dentro de un grupo de
estados visuales son mutuamente excluyentes. Estados visuales y grupos de estado visual se identifican
mediante cadenas de texto simple.
Xamarin.Forms Visual State Manager define un grupo de estados visuales denominado "CommonStates" con
tres estados visuales:
"Normal"
"Deshabilitado"
"Con foco"
Es compatible con todas las clases que derivan de este grupo de estados visuales VisualElement , que es la
clase base para View y Page .
También puede definir sus propios grupos de estados visuales y estados visuales, como en este artículo se
muestran.

NOTE
Xamarin.Forms los desarrolladores familiarizados con desencadenadores son conscientes de que los desencadenadores
también pueden realizar cambios en los objetos visuales en la interfaz de usuario según los cambios en las propiedades
de una vista o la activación de eventos. Sin embargo, usar desencadenadores para tratar con varias combinaciones de
estos cambios puede ser algo confuso. Históricamente, el Administrador de estado Visual se introdujo en entornos
basados en XAML de Windows para solucionar la confusión resultante de las combinaciones de estados visuales. VSM
los estados visuales dentro de un grupo de estados visuales siempre son mutuamente excluyentes. En cualquier
momento, solo un estado de cada grupo es el estado actual.

Los Estados comunes


El Administrador de estado Visual permite incluir secciones en el archivo XAML que se puede cambiar la
apariencia visual de una vista si la vista normal o está deshabilitado, o tiene el foco de entrada. Estos se
conocen como el Estados comunes.
Por ejemplo, suponga que tiene un Entry vista en la página, y desea que la apariencia visual de la Entry para
cambiar de las maneras siguientes:
El Entry debe tener un rosa en segundo plano cuando el Entry está deshabilitado.
El Entry debe tener un fondo verde lima con normalidad.
El Entry debe expandir a dos veces su altura normal cuando tiene foco de entrada.
Puede asociar el marcado VSM a una vista individual, o puede definir en un estilo si se aplica a varias vistas.
Las dos secciones siguientes describen estos enfoques.
Marcado VSM en una vista
Para asociar el marcado VSM a un Entry ver, separar primero el Entry en etiquetas de inicio y finalización:

<Entry FontSize="18">

</Entry>

Asignó un tamaño de fuente explícita porque uno de los Estados que usará el FontSize propiedad duplicar el
tamaño del texto en el Entry .
A continuación, inserte VisualStateManager.VisualStateGroups etiquetas entre las etiquetas:

<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>

</VisualStateManager.VisualStateGroups>
</Entry>

VisualStateGroups es una propiedad enlazable adjunta definida por el VisualStateManager clase. (Para obtener
más información sobre las propiedades enlazables adjuntas, consulte el artículo propiedades adjuntas.) Se
trata cómo el VisualStateGroups propiedad está asociada a la Entry objeto.
El VisualStateGroups propiedad es de tipo VisualStateGroupList , que es una colección de VisualStateGroup
objetos. Dentro de la VisualStateManager.VisualStateGroups etiquetas, inserte un par de VisualStateGroup
etiquetas para cada grupo de estados visuales que desea incluir:

<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">

</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>

Tenga en cuenta que el VisualStateGroup etiqueta tiene una x:Name atributo que indica el nombre del grupo.
El VisualStateGroup clase define un Name propiedad que puede usar en su lugar:

<VisualStateGroup Name="CommonStates">

Puede usar x:Name o Name , pero no ambos en el mismo elemento.


El VisualStateGroup clase define una propiedad denominada States , que es una colección de VisualState
objetos. States es el propiedad de contenido de VisualStateGroups por lo que puede incluir la VisualState
etiquetas directamente entre el VisualStateGroup etiquetas. (Contenido de las propiedades se describen en el
artículo esencial sintaxis XAML.)
El siguiente paso es incluir un par de etiquetas para cada estado visual en ese grupo. También pueden
identificarse mediante x:Name o Name :

<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">

</VisualState>

<VisualState x:Name="Focused">

</VisualState>

<VisualState x:Name="Disabled">

</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>

VisualState define una propiedad denominada Setters , que es una colección de Setter objetos. Estas son
las mismas Setter objetos que se usan en un Style objeto.
Setters es no la propiedad content de VisualState , por lo que es necesario incluir las etiquetas de elemento
de propiedad para el Setters propiedad:

<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>

</VisualState.Setters>
</VisualState>

<VisualState x:Name="Focused">
<VisualState.Setters>

</VisualState.Setters>
</VisualState>

<VisualState x:Name="Disabled">
<VisualState.Setters>

</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>

Ahora puede insertar uno o varios Setter objetos entre cada par de Setters etiquetas. Estos son los Setter
objetos que definen los estados visuales que se ha descrito anteriormente:
<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="FontSize" Value="36" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Pink" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>

Cada Setter etiqueta indica el valor de una propiedad determinada cuando ese estado está activo. Cualquier
propiedad que se hace referencia a un Setter objeto debe estar respaldado por una propiedad enlazable.
Similar a este marcado es la base de la VSM en la vista página en el VsmDemos programa de ejemplo. La
página incluye tres Entry vistas, pero solo la segunda se tiene la marca de VSM conectada a ella:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:VsmDemos"
x:Class="VsmDemos.MainPage"
Title="VSM Demos">

<StackLayout>
<StackLayout.Resources>
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
</Style>

<Style TargetType="Label">
<Setter Property="Margin" Value="20, 30, 20, 0" />
<Setter Property="FontSize" Value="Large" />
</Style>
</StackLayout.Resources>

<Label Text="Normal Entry:" />

<Entry />

<Label Text="Entry with VSM: " />

<Entry>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">

<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="FontSize" Value="36" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Pink" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

<Entry.Triggers>
<DataTrigger TargetType="Entry"
Binding="{Binding Source={x:Reference entry3},
Path=Text.Length}"
Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Entry.Triggers>
</Entry>

<Label Text="Entry to enable 2nd Entry:" />

<Entry x:Name="entry3"
Text=""
Placeholder="Type something to enable 2nd Entry" />
</StackLayout>
</ContentPage>
Tenga en cuenta que el segundo Entry también tiene un DataTrigger como parte de su Trigger colección.
Esto hace que el Entry deshabilitarse hasta que no se escribe algo en la tercera Entry . Esta es la página de
inicio que se ejecutan en iOS, Android y la plataforma Universal de Windows (UWP ):

El estado visual actual es "Disabled" por lo que el fondo de la segunda Entry es de color fucsia en iOS y
Android pantallas. La implementación de UWP de Entry no permite establecer el fondo de color cuando el
Entry está deshabilitado.

Cuando escriba algún texto en la tercera Entry , el segundo Entry modificadores en el estado "Normal" y el
fondo es ahora CAL:

Al tocar el segundo Entry , obtiene el foco de entrada. Se activa en estado "Focused" y se expande al doble del
alto:
Tenga en cuenta que el Entry no conserva el fondo de color verde cuando recibe el foco de entrada. Como
Visual State Manager cambia entre los estados visuales, no están establecidas las propiedades establecidas por
el estado anterior. Tenga en cuenta que los estados visuales son mutuamente excluyentes. El estado "Normal"
no significa solamente que el Entry está habilitado. Esto significa que el Entry está habilitado y no tiene el
foco de entrada.
Si desea que el Entry para tener un fondo de color verde en el estado "Enfocado", agregue otro Setter a ese
estado visual:

<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="FontSize" Value="36" />
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>

En orden para estas Setter objetos funcione correctamente, un VisualStateGroup debe contener VisualState
objetos para todos los Estados de ese grupo. Si no hay un estado visual que no tiene ningún Setter objetos,
incluirla como una etiqueta vacía:

<VisualState x:Name="Normal" />

Marcado de administrador de estado visual en un estilo


A menudo es necesario compartir el mismo marcado Visual State Manager entre dos o más vistas. En este
caso, querrá poner el marcado en un Style definición.
Aquí está la existente implícito Style para el Entry elementos en el VSM en vista página:

<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
</Style>

Agregar Setter las etiquetas para el VisualStateManager.VisualStateGroups propiedad enlazable asociada:


<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VisualStateManager.VisualStateGroups">

</Setter>
</Style>

La propiedad de contenido para Setter es Value , por lo que el valor de la Value propiedad se puede
especificar directamente dentro de esas etiquetas. Que es propiedad de tipo VisualStateGroupList :

<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>

</VisualStateGroupList>
</Setter>
</Style>

Dentro de estas etiquetas pueden incluir uno o varios VisualStateGroup objetos:

<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">

</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>

El resto del marcado VSM es igual que antes.


Este es el VSM en estilo página que muestra el marcado VSM completo:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VsmDemos.VsmInStylePage"
Title="VSM in Style">
<StackLayout>
<StackLayout.Resources>
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">

<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="FontSize" Value="36" />
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Pink" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>

<Style TargetType="Label">
<Setter Property="Margin" Value="20, 30, 20, 0" />
<Setter Property="FontSize" Value="Large" />
</Style>
</StackLayout.Resources>

<Label Text="Normal Entry:" />

<Entry />

<Label Text="Entry with VSM: " />

<Entry>
<Entry.Triggers>
<DataTrigger TargetType="Entry"
Binding="{Binding Source={x:Reference entry3},
Path=Text.Length}"
Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Entry.Triggers>
</Entry>

<Label Text="Entry to enable 2nd Entry:" />

<Entry x:Name="entry3"
Text=""
Placeholder="Type something to enable 2nd Entry" />
</StackLayout>
</ContentPage>
Ahora todos los Entry vistas en esta página responden a la misma manera para sus estados visuales. Tenga
en cuenta también que el estado de "Focused" incluye ahora un segundo Setter que ofrece cada Entry una
CAL fondo también cuando tiene foco de entrada:

Definir sus propios estados visuales


Cada clase que derive de VisualElement admite los tres estados comunes "Normal", "Con foco" y "Disabled".
Internamente, el VisualElement clase detecta cuando se está convirtiendo en habilitado o deshabilitado, o con
y sin foco y llama a estático VisualStateManager.GoToState método:

VisualStateManager.GoToState(this, "Focused");

Este es el único código Visual State Manager que encontrará en el VisualElement clase. Dado que GoToState
se llama para cada objeto en función de cada clase que derive de VisualElement , puede utilizar Visual State
Manager con cualquier VisualElement objeto para responder a estos cambios.
Curiosamente, el nombre del grupo de estados visuales "CommonStates" no se mencionan explícitamente en
VisualElement . El nombre del grupo no es parte de la API para el Administrador de estado Visual. Dentro de
uno de los dos programa de ejemplo mostrado hasta ahora, puede cambiar el nombre del grupo de
"CommonStates" en cualquier otro valor y el programa seguirá funcionando. El nombre del grupo es
simplemente una descripción general de los Estados de ese grupo. Implícitamente se entiende que los estados
visuales en todos los grupos se excluyen mutuamente: Un estado y el estado de un único es actual en cualquier
momento.
Si desea implementar sus propios estados visuales, deberá llamar a VisualStateManager.GoToState desde el
código. Con más frecuencia, hará que esta llamada desde el archivo de código subyacente de la clase de
página.
El VSM validación página en el VsmDemos ejemplo muestra cómo utilizar Visual State Manager en relación
con la validación de entrada. El archivo XAML consta de dos Label elementos, un Entry , y Button :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VsmDemos.VsmValidationPage"
Title="VSM Validation">
<StackLayout Padding="10, 10">

<Label Text="Enter a U.S. phone number:"


FontSize="Large" />

<Entry Placeholder="555-555-5555"
FontSize="Large"
Margin="30, 0, 0, 0"
TextChanged="OnTextChanged" />

<Label x:Name="helpLabel"
Text="Phone number must be of the form 555-555-5555, and not begin with a 0 or 1">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="ValidityStates">

<VisualState Name="Valid">
<VisualState.Setters>
<Setter Property="TextColor" Value="Transparent" />
</VisualState.Setters>
</VisualState>

<VisualState Name="Invalid" />

</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Label>

<Button x:Name="submitButton"
Text="Submit"
FontSize="Large"
Margin="0, 20"
VerticalOptions="Center"
HorizontalOptions="Center">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="ValidityStates">

<VisualState Name="Valid" />

<VisualState Name="Invalid">
<VisualState.Setters>
<Setter Property="IsEnabled" Value="False" />
</VisualState.Setters>
</VisualState>

</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Button>
</StackLayout>
</ContentPage>

Marcado VSM está asociado a la segunda Label (denominado helpLabel ) y el Button (denominado
submitButton ). Hay dos estados se excluyen mutuamente, denominados "Valid" y "No válido". Tenga en cuenta
que contiene cada uno de los dos grupos de "ValidationState" VisualState etiquetas para "Valid" y "No válido",
aunque uno de ellos está vacío en cada caso.
Si el Entry no contiene un número de teléfono válido, a continuación, el estado actual es "No válido" por lo
que el segundo Label está visible y el Button está deshabilitado:
Cuando se escribe un número de teléfono válido, el estado actual es "Valid". El segundo Entry desaparece y el
Button ahora está habilitado:

El archivo de código subyacente es responsable de control de la TextChanged evento desde el Entry . El


controlador utiliza una expresión regular para determinar si la cadena de entrada es válida o no. El método en
el archivo de código subyacente denominado GoToState llama estático VisualStateManager.GoToState método
para ambos helpLabel y submitButton :
public partial class VsmValidationPage : ContentPage
{
public VsmValidationPage ()
{
InitializeComponent ();

GoToState(false);
}

void OnTextChanged(object sender, TextChangedEventArgs args)


{
bool isValid = Regex.IsMatch(args.NewTextValue, @"^[2-9]\d{2}-\d{3}-\d{4}$");
GoToState(isValid);
}

void GoToState(bool isValid)


{
string visualState = isValid ? "Valid" : "Invalid";
VisualStateManager.GoToState(helpLabel, visualState);
VisualStateManager.GoToState(submitButton, visualState);
}
}

Observe también que el GoToState se llama al método desde el constructor para inicializar el estado. Siempre
debe haber un estado actual. Pero no en el código está ahí cualquier referencia al nombre del grupo de estados
visuales, aunque se hace referencia en el XAML como "ValidationStates" para fines de claridad.
Tenga en cuenta que el archivo de código subyacente debe tener en cuenta todos los objetos en la página que
se ve afectada por estos estados visuales y llamar a VisualStateManager.GoToState para cada uno de estos
objetos. En este ejemplo, es solo dos objetos (el Label y Button ), pero podría ser varias más.
Tal vez se pregunte: Si el archivo de código subyacente debe hacer referencia a todos los objetos en la página
que se ve afectado por estos estados visuales, ¿por qué no el archivo de código subyacente basta con acceder a
los objetos directamente? Seguramente podría. Sin embargo, la ventaja de usar VSM es que puede controlar
cómo visual elementos reaccionan a otro estado completamente en XAML, que conserva todo el diseño de
interfaz de usuario en una ubicación. Esto evita la apariencia visual de configuración mediante el acceso a los
elementos visuales directamente desde el código subyacente.
Puede ser tentador que considere la posibilidad de derivar una clase de Entry y quizás definir una propiedad
que se puede establecer para una función de validación externo. La clase que derive de Entry , a continuación,
puede llamar a la VisualStateManager.GoToState método. Este esquema funcionará bien, pero solo si el Entry
era el único objeto afectado por los diferentes estados visuales. En este ejemplo, un Label y un Button son
también se ve afectado. No hay ninguna manera de marcado VSM adjunta a un Entry a otros objetos en la
página y no hay forma de control para marcado VSM asociado a estos otros objetos para hacer referencia a un
cambio en el estado visual de otro objeto.

Mediante el Administrador de estado Visual para el diseño adaptable


Puede cambiar el tamaño de una aplicación que se ejecuta en un teléfono normalmente puede verse en una
vertical o relación de aspecto horizontal y un programa de Xamarin.Forms que se ejecuta en el escritorio de
Xamarin.Forms para asumir diferentes tamaños y proporciones. Una aplicación bien diseñada puede mostrar
su contenido distinto para estos distintos factores de forma de ventana o página.
Esta técnica se conoce a veces como diseño adaptable. Debido a un diseño adaptable implica únicamente
objetos visuales de un programa, es una aplicación de Visual State Manager ideal.
Un ejemplo sencillo es una aplicación que muestra una pequeña colección de botones que afectan al contenido
de la aplicación. En modo vertical, se pueden mostrar estos botones en una fila horizontal en la parte superior
de la página:

En modo horizontal, la matriz de botones podría mover a un lado y muestra en una columna:

De arriba a abajo, el programa se está ejecutando en la plataforma Universal de Windows, iOS y Android.
El diseño adaptable VSM página en el VsmDemos ejemplo define un grupo denominado "OrientationStates"
con dos estados visuales denominados "Vertical" y "Horizontal". (Un enfoque más complejo podría basarse en
varios diferentes anchos de página o ventana).
Marcado VSM se produce en cuatro lugares en el archivo XAML. El StackLayout denominado mainStack
contiene el menú y el contenido, que es un Image elemento. Esto StackLayout debe tener una orientación
vertical en modo vertical y una orientación horizontal en modo horizontal:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VsmDemos.VsmAdaptiveLayoutPage"
Title="VSM Adaptive Layout">

<StackLayout x:Name="mainStack">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="OrientationStates">

<VisualState Name="Portrait">
<VisualState.Setters>
<Setter Property="Orientation" Value="Vertical" />
</VisualState.Setters>
</VisualState>

<VisualState Name="Landscape">
<VisualState.Setters>
<Setter Property="Orientation" Value="Horizontal" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

<ScrollView x:Name="menuScroll">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="OrientationStates">

<VisualState Name="Portrait">
<VisualState.Setters>
<Setter Property="Orientation" Value="Horizontal" />
</VisualState.Setters>
</VisualState>

<VisualState Name="Landscape">
<VisualState.Setters>
<Setter Property="Orientation" Value="Vertical" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

<StackLayout x:Name="menuStack">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="OrientationStates">

<VisualState Name="Portrait">
<VisualState.Setters>
<Setter Property="Orientation" Value="Horizontal" />
</VisualState.Setters>
</VisualState>

<VisualState Name="Landscape">
<VisualState.Setters>
<Setter Property="Orientation" Value="Vertical" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

<StackLayout.Resources>
<Style TargetType="Button">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup Name="OrientationStates">

<VisualState Name="Portrait">
<VisualState Name="Portrait">
<VisualState.Setters>
<Setter Property="HorizontalOptions" Value="CenterAndExpand" />
<Setter Property="Margin" Value="10, 5" />
</VisualState.Setters>
</VisualState>

<VisualState Name="Landscape">
<VisualState.Setters>
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="Margin" Value="10" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</StackLayout.Resources>

<Button Text="Banana"
Command="{Binding SelectedCommand}"
CommandParameter="Banana.jpg" />

<Button Text="Face Palm"


Command="{Binding SelectedCommand}"
CommandParameter="FacePalm.jpg" />

<Button Text="Monkey"
Command="{Binding SelectedCommand}"
CommandParameter="monkey.png" />

<Button Text="Seated Monkey"


Command="{Binding SelectedCommand}"
CommandParameter="SeatedMonkey.jpg" />
</StackLayout>
</ScrollView>

<Image x:Name="image"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand" />
</StackLayout>
</ContentPage>

Interno ScrollView denominado menuScroll y StackLayout denominado menuStack implementar en el menú


de botones. La orientación de estos diseños es opuesta de mainStack . Debe ser el menú horizontal en modo
vertical y vertical en modo horizontal.
La cuarta sección de marcado VSM está en un estilo implícito para los botones sí mismos. Este marcado
establece VerticalOptions , HorizontalOptions , y Margin propiedades específicas de las orientaciones
horizontal y portait.
Los conjuntos de archivos de código subyacente del BindingContext propiedad de menuStack para
implementar Button comandos y también se adjunta un controlador para el SizeChanged eventos de la
página:
public partial class VsmAdaptiveLayoutPage : ContentPage
{
public VsmAdaptiveLayoutPage ()
{
InitializeComponent ();

SizeChanged += (sender, args) =>


{
string visualState = Width > Height ? "Landscape" : "Portrait";
VisualStateManager.GoToState(mainStack, visualState);
VisualStateManager.GoToState(menuScroll, visualState);
VisualStateManager.GoToState(menuStack, visualState);

foreach (View child in menuStack.Children)


{
VisualStateManager.GoToState(child, visualState);
}
};

SelectedCommand = new Command<string>((filename) =>


{
image.Source = ImageSource.FromResource("VsmDemos.Images." + filename);
});

menuStack.BindingContext = this;
}

public ICommand SelectedCommand { private set; get; }


}

El llamadas del controlador VisualStateManager.GoToState para las dos StackLayout y


SizeChanged
ScrollView elementos y, a continuación, recorre en bucle los elementos secundarios de menuStack para llamar
a VisualStateManager.GoToState para el Button elementos.
Puede parecer como si el archivo de código subyacente puede controlar los cambios de orientación más
directamente estableciendo las propiedades de elementos en el archivo XAML, pero Visual State Manager es
definitivamente un enfoque más estructurado. Todos los objetos visuales se mantienen en el archivo XAML,
donde se convierten en más fáciles examinar, mantienen y modificar.

Administrador de estado visual con Xamarin.University


Vídeo de Xamarin.Forms 3.0 Visual State Manager

Vínculos relacionados
VsmDemos
Xamarin.Forms WebView
11/07/2019 • 19 minutes to read • Edit Online

descargar el ejemplo
WebView es una vista para mostrar la web y el contenido HTML en la aplicación. A diferencia de OpenUri , que
lleva al usuario en el explorador web en el dispositivo, WebView muestra el contenido HTML dentro de la
aplicación.

Contenido
WebView admite los siguientes tipos de contenido:
Sitios Web HTML y CSS – WebView es totalmente compatible con sitios Web escritos con HTML y CSS,
incluida la compatibilidad con JavaScript.
Documentos – porque WebView se implementa con componentes nativos en cada plataforma, WebView es
capaz de mostrar los documentos que están visibles en cada plataforma. Esto significa que los archivos PDF
funcionan en iOS y Android.
Las cadenas HTML – WebView puede mostrar las cadenas HTML de la memoria.
Archivos locales – WebView puede presentar cualquiera de los tipos de contenido anteriores incrustado en la
aplicación.

NOTE
WebView en Windows no admite Silverlight, Flash o los controles ActiveX, incluso si son compatibles con Internet Explorer
en esa plataforma.

Sitios web
Para mostrar un sitio Web desde internet, establezca el WebView del Source propiedad a una dirección URL de
cadena:

var browser = new WebView


{
Source = "http://xamarin.com"
};

NOTE
Las direcciones URL se deben formar totalmente con el protocolo especificado (es decir, debe tener "http://" o "https://"
antepuesto a él).

iOS y ATS
Desde la versión 9, iOS solo permitirá a su aplicación comunicarse con servidores que implementan la seguridad
de prácticas recomendadas de forma predeterminada. Los valores que se deben establecer Info.plist para
habilitar la comunicación con servidores no seguras.

NOTE
Si la aplicación requiere una conexión a un sitio Web no seguro, siempre debe escribir el dominio como una excepción
mediante NSExceptionDomains en lugar de desactivar que ATS completamente utilizando NSAllowsArbitraryLoads .
NSAllowsArbitraryLoads solo debe usarse en situaciones de emergencias extremas.

El siguiente muestra cómo habilitar un dominio específico (en este caso en xamarin.com) para omitir los
requisitos de ATS:

<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>xamarin.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
</dict>
</dict>
</dict>
...
</key>

Es una práctica recomendada para permitir solo algunos dominios omitir ATS, lo que permite utilizar los sitios de
confianza y seguir disfrutando de la seguridad adicional sobre los dominios de confianza. A continuación muestra
el método menos seguro de deshabilitar ATS para la aplicación:

<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads </key>
<true/>
</dict>
...
</key>
Consulte App Transport Security para obtener más información acerca de esta nueva característica de iOS 9.
Cadenas HTML
Si desea presentar una cadena HTML que se define de forma dinámica en el código, deberá crear una instancia de
HtmlWebViewSource :

var browser = new WebView();


var htmlSource = new HtmlWebViewSource();
htmlSource.Html = @"<html><body>
<h1>Xamarin.Forms</h1>
<p>Welcome to WebView.</p>
</body></html>";
browser.Source = htmlSource;

En el código anterior, @ se usa para marcar el código HTML como una cadena literal, lo que significa que se
omiten todos los caracteres de escape habituales.

NOTE
Puede ser necesario establecer el WidthRequest y HeightRequest propiedades de la WebView para ver el contenido
HTML, dependiendo del diseño el WebView es un elemento secundario. Por ejemplo, esto es necesario en un
StackLayout .

Contenido HTML
WebView puede mostrar el contenido de HTML, CSS y Javascript incrustados dentro de la aplicación. Por
ejemplo:
<html>
<head>
<title>Xamarin Forms</title>
</head>
<body>
<h1>Xamrin.Forms</h1>
<p>This is an iOS web page.</p>
<img src="XamarinLogo.png" />
</body>
</html>

CSS:

html,body {
margin:0;
padding:10;
}
body,p,h1 {
font-family: Chalkduster;
}

Tenga en cuenta que las fuentes especificadas en las CSS anterior debe personalizarse para cada plataforma, ya
que no todas las plataformas tiene las mismas fuentes.
Para mostrar local contenido mediante un WebView , deberá abrir el archivo HTML como cualquier otro, a
continuación, cargar el contenido como una cadena en el Html propiedad de un HtmlWebViewSource . Para obtener
más información sobre los archivos de apertura, consulte trabajar con archivos.
Las capturas de pantalla siguientes muestran el resultado de mostrar el contenido local en cada plataforma:

Aunque la primera página se ha cargado, el WebView no tiene conocimiento de dónde procede el código HTML.
Que es un problema cuando se trabaja con páginas que hacen referencia a los recursos locales. Cuando el vínculo
local páginas unos de otros, una página hace uso de un archivo JavaScript independiente o una página se vincula
a una hoja de estilos CSS son ejemplos de cuándo esto puede suceder.
Para resolver este problema, deberá indicar el WebView dónde encontrar los archivos en el sistema de archivos.
Hacerlo estableciendo la BaseUrl propiedad en el HtmlWebViewSource utilizado por el WebView .
Dado que el sistema de archivos en cada uno de los sistemas operativos es diferente, deberá determinar esa
dirección URL en cada plataforma. Xamarin.Forms presenta el DependencyService para resolver las dependencias
en tiempo de ejecución en cada plataforma.
Para usar el DependencyService , primero defina una interfaz que se puede implementar en cada plataforma:

public interface IBaseUrl { string Get(); }

Tenga en cuenta que hasta que la interfaz se implementa en cada plataforma, no se ejecutará la aplicación. En el
proyecto común, asegúrese de que recuerde establecer la BaseUrl utilizando el DependencyService :

var source = new HtmlWebViewSource();


source.BaseUrl = DependencyService.Get<IBaseUrl>().Get();

A continuación, se deben proporcionar implementaciones de la interfaz para cada plataforma.


iOS
En iOS, el contenido web debe estar ubicado en el directorio raíz del proyecto o recursos directorio con la acción
de compilación BundleResource, tal y como se muestra a continuación:
Visual Studio
Visual Studio para Mac
El BaseUrl debe establecerse en la ruta de acceso de la agrupación principal:

[assembly: Dependency (typeof (BaseUrl_iOS))]


namespace WorkingWithWebview.iOS
{
public class BaseUrl_iOS : IBaseUrl
{
public string Get()
{
return NSBundle.MainBundle.BundlePath;
}
}
}

Android
En Android, coloque las imágenes, CSS y HTML en la carpeta de activos con la acción de compilación
AndroidAsset como se muestra a continuación:
Visual Studio
Visual Studio para Mac
En Android, el BaseUrl debe establecerse en "file:///android_asset/" :

[assembly: Dependency (typeof(BaseUrl_Android))]


namespace WorkingWithWebview.Android
{
public class BaseUrl_Android : IBaseUrl
{
public string Get()
{
return "file:///android_asset/";
}
}
}

En Android, archivos en el activos carpeta también se puede acceder mediante el contexto de Android actual, que
se expone mediante el MainActivity.Instance propiedad:

var assetManager = MainActivity.Instance.Assets;


using (var streamReader = new StreamReader (assetManager.Open ("local.html")))
{
var html = streamReader.ReadToEnd ();
}

Plataforma universal de Windows


En los proyectos de plataforma Universal de Windows (UWP ), coloque HTML, CSS y las imágenes en la raíz del
proyecto con la acción de compilación establecida en contenido.
El BaseUrl debe establecerse en "ms-appx-web:///" :

[assembly: Dependency(typeof(BaseUrl))]
namespace WorkingWithWebview.UWP
{
public class BaseUrl : IBaseUrl
{
public string Get()
{
return "ms-appx-web:///";
}
}
}

Navegación
Vista Web admite la navegación a través de varios métodos y propiedades que están disponible:
GoForward() – si CanGoForward es true, una llamada a GoForward se desplaza hacia delante a la siguiente
página visitada.
GoBack() – si CanGoBack es true, una llamada a GoBack navegará a la última página visitada.
CanGoBack – true si hay páginas para ir a, false si el explorador se encuentre en la dirección URL de
inicio.
CanGoForward – true si el usuario ha navegado hacia atrás y puede avanzar a una página que ya ha
visitado.
En las páginas, WebView no es compatible con los gestos multitoque. Es importante para asegurarse de que el
contenido está optimizada para móviles y aparece sin la necesidad de zoom.
Es habitual que las aplicaciones mostrar un vínculo dentro de un WebView , en lugar del explorador del dispositivo.
En estos casos, resulta útil permitir la navegación normal, pero cuando el usuario eligió una mientras están en el
vínculo de inicio, la aplicación debe volver a la vista de aplicación normal.
Para habilitar este escenario, utilice las propiedades y métodos de navegación integrados.
Empiece por crear la página de la vista de explorador:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="WebViewSample.InAppBrowserXaml"
Title="Browser">
<StackLayout Margin="20">
<StackLayout Orientation="Horizontal">
<Button Text="Back" HorizontalOptions="StartAndExpand" Clicked="OnBackButtonClicked" />
<Button Text="Forward" HorizontalOptions="EndAndExpand" Clicked="OnForwardButtonClicked" />
</StackLayout>
<!-- WebView needs to be given height and width request within layouts to render. -->
<WebView x:Name="webView" WidthRequest="1000" HeightRequest="1000" />
</StackLayout>
</ContentPage>

En el código subyacente:
public partial class InAppBrowserXaml : ContentPage
{
public InAppBrowserXaml(string URL)
{
InitializeComponent();
webView.Source = URL;
}

async void OnBackButtonClicked(object sender, EventArgs e)


{
if (webView.CanGoBack)
{
webView.GoBack();
}
else
{
await Navigation.PopAsync();
}
}

void OnForwardButtonClicked(object sender, EventArgs e)


{
if (webView.CanGoForward)
{
webView.GoForward();
}
}
}

Ya está.

Eventos
WebView provoca los eventos siguientes para ayudarle a responder a los cambios de estado:
Navigating : evento se genera cuando la vista Web comienza a cargar una nueva página.
Navigated : evento se genera cuando se carga la página y se ha detenido la navegación.
ReloadRequested : evento se genera cuando se realiza una solicitud para volver a cargar el contenido actual.
El WebNavigatingEventArgs objeto que acompaña a la Navigating eventos tiene cuatro propiedades:
Cancel : indica si se deben cancelar la navegación.
NavigationEvent : el evento de navegación que se produjo.
Source : el elemento que realiza la navegación.
Url : el destino de navegación.

El WebNavigatedEventArgs objeto que acompaña a la Navigated eventos tiene cuatro propiedades:


NavigationEvent : el evento de navegación que se produjo.
Result : describe el resultado de la exploración, utilizando un WebNavigationResult miembro de enumeración.
Los valores válidos son Cancel , Failure , Success y Timeout .
Source : el elemento que realiza la navegación.
Url : el destino de navegación.

Si prevé que usará las páginas Web que toman mucho tiempo en cargar, considere el uso de la Navigating y
Navigated eventos para implementar un indicador de estado. Por ejemplo:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="WebViewSample.LoadingLabelXaml"
Title="Loading Demo">
<StackLayout>
<!--Loading label should not render by default.-->
<Label x:Name="labelLoading" Text="Loading..." IsVisible="false" />
<WebView HeightRequest="1000" WidthRequest="1000" Source="http://www.xamarin.com"
Navigated="webviewNavigated" Navigating="webviewNavigating" />
</StackLayout>
</ContentPage>

Los dos controladores de eventos:

void webviewNavigating(object sender, WebNavigatingEventArgs e)


{
labelLoading.IsVisible = true;
}

void webviewNavigated(object sender, WebNavigatedEventArgs e)


{
labelLoading.IsVisible = false;
}

Esto da como resultado el siguiente resultado (cargar):


Termina de cargar:
Volver a cargar contenido
WebView tiene un Reload método que se puede usar para volver a cargar el contenido actual:

var webView = new WebView();


...
webView.Reload();

Cuando el Reload método se invoca el ReloadRequested se desencadena el evento, que indica que se ha realizado
una solicitud para volver a cargar el contenido actual.

Rendimiento
Ahora, los exploradores web populares adoptarán las tecnologías como la representación y la compilación de
JavaScript de aceleración de hardware. En iOS, de forma predeterminada, Xamarin.Forms WebView se
implementa mediante el UIWebView clase y muchas de estas tecnologías no están disponibles en esta
implementación. Sin embargo, una aplicación puede participar en utilizar iOS WkWebView clase implemente
Xamarin.Forms WebView , que es compatible con una navegación más rápida. Esto se consigue agregando el
código siguiente a la AssemblyInfo.cs archivo en el proyecto de la plataforma iOS para la aplicación:

// Opt-in to using WkWebView instead of UIWebView.


[assembly: ExportRenderer(typeof(WebView), typeof(Xamarin.Forms.Platform.iOS.WkWebViewRenderer))]

WebView en Android de forma predeterminada es tan rápido como el explorador integrado.


El UWP WebView usa el motor de representación Microsoft Edge. Los dispositivos de escritorio y de tableta
deben aparecer el mismo rendimiento como con el propio explorador Edge.

Permisos
En orden para WebView para que funcione, debe asegurarse de que los permisos se establecen para cada
plataforma. Tenga en cuenta que en algunas plataformas, WebView funcionará en modo de depuración, pero no
cuando se compila para la versión. Eso es porque algunos permisos, como los de acceso a internet en Android, se
establecen de manera predeterminada Visual Studio para Mac en el modo de depuración.
UWP – requiere la capacidad de Internet (cliente y servidor) al mostrar el contenido de la red.
Android – requiere INTERNET solo al mostrar el contenido de la red. El contenido local no requiere ningún
permiso especial.
iOS – no requiere permisos especiales.

Diseño
A diferencia de la mayoría de vistas de Xamarin.Forms, WebView requiere que HeightRequest y WidthRequest se
especifican al contenido en StackLayout o RelativeLayout. Si no especifica las propiedades, los WebView no se
representará.
Los ejemplos siguientes muestran los diseños que dar lugar a trabajar, representación WebView s:
StackLayout con WidthRequest & HeightRequest:
<StackLayout>
<Label Text="test" />
<WebView Source="http://www.xamarin.com/"
HeightRequest="1000"
WidthRequest="1000" />
</StackLayout>

RelativeLayout con WidthRequest & HeightRequest:

<RelativeLayout>
<Label Text="test"
RelativeLayout.XConstraint= "{ConstraintExpression
Type=Constant, Constant=10}"
RelativeLayout.YConstraint= "{ConstraintExpression
Type=Constant, Constant=20}" />
<WebView Source="http://www.xamarin.com/"
RelativeLayout.XConstraint="{ConstraintExpression Type=Constant,
Constant=10}"
RelativeLayout.YConstraint="{ConstraintExpression Type=Constant,
Constant=50}"
WidthRequest="1000" HeightRequest="1000" />
</RelativeLayout>

AbsoluteLayout sin WidthRequest & HeightRequest:

<AbsoluteLayout>
<Label Text="test" AbsoluteLayout.LayoutBounds="0,0,100,100" />
<WebView Source="http://www.xamarin.com/"
AbsoluteLayout.LayoutBounds="0,150,500,500" />
</AbsoluteLayout>

Cuadrícula sin WidthRequest & HeightRequest. Cuadrícula es uno de los diseños de algunas que no necesita
especificar los anchos y altos solicitadas.:

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="test" Grid.Row="0" />
<WebView Source="http://www.xamarin.com/" Grid.Row="1" />
</Grid>

Invocación de JavaScript
WebView incluye la capacidad para invocar una función de JavaScript desde C#y se devuelve ningún resultado
para la llamada a C# código. Esto se consigue con la WebView.EvaluateJavaScriptAsync método, que se muestra en
el ejemplo siguiente de la WebView ejemplo:

var numberEntry = new Entry { Text = "5" };


var resultLabel = new Label();
var webView = new WebView();
...

int number = int.Parse(numberEntry.Text);


string result = await webView.EvaluateJavaScriptAsync($"factorial({number})");
resultLabel.Text = $"Factorial of {number} is {result}.";
El WebView.EvaluateJavaScriptAsync método evalúa el código JavaScript que se especifica como argumento y
devuelve los resultados como un string . En este ejemplo, el factorial se invoca la función de JavaScript, que
devuelve el factorial de number como resultado. Esta función se define en el código HTML local de JavaScript de
archivos que el WebView carga y se muestra en el ejemplo siguiente:

<html>
<body>
<script type="text/javascript">
function factorial(num) {
if (num === 0 || num === 1)
return 1;
for (var i = num - 1; i >= 1; i--) {
num *= i;
}
return num;
}
</script>
</body>
</html>

Vínculos relacionados
Trabajar con la vista Web (ejemplo)
Vista Web (ejemplo)
Características de la plataforma de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Xamarin.Forms es extensible y le permite incorporar características específicas de plataforma mediante efectos,


representadores personalizados, DependencyService, el MessagingCentery mucho más.

Android
Esta guía describe los detalles de plataforma Android proporcionados por Xamarin.Forms y cómo implementar
Material Design mediante la actualización de aplicaciones de Xamarin.Forms Android existentes.

Clase de dispositivo
Esta guía describe cómo usar el Device clase para crear un comportamiento específico de la plataforma en código
compartido y la interfaz de usuario (incluido el uso de XAML ) y cómo interactuar con los controles de interfaz de
usuario de subprocesos en segundo plano.

iOS
Esta guía describen los detalles plataforma de iOS proporcionados por Xamarin.Forms y cómo realizar aplicar
estilos a través de iOS adicionales Info.plist y UIAppearance API.

Formularios nativos
Formularios nativos permiten Xamarin.Forms ContentPage -derivados de las páginas que será consumido por los
proyectos nativos de Xamarin.iOS, Xamarin.Android y plataforma Universal de Windows (UWP ).

Vistas nativas
Pueden hacer referencia directamente a vistas nativas de iOS, Android y la plataforma Universal de Windows de
Xamarin.Forms. Se pueden establecer las propiedades y los controladores de eventos en vistas nativas, y pueden
interactuar con las vistas de Xamarin.Forms.

Funcionalidades específicas de plataforma


Funcionalidades específicas de plataforma permiten utilizar la funcionalidad que solo está disponible en una
plataforma concreta, sin necesidad de representadores personalizados o los efectos. Además, los proveedores
pueden crear sus propias funcionalidades específicas de plataforma con efectos.

Windows
Esta guía describe los detalles plataforma de Windows proporcionados Xamarin.Forms y cómo agregar un
proyecto UWP a una solución existente de Xamarin.Forms.
Características de la plataforma Android
11/07/2019 • 5 minutes to read • Edit Online

Desarrollo de aplicaciones de Xamarin.Forms para Android requiere Visual Studio. El página requisitos contiene
más información sobre los requisitos previos.

Funcionalidades específicas de plataforma


Funcionalidades específicas de plataforma permiten utilizar la funcionalidad que solo está disponible en una
plataforma concreta, sin necesidad de implementar los representadores personalizados o los efectos.
La siguiente funcionalidad específica de la plataforma se proporciona para los diseños en Android, páginas y las
vistas de Xamarin.Forms:
Controlar el orden Z de los elementos visuales para determinar el orden de dibujo. Para obtener más
información, consulte VisualElement elevación en Android.
Deshabilitar el modo de color heredado en compatible VisualElement . Para obtener más información,
consulte VisualElement modo de Color heredado en Android.
Se proporciona la siguiente funcionalidad específica de la plataforma para las vistas de Xamarin.Forms en
Android:
Utilizando los valores de la sombra de los botones de Android y el relleno predeterminado. Para obtener más
información, consulte relleno de botón y las sombras en Android.
Establecer el método de entrada de opciones del editor para el teclado en pantalla para una Entry . Para
obtener más información, consulte opciones del Editor de métodos de entrada de entrada en Android.
Habilitación de una sombra paralela en un ImageButton . Para obtener más información, consulte ImageButton
Drop sombras en Android.
Habilitar el desplazamiento rápido en un ListView para obtener más información, consulte desplazamiento
rápido ListView en Android.
Controlar si un WebView puede mostrar contenido mixto. Para obtener más información, consulte WebView al
contenido mixto en Android.
Habilitar zoom en un WebView . Para obtener más información, consulte WebView Zoom en Android.
Se proporciona la siguiente funcionalidad específica de la plataforma para las páginas de Xamarin.Forms en
Android:
Establecer el alto de la barra de navegación en un NavigationPage . Para obtener más información, consulte
alto de la barra de NavigationPage en Android.
Deshabilitar las animaciones de transición al navegar a través de páginas en un TabbedPage . Para obtener más
información, consulte animaciones de transición de página TabbedPage en Android.
Habilitar el gesto de deslizar rápidamente entre las páginas de un TabbedPage . Para obtener más información,
consulte TabbedPage página desplazándose en Android.
Establecer la posición de la barra de herramientas y el color en un TabbedPage . Para obtener más información,
consulte TabbedPage barra de herramientas ubicación y Color en Android.
Se proporciona la siguiente funcionalidad específica de la plataforma para Xamarin.Forms Application clase en
Android:
Establecer el modo de funcionamiento de un teclado en pantalla. Para obtener más información, consulte el
modo de entrada de teclado temporal en Android.
Deshabilitar la Disappearing y Appearing página eventos de ciclo de vida en pausa y reanudar,
respectivamente, para las aplicaciones que usan AppCompat. Para obtener más información, consulte eventos
de ciclo de vida de página en Android.

Compatibilidad con la plataforma


Originalmente, el valor predeterminado de proyecto de Xamarin.Forms para Android usa un estilo anterior de la
representación del control que era común anteriores a Android 5.0. Las aplicaciones compiladas con la plantilla
tienen FormsApplicationActivity como clase base de su actividad principal.

Diseño material a través de AppCompat


Ahora, use los proyectos de Xamarin.Forms Android FormsAppCompatActivity como clase base de su actividad
principal. Esta clase usa AppCompat características proporcionadas por Android para implementar los temas de
diseño de materiales.
Para agregar temas de diseño de Material a su proyecto de Xamarin.Forms Android, siga el admiten instrucciones
de instalación de AppCompat
Este es el Todo ejemplo con el valor predeterminado FormsApplicationActivity :

Y este es el mismo código después de actualizar el proyecto para usar FormsAppCompatActivity (y agregar la
información del tema adicional):
NOTE
Cuando se usa FormsAppCompatActivity , clases base para algunos representadores personalizados Android será diferente.

Vínculos relacionados
Agregar compatibilidad con Material Design
Adición de AppCompat y Material Design
11/07/2019 • 4 minutes to read • Edit Online

Siga estos pasos para convertir aplicaciones de Xamarin.Forms Android existentes para usar AppCompat y
Material Design

Información general
Estas instrucciones explican cómo actualizar las aplicaciones de Xamarin.Forms Android existentes para usar la
biblioteca AppCompat y habilitar el Material de diseño en la versión de Android de sus aplicaciones de
Xamarin.Forms.
1. Actualización de Xamarin.Forms
Asegúrese de que la solución está usando Xamarin.Forms 2.0 o posterior. Si es necesario, actualice el paquete de
Xamarin.Forms Nuget a 2.0.
2. Comprobar la versión de Android
Asegúrese de que .NET framework de destino del proyecto Android es Android 6.0 (Marshmallow ). Compruebe el
proyecto Android > Opciones > compilar > General configuración garantiza que el marco de trabajo corrent
está seleccionado:

3. Agregar nuevos temas para admitir Material Design


Cree los tres archivos siguientes en el proyecto de Android y pegue el contenido siguiente. Google Proporciona
una Guía de estilo y un generador de la paleta de colores para ayudarle a elegir una combinación de colores
alternativos a la especificada.
Resources/values/colors.xml

<resources>
<color name="primary">#2196F3</color>
<color name="primaryDark">#1976D2</color>
<color name="accent">#FFC107</color>
<color name="window_background">#F5F5F5</color>
</resources>

Resources/values/style.xml
<resources>
<style name="MyTheme" parent="MyTheme.Base">
</style>
<style name="MyTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primaryDark</item>
<item name="colorAccent">@color/accent</item>
<item name="android:windowBackground">@color/window_background</item>
<item name="windowActionModeOverlay">true</item>
</style>
</resources>

Un estilo adicionales debe incluirse en el valores v21 carpeta para aplicar propiedades específicas cuando se
ejecuta en Android Lollipop y versiones más recientes.
Resources/values-v21/style.xml

<resources>
<style name="MyTheme" parent="MyTheme.Base">
<!--If you are using MasterDetailPage you will want to set these, else you can leave them out-->
<!--<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>-->
</style>
</resources>

4. Update AndroidManifest.xml
Para garantizar este nuevo tema información sea utilizado, establezca el tema en el AndroidManifest archivo
agregando android:theme="@style/MyTheme" (deje el resto del código XML tal como estaba).
Properties/AndroidManifest.xml

...
<application android:label="AppName" android:icon="@drawable/icon"
android:theme="@style/MyTheme">
...

5. Proporcionar diseños de pestaña y la barra de herramientas


Crear Tabbar.axml y Toolbar.axml archivos en el y diseño de los recursos directorio y péguelo en su contenido
desde abajo:
Resources/layout/Tabbar.axml

<android.support.design.widget.TabLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:tabIndicatorColor="@android:color/white"
app:tabGravity="fill"
app:tabMode="fixed" />

Algunas propiedades de las pestañas se han establecido como la gravedad de la ficha fill y modo a fixed . Si
tiene muchas pestañas desea cambiarlo a desplazable - lea el Android TabLayout documentación para obtener
más información.
Resources/layout/Toolbar.axml

<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways" />

En estos archivos estamos creando un tema específico de la barra de herramientas que puede variar para la
aplicación. Hacer referencia a la barra de herramientas Hello del blog para obtener más información.
6. Actualización de la MainActivity

En las aplicaciones existentes de Xamarin.Forms el MainActivity.cs clase heredará de FormsApplicationActivity .


Esto se debe reemplazar con FormsAppCompatActivity para habilitar la funcionalidad nueva.
MainActivity.cs

public class MainActivity : FormsAppCompatActivity // was FormsApplicationActivity

Por último, "conectar" los diseños del nuevo del paso 5 en el OnCreate método, como se muestra aquí:

protected override void OnCreate(Bundle bundle)


{
// set the layout resources first
FormsAppCompatActivity.ToolbarResource = Resource.Layout.Toolbar;
FormsAppCompatActivity.TabLayoutResource = Resource.Layout.Tabbar;

// then call base.OnCreate and the Xamarin.Forms methods


base.OnCreate(bundle);
Forms.Init(this, bundle);
LoadApplication(new App());
}
Relleno de botón y las sombras en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma controla si los botones de Xamarin.Forms usan los valores de sombra de
botones de Android y el relleno predeterminado. Se consume en XAML estableciendo el
Button.UseDefaultPadding y Button.UseDefaultShadow adjunta propiedades a boolean valores:

<ContentPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core">
<StackLayout>
...
<Button ...
android:Button.UseDefaultPadding="true"
android:Button.UseDefaultShadow="true" />
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

button.On<Android>().SetUseDefaultPadding(true).SetUseDefaultShadow(true);

El Button.On<Android>método especifica que solo se ejecutarán este específicos de la plataforma en Android. El


Button.SetUseDefaultPadding y Button.SetUseDefaultShadow métodos, en el
Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de nombres, se utilizan para controlar si los botones
de Xamarin.Forms usan el valor predeterminado relleno y los valores de la sombra de botones de Android.
Además, el Button.UseDefaultPadding y Button.UseDefaultShadow métodos pueden usarse para devolver si un
botón usa el valor predeterminado de relleno valor y el valor de la sombra de forma predeterminada,
respectivamente.
El resultado es que los botones de Xamarin.Forms pueden usar el relleno predeterminado y los valores de sombra
de los botones de Android:

Tenga en cuenta que en la captura de pantalla encima de cada Button tiene definiciones idénticas, salvo que el
derecho Button utiliza el relleno predeterminado y los valores de sombra de los botones de Android.
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Opciones del Editor de métodos de entrada de
entrada en Android
11/07/2019 • 4 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma Android establece el método de entrada de opciones del editor (IME ) para el
teclado en pantalla para una Entry . Esto incluye la configuración del botón de acción del usuario en la esquina
inferior del teclado en pantalla y las interacciones con el Entry . Se consume en XAML estableciendo el
Entry.ImeOptions propiedad adjunta a un valor de la ImeFlags enumeración:

<ContentPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core">
<StackLayout ...>
<Entry ... android:Entry.ImeOptions="Send" />
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

entry.On<Android>().SetImeOptions(ImeFlags.Send);

El Entry.On<Android> método especifica que solo se ejecutarán este específicos de la plataforma en Android. El
Entry.SetImeOptions método, en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de nombres, se
usa para establecer la opción de acción del método de entrada para el teclado en pantalla para el Entry , con el
ImeFlags enumeración que proporciona los siguientes valores:

Default : indica que se requiere ninguna acción específica de clave y que el control subyacente producirá sí
solo si puede. Esto será Next o Done .
None : indica que ninguna clave de la acción estará disponible.
Go : indica que la clave de acción llevará a cabo una operación de "Ir", al usuario en el destino del texto escrito.
Search : indica que la clave de acción realiza una operación de "búsqueda", al usuario a los resultados de
buscar el texto han escrito.
Send : indica que la clave de acción llevará a cabo una operación de "Enviar", que se entrega el texto a su
destino.
Next : indica que la clave de acción llevará a cabo una operación de "siguiente", tomar el usuario al siguiente
campo que aceptará el texto.
Done : indica que la clave de acción llevará a cabo una operación de "done", el teclado en pantalla de cierre.
Previous : indica que la clave de acción llevará a cabo una operación de "anterior", tomar el usuario al campo
anterior que aceptará el texto.
ImeMaskAction – la máscara para seleccionar las opciones de acción.
NoPersonalizedLearning : indica que el corrector ortográfico se obtenga la información del usuario, ni sugerir
correcciones según lo que el usuario ha escrito anteriormente.
NoFullscreen : indica que la interfaz de usuario no debería pasar a pantalla completa.
NoExtractUi : indica que no se mostrará ninguna interfaz de usuario para el texto extraído.
NoAccessoryAction : indica que no se mostrará ninguna interfaz de usuario para las acciones personalizadas.

El resultado es que un determinado ImeFlags se aplica el valor para el teclado en pantalla para el Entry , que
establece el método de entrada en las opciones del editor:

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
ImageButton sombras en Android
11/07/2019 • 3 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma se usan para habilitar una sombra paralela en un ImageButton . Se
consume en XAML estableciendo el ImageButton.IsShadowEnabled propiedad enlazable a true , junto con un
número de propiedades enlazables opcionales adicionales que controlan el efecto de sombra:

<ContentPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core">
<StackLayout Margin="20">
<ImageButton ...
Source="XamarinLogo.png"
BackgroundColor="GhostWhite"
android:ImageButton.IsShadowEnabled="true"
android:ImageButton.ShadowColor="Gray"
android:ImageButton.ShadowRadius="12">
<android:ImageButton.ShadowOffset>
<Size>
<x:Arguments>
<x:Double>10</x:Double>
<x:Double>10</x:Double>
</x:Arguments>
</Size>
</android:ImageButton.ShadowOffset>
</ImageButton>
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

var imageButton = new Xamarin.Forms.ImageButton { Source = "XamarinLogo.png", BackgroundColor =


Color.GhostWhite, ... };
imageButton.On<Android>()
.SetIsShadowEnabled(true)
.SetShadowColor(Color.Gray)
.SetShadowOffset(new Size(10, 10))
.SetShadowRadius(12);

IMPORTANT
Se dibuja una sombra paralela como parte de la ImageButton en segundo plano y el fondo se dibuja solo si el
BackgroundColor se establece la propiedad. Por lo tanto, una sombra paralela no se dibujarán si el
ImageButton.BackgroundColor no se establece la propiedad.

El ImageButton.On<Android> método especifica que solo se ejecutarán este específicos de la plataforma en Android.
El ImageButton.SetIsShadowEnabled método, en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de
nombres, se usa para controlar si una sombra paralela está habilitada en el ImageButton . Además, se pueden
invocar los métodos siguientes para controlar el efecto de sombra:
SetShadowColor : establece el color de la sombra paralela. El color predeterminado es Color.Default .
SetShadowOffset : establece el desplazamiento de la sombra paralela. El desplazamiento cambia la dirección de
la sombra se convierte y se especifica como un Size valor. El Size los valores de la estructura se expresan en
unidades independientes del dispositivo, con el primer valor que se va a la distancia a la izquierda (valor
negativo) o la derecha (valor positivo) y el segundo valor que se va a la distancia anterior (negativo) o por
debajo (valor positivo) . El valor predeterminado de esta propiedad es (0.0, 0.0), que da como resultado la
sombra que se va a convertir en torno a cada lado de la ImageButton .
SetShadowRadius : establece el radio de desenfoque utilizado para representar la sombra paralela. El valor de
radio predeterminado es 10,0.

NOTE
Se puede consultar el estado de una sombra paralela mediante una llamada a la GetIsShadowEnabled , GetShadowColor ,
GetShadowOffset , y GetShadowRadius métodos.

El resultado es que se puede habilitar una sombra paralela en un ImageButton :

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Fast ListView desplazamiento en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma se usan para habilitar el desplazamiento rápido a través de los datos en un
ListView . Se consume en XAML estableciendo el ListView.IsFastScrollEnabled propiedad adjunta un boolean
valor:

<ContentPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core">
<StackLayout Margin="20">
...
<ListView ItemsSource="{Binding GroupedEmployees}"
GroupDisplayBinding="{Binding Key}"
IsGroupingEnabled="true"
android:ListView.IsFastScrollEnabled="true">
...
</ListView>
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

var listView = new Xamarin.Forms.ListView { IsGroupingEnabled = true, ... };


listView.SetBinding(ItemsView<Cell>.ItemsSourceProperty, "GroupedEmployees");
listView.GroupDisplayBinding = new Binding("Key");
listView.On<Android>().SetIsFastScrollEnabled(true);

El ListView.On<Android>método especifica que solo se ejecutarán este específicos de la plataforma en Android. El


ListView.SetIsFastScrollEnabled método, en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de
nombres, se usa para habilitar el desplazamiento rápido a través de los datos en un ListView . Además, el
SetIsFastScrollEnabled método puede utilizarse para activar o desactivar el desplazamiento rápido mediante una
llamada a la IsFastScrollEnabled método para devolver si se habilita el desplazamiento rápido:

listView.On<Android>().SetIsFastScrollEnabled(!listView.On<Android>().IsFastScrollEnabled());

El resultado es ese desplazamiento rápido a través de los datos en un ListView puede habilitarse, que cambia el
tamaño de la miniatura de desplazamiento:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Alto de la barra de NavigationPage en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma establece el alto de la barra de navegación en un NavigationPage . Se
consume en XAML estableciendo el NavigationPage.BarHeight propiedad enlazable en un valor entero:

<NavigationPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat;assembly=Xamarin.Forms.Core"
android:NavigationPage.BarHeight="450">
...
</NavigationPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat;
...

public class AndroidNavigationPageCS : Xamarin.Forms.NavigationPage


{
public AndroidNavigationPageCS()
{
On<Android>().SetBarHeight(450);
}
}

El NavigationPage.On<Android> método especifica que solo se ejecutarán este específicos de la plataforma en la


compatibilidad de aplicaciones Android. El NavigationPage.SetBarHeight método, en el
Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat espacio de nombres, se usa para establecer el alto
de la barra de navegación en un NavigationPage . Además, el NavigationPage.GetBarHeight método puede
utilizarse para devolver el alto de la barra de navegación en el NavigationPage .
El resultado es que el alto de la barra de navegación en un NavigationPage se pueden establecer:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Eventos de ciclo de vida de página en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma se usan para deshabilitar la Disappearing y Appearing eventos de página
en la aplicación pausar y reanudar, respectivamente, para las aplicaciones que usan AppCompat. Además, incluye
la capacidad para controlar si se muestra el teclado en pantalla al reanudar si se mostrara en pausa, siempre que el
modo de funcionamiento del teclado en pantalla se establece en WindowSoftInputModeAdjust.Resize .

NOTE
Tenga en cuenta que estos eventos están habilitados de forma predeterminada para conservar el comportamiento existente
para las aplicaciones que dependen de los eventos. Deshabilitar estos eventos hace que el ciclo de eventos AppCompat
coincide con el ciclo de eventos anteriores a AppCompat.

Esta plataforma específicas que pueden utilizarse en XAML estableciendo el


Application.SendDisappearingEventOnPause , Application.SendAppearingEventOnResume , y
Application.ShouldPreserveKeyboardOnResume propiedades adjuntas a boolean valores:

<Application ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
xmlns:androidAppCompat="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat;assembly=Xamarin.Forms.Core"
android:Application.WindowSoftInputModeAdjust="Resize"
androidAppCompat:Application.SendDisappearingEventOnPause="false"
androidAppCompat:Application.SendAppearingEventOnResume="false"
androidAppCompat:Application.ShouldPreserveKeyboardOnResume="true">
...
</Application>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat;
...

Xamarin.Forms.Application.Current.On<Android>()
.UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize)
.SendDisappearingEventOnPause(false)
.SendAppearingEventOnResume(false)
.ShouldPreserveKeyboardOnResume(true);

El Application.Current.On<Android> método especifica que solo se ejecutarán este específicos de la plataforma en


Android. El Application.SendDisappearingEventOnPause método, en el
Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat espacio de nombres, se usa para habilitar o
deshabilitar la activación de la Disappearing eventos de página, cuando la aplicación entra en segundo plano. El
Application.SendAppearingEventOnResume método se utiliza para habilitar o deshabilitar la activación de la
Appearing eventos de página, cuando se reanuda la aplicación en segundo plano. El
Application.ShouldPreserveKeyboardOnResume se usa el método de control si el teclado en pantalla se muestra en la
reanudación, si se mostró en pausa, proporcionado que se establece el modo de funcionamiento del teclado en
pantalla en WindowSoftInputModeAdjust.Resize .
El resultado es que el Disappearing y Appearing eventos de página no se desencadena en pausa de la aplicación y
reanudación, respectivamente, y fue el teclado en pantalla de ese if que se muestra cuando la aplicación estaba en
pausa, también se mostrará cuando se reanuda la aplicación:

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Modo de entrada de teclado en pantalla en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma se usa para establecer el modo de funcionamiento para un área de entrada
de teclado en pantalla y se consume en XAML estableciendo el Application.WindowSoftInputModeAdjust propiedad
adjunta a un valor de la WindowSoftInputModeAdjust enumeración:

<Application ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:Application.WindowSoftInputModeAdjust="Resize">
...
</Application>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

App.Current.On<Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);

El Application.On<Android> método especifica que solo se ejecutarán este específicos de la plataforma en Android.
El Application.UseWindowSoftInputModeAdjust método, en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific
espacio de nombres, se usa para establecer el modo de funcionamiento del área de entrada de teclado en pantalla,
con el WindowSoftInputModeAdjust enumeración que proporciona dos valores: Pan y Resize . El Pan valor usa la
AdjustPan la opción de ajuste, que no cambiar el tamaño de la ventana cuando un control de entrada tiene el foco.
En su lugar, el contenido de la ventana distribuido para que el foco actual no está oculto por el teclado en pantalla.
El Resize valor usa la AdjustResize la opción de ajuste, que cambia el tamaño de la ventana cuando un control
de entrada tiene el foco, para dejar espacio para el teclado en pantalla.
El resultado es que el teclado en pantalla se puede establecer el modo de funcionamiento cuando un control de
entrada tiene el foco del área de entrada:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Página TabbedPage desplazándose en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma se utilizan para habilitar el gesto de deslizar rápidamente con un gesto del
dedo horizontal entre las páginas de un TabbedPage . Se consume en XAML estableciendo el
TabbedPage.IsSwipePagingEnabled propiedad adjunta un boolean valor:

<TabbedPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:TabbedPage.OffscreenPageLimit="2"
android:TabbedPage.IsSwipePagingEnabled="true">
...
</TabbedPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

On<Android>().SetOffscreenPageLimit(2)
.SetIsSwipePagingEnabled(true);

El TabbedPage.On<Android> método especifica que solo se ejecutarán este específicos de la plataforma en Android.
El TabbedPage.SetIsSwipePagingEnabled método, en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific
espacio de nombres, se utiliza para habilitar el gesto de deslizar rápidamente entre las páginas de un TabbedPage .
Además, el TabbedPage clase en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de nombres
también tiene un EnableSwipePaging método que permite este específicos de la plataforma, y un
DisableSwipePaging método que deshabilita Esta plataforma específica. El TabbedPage.OffscreenPageLimit
propiedad adjunta, y SetOffscreenPageLimit método, se utilizan para establecer el número de páginas que se
deben conservar en un estado de inactividad a cada lado de la página actual.
El resultado es que la paginación a través de las páginas se muestran por Deslizar rápidamente un TabbedPage
está habilitado:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Animaciones de transición de página TabbedPage en
Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma se usan para deshabilitar las animaciones de transición al navegar a través
de páginas, ya sea mediante programación o cuando se usa la barra de pestañas, en un TabbedPage . Se consume
en XAML estableciendo el TabbedPage.IsSmoothScrollEnabled propiedad enlazable a false :

<TabbedPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:TabbedPage.IsSmoothScrollEnabled="false">
...
</TabbedPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

On<Android>().SetIsSmoothScrollEnabled(false);

El TabbedPage.On<Android> método especifica que solo se ejecutarán este específicos de la plataforma en Android.
El TabbedPage.SetIsSmoothScrollEnabled método, en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific
espacio de nombres, se usa para controlar si las animaciones de transición se mostrará al navegar entre páginas
en un TabbedPage . Además, el TabbedPage clase en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific
espacio de nombres también tiene los métodos siguientes:
IsSmoothScrollEnabled , que se usa para recuperar datos si las animaciones de transición se mostrará al navegar
entre páginas en un TabbedPage .
EnableSmoothScroll , que se usa para habilitar animaciones de transición al navegar entre páginas en un
TabbedPage .
DisableSmoothScroll , que se usa para deshabilitar las animaciones de transición al navegar entre páginas en un
TabbedPage .

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Colocación de la barra de herramientas TabbedPage
y Color en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo

IMPORTANT
Las funcionalidades específicas de plataforma que establece el color de la barra de herramientas en un TabbedPage ahora
están obsoletos y se han reemplazado por la SelectedTabColor y UnselectedTabColor propiedades. Para obtener más
información, consulte creando una TabbedPage.

Estas funcionalidades específicas de plataforma se utilizan para establecer la selección de ubicación y color de la
barra de herramientas en un TabbedPage . Que se consumen en XAML estableciendo el
TabbedPage.ToolbarPlacement propiedad adjunta a un valor de la ToolbarPlacement enumeración y el
TabbedPage.BarItemColor y TabbedPage.BarSelectedItemColor adjunta propiedades a un Color :

<TabbedPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:TabbedPage.ToolbarPlacement="Bottom"
android:TabbedPage.BarItemColor="Black"
android:TabbedPage.BarSelectedItemColor="Red">
...
</TabbedPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

On<Android>().SetToolbarPlacement(ToolbarPlacement.Bottom)
.SetBarItemColor(Color.Black)
.SetBarSelectedItemColor(Color.Red);

El TabbedPage.On<Android> método especifica que solo se ejecutarán estas funcionalidades específicas de


plataforma en Android. El TabbedPage.SetToolbarPlacement método, en el
Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de nombres, se usa para establecer la posición de la
barra de herramientas en un TabbedPage , con el ToolbarPlacement enumeración que proporciona los siguientes
valores:
Default : indica que la barra de herramientas se coloca en la ubicación predeterminada en la página. Se trata
de la parte superior de la página en teléfonos y la parte inferior de la página en otras expresiones de
dispositivo.
Top : indica que la barra de herramientas se coloca en la parte superior de la página.
Bottom : indica que la barra de herramientas se coloca en la parte inferior de la página.

Además, el TabbedPage.SetBarItemColor y TabbedPage.SetBarSelectedItemColor métodos se usan para establecer el


color de elementos de barra de herramientas y elementos de barra de herramientas seleccionado,
respectivamente.

NOTE
El GetToolbarPlacement , GetBarItemColor , y GetBarSelectedItemColor métodos pueden usarse para recuperar la
selección de ubicación y color de la TabbedPage barra de herramientas.

El resultado es que se pueden establecer la posición de la barra de herramientas, el color de los elementos de la
barra de herramientas y el color del elemento seleccionado de la barra de herramientas en un TabbedPage :

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Elevación VisualElement en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma se usa para controlar la elevación o el orden Z de los elementos visuales en
aplicaciones que tienen como destino API 21 o mayor. La elevación de un elemento visual determina su orden de
dibujo, con elementos visuales con los valores más altos de Z occluding elementos visuales con los valores más
bajos de Z. Se consume en XAML estableciendo el VisualElement.Elevation propiedad adjunta un boolean valor:

<ContentPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
Title="Elevation">
<StackLayout>
<Grid>
<Button Text="Button Beneath BoxView" />
<BoxView Color="Red" Opacity="0.2" HeightRequest="50" />
</Grid>
<Grid Margin="0,20,0,0">
<Button Text="Button Above BoxView - Click Me" android:VisualElement.Elevation="10"/>
<BoxView Color="Red" Opacity="0.2" HeightRequest="50" />
</Grid>
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:


using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

public class AndroidElevationPageCS : ContentPage


{
public AndroidElevationPageCS()
{
...
var aboveButton = new Button { Text = "Button Above BoxView - Click Me" };
aboveButton.On<Android>().SetElevation(10);

Content = new StackLayout


{
Children =
{
new Grid
{
Children =
{
new Button { Text = "Button Beneath BoxView" },
new BoxView { Color = Color.Red, Opacity = 0.2, HeightRequest = 50 }
}
},
new Grid
{
Margin = new Thickness(0,20,0,0),
Children =
{
aboveButton,
new BoxView { Color = Color.Red, Opacity = 0.2, HeightRequest = 50 }
}
}
}
};
}
}

El Button.On<Android>método especifica que solo se ejecutarán este específicos de la plataforma en Android. El


VisualElement.SetElevation método, en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de
nombres, se usa para establecer la elevación del elemento visual que acepta valores NULL float . Además, el
VisualElement.GetElevation método puede utilizarse para recuperar el valor de elevación de un elemento visual.

El resultado es que se puede controlar la elevación de los elementos visuales para que los elementos visuales con
los valores más altos de Z occlude elementos visuales con los valores más bajos de Z. Por lo tanto, en este ejemplo
el segundo Button se representa por encima del BoxView porque tiene un valor mayor de elevación:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Modo de Color VisualElement heredado en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Algunas de las vistas de Xamarin.Forms cuentan con un modo de color heredado. En este modo, cuando el
IsEnabled se establece la propiedad de la vista en false , la vista invalida los colores establecidos por el usuario
con los colores nativo predeterminado para el estado deshabilitado. Para hacia atrás compatibilidad, este modo
heredado de color permanece el comportamiento predeterminado para las vistas admitidas.
Este Android específicos de plataforma deshabilita este modo heredado de color, para que los colores establecidos
en una vista por el usuario permanezcan incluso cuando la vista está deshabilitada. Se consume en XAML
estableciendo el VisualElement.IsLegacyColorModeEnabled propiedad adjunta false :

<ContentPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core">
<StackLayout>
...
<Button Text="Button"
TextColor="Blue"
BackgroundColor="Bisque"
android:VisualElement.IsLegacyColorModeEnabled="False" />
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

_legacyColorModeDisabledButton.On<Android>().SetIsLegacyColorModeEnabled(false);

El VisualElement.On<Android> método especifica que solo se ejecutarán este específicos de la plataforma en


Android. El VisualElement.SetIsLegacyColorModeEnabled método, en el
Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de nombres, se usa para controlar si se deshabilita el
modo de color heredado. Además, el VisualElement.GetIsLegacyColorModeEnabled método puede utilizarse para
devolver si está deshabilitado el modo de color heredado.
El resultado es que se puede deshabilitar el modo de color heredados, para que los colores establecidos en una
vista por el usuario permanezcan incluso cuando se deshabilita la vista:
NOTE
Al establecer un VisualStateGroup en una vista, se omite completamente el modo de color heredado. Para obtener más
información acerca de los estados visuales, vea Xamarin.Forms Visual State Manager.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
WebView mezclado en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma controla si un WebView puede mostrar contenido mixto en aplicaciones
que tienen como destino API 21 o posterior. El contenido mixto es el contenido que se cargó inicialmente a través
de una conexión HTTPS, pero que cargan recursos (por ejemplo, imágenes, audio, vídeo, hojas de estilo, scripts) en
una conexión HTTP. Se consume en XAML estableciendo el WebView.MixedContentMode propiedad adjunta a un
valor de la MixedContentHandling enumeración:

<ContentPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core">
<WebView ... android:WebView.MixedContentMode="AlwaysAllow" />
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

webView.On<Android>().SetMixedContentMode(MixedContentHandling.AlwaysAllow);

El WebView.On<Android>método especifica que solo se ejecutarán este específicos de la plataforma en Android. El


WebView.SetMixedContentMode método, en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de
nombres, se usa para controlar si se puede mostrar contenido mixto, con el MixedContentHandling enumeración
que proporciona tres valores posibles:
AlwaysAllow : indica que el WebView permitirá a un origen HTTPS cargar el contenido desde un origen HTTP.
NeverAllow : indica que el WebView no permitirá que un origen HTTPS cargar el contenido desde un origen
HTTP.
CompatibilityMode : indica que el WebView intentará sea compatible con el enfoque del explorador de web del
dispositivo más reciente. Puede tener algún contenido HTTP se puede cargar un origen de HTTPS y se
bloquearán otros tipos de contenido. Los tipos de contenido que se bloquean o permiten pueden cambiar con
cada versión de sistema operativo.
El resultado es que un determinado MixedContentHandling valor se aplica a la WebView , que controla si se puede
mostrar contenido mixto:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Zoom de WebView en Android
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este Android específicos de plataforma permite acercar para alejar y un control de zoom en un WebView . Se
consume en XAML estableciendo el WebView.EnableZoomControls y WebView.DisplayZoomControls propiedades
enlazables a boolean valores:

<ContentPage ...
xmlns:android="clr-
namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core">
<WebView Source="https://www.xamarin.com"
android:WebView.EnableZoomControls="true"
android:WebView.DisplayZoomControls="true" />
</ContentPage>

El WebView.EnableZoomControls propiedad enlazable controla si pinch a zoom está habilitado en el WebView y el


WebView.DisplayZoomControls propiedad enlazable controla si los controles de zoom se superponen en el WebView .
Como alternativa, se puede consumir el específico de la plataforma desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
...

webView.On<Android>()
.EnableZoomControls(true)
.DisplayZoomControls(true);

El WebView.On<Android> método especifica que solo se ejecutarán este específicos de la plataforma en Android. El
WebView.EnableZoomControls método, en el Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de
nombres, se usa para controlar si pinch a zoom está habilitado en el WebView . El WebView.DisplayZoomControls
método, en el mismo espacio de nombres, se utiliza para controlar si los controles de zoom se superponen en el
WebView . Además, el WebView.ZoomControlsEnabled y WebView.ZoomControlsDisplayed métodos pueden usarse para
devolver si se habilitan los controles de zoom y acercar para alejar, respectivamente.
El resultado es que acercar para alejar puede habilitarse en un WebView , y se pueden superponer los controles de
zoom en el WebView :
IMPORTANT
Controles de zoom deben ser habilitados y muestra, a través de las respectivas propiedades enlazables o métodos, que se
superpone en un WebView .

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
AndroidSpecific API
AndroidSpecific.AppCompat API
Clase Device de Xamarin.Forms
11/07/2019 • 11 minutes to read • Edit Online

descargar el ejemplo
La clase Device contiene un número de propiedades y métodos para ayudar a los desarrolladores personalizar
diseño y funcionalidad de acuerdo con cada plataforma.
Además de métodos y propiedades para el código de destino en los tipos de hardware específico y tamaños, el
Device clase incluye métodos que se pueden usar para interactuar con los controles de interfaz de usuario de
subprocesos en segundo plano. Para obtener más información, consulte interactúe con la interfaz de usuario de
subprocesos en segundo plano.

Proporcionar valores específicos de la plataforma


Antes de Xamarin.Forms 2.3.4, se pudo obtener la plataforma de la aplicación se ejecuta en examinando el
Device.OS propiedad y compararla con la TargetPlatform.iOS , TargetPlatform.Android ,
TargetPlatform.WinPhone , y TargetPlatform.Windows valores de enumeración. De forma similar, uno de los
Device.OnPlatform sobrecargas podrían usarse para proporcionar los valores específicos de la plataforma a un
control.
Sin embargo, desde Xamarin.Forms 2.3.4 estas API se ha desusado y reemplazado. El Device clase ahora
contiene las constantes de cadena pública que identifican las plataformas: Device.iOS , Device.Android ,
Device.WinPhone () en desuso), Device.WinRT (en desuso) Device.UWP , y Device.macOS . De forma similar, el
Device.OnPlatform sobrecargas se han reemplazado con el OnPlatform y On API.

En C#, se pueden proporcionar valores específicos por plataforma mediante la creación de una instruccion
switch con la propiedad Device.RuntimePlatform y, a continuación, proporcionar las instrucciones case para
las plataformas necesarias:

double top;
switch (Device.RuntimePlatform)
{
case Device.iOS:
top = 20;
break;
case Device.Android:
case Device.UWP:
default:
top = 0;
break;
}
layout.Margin = new Thickness(5, top, 5, 0);

Las clases OnPlatform y On proporcionan la misma funcionalidad en XAML:


<StackLayout>
<StackLayout.Margin>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0,20,0,0" />
<On Platform="Android, UWP" Value="0,0,0,0" />
</OnPlatform>
</StackLayout.Margin>
...
</StackLayout>

El OnPlatform clase es una clase genérica que debe crearse con un x:TypeArguments atributo que coincida con el
tipo de destino. En la clase On , el atributo Platform puede aceptar un único valor string o varios valores
string delimitados por comas.

IMPORTANT
Proporcionar un valor incorrecto en el atributo Platform de la clase On no producirá un error. En su lugar, el código se
ejecutará sin el valor específico de la plataforma que se va a aplicar.

Como alternativa, el OnPlatform se puede usar extensión de marcado en XAML para personalizar la apariencia
de la interfaz de usuario en forma de acuerdo con la plataforma. Para obtener más información, consulte
OnPlatform Markup Extension.

Device.Idiom
El Device.Idiom propiedad puede usarse para modificar los diseños o funcionalidad en función del dispositivo
de la aplicación se ejecuta en. El enumerador TargetIdiom contiene los siguientes valores:
Teléfono – iPhone, iPod touch y los dispositivos Android más estrechas que 600 DIP ^
Tablet : iPad, los dispositivos de Windows y dispositivos Android más amplio que 600 DIP ^
Escritorio : solo se devuelven en aplicaciones para UWP en equipos de escritorio de Windows 10 (devuelve
Phone en dispositivos móviles de Windows, como en escenarios de Continuum )
TV: dispositivos de TV con Tizen
Watch – relojes Tizen
Unsupported : no se utiliza
^ DIP no es necesariamente el número de píxeles físicos
El Idiom propiedad resulta especialmente útil para la creación de diseños que se benefician de las pantallas más
grandes, similar al siguiente:

if (Device.Idiom == TargetIdiom.Phone) {
// layout views vertically
} else {
// layout views horizontally for a larger display (tablet or desktop)
}

El OnIdiom clase proporciona la misma funcionalidad en XAML:


<StackLayout>
<StackLayout.Margin>
<OnIdiom x:TypeArguments="Thickness">
<OnIdiom.Phone>0,20,0,0</OnIdiom.Phone>
<OnIdiom.Tablet>0,40,0,0</OnIdiom.Tablet>
<OnIdiom.Desktop>0,60,0,0</OnIdiom.Desktop>
</OnIdiom>
</StackLayout.Margin>
...
</StackLayout>

El OnIdiom clase es una clase genérica que debe crearse con un x:TypeArguments atributo que coincida con el
tipo de destino.
Como alternativa, el OnIdiom se puede usar extensión de marcado en XAML para personalizar la apariencia de
la interfaz de usuario en función de la expresión del dispositivo se está ejecutando la aplicación en. Para obtener
más información, consulte OnIdiom Markup Extension.

Device.FlowDirection
El Device.FlowDirection valor recupera un FlowDirection valor de enumeración que representa la dirección del
flujo actual que usa el dispositivo. La dirección de flujo es la dirección en la que el ojo humano lee los elementos
de la interfaz de usuario en la página. Los valores de la enumeración son:
LeftToRight
RightToRight
MatchParent

En XAML, el valor Device.FlowDirection se puede recuperar mediante la extensión de marcado x:Static :

<ContentPage ... FlowDirection="{x:Static Device.FlowDirection}"> />

El código equivalente en C# es:

this.FlowDirection = Device.FlowDirection;

Para obtener más información acerca de la dirección del flujo, consulte localización de derecha a izquierda.

Device.Styles
La propiedad Styles contiene las definiciones de estilo integradas que pueden aplicarse a algunos de los
controles (como Label ) la propiedad Style . Los estilos disponibles son:
BodyStyle
CaptionStyle
ListItemDetailTextStyle
ListItemTextStyle
SubtitleStyle
TitleStyle

Device.GetNamedSize
GetNamedSize puede utilizarse al establecer FontSize en código C#:
myLabel.FontSize = Device.GetNamedSize (NamedSize.Small, myLabel);
someLabel.FontSize = Device.OnPlatform (
24, // hardcoded size
Device.GetNamedSize (NamedSize.Medium, someLabel),
Device.GetNamedSize (NamedSize.Large, someLabel)
);

Device.OpenUri
El metodo OpenUri puede usarse para desencadenar operaciones en la plataforma nativa, como abrir una
dirección URL en el explorador web nativo (Safari en iOS o Internet en Android).

Device.OpenUri(new Uri("https://evolve.xamarin.com/"));

El ejemplo WebView incluye un ejemplo que usa OpenUri para abrir las direcciones URL y desencadenar
también llamadas telefónicas.
El ejemplo mapas también usa Device.OpenUri para mostrar mapas y las direcciones mediante la aplicacion de
Mapas nativa en iOS y Android.

Device.StartTimer
La clase Device también tiene un metodo StartTimer que proporciona una manera sencilla de desencadenar
tareas dependientes del tiempo que funcionan en el código común de Xamarin.Forms, incluidas una biblioteca
.NET Standard. Pasa un valor TimeSpan para establecer el intervalo y devuelve true para mantener el
temporizador en ejecución o false para detenerlo después de la invocación actual.

Device.StartTimer (new TimeSpan (0, 0, 60), () => {


// do something every 60 seconds
return true; // runs again, or false to stop
});

Si el código del temporizador interactúa con la interfaz de usuario (como establecer el texto de un Label o
mostrar una alerta) debe realizarse dentro de una expresión BeginInvokeOnMainThread (ver abajo).

Interactuar con la interfaz de usuario de subprocesos en segundo


plano
Mayoría de los sistemas operativos, incluidos iOS, Android y la plataforma Universal de Windows, utilice un
modelo de subprocesamiento único para el código que implican la interfaz de usuario. Este subproceso se suele
denominar el subproceso principal o el subproceso de interfaz de usuario. Una consecuencia de este modelo es
que todo el código que tiene acceso a elementos de la interfaz de usuario debe ejecutar en el subproceso
principal de la aplicación.
A veces, las aplicaciones usar subprocesos en segundo plano para realizar operaciones potencialmente larga,
como la recuperación de datos de un servicio web. Si necesita código que se ejecuta en un subproceso en
segundo plano tener acceso a elementos de la interfaz de usuario, se debe ejecutar ese código en el subproceso
principal.
El Device clase incluye lo siguiente static elementos desde subprocesos de fondos de la interfaz de métodos
que se pueden usar para interactuar con el usuario:
MÉTODO ARGUMENTOS VALORES DEVUELTOS FINALIDAD

BeginInvokeOnMainThread Action void Invoca un Action en el


subproceso principal y no
espera a que finalice.

InvokeOnMainThreadAsync<T> Func<T> Task<T> Invoca un Func<T> en el


subproceso principal y
espera a que finalice.

InvokeOnMainThreadAsync Action Task Invoca un Action en el


subproceso principal y
espera a que finalice.

InvokeOnMainThreadAsync<T> Func<Task<T>> Task<T> Invoca un Func<Task<T>>


en el subproceso principal y
espera a que finalice.

InvokeOnMainThreadAsync Func<Task> Task Invoca un Func<Task> en


el subproceso principal y
espera a que finalice.

GetMainThreadSynchronizationContextAsync Task<SynchronizationContext>Devuelve el
SynchronizationContext
del subproceso principal.

El código siguiente muestra un ejemplo del uso de la BeginInvokeOnMainThread método:

Device.BeginInvokeOnMainThread (() =>


{
// interact with UI elements
});

Vínculos relacionados
Ejemplo Device
Ejemplo Styles
Device
características de la plataforma de iOS de
Xamarin.Forms
11/07/2019 • 7 minutes to read • Edit Online

Desarrollo de aplicaciones de Xamarin.Forms para iOS requiere Visual Studio. El página requisitos contiene más
información sobre los requisitos previos.

Funcionalidades específicas de plataforma


Funcionalidades específicas de plataforma permiten utilizar la funcionalidad que solo está disponible en una
plataforma concreta, sin necesidad de implementar los representadores personalizados o los efectos.
La siguiente funcionalidad específica de la plataforma se proporciona para las vistas de Xamarin.Forms, páginas y
diseños en iOS:
Desenfoque ninguna compatibilidad con VisualElement . Para obtener más información, consulte
VisualElement desenfoque en iOS.
Deshabilitar el modo de color heredado en compatible VisualElement . Para obtener más información, consulte
VisualElement modo de Color heredado en iOS.
Habilitación de una sombra paralela en un VisualElement . Para obtener más información, consulte
VisualElement Drop sombras en iOS.
Se proporciona la siguiente funcionalidad específica de la plataforma para las vistas de Xamarin.Forms en iOS:
Establecer el Cell color de fondo. Para obtener más información, consulte Color de fondo de celda en iOS.
Asegurarse de que especifica el texto se ajusta a un Entry ajustando el tamaño de fuente. Para obtener más
información, consulte tamaño de fuente de entrada en iOS.
Establecer el color de cursor de un Entry . Para obtener más información, consulte Color de Cursor de
movimiento en iOS.
Controlar si ListView las celdas de encabezado float durante el desplazamiento. Para obtener más
información, consulte ListView el estilo del encabezado de grupo en iOS.
Controlar si las animaciones de fila están deshabilitadas cuando la ListView se está actualizando la colección
de elementos. Para obtener más información, consulte ListView fila animaciones en iOS.
Establecer el estilo del separador en un ListView . Para obtener más información, consulte estilo del separador
de ListView en iOS.
Controlar cuándo se produce la selección de elementos en un Picker . Para obtener más información, consulte
selector de selección de elementos en iOS.
Habilitar la Slider.Value propiedad debe establecerse si pulsa en una posición en la Slider barra, en lugar de
tener que arrastrar la Slider thumb. Para obtener más información, consulte pulse de Thumb deslizante en
iOS.
Se proporciona la siguiente funcionalidad específica de la plataforma para las páginas de Xamarin.Forms en iOS:
Ocultar el separador de barra de navegación en un NavigationPage . Para obtener más información, consulte
separador de barra NavigationPage en iOS.
Controlar si la barra de navegación es translúcida. Para obtener más información, consulte translucidez de
barra de navegación en iOS.
Controlar si el texto de la barra de estado de color en un NavigationPage se ajusta para que coincida con la
luminosidad de la barra de navegación. Para obtener más información, consulte NavigationPage barra el modo
de Color de texto en iOS.
Controlar si el título de página se muestra como un título en la barra de navegación de página grande. Para
obtener más información, consulte grandes títulos de página en iOS.
Establecer la visibilidad del indicador principal en un Page . Para obtener más información, consulte inicio
indicador visibilidad en iOS.
Establecer la visibilidad de la barra de estado en un Page . Para obtener más información, consulte visibilidad
de barra de estado de página en iOS.
Garantizar ese contenido de la página se coloca en un área de la pantalla que sea seguro para todos los
dispositivos iOS. Para obtener más información, consulte Guía de diseño de área segura en iOS.
Establecer el estilo de presentación de páginas modales en un iPad. Para obtener más información, consulte
iPad estilo de presentación de página Modal.
Se proporciona la siguiente funcionalidad específica de la plataforma para los diseños de Xamarin.Forms en iOS:
Controlar si un ScrollView controla un movimiento táctil o pasa a su contenido. Para obtener más
información, consulte ScrollView toques contenido en iOS.
Se proporciona la siguiente funcionalidad específica de la plataforma para Xamarin.Forms Application clase en
iOS:
Deshabilitando el escalado para tamaños de fuente con nombre de accesibilidad. Para obtener más
información, consulte accesibilidad escalado denominada tamaños de fuente en iOS.
Habilitación de diseño del control y la representación de las actualizaciones que se realizará en el subproceso
principal. Para obtener más información, consulte Main subprocesos controlar las actualizaciones en iOS.
Habilitar un PanGestureRecognizer en una vista desplazable para capturar y compartir el movimiento
panorámico con la vista de desplazamiento. Para obtener más información, consulte simultáneas
reconocimiento de gestos de movimiento panorámico en iOS.

de formato específica de iOS


Xamarin.Forms permite estilos de la interfaz de usuario entre plataformas y los colores para establecerse - pero
hay otras opciones para establecer el tema de su iOS mediante las API de plataforma en el proyecto de iOS.
Obtenga más acerca del formato de la interfaz de usuario mediante las API de iOS específicas, como Info.plist
configuración y el UIAppearance API.

Otras características de iOS


Mediante representadores personalizados, DependencyServicey el MessagingCenter, es posible incorporar una
gran variedad de funciones nativas en Aplicaciones de Xamarin.Forms para iOS.
Color de fondo de celda en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este iOS específicos de la plataforma establece el color de fondo predeterminado de Cell instancias. Se consume
en XAML estableciendo el Cell.DefaultBackgroundColor propiedad enlazable para un Color :

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout Margin="20">
<ListView ItemsSource="{Binding GroupedEmployees}"
IsGroupingEnabled="true">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell ios:Cell.DefaultBackgroundColor="Teal">
<Label Margin="10,10"
Text="{Binding Key}"
FontAttributes="Bold" />
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
...
</ListView>
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

var viewCell = new ViewCell { View = ... };


viewCell.On<iOS>().SetDefaultBackgroundColor(Color.Teal);

El ListView.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


Cell.SetDefaultBackgroundColor método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de
nombres, Establece el color de fondo de celda en un Color . Además, el Cell.DefaultBackgroundColor método
puede utilizarse para recuperar el color de fondo de celda actual.
El resultado es que el color de fondo en un Cell se puede establecer en un determinado Color :
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Color de Cursor de movimiento en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este iOS específicos de la plataforma establece el color de cursor de un Entry en un color especificado. Se
consume en XAML estableciendo el Entry.CursorColor propiedad enlazable para un Color :

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout>
<Entry ... ios:Entry.CursorColor="LimeGreen" />
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

var entry = new Xamarin.Forms.Entry();


entry.On<iOS>().SetCursorColor(Color.LimeGreen);

El Entry.On<iOS> método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El
Entry.SetCursorColor método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres,
Establece el color de cursor en un determinado Color . Además, el Entry.GetCursorColor método puede utilizarse
para recuperar el color actual del cursor.
El resultado es que el color del cursor en un Entry se puede establecer en un determinado Color :

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Tamaño de fuente de entrada en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS se usan para escalar el tamaño de fuente de un Entry para asegurarse de
que se ajuste el texto de entrada en el control. Se consume en XAML estableciendo el
Entry.AdjustsFontSizeToFitWidth propiedad adjunta un boolean valor:

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
<StackLayout Margin="20">
<Entry x:Name="entry"
Placeholder="Enter text here to see the font size change"
FontSize="22"
ios:Entry.AdjustsFontSizeToFitWidth="true" />
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

entry.On<iOS>().EnableAdjustsFontSizeToFitWidth();

El Entry.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


Entry.EnableAdjustsFontSizeToFitWidth método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de
nombres, se usa para escalar el tamaño de fuente del texto de entrada para asegurarse de que se ajuste el Entry .
Además, el Entry clase en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres también tiene
un DisableAdjustsFontSizeToFitWidth método que se deshabilita este específicos de la plataforma, y un
SetAdjustsFontSizeToFitWidth método que se puede usar para cambiar el tamaño de fuente escalado mediante
una llamada a la AdjustsFontSizeToFitWidth método:

entry.On<iOS>().SetAdjustsFontSizeToFitWidth(!entry.On<iOS>().AdjustsFontSizeToFitWidth());

El resultado es que el tamaño de fuente de la Entry se escala para asegurarse de que se ajuste el texto de entrada
en el control:

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Adición de formato específica de iOS
11/07/2019 • 4 minutes to read • Edit Online

Una manera de establecer iOS específicos de formato es crear un representador personalizado para un control y
los estilos específicos de la plataforma y los colores para cada plataforma.
Otras opciones para controlar la manera en la apariencia de la aplicación de iOS Xamarin.Forms incluyen:
Configuración Mostrar opciones en Info.plist
Establecer estilos de control a través de la UIAppearance API
Estas alternativas se describen a continuación.

Personalización de Info.plist
El Info.plist archivo le permite configurar algunos aspectos de renderering de una aplicación de iOS, por ejemplo,
cómo (y si) se muestra la barra de estado.
Por ejemplo, el ejemplo "todo" usa el código siguiente para establecer la barra de color y color del texto de
navegación en todas las plataformas:

var nav = new NavigationPage (new TodoListPage ());


nav.BarBackgroundColor = Color.FromHex("91CA47");
nav.BarTextColor = Color.White;

El resultado se muestra en el siguiente fragmento de pantalla. Tenga en cuenta que los elementos de la barra de
estado son de color negros (no se puede establecer en Xamarin.Forms porque es una característica específica de la
plataforma).

Lo ideal es que la barra de estado también sería blanco: algo podemos realizar directamente en el proyecto de iOS.
Agregue las siguientes entradas a la Info.plist para forzar la barra de estado blanco:

o editar correspondiente Info.plist directamente a incluyen:

<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>

Ahora cuando se ejecuta la aplicación, la barra de navegación es verde y su texto es blanco (debido a un formato
de Xamarin.Forms) y el texto de la barra de estado también es blanco gracias a la configuración específica de iOS:
UIAppearance API
El UIAppearance API puede usarse para establecer propiedades visuales en muchos controles de iOS sin tener que
crear un representador personalizado.
Adición de una sola línea de código para el AppDelegate.cs FinishedLaunching método aplicar estilo a todos los
controles de un tipo determinado mediante sus Appearance propiedad. El código siguiente contiene dos ejemplos:
globalmente aplicar estilos a la pestaña de barras y cambiar el control:
AppDelegate.cs en el proyecto de iOS

public override bool FinishedLaunching (UIApplication app, NSDictionary options)


{
// tab bar
UITabBar.Appearance.SelectedImageTintColor = UIColor.FromRGB(0x91, 0xCA, 0x47); // green
// switch
UISwitch.Appearance.OnTintColor = UIColor.FromRGB(0x91, 0xCA, 0x47); // green
// required Xamarin.Forms code
Forms.Init ();
LoadApplication (new App ());
return base.FinishedLaunching (app, options);
}

UITabBar
De forma predeterminada, el icono de barra de la pestaña seleccionada en un TabbedPage sería azul:

Para cambiar este comportamiento, establezca el UITabBar.Appearance propiedad:

UITabBar.Appearance.SelectedImageTintColor = UIColor.FromRGB(0x91, 0xCA, 0x47); // green

Esto hace que la pestaña seleccionada a verde:

Uso de esta API le permite personalizar la apariencia de Xamarin.Forms TabbedPage en iOS con muy poco código.
Hacer referencia a la receta personalizar pestañas para obtener más detalles sobre el uso de un representador
personalizado para establecer una fuente específica de la pestaña.
UISwitch
El Switch control es otro ejemplo que puede cambiar fácilmente el estilo:

UISwitch.Appearance.OnTintColor = UIColor.FromRGB(0x91, 0xCA, 0x47); // green

Estas capturas de dos pantalla mostrar el valor predeterminado UISwitch control a la izquierda y la versión
personalizada (opción Appearance ) a la derecha en el ejemplo "todo":
Otros controles
Muchos controles de interfaz de usuario de iOS pueden tener sus colores predeterminados y otros atributos
establecidos mediante la UIAppearance API.

Vínculos relacionados
UIAppearance
Personalizar las fichas
Estilo de presentación de la página modal de iPad
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS se usan para establecer el estilo de presentación de una página modal en un
iPad. Se consume en XAML estableciendo el Page.ModalPresentationStyle propiedad enlazable para un
UIModalPresentationStyle valor de enumeración:

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.ModalPresentationStyle="FormSheet">
...
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

public class iOSModalFormSheetPageCS : ContentPage


{
public iOSModalFormSheetPageCS()
{
On<iOS>().SetModalPresentationStyle(UIModalPresentationStyle.FormSheet);
...
}
}

El Page.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


Page.SetModalPresentationStyle método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de
nombres, se usa para establecer el estilo de presentación modal en un Page especificando uno de los siguientes
UIModalPresentationStyle (enumeración) valores:

FullScreen , que establece el estilo de presentación modal para abarcar toda la pantalla. De forma
predeterminada, las páginas modales se muestran con este estilo de presentación.
FormSheet , que establece el estilo de presentación modal sea menor que la pantalla y centrado en.

Además, el GetModalPresentationStylemétodo puede utilizarse para recuperar el valor actual de la


UIModalPresentationStyle enumeración que se aplica a la Page .
El resultado es que el estilo de presentación modal en un Page se pueden establecer:
NOTE
Las páginas que usan este específicos de la plataforma para establecer el estilo de presentación modal deben usar la
navegación modal. Para obtener más información, consulte páginas modales de Xamarin.Forms.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Títulos de página grande en iOS
11/07/2019 • 3 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS se usan para mostrar el título de página como un título de gran tamaño en
la barra de navegación de un NavigationPage , para los dispositivos que usan iOS 11 o superior. Un gran título
está alineado a la izquierda y usa una fuente mayor y realiza la transición a un título estándar como el usuario
comienza a desplazarse por el contenido, por lo que se usan de forma eficaz el espacio en pantalla. Sin embargo,
en orientación horizontal, el título devolverá al centro de la barra de navegación para optimizar el diseño del
contenido. Se consume en XAML estableciendo el NavigationPage.PrefersLargeTitles propiedad adjunta un
boolean valor:

<NavigationPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
...
ios:NavigationPage.PrefersLargeTitles="true">
...
</NavigationPage>

Como alternativa puede usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

var navigationPage = new Xamarin.Forms.NavigationPage(new iOSLargeTitlePageCS());


navigationPage.On<iOS>().SetPrefersLargeTitles(true);

El NavigationPage.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


NavigationPage.SetPrefersLargeTitle método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific controla el
espacio de nombres, si están habilitados los títulos de gran tamaño.
Siempre que los títulos de gran tamaño están habilitados en el NavigationPage , todas las páginas de la pila de
navegación mostrará los títulos de gran tamaño. Este comportamiento puede invalidarse en páginas
estableciendo el Page.LargeTitleDisplay propiedad adjunta a un valor de la LargeTitleDisplayMode enumeración:

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
Title="Large Title"
ios:Page.LargeTitleDisplay="Never">
...
</ContentPage>

Como alternativa, se puede invalidar el comportamiento de la página de C# con la API fluida:


using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

public class iOSLargeTitlePageCS : ContentPage


{
public iOSLargeTitlePageCS(ICommand restore)
{
On<iOS>().SetLargeTitleDisplay(LargeTitleDisplayMode.Never);
...
}
...
}

El Page.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


Page.SetLargeTitleDisplay método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres,
controla el comportamiento de título grande en la Page , con el LargeTitleDisplayMode enumeración que
proporciona tres posibles valores:
Always : fuerza la barra de navegación y la fuente tamaño usando el formato grande.
Automatic : use el mismo estilo (grande o pequeño) que el elemento anterior en la pila de navegación.
Never – forzar el uso de la barra de navegación de formato estándar, pequeñas.

Además, el SetLargeTitleDisplay método puede utilizarse para activar o desactivar los valores de enumeración
mediante una llamada a la LargeTitleDisplay método, que devuelve el valor actual LargeTitleDisplayMode :

switch (On<iOS>().LargeTitleDisplay())
{
case LargeTitleDisplayMode.Always:
On<iOS>().SetLargeTitleDisplay(LargeTitleDisplayMode.Automatic);
break;
case LargeTitleDisplayMode.Automatic:
On<iOS>().SetLargeTitleDisplay(LargeTitleDisplayMode.Never);
break;
case LargeTitleDisplayMode.Never:
On<iOS>().SetLargeTitleDisplay(LargeTitleDisplayMode.Always);
break;
}

El resultado es que un determinado LargeTitleDisplayMode se aplica a la Page , que controla el comportamiento


de título grande:

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Estilo de encabezado de grupo de ListView en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este controles específicos de la plataforma de iOS si ListView las celdas de encabezado float durante el
desplazamiento. Se consume en XAML estableciendo el ListView.GroupHeaderStyle propiedad enlazable en un
valor de la GroupHeaderStyle enumeración:

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout Margin="20">
<ListView ... ios:ListView.GroupHeaderStyle="Grouped">
...
</ListView>
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

listView.On<iOS>().SetGroupHeaderStyle(GroupHeaderStyle.Grouped);

El ListView.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


ListView.SetGroupHeaderStyle método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de
nombres, se utiliza para controlar si ListView las celdas de encabezado float durante el desplazamiento. El
GroupHeaderStyle enumeración proporciona dos valores posibles:

Plain : indica que las celdas de encabezado float cuando la ListView es desplazado (valor predeterminado).
Grouped : indica que las celdas de encabezado no flotantes cuando el ListView se desplaza.

Además, el ListView.GetGroupHeaderStyle método puede utilizarse para devolver el GroupHeaderStyle que se


aplica a la ListView .
El resultado es que un determinado GroupHeaderStyle valor se aplica a la ListView , que controla si las celdas de
encabezado float durante el desplazamiento:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Animaciones de la fila de ListView en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este controles específicos de la plataforma de iOS si las animaciones de fila están deshabilitados cuando la
ListView se está actualizando la colección de elementos. Se consume en XAML estableciendo el
ListView.RowAnimationsEnabled propiedad enlazable a false :

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout Margin="20">
<ListView ... ios:ListView.RowAnimationsEnabled="false">
...
</ListView>
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

listView.On<iOS>().SetRowAnimationsEnabled(false);

El método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


ListView.On<iOS>
ListView.SetRowAnimationsEnabled método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de
nombres, se usa para controlar si las animaciones de fila son deshabilitada cuando el ListView se está
actualizando la colección de elementos. Además, el ListView.GetRowAnimationsEnabled método puede utilizarse
para devolver si se deshabilitan las animaciones de la fila en la ListView .

NOTE
ListView las animaciones de la fila se habilitan de forma predeterminada. Por lo tanto, se produce una animación cuando
se inserta una nueva fila en un ListView .

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Estilo del separador de ListView en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este iOS específicos de la plataforma controla si el separador entre las celdas de un ListView usa todo el ancho
de la ListView . Se consume en XAML estableciendo el ListView.SeparatorStyle propiedad adjunta a un valor de
la SeparatorStyle enumeración:

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout Margin="20">
<ListView ... ios:ListView.SeparatorStyle="FullWidth">
...
</ListView>
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

listView.On<iOS>().SetSeparatorStyle(SeparatorStyle.FullWidth);

El ListView.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


ListView.SetSeparatorStyle método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres,
se usa para controlar si el separador entre las celdas de la ListView usa completo ancho de la ListView , con el
SeparatorStyle enumeración que proporciona dos valores posibles:

Default : indica el comportamiento de separador de iOS de forma predeterminada. Este es el comportamiento


predeterminado en Xamarin.Forms.
FullWidth : indica que se dibujarán los separadores de uno de los bordes de la ListView a otro.

El resultado es que un determinado SeparatorStyle valor se aplica a la ListView , que controla el ancho del
separador entre las celdas:
NOTE
Una vez que se ha establecido el estilo del separador en FullWidth , no se puede cambiar a Default en tiempo de
ejecución.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Actualizaciones de Control de subproceso principal
en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS permite controlar el diseño y representación de las actualizaciones que se
realizará en el subproceso principal, en lugar de que se va a realizar en un subproceso en segundo plano. Debería
ser necesario con poca frecuencia, pero en algunos casos puede evitar que se bloquee. Su XAML consumido en
estableciendo el Application.HandleControlUpdatesOnMainThread propiedad enlazable a true :

<Application ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Application.HandleControlUpdatesOnMainThread="true">
...
</Application>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

Xamarin.Forms.Application.Current.On<iOS>().SetHandleControlUpdatesOnMainThread(true);

El Application.On<iOS> método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El
Application.SetHandleControlUpdatesOnMainThread método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific
espacio de nombres, se usa para controlar si el diseño del control y la representación de las actualizaciones se
realizan en el subproceso principal, en lugar de que se va a realizar en un subproceso en segundo plano. Además,
el Application.GetHandleControlUpdatesOnMainThread método puede utilizarse para devolver si el diseño de
controles y la representación de las actualizaciones se realizan en el subproceso principal.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Separador de barra NavigationPage en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS ocultan la línea de separación y la sombra que se encuentra en la parte
inferior de la barra de navegación en un NavigationPage . Se consume en XAML estableciendo el
NavigationPage.HideNavigationBarSeparator propiedad enlazable a false :

<NavigationPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:NavigationPage.HideNavigationBarSeparator="true">

</NavigationPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;

public class iOSTitleViewNavigationPageCS : Xamarin.Forms.NavigationPage


{
public iOSTitleViewNavigationPageCS()
{
On<iOS>().SetHideNavigationBarSeparator(true);
}
}

El NavigationPage.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


NavigationPage.SetHideNavigationBarSeparator método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific
espacio de nombres, se usa para controlar si se oculta el separador de barra de navegación. Además, el
NavigationPage.HideNavigationBarSeparator método puede utilizarse para devolver si se oculta el separador de
barra de navegación.
El resultado es que el separador de barra de navegación en un NavigationPage puede estar oculto:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Modo de Color de texto de barra de NavigationPage
en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este controles específicos de la plataforma si el texto de la barra de estado de color en un NavigationPage se ajusta
para que coincida con la luminosidad de la barra de navegación. Se consume en XAML estableciendo el
NavigationPage.StatusBarTextColorMode propiedad adjunta a un valor de la StatusBarTextColorMode enumeración:

<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
x:Class="PlatformSpecifics.iOSStatusBarTextColorModePage">
<MasterDetailPage.Master>
<ContentPage Title="Master Page Title" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage BarBackgroundColor="Blue" BarTextColor="White"
ios:NavigationPage.StatusBarTextColorMode="MatchNavigationBarTextLuminosity">
<x:Arguments>
<ContentPage>
<Label Text="Slide the master page to see the status bar text color mode change." />
</ContentPage>
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

IsPresentedChanged += (sender, e) =>


{
var mdp = sender as MasterDetailPage;
if (mdp.IsPresented)
((Xamarin.Forms.NavigationPage)mdp.Detail)
.On<iOS>()
.SetStatusBarTextColorMode(StatusBarTextColorMode.DoNotAdjust);
else
((Xamarin.Forms.NavigationPage)mdp.Detail)
.On<iOS>()
.SetStatusBarTextColorMode(StatusBarTextColorMode.MatchNavigationBarTextLuminosity);
};

El NavigationPage.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


NavigationPage.SetStatusBarTextColorMode método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio
de nombres, los controles si el texto de la barra de estado de color en el NavigationPage se ajusta para que
coincida con el luminosidad de la barra de navegación con el StatusBarTextColorMode enumeración que
proporciona dos valores posibles:
DoNotAdjust : indica que no se debe ajustar el color del texto de la barra de estado.
MatchNavigationBarTextLuminosity : indica que el color del texto de la barra de estado debe coincidir con la
luminosidad de la barra de navegación.
Además, el GetStatusBarTextColorModemétodo puede utilizarse para recuperar el valor actual de la
StatusBarTextColorMode enumeración que se aplica a la NavigationPage .
El resultado es que el estado de la barra de color del texto en un NavigationPage se puede ajustar para que
coincida con la luminosidad de la barra de navegación. En este ejemplo, lo cambios de color del texto de la barra
de estado como el usuario alterna entre el Master y Detail páginas de un MasterDetailPage :

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
NavigationPage barra translucidez en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS se usan para cambiar la transparencia de la barra de navegación en un
NavigationPage y se consume en XAML estableciendo el NavigationPage.IsNavigationBarTranslucent propiedad
adjunta a un boolean valor:

<NavigationPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
BackgroundColor="Blue"
ios:NavigationPage.IsNavigationBarTranslucent="true">
...
</NavigationPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

(App.Current.MainPage as Xamarin.Forms.NavigationPage).BackgroundColor = Color.Blue;


(App.Current.MainPage as Xamarin.Forms.NavigationPage).On<iOS>().EnableTranslucentNavigationBar();

El NavigationPage.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


NavigationPage.EnableTranslucentNavigationBar método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific
espacio de nombres, se utiliza para hacer que la barra de navegación translúcido. Además, el NavigationPage clase
en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres también tiene un
DisableTranslucentNavigationBar método que restaura la barra de navegación a su estado predeterminado, y un
SetIsNavigationBarTranslucent método que se puede usar para activar o desactivar la transparencia de la barra de
navegación mediante una llamada a la IsNavigationBarTranslucent método:

(App.Current.MainPage as Xamarin.Forms.NavigationPage)
.On<iOS>()
.SetIsNavigationBarTranslucent(!(App.Current.MainPage as Xamarin.Forms.NavigationPage).On<iOS>
().IsNavigationBarTranslucent());

El resultado es que se puede cambiar la transparencia de la barra de navegación:

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Visibilidad de indicador principal de iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS establece la visibilidad del indicador principal de un Page . Se consume en
XAML estableciendo el Page.PrefersHomeIndicatorAutoHidden propiedad enlazable para un boolean :

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.PrefersHomeIndicatorAutoHidden="true">
...
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

On<iOS>().SetPrefersHomeIndicatorAutoHidden(true);

El método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


Page.On<iOS>
Page.SetPrefersHomeIndicatorAutoHidden método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio
de nombres, controla la visibilidad del indicador principal. Además, el Page.PrefersHomeIndicatorAutoHidden
método puede utilizarse para recuperar la visibilidad del indicador principal.
El resultado es que la visibilidad del indicador principal en un Page se pueden controlar:

NOTE
Este específicos de la plataforma se pueden aplicar a ContentPage , MasterDetailPage , NavigationPage ,y
TabbedPage objetos.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Visibilidad de barra de estado de página en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS se usan para establecer la visibilidad de la barra de estado en un Page , e
incluye la capacidad para controlar cómo la barra de estado entra o sale de la Page . Se consume en XAML
estableciendo el Page.PrefersStatusBarHidden propiedad adjunta a un valor de la StatusBarHiddenMode
enumeración y, opcionalmente, el Page.PreferredStatusBarUpdateAnimation propiedad adjunta a un valor de la
UIStatusBarAnimation enumeración:

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.PrefersStatusBarHidden="True"
ios:Page.PreferredStatusBarUpdateAnimation="Fade">
...
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

On<iOS>().SetPrefersStatusBarHidden(StatusBarHiddenMode.True)
.SetPreferredStatusBarUpdateAnimation(UIStatusBarAnimation.Fade);

El Page.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


Page.SetPrefersStatusBarHidden método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de
nombres, se usa para establecer la visibilidad de la barra de estado en un Page especificando uno de los
StatusBarHiddenMode valores de enumeración: Default , True , o False . El StatusBarHiddenMode.True y
StatusBarHiddenMode.False valores establecen la visibilidad de la barra de estado, independientemente de la
orientación del dispositivo y el StatusBarHiddenMode.Default valor oculta la barra de estado en un entorno de
compact verticalmente.
El resultado es que la visibilidad de la barra de estado en un Page se pueden establecer:
NOTE
En un TabbedPage , especificado StatusBarHiddenMode valor de enumeración también actualizará la barra de estado en
todas las páginas secundarias. En todos los demás Page -tipos derivados, especificados StatusBarHiddenMode valor de
enumeración solo actualizará la barra de estado en la página actual.

El Page.SetPreferredStatusBarUpdateAnimation método se usa para establecer cómo la barra de estado entra o sale
de la Page especificando uno de los UIStatusBarAnimation valores de enumeración: None , Fade , o Slide . Si el
Fade o Slide se especifica el valor de enumeración, 0.25 segunda animación se ejecuta como la barra de estado
entra o sale de la Page .

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Selector de selección de elementos en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este iOS específicos de la plataforma controla cuándo se produce la selección de elementos en un Picker , lo
que permite al usuario especificar que la selección de elementos se produce al examinar los elementos del
control, o solo una vez que la realiza botón está presionado. Se consume en XAML estableciendo el
Picker.UpdateMode propiedad adjunta a un valor de la UpdateMode enumeración:

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout Margin="20">
<Picker ... Title="Select a monkey" ios:Picker.UpdateMode="WhenFinished">
...
</Picker>
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

picker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);

El Picker.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


Picker.SetUpdateMode método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres, se
usa para controlar cuándo se produce la selección de elementos, con el UpdateMode enumeración que
proporciona dos valores posibles:
Immediately : selección de elementos se produce cuando el usuario explora los elementos de la Picker . Este
es el comportamiento predeterminado en Xamarin.Forms.
WhenFinished : selección de elementos solo se produce una vez que el usuario ha presionado el realiza
situado en la Picker .
Además, el SetUpdateMode método puede utilizarse para activar o desactivar los valores de enumeración
mediante una llamada a la UpdateMode método, que devuelve el valor actual UpdateMode :

switch (picker.On<iOS>().UpdateMode())
{
case UpdateMode.Immediately:
picker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
break;
case UpdateMode.WhenFinished:
picker.On<iOS>().SetUpdateMode(UpdateMode.Immediately);
break;
}

El resultado es que un determinado UpdateMode se aplica a la Picker , que controla cuándo se produce la
selección de elementos:

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Guía de diseño de área segura en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Se usan este específicos de plataforma de iOS para asegurarse de que el contenido de la página se coloca en un
área de la pantalla que es seguro para todos los dispositivos que usan iOS 11 y versiones posteriores. En concreto,
le ayudará a asegurarse de que el contenido no recortado por dispositivo esquinas redondeadas, el indicador
principal o el alojamiento del sensor en un iPhone X. Se consume en XAML estableciendo el Page.UseSafeArea
propiedad adjunta un boolean valor:

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
Title="Safe Area"
ios:Page.UseSafeArea="true">
<StackLayout>
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

On<iOS>().SetUseSafeArea(true);

El Page.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


Page.SetUseSafeArea método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific controla el espacio de
nombres, si está habilitada la Guía de diseño de área segura.
El resultado es que el contenido de la página puede colocarse en un área de la pantalla que es seguro para todos
los iPhone:
NOTE
El área segura definido por Apple que se usa en Xamarin.Forms para establecer el Page.Padding propiedad y reemplazará
los valores anteriores de esta propiedad que se han establecido.

El área segura se puede personalizar mediante la recuperación de su Thickness valor con el Page.SafeAreaInsets
método desde el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres. A continuación, se puede
modificar como necesarios y volver a asignar a la Padding propiedad en el constructor de la página o
OnAppearing invalidar:

protected override void OnAppearing()


{
base.OnAppearing();

var safeInsets = On<iOS>().SafeAreaInsets();


safeInsets.Left = 20;
Padding = safeInsets;
}

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
ScrollView toques contenido en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Se desencadena un temporizador implícito cuando se inicia un movimiento táctil en un ScrollView en iOS y el
ScrollView decide, en función de la acción del usuario dentro del intervalo del temporizador, independientemente
de que se debe controlar el gesto o pasarlo a su contenido. De forma predeterminada, el archivo iOS ScrollView
toques contenido retrasos, pero esto puede causar problemas en algunas circunstancias con el ScrollView
contenido no ganando el gesto cuando debería. Por lo tanto, este controles específicos de la plataforma si un
ScrollView controla un movimiento táctil o pasa a su contenido. Se consume en XAML estableciendo el
ScrollView.ShouldDelayContentTouches propiedad adjunta un boolean valor:

<MasterDetailPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<MasterDetailPage.Master>
<ContentPage Title="Menu" BackgroundColor="Blue" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<ContentPage>
<ScrollView x:Name="scrollView" ios:ScrollView.ShouldDelayContentTouches="false">
<StackLayout Margin="0,20">
<Slider />
<Button Text="Toggle ScrollView DelayContentTouches" Clicked="OnButtonClicked" />
</StackLayout>
</ScrollView>
</ContentPage>
</MasterDetailPage.Detail>
</MasterDetailPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

scrollView.On<iOS>().SetShouldDelayContentTouches(false);

El ScrollView.On<iOS> método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El
ScrollView.SetShouldDelayContentTouches método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio
de nombres, se utiliza para controlar si un ScrollView controla un movimiento táctil o pasa a su contenido.
Además, el SetShouldDelayContentTouches método puede utilizarse para activar o desactivar retrasar toques
contenidos mediante una llamada a la ShouldDelayContentTouches método para devolver si se retrasan toques de
contenido:

scrollView.On<iOS>().SetShouldDelayContentTouches(!scrollView.On<iOS>().ShouldDelayContentTouches());

El resultado es que un ScrollView puede deshabilitar retrasar recibir contenidos toques, así que en este escenario
el Slider recibe el gesto en lugar de Detail página de la MasterDetailPage :
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Reconocimiento de gestos de movimiento
panorámico simultáneas en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Cuando un PanGestureRecognizer se adjunta a una vista dentro de una vista desplazable, todo el pan gestos
capturados por el PanGestureRecognizer y no se pasan a la vista de desplazamiento. Por lo tanto, ya no se
desplazará la vista de desplazamiento.
Este específicos de plataforma de iOS permite un PanGestureRecognizer en una vista desplazable para capturar y
compartir el movimiento panorámico con la vista de desplazamiento. Se consume en XAML estableciendo el
Application.PanGestureRecognizerShouldRecognizeSimultaneously propiedad adjunta true :

<Application ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Application.PanGestureRecognizerShouldRecognizeSimultaneously="true">
...
</Application>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

Xamarin.Forms.Application.Current.On<iOS>().SetPanGestureRecognizerShouldRecognizeSimultaneously(true);

El Application.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


Application.SetPanGestureRecognizerShouldRecognizeSimultaneously método, en el
Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres, se usa para controlar si un reconocedor de
movimiento panorámico en una vista desplazable capturará el movimiento panorámico, o capturar y compartir el
desplazamiento panorámico con la vista desplazable de gestos. Además, el
Application.GetPanGestureRecognizerShouldRecognizeSimultaneously método puede utilizarse para devolver si se
comparte el movimiento panorámico con la vista de desplazamiento que contiene el PanGestureRecognizer .
Por lo tanto, con este específicos de la plataforma habilitada, cuando un ListView contiene un
PanGestureRecognizer , tanto el ListView y PanGestureRecognizer recibirán el movimiento panorámico y
procesarlo. Sin embargo, con este específicos de la plataforma deshabilitada, cuando un ListView contiene un
PanGestureRecognizer , el PanGestureRecognizer capturará el movimiento panorámico y procesarlos y el ListView
no recibirá el movimiento panorámico.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Control deslizante Thumb pulsar en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS permite la Slider.Value propiedad debe establecerse si pulsa en una
posición en la Slider barra, en lugar de tener que arrastrar la Slider thumb. Se consume en XAML
estableciendo el Slider.UpdateOnTap propiedad enlazable a true :

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout ...>
<Slider ... ios:Slider.UpdateOnTap="true" />
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

var slider = new Xamarin.Forms.Slider();


slider.On<iOS>().SetUpdateOnTap(true);

El Slider.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


Slider.SetUpdateOnTap método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres, se
usa para controlar si una derivación de la Slider barra establecerá el Slider.Value propiedad. Además, el
Slider.GetUpdateOnTap método puede utilizarse para devolver si una derivación de en el Slider barra establecerá
el Slider.Value propiedad.
El resultado es que una derivación de en el Slider puede mover la barra de la Slider thumb y establezca el
Slider.Value propiedad:

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Desenfoque VisualElement en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS se usa para difuminar el contenido en capas por debajo de ella y pueden
aplicarse a cualquier VisualElement . Se consume en XAML estableciendo el VisualElement.BlurEffect propiedad
adjunta a un valor de la BlurEffectStyle enumeración:

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
...
<AbsoluteLayout HorizontalOptions="Center">
<Image Source="monkeyface.png" />
<BoxView x:Name="boxView" ios:VisualElement.BlurEffect="ExtraLight" HeightRequest="300"
WidthRequest="300" />
</AbsoluteLayout>
...
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

boxView.On<iOS>().UseBlurEffect(BlurEffectStyle.ExtraLight);

El BoxView.On<iOS> método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El
VisualElement.UseBlurEffect método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres,
se usa para aplicar el efecto de desenfoque, con el BlurEffectStyle enumeración que proporciona cuatro valores:
None , ExtraLight , Light , y Dark .

El resultado es que un determinado BlurEffectStyle se aplica a la BoxView instancia que desenfoca la Image por
niveles por debajo de ella:
NOTE
Al agregar un efecto de desenfoque a una VisualElement , eventos de toque, todavía recibirán el VisualElement .

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
VisualElement Drop sombras en iOS
11/07/2019 • 3 minutes to read • Edit Online

descargar el ejemplo
Este específicos de plataforma de iOS se utilizan para habilitar una sombra paralela en un VisualElement . Se
consume en XAML estableciendo el VisualElement.IsShadowEnabled propiedad adjunta true , junto con un
número de adicional opcional adjunta propiedades que controlan el efecto de sombra:

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout Margin="20">
<BoxView ...
ios:VisualElement.IsShadowEnabled="true"
ios:VisualElement.ShadowColor="Purple"
ios:VisualElement.ShadowOpacity="0.7"
ios:VisualElement.ShadowRadius="12">
<ios:VisualElement.ShadowOffset>
<Size>
<x:Arguments>
<x:Double>10</x:Double>
<x:Double>10</x:Double>
</x:Arguments>
</Size>
</ios:VisualElement.ShadowOffset>
</BoxView>
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

var boxView = new BoxView { Color = Color.Aqua, WidthRequest = 100, HeightRequest = 100 };
boxView.On<iOS>()
.SetIsShadowEnabled(true)
.SetShadowColor(Color.Purple)
.SetShadowOffset(new Size(10,10))
.SetShadowOpacity(0.7)
.SetShadowRadius(12);

El VisualElement.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


VisualElement.SetIsShadowEnabled método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de
nombres, se usa para controlar si una sombra paralela está habilitada en el VisualElement . Además, se pueden
invocar los métodos siguientes para controlar el efecto de sombra:
SetShadowColor : establece el color de la sombra paralela. El color predeterminado es Color.Default .
SetShadowOffset : establece el desplazamiento de la sombra paralela. El desplazamiento cambia la dirección de
la sombra se convierte y se especifica como un Size valor. El Size los valores de la estructura se expresan en
unidades independientes del dispositivo, con el primer valor que se va a la distancia a la izquierda (valor
negativo) o la derecha (valor positivo) y el segundo valor que se va a la distancia anterior (negativo) o por
debajo (valor positivo) . El valor predeterminado de esta propiedad es (0.0, 0.0), que da como resultado la
sombra que se va a convertir en torno a cada lado de la VisualElement .
SetShadowOpacity : establece la opacidad de la sombra paralela, con el valor está en el intervalo 0.0
(transparente) a 1.0 (opaco). El valor de opacidad predeterminado es 0,5.
SetShadowRadius : establece el radio de desenfoque utilizado para representar la sombra paralela. El valor de
radio predeterminado es 10,0.

NOTE
Se puede consultar el estado de una sombra paralela mediante una llamada a la GetIsShadowEnabled , GetShadowColor ,
GetShadowOffset , GetShadowOpacity , y GetShadowRadius métodos.

El resultado es que se puede habilitar una sombra paralela en un VisualElement :

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Modo de Color VisualElement heredado en iOS
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Algunas de las vistas de Xamarin.Forms cuentan con un modo de color heredado. En este modo, cuando el
IsEnabled se establece la propiedad de la vista en false , la vista invalida los colores establecidos por el usuario
con los colores nativo predeterminado para el estado deshabilitado. Para hacia atrás compatibilidad, este modo
heredado de color permanece el comportamiento predeterminado para las vistas admitidas.
Esta plataforma iOS específicos deshabilita este modo heredado de color en un VisualElement , de modo que los
colores establecidos en una vista por el usuario permanezcan incluso cuando se deshabilita la vista. Se consume
en XAML estableciendo el VisualElement.IsLegacyColorModeEnabled propiedad adjunta false :

<ContentPage ...
xmlns:ios="clr-
namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout>
...
<Button Text="Button"
TextColor="Blue"
BackgroundColor="Bisque"
ios:VisualElement.IsLegacyColorModeEnabled="False" />
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

_legacyColorModeDisabledButton.On<iOS>().SetIsLegacyColorModeEnabled(false);

El VisualElement.On<iOS>método especifica que solo se ejecutarán este específicos de la plataforma de iOS. El


VisualElement.SetIsLegacyColorModeEnabled método, en el Xamarin.Forms.PlatformConfiguration.iOSSpecific
espacio de nombres, se usa para controlar si se deshabilita el modo de color heredado. Además, el
VisualElement.GetIsLegacyColorModeEnabled método puede utilizarse para devolver si está deshabilitado el modo
de color heredado.
El resultado es que se puede deshabilitar el modo de color heredados, para que los colores establecidos en una
vista por el usuario permanezcan incluso cuando se deshabilita la vista:
NOTE
Al establecer un VisualStateGroup en una vista, se omite completamente el modo de color heredado. Para obtener más
información acerca de los estados visuales, vea Xamarin.Forms Visual State Manager.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
iOSSpecific API
Xamarin.Forms en proyectos nativos de Xamarin
17/07/2019 • 22 minutes to read • Edit Online

Descargar el ejemplo
Normalmente, una aplicación de Xamarin.Forms incluye una o más páginas que se derivan de ContentPage , y
estas páginas se comparten con todas las plataformas en un proyecto compartido o un proyecto de biblioteca de
.NET Standard. Sin embargo, permite formularios nativos ContentPage -derivada páginas agregarse directamente
a las aplicaciones de Xamarin.iOS, Xamarin.Android y UWP nativas. En comparación a tener el proyecto nativo
consumir ContentPage -derivadas páginas desde un proyecto de biblioteca estándar de .NET o un proyecto
compartido, la ventaja de agregar páginas directamente a proyectos nativos es que las páginas se pueden
extender con vistas nativas. A continuación, se pueden denominar vistas nativas en XAML con x:Name y que se
hace referencia desde el código subyacente. Para obtener más información acerca de vistas nativas, consulte vistas
nativas.
El proceso para consumir un Xamarin.Forms ContentPage -página derivada en un proyecto nativo es como sigue:
1. Agregue el paquete de Xamarin.Forms NuGet en el proyecto nativo.
2. Agregar el ContentPage -derivados de página y las dependencias, en el proyecto nativo.
3. Llame al método Forms.Init .
4. Construir una instancia de la ContentPage -página derivada y convertirlo al tipo nativo apropiado con uno de
los siguientes métodos de extensión: CreateViewController para iOS, CreateSupportFragment para Android, o
CreateFrameworkElement para UWP.
5. Vaya a la representación de tipo nativo de la ContentPage -mediante la API nativa barra de navegación de
página derivada.
Xamarin.Forms debe inicializarse llamando el Forms.Init método antes de que un proyecto nativo se puede
construir un ContentPage -página derivada. Elegir cuándo deben hacerlo principalmente depende cuando sea más
conveniente en el flujo de la aplicación: puede realizarse al iniciar la aplicación, o justo antes de la ContentPage -
página derivada se construye. En este artículo y las aplicaciones de ejemplo que lo acompaña, el Forms.Init se
llama al método al iniciarse la aplicación.

NOTE
El NativeForms solución de aplicación de ejemplo no contiene todos los proyectos de Xamarin.Forms. 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 -derivados de las páginas. Sin embargo, no hay ningún motivo
por qué no podían consumir los proyectos nativos ContentPage -deriva de las páginas de un proyecto de biblioteca
estándar de .NET o un proyecto compartido.

Al utilizar formularios nativos, Xamarin.Forms características como DependencyService , MessagingCenter y el


motor de enlace de datos, todo el trabajo todavía. Sin embargo, navegación de página debe realizarse mediante la
API de exploración nativo.

iOS
En iOS, el FinishedLaunching invalidar en el AppDelegate clase suele ser el lugar para ejecutar la aplicación las
tareas relacionadas con el inicio. Se llama después de la aplicación ha lanzado y normalmente se reemplaza para
configurar la ventana principal y ver el controlador. El siguiente ejemplo de código muestra la AppDelegate clase
en la aplicación de ejemplo:

[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public static string FolderPath { get; private set; }

public static AppDelegate Instance;

UIWindow _window;
UINavigationController _navigation;

public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)


{
Forms.Init();

Instance = this;
_window = new UIWindow(UIScreen.MainScreen.Bounds);

UINavigationBar.Appearance.SetTitleTextAttributes(new UITextAttributes
{
TextColor = UIColor.Black
});

FolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
UIViewController mainPage = new NotesPage().CreateViewController();
mainPage.Title = "Notes";

_navigation = new UINavigationController(mainPage);


_window.RootViewController = _navigation;
_window.MakeKeyAndVisible();

return true;
}
...
}

El FinishedLaunching método realiza las siguientes tareas:


Xamarin.Forms se inicializa mediante una llamada a la Forms.Init método.
Una referencia a la AppDelegate clase se almacena en el static Instance campo. Esto es proporcionar un
mecanismo para otras clases llamar a métodos definidos en el AppDelegate clase.
El UIWindow , que es el contenedor principal para las vistas en las aplicaciones nativas para iOS, se crea.
El FolderPath propiedad se inicializa en una ruta de acceso en el dispositivo donde se almacenarán los datos
de la nota.
El NotesPage (clase), que es un objeto Xamarin.Forms ContentPage -derivados de la página definida en XAML,
que se crea y se convierte en un UIViewController utilizando el CreateViewController método de extensión.
El Title propiedad de la UIViewController está establecida, lo que se mostrará en el UINavigationBar .
Un UINavigationController se crea para administrar la navegación jerárquica. El UINavigationController clase
administra una pila de controladores de vista y el UIViewController pasado en el constructor se presentarán
inicialmente cuando el UINavigationController está cargado.
El UINavigationController instancia se establece como el nivel superior UIViewController para el UIWindow y el
UIWindow está establecida como la ventana de la clave para la aplicación y se hace visible.

Una vez el FinishedLaunching ha ejecutado el método, definido por la interfaz de usuario en Xamarin.Forms
NotesPage clase se mostrará como se muestra en la captura de pantalla siguiente:
Interactuar con la interfaz de usuario, por ejemplo, al pulsar la + Button , dará como resultado el siguiente
controlador de eventos en el NotesPage ejecutando el código subyacente:

void OnNoteAddedClicked(object sender, EventArgs e)


{
AppDelegate.Instance.NavigateToNoteEntryPage(new Note());
}

El static AppDelegate.Instance campo permite la AppDelegate.NavigateToNoteEntryPage invocar método, que se


muestra en el ejemplo de código siguiente:

public void NavigateToNoteEntryPage(Note note)


{
UIViewController noteEntryPage = new NoteEntryPage
{
BindingContext = note
}.CreateViewController();
noteEntryPage.Title = "Note Entry";
_navigation.PushViewController(noteEntryPage, true);
}

El NavigateToNoteEntryPagemétodo convierte Xamarin.Forms ContentPage -derivados de la página para un


UIViewController con el CreateViewController método de extensión y los conjuntos de la Title propiedad de la
UIViewController . El UIViewController , a continuación, se inserta en UINavigationController por el
PushViewController método. Por lo tanto, se define la interfaz de usuario en Xamarin.Forms NoteEntryPage clase
se mostrará como se muestra en la captura de pantalla siguiente:

Cuando el NoteEntryPage se muestra, puntee en la parte posterior flecha se mostrará el mensaje el


UIViewController para el NoteEntryPage clase desde el UINavigationController , devolver al usuario la
UIViewController para el NotesPage clase.

WARNING
El Popping de un UIViewController en la navegación nativo de iOS pila no eliminará automáticamente
UIViewController s. Es responsabilidad del programador asegurarse de que cualquier UIViewController que ya no se
necesita tiene su Dispose() llama al método, lo contrario, el UIViewController y adjunta Page quedarán huérfanos y
no se recopilarán por el recolector de elementos no utilizados lo que produce una pérdida de memoria.

Android
En Android, el OnCreate invalidar en el MainActivity clase suele ser el lugar para ejecutar la aplicación las tareas
relacionadas con el inicio. El siguiente ejemplo de código 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);
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));
Android.Support.V4.App.Fragment mainPage = new NotesPage().CreateSupportFragment(this);
SupportFragmentManager
.BeginTransaction()
.Replace(Resource.Id.fragment_frame_layout, mainPage)
.Commit();
...
}
...
}

El OnCreate método realiza las siguientes tareas:


Xamarin.Forms se inicializa mediante una llamada a la Forms.Init método.
Una referencia a la MainActivity clase se almacena en el static Instance campo. Esto es proporcionar un
mecanismo para otras clases llamar a métodos definidos en el MainActivity clase.
El Activity se establece el contenido de un recurso de diseño. En la aplicación de ejemplo, el diseño consta de
un LinearLayout que contiene un Toolbar y un FrameLayout para que actúe como un contenedor de
fragmento.
El Toolbar se recupera y se establece como la barra de acciones para el Activity , y se establece el título de la
barra de acción.
El FolderPath propiedad se inicializa en una ruta de acceso en el dispositivo donde se almacenarán los datos
de la nota.
El NotesPage (clase), que es un objeto Xamarin.Forms ContentPage -derivados de la página definida en XAML,
que se crea y se convierte en un Fragment utilizando el CreateSupportFragment método de extensión.
El SupportFragmentManager clase se crea y se confirma una transacción que reemplaza el FrameLayout instancia
con el Fragment para el NotesPage clase.

Para obtener más información sobre los fragmentos, vea fragmentos.


Una vez el OnCreate ha ejecutado el método, definido por la interfaz de usuario en Xamarin.Forms NotesPage
clase se mostrará como se muestra en la captura de pantalla siguiente:
Interactuar con la interfaz de usuario, por ejemplo, al pulsar la + Button , dará como resultado el siguiente
controlador de eventos en el NotesPage ejecutando el código subyacente:

void OnNoteAddedClicked(object sender, EventArgs e)


{
MainActivity.Instance.NavigateToNoteEntryPage(new Note());
}

El static MainActivity.Instance campo permite la MainActivity.NavigateToNoteEntryyPage invocar método, que


se muestra en el ejemplo de código siguiente:

public void NavigateToNoteEntryPage(Note note)


{
Android.Support.V4.App.Fragment noteEntryPage = new NoteEntryPage
{
BindingContext = note
}.CreateSupportFragment(this);
SupportFragmentManager
.BeginTransaction()
.AddToBackStack(null)
.Replace(Resource.Id.fragment_frame_layout, noteEntryPage)
.Commit();
}

El NavigateToNoteEntryPage método convierte Xamarin.Forms ContentPage -derivados de la página a un


Fragment con el CreateSupportFragment método de extensión y agrega el Fragment al fragmento de la pila de
retroceso. Por lo tanto, se define la interfaz de usuario en Xamarin.Forms NoteEntryPage se mostrará como se
muestra en la captura de pantalla siguiente:

Cuando el NoteEntryPage se muestra, puntee en la parte posterior flecha se mostrará el mensaje el Fragment para
el NoteEntryPage desde la pila de retroceso del fragmento, devolver al usuario la Fragment para el NotesPage
clase.
Habilitar la compatibilidad con la navegación hacia atrás
El SupportFragmentManager clase tiene un BackStackChanged evento que se desencadena cada vez que cambia el
contenido de la pila de retroceso de fragmento. El OnCreate método en el MainActivity clase contiene un
controlador de eventos anónimos 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 hay uno o más
Fragment instancias en el fragmento de la pila de retroceso. La respuesta al pulsar el botón Atrás se controla
mediante el OnOptionsItemSelected invalidar:

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);
}

El OnOptionsItemSelected invalidación se llama siempre que se selecciona un elemento en el menú de opciones.


Esta implementación aparece en el fragmento actual de la pila de retroceso del fragmento, siempre que se ha
seleccionado el botón Atrás y hay uno o más Fragment instancias en el fragmento de la pila de retroceso.
Varias actividades
Cuando una aplicación se compone de varias actividades ContentPage -páginas derivadas se pueden incrustar en
cada una de las actividades. En este escenario, el Forms.Init método debe llamarse únicamente en el OnCreate la
invalidación de la primera Activity que incrusta un Xamarin.Forms ContentPage . Sin embargo, esto tiene las
siguientes consecuencias:
El valor de Xamarin.Forms.Color.Accent se tomará de la Activity que llama el Forms.Init método.
El valor de Xamarin.Forms.Application.Current se asociará el Activity que llama el Forms.Init método.

Elija un archivo
Al incrustar un ContentPage -derivados de la página que utiliza un WebView que necesita para compatibilidad con
HTML "Elegir el archivo" botón, el Activity será necesario 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, nativo App clase suele ser el lugar para ejecutar la aplicación las tareas relacionadas con el inicio.
Xamarin.Forms es normalmente inicializado, en las aplicaciones de UWP de Xamarin.Forms, en el OnLaunched
invalidar en nativo App (clase), para pasar el LaunchActivatedEventArgs argumento para el Forms.Init método.
Por este motivo, las aplicaciones nativas de UWP que consumen un Xamarin.Forms ContentPage -más fácilmente
puede llamar page derivada el Forms.Init método desde el App.OnLaunched método.
De forma predeterminada, nativo App clase inicia la MainPage clase como la primera página de la aplicación. El
siguiente ejemplo de código muestra la MainPage clase en la aplicación de ejemplo:
public sealed partial class MainPage : Page
{
public static MainPage Instance;

public static string FolderPath { get; private set; }

public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Enabled;
Instance = this;
FolderPath =
Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData));
this.Content = new Notes.UWP.Views.NotesPage().CreateFrameworkElement();
}
...
}

El MainPage constructor realiza las siguientes tareas:


Almacenamiento en caché está habilitado para la página, para que un nuevo MainPage no se construye cuando
un usuario navega a la página.
Una referencia a la MainPage clase se almacena en el static Instance campo. Esto es proporcionar un
mecanismo para otras clases llamar a métodos definidos en el MainPage clase.
El FolderPath propiedad se inicializa en una ruta de acceso en el dispositivo donde se almacenarán los datos
de la nota.
El NotesPage (clase), que es un objeto Xamarin.Forms ContentPage -derivados de la página definida en XAML,
que se crea y se convierte en un FrameworkElement utilizando el CreateFrameworkElement método de extensión
y, después, establezca como contenido de la MainPage clase.

Una vez el MainPage ha ejecutado el constructor, definido por la interfaz de usuario en Xamarin.Forms NotesPage
clase se mostrará como se muestra en la captura de pantalla siguiente:

Interactuar con la interfaz de usuario, por ejemplo, al pulsar la + Button , dará como resultado el siguiente
controlador de eventos en el NotesPage ejecutando el código subyacente:

void OnNoteAddedClicked(object sender, EventArgs e)


{
MainPage.Instance.NavigateToNoteEntryPage(new Note());
}

El static MainPage.Instance campo permite la MainPage.NavigateToNoteEntryPage invocar método, que se


muestra en el ejemplo de código siguiente:
public void NavigateToNoteEntryPage(Note note)
{
this.Frame.Navigate(new NoteEntryPage
{
BindingContext = note
});
}

Navegación en UWP se suele realizar con la Frame.Navigate método, que toma un Page argumento.
Xamarin.Forms define una Frame.Navigate método de extensión que toma un ContentPage -derivados de la
instancia de la página. Por lo tanto, cuando el NavigateToNoteEntryPage método se ejecuta, la interfaz de usuario
definido en Xamarin.Forms NoteEntryPage se mostrará como se muestra en la captura de pantalla siguiente:

Cuando el NoteEntryPage se muestra, puntee en la parte posterior flecha se mostrará el mensaje el


FrameworkElement para el NoteEntryPage desde la pila de retroceso de la aplicación, se devuelve al usuario la
FrameworkElement para el NotesPage clase.
Habilitar la compatibilidad con la navegación hacia atrás
En UWP, las aplicaciones deben habilitar la navegación hacia atrás para todos los software y hardware Atrás
botones, a través de diferentes factores de forma. Esto puede realizarse mediante el registro de un controlador de
eventos para el BackRequested evento, que se puede realizar en el OnLaunched método nativo App clase:

protected override void OnLaunched(LaunchActivatedEventArgs e)


{
Frame rootFrame = Window.Current.Content as Frame;

if (rootFrame == null)
{
...
// Place the frame in the current Window
Window.Current.Content = rootFrame;

SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
}
...
}

Cuando se inicia la aplicación, el GetForCurrentView método recupera el SystemNavigationManager objeto asociado


a la vista actual, a continuación, registra un controlador de eventos para el BackRequested eventos. La aplicación
solo recibe este evento si es la aplicación de primer plano y, en respuesta, llama a la 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();
}
}

El OnBackRequested llamadas del controlador de eventos el GoBack método en el marco de la raíz de la aplicación
y establece el BackRequestedEventArgs.Handled propiedad a true para marcar el evento como controlado. Error al
marcar el evento como controlado podría producir en el evento que se pasa por alto.
La aplicación decide si se deben mostrar un botón Atrás en la barra de título. Esto se consigue estableciendo la
AppViewBackButtonVisibility propiedad en uno de los AppViewBackButtonVisibility valores de enumeración:

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 de eventos,


actualiza la visibilidad del título del botón Atrás de la barra cuando tenga lugar la navegación de página. Esto
garantiza que el botón Atrás de barra de título está visible si la pila de retroceso de la aplicación no está vacía, o
quita de la barra de título si está vacía la pila de retroceso de la aplicación.
Para obtener más información sobre la compatibilidad con la navegación hacia atrás en UWP, consulte historial de
navegación hacia atrás o navegación para aplicaciones UWP.

Vínculos relacionados
NativeForms (ejemplo)
Vistas nativas
Vistas nativas en Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Pueden hacer referencia directamente a vistas nativas de iOS, Android y la plataforma Universal de Windows
(UWP ) de Xamarin.Forms. Se pueden establecer las propiedades y los controladores de eventos en vistas nativas,
y pueden interactuar con las vistas de Xamarin.Forms.

Vistas nativas en XAML


Vistas nativas de iOS, Android y UWP pueden hacer referencia directamente desde las páginas de
Xamarin.Forms creadas con XAML.

Vistas nativas en C#
Vistas nativas de iOS, Android y UWP pueden hacer referencia directamente desde las páginas de
Xamarin.Forms creadas con C#.

Vínculos relacionados
Formularios nativos
Vistas nativas en XAML
11/07/2019 • 24 minutes to read • Edit Online

descargar el ejemplo
Vistas nativas de iOS, Android y la plataforma Universal de Windows pueden hacer referencia directamente desde
los archivos XAML de Xamarin.Forms. Se pueden establecer las propiedades y los controladores de eventos en
vistas nativas, y pueden interactuar con las vistas de Xamarin.Forms. En este artículo se muestra cómo consumir
vistas nativas en archivos XAML de Xamarin.Forms.
En este artículo se trata los temas siguientes:
Consumo de vistas nativas – el proceso para utilizar una vista en XAML nativa.
Uso de enlaces nativos : a y desde las propiedades de vistas nativas de enlace de datos.
Pasar argumentos a vistas nativas : pasar argumentos a los constructores de la vista nativa y llamar a vista
nativa de los métodos de fábrica.
Que hace referencia a vistas nativas desde código – recuperar instancias de vista nativa declarados en un
archivo XAML, el archivo de código subyacente.
Creación de subclases de vistas nativas : creación de subclases de vistas nativas para definir una API compatible
con XAML.

Información general
Para insertar una vista nativa a un archivo XAML de Xamarin.Forms:
1. Agregar un xmlns declaración de espacio de nombres en el archivo XAML para el espacio de nombres que
contiene la vista nativa.
2. Cree una instancia de la vista nativa en el archivo XAML.

IMPORTANT
XAML compilado debe deshabilitarse para todas las páginas XAML que utilice vistas nativas. Esto puede realizarse mediante
la decoración de la clase de código subyacente para la página XAML con el
[XamlCompilation(XamlCompilationOptions.Skip)] atributo. Para obtener más información sobre la compilación de
XAML, vea compilación XAML en Xamarin.Forms.

Para hacer referencia a una vista nativa desde un archivo de código subyacente, debe usar un proyecto de activos
compartidos (SAP ) y ajustar el código específico de plataforma con directivas de compilación condicional. Para
obtener más información, consulte que hace referencia a vistas nativas desde código.

Consumo de vistas nativas


En el ejemplo de código siguiente se muestra cómo consumir vistas nativas para cada plataforma para un objeto
Xamarin.Forms ContentPage :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
xmlns:androidLocal="clr-
namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
xmlns:win="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
x:Class="NativeViews.NativeViewDemo">
<StackLayout Margin="20">
<ios:UILabel Text="Hello World" TextColor="{x:Static ios:UIColor.Red}" View.HorizontalOptions="Start"
/>
<androidWidget:TextView Text="Hello World" x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
/>
<win:TextBlock Text="Hello World" />
</StackLayout>
</ContentPage>

Así como especificar el clr-namespace y assembly para un espacio de nombres de vista nativo, un targetPlatform
también debe especificarse. Esto se debe establecer en uno de los valores de la TargetPlatform enumeración y
normalmente se establecerá en iOS , Android , o Windows . En tiempo de ejecución, el analizador XAML pasará por
alto los prefijos de espacios de nombres XML que tienen un targetPlatform que no coincide con la plataforma en
la que se ejecuta la aplicación.
Cada declaración de espacio de nombres puede utilizarse para hacer referencia a cualquier clase o estructura del
espacio de nombres especificado. Por ejemplo, el ios declaración de espacio de nombres se puede usar para
hacer referencia a cualquier clase o estructura desde iOS UIKit espacio de nombres. Se pueden establecer
propiedades de la vista nativa a través de XAML, pero deben coincidir con los tipos de propiedad y objeto. Por
ejemplo, el UILabel.TextColor propiedad está establecida en UIColor.Red utilizando el x:Static extensión de
marcado y el ios espacio de nombres.
Propiedades enlazables y adjuntadas propiedades enlazables también pueden establecerse en vistas nativas
mediante el uso de la Class.BindableProperty="value" sintaxis. Cada vista nativa se encapsula en una plataforma
específica NativeViewWrapper instancia, que se deriva de la Xamarin.Forms.View clase. Establecer una propiedad
enlazable o una propiedad enlazable adjunta en una vista nativa, el valor de propiedad transfiere al contenedor. Por
ejemplo, se puede especificar un diseño centrado horizontal estableciendo View.HorizontalOptions="Center" en la
vista nativa.

NOTE
Tenga en cuenta que los estilos no se puede usar con vistas nativas, porque los estilos solo pueden tener como destino las
propiedades que están respaldadas por BindableProperty objetos.

Constructores de widget Android requieren generalmente el Android Context objeto como un argumento y esto
es posible acceder a través de una propiedad estática en el MainActivity clase. Por lo tanto, al crear un widget de
Android en XAML, el Context objeto normalmente se debe pasar al constructor del widget mediante el
x:Arguments atributo con un x:Static extensión de marcado. Para obtener más información, consulte pasar
argumentos a vistas nativas.
NOTE
Tenga en cuenta que una vista nativa con nombres x:Name no es posible en un proyecto de biblioteca estándar de .NET o
un proyecto de activos compartidos (SAP). Si lo hace, se generará una variable del tipo nativo, lo que provocará un error de
compilación. Sin embargo, las vistas nativas pueden envolverse en ContentView instancias y recuperarse en el archivo de
código subyacente, siempre que se utilice un SAP. Para obtener más información, consulte Referencia a una vista nativa
desde el código.

Enlaces nativos
Enlace de datos se utiliza para sincronizar una interfaz de usuario con su origen de datos y simplifica la forma en
que una aplicación de Xamarin.Forms muestra e interactúa con sus datos. Siempre que el objeto fuente
implemente la interfaz INotifyPropertyChanged , los cambios en el objeto fuente se envían automáticamente al
objeto objetivo por el marco vinculante, y los cambios en el objeto objetivo opcionalmente se pueden enviar al
objeto fuente.
Las propiedades de las vistas nativas también pueden usar el enlace de datos. El siguiente ejemplo de código
demuestra el enlace de datos usando propiedades de vistas nativas:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
xmlns:androidLocal="clr-
namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
xmlns:win="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
xmlns:local="clr-namespace:NativeSwitch"
x:Class="NativeSwitch.NativeSwitchPage">
<StackLayout Margin="20">
<Label Text="Native Views Demo" FontAttributes="Bold" HorizontalOptions="Center" />
<Entry Placeholder="This Entry is bound to the native switch" IsEnabled="{Binding IsSwitchOn}" />
<ios:UISwitch On="{Binding Path=IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=ValueChanged}"
OnTintColor="{x:Static ios:UIColor.Red}"
ThumbTintColor="{x:Static ios:UIColor.Blue}" />
<androidWidget:Switch x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
Checked="{Binding Path=IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=CheckedChange}"
Text="Enable Entry?" />
<win:ToggleSwitch Header="Enable Entry?"
OffContent="No"
OnContent="Yes"
IsOn="{Binding IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=Toggled}" />
</StackLayout>
</ContentPage>

La página contiene una propiedad Entry cuya IsEnabled se une a la NativeSwitchPageViewModel.IsSwitchOn


propiedad. El BindingContext de la página se establece en una nueva instancia de la NativeSwitchPageViewModel
clase en el archivo de código subyacente, con la clase ViewModel que implementa el INotifyPropertyChanged
interfaz.
La página también contiene un conmutador para cada plataforma. Cada conmutador nativa usa un TwoWay enlace
para actualizar el valor de la NativeSwitchPageViewModel.IsSwitchOn propiedad. Por lo tanto, cuando el conmutador
está desactivado, el Entry está deshabilitado, y cuando el modificador está activado, el Entry está habilitado. Las
capturas de pantalla siguientes muestran cómo esta funcionalidad en cada plataforma:
Automáticamente se admiten enlaces bidireccionales siempre que implemente la propiedad nativa
INotifyPropertyChanged , admite la observación de pares clave-valor ( KVO ) en iOS o es una DependencyProperty en
UWP. Sin embargo, muchas vistas nativas no admiten la notificación de cambio de propiedad. Estas vistas, puede
especificar un UpdateSourceEventName valor de propiedad como parte de la expresión de enlace. Esta propiedad
debe establecerse en el nombre de un evento en la vista nativo que indica cuándo ha cambiado la propiedad de
destino. Luego, cuando cambia el valor del conmutador nativo, se notifica a la clase Binding que el usuario ha
cambiado el valor del conmutador y se actualiza el valor de la propiedad NativeSwitchPageViewModel.IsSwitchOn .

Pasar argumentos a vistas nativas


Los argumentos de constructor se pueden pasar a vistas nativas usando el atributo x:Arguments con una
x:Static extensión de marcado. Además, los métodos de fábrica de vista nativa ( public static métodos que
devuelven objetos o valores del mismo tipo que la clase o estructura que define los métodos) pueden invocarse
especificando el nombre del método utilizando el atributo x:FactoryMethod y sus argumentos utilizando el atributo
x:Arguments .

El ejemplo de código siguiente muestra ambas técnicas:


<ContentPage ...
xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
xmlns:androidGraphics="clr-namespace:Android.Graphics;assembly=Mono.Android;targetPlatform=Android"
xmlns:androidLocal="clr-
namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
xmlns:winMedia="clr-namespace:Windows.UI.Xaml.Media;assembly=Windows, Version=255.255.255.255,
Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
xmlns:winText="clr-namespace:Windows.UI.Text;assembly=Windows, Version=255.255.255.255,
Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
xmlns:winui="clr-namespace:Windows.UI;assembly=Windows, Version=255.255.255.255, Culture=neutral,
PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows">
...
<ios:UILabel Text="Simple Native Color Picker" View.HorizontalOptions="Center">
<ios:UILabel.Font>
<ios:UIFont x:FactoryMethod="FromName">
<x:Arguments>
<x:String>Papyrus</x:String>
<x:Single>24</x:Single>
</x:Arguments>
</ios:UIFont>
</ios:UILabel.Font>
</ios:UILabel>
<androidWidget:TextView x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
Text="Simple Native Color Picker"
TextSize="24"
View.HorizontalOptions="Center">
<androidWidget:TextView.Typeface>
<androidGraphics:Typeface x:FactoryMethod="Create">
<x:Arguments>
<x:String>cursive</x:String>
<androidGraphics:TypefaceStyle>Normal</androidGraphics:TypefaceStyle>
</x:Arguments>
</androidGraphics:Typeface>
</androidWidget:TextView.Typeface>
</androidWidget:TextView>
<winControls:TextBlock Text="Simple Native Color Picker"
FontSize="20"
FontStyle="{x:Static winText:FontStyle.Italic}"
View.HorizontalOptions="Center">
<winControls:TextBlock.FontFamily>
<winMedia:FontFamily>
<x:Arguments>
<x:String>Georgia</x:String>
</x:Arguments>
</winMedia:FontFamily>
</winControls:TextBlock.FontFamily>
</winControls:TextBlock>
...
</ContentPage>

El método de generador que se usa para establecer el UILabel.Font propiedad a un nuevo


UIFont.FromName
UIFont en iOS. El UIFont nombre y tamaño se especifican mediante los argumentos del método que son
elementos secundarios de la x:Arguments atributo.
El método de generador que se usa para establecer el TextView.Typeface propiedad a un nuevo
Typeface.Create
Typeface en Android. El Typeface nombre de familia y estilo se especifican mediante los argumentos del método
que son elementos secundarios de la x:Arguments atributo.
El FontFamily constructor se usa para establecer el TextBlock.FontFamily propiedad a un nuevo FontFamily en la
plataforma Universal de Windows (UWP ). El FontFamily nombre se especifica mediante el argumento del método
que es un elemento secundario de la x:Arguments atributo.
NOTE
Argumentos deben coincidir con los tipos requeridos por el constructor o método generador.

Las capturas de pantalla siguientes muestran el resultado de especificar los argumentos de método y constructor
del generador para establecer la fuente en diferentes vistas nativas:

Para obtener más información sobre cómo pasar argumentos en XAML, vea pasar argumentos en XAML.

Que hace referencia a vistas nativas desde código


Aunque no se puede nombrar una vista nativa con el x:Name atributo, es posible recuperar una instancia de la
vista nativa declarada en un archivo XAML de su archivo de código subyacente en un proyecto de acceso
compartido, siempre que la vista nativa es un elemento secundario de un ContentView que especifica un x:Name
valor del atributo. A continuación, dentro de las directivas de compilación condicional en el archivo de código
subyacente, debe:
1. Recuperar el ContentView.Content propiedad de valor y convertirlo en una plataforma específica
NativeViewWrapper tipo.
2. Recuperar el NativeViewWrapper.NativeElement propiedad y convertirlo en el tipo de vista nativo.

A continuación, se pueden invocar las API nativas en la vista nativa para realizar las operaciones deseadas. Este
enfoque también ofrece la ventaja que varias vistas XAML nativas para diferentes plataformas pueden ser
elementos secundarios del mismo ContentView . En el ejemplo de código siguiente se muestra esta técnica:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
xmlns:androidLocal="clr-
namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
xmlns:local="clr-namespace:NativeViewInsideContentView"
x:Class="NativeViewInsideContentView.NativeViewInsideContentViewPage">
<StackLayout Margin="20">
<ContentView x:Name="contentViewTextParent" HorizontalOptions="Center"
VerticalOptions="CenterAndExpand">
<ios:UILabel Text="Text in a UILabel" TextColor="{x:Static ios:UIColor.Red}" />
<androidWidget:TextView x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
Text="Text in a TextView" />
<winControls:TextBlock Text="Text in a TextBlock" />
</ContentView>
<ContentView x:Name="contentViewButtonParent" HorizontalOptions="Center"
VerticalOptions="EndAndExpand">
<ios:UIButton TouchUpInside="OnButtonTap" View.HorizontalOptions="Center"
View.VerticalOptions="Center" />
<androidWidget:Button x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
Text="Scale and Rotate Text"
Click="OnButtonTap" />
<winControls:Button Content="Scale and Rotate Text" />
</ContentView>
</StackLayout>
</ContentPage>

En el ejemplo anterior, las vistas nativas para cada plataforma son elementos secundarios de ContentView
controles, con el x:Name valor del atributo que se usa para recuperar el ContentView en el código subyacente:
public partial class NativeViewInsideContentViewPage : ContentPage
{
public NativeViewInsideContentViewPage()
{
InitializeComponent();

#if __IOS__
var wrapper = (Xamarin.Forms.Platform.iOS.NativeViewWrapper)contentViewButtonParent.Content;
var button = (UIKit.UIButton)wrapper.NativeView;
button.SetTitle("Scale and Rotate Text", UIKit.UIControlState.Normal);
button.SetTitleColor(UIKit.UIColor.Black, UIKit.UIControlState.Normal);
#endif
#if __ANDROID__
var wrapper = (Xamarin.Forms.Platform.Android.NativeViewWrapper)contentViewTextParent.Content;
var textView = (Android.Widget.TextView)wrapper.NativeView;
textView.SetTextColor(Android.Graphics.Color.Red);
#endif
#if WINDOWS_UWP
var textWrapper = (Xamarin.Forms.Platform.UWP.NativeViewWrapper)contentViewTextParent.Content;
var textBlock = (Windows.UI.Xaml.Controls.TextBlock)textWrapper.NativeElement;
textBlock.Foreground = new Windows.UI.Xaml.Media.SolidColorBrush(Windows.UI.Colors.Red);
var buttonWrapper = (Xamarin.Forms.Platform.UWP.NativeViewWrapper)contentViewButtonParent.Content;
var button = (Windows.UI.Xaml.Controls.Button)buttonWrapper.NativeElement;
button.Click += (sender, args) => OnButtonTap(sender, EventArgs.Empty);
#endif
}

async void OnButtonTap(object sender, EventArgs e)


{
contentViewButtonParent.Content.IsEnabled = false;
contentViewTextParent.Content.ScaleTo(2, 2000);
await contentViewTextParent.Content.RotateTo(360, 2000);
contentViewTextParent.Content.ScaleTo(1, 2000);
await contentViewTextParent.Content.RelRotateTo(360, 2000);
contentViewButtonParent.Content.IsEnabled = true;
}
}

El ContentView.Content acceso a la propiedad para recuperar la vista ajustada nativa como una plataforma
específica NativeViewWrapper instancia. El NativeViewWrapper.NativeElement , a continuación, se obtiene acceso de
propiedad para recuperar la vista nativa como tipo nativo. A continuación, se invoca la API nativa de la vista para
llevar a cabo las operaciones deseadas.
IOS y Android botones nativos comparten el mismo OnButtonTap controlador de eventos, porque cada botón
nativo consume un EventHandler delegar en respuesta a un evento de toque. Sin embargo, la plataforma
Universal de Windows (UWP ) usa un independiente RoutedEventHandler , que a su vez los consume el
OnButtonTap controlador de eventos en este ejemplo. Por lo tanto, cuando un botón nativo se hace clic en, el
OnButtonTap ejecuta el controlador de eventos, que se escala y gira el control nativo dentro de la ContentView
denominado contentViewTextParent . Las capturas de pantalla siguientes muestran este que se producen en cada
plataforma:
Creación de subclases de vistas nativas
Muchos iOS y Android vistas nativas no son adecuadas para crear instancias en XAML porque usan métodos, en
lugar de propiedades, para configurar el control. Vistas nativas de subclase de contenedores que definen una API
compatible con XAML más que usa las propiedades para el control de la instalación, y que usa eventos
independientes de la plataforma es la solución a este problema. El ajustada nativas vistas pueden, a continuación,
se coloca en un proyecto de activos compartidos (SAP ) y rodeadas con directivas de compilación condicional, o
colocar en los proyectos específicos de la plataforma y de XAML al que hace referencia en un proyecto de
biblioteca de .NET Standard.
En el ejemplo de código siguiente se muestra una página de Xamarin.Forms que consume una subclase de vistas
nativas:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
xmlns:iosLocal="clr-
namespace:SubclassedNativeControls.iOS;assembly=SubclassedNativeControls.iOS;targetPlatform=iOS"
xmlns:android="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
xmlns:androidLocal="clr-
namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
xmlns:androidLocal="clr-
namespace:SubclassedNativeControls.Droid;assembly=SubclassedNativeControls.Droid;targetPlatform=Android"
xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
xmlns:local="clr-namespace:SubclassedNativeControls"
x:Class="SubclassedNativeControls.SubclassedNativeControlsPage">
<StackLayout Margin="20">
<Label Text="Subclassed Native Views Demo" FontAttributes="Bold" HorizontalOptions="Center" />
<StackLayout Orientation="Horizontal">
<Label Text="You have chosen:" />
<Label Text="{Binding SelectedFruit}" />
</StackLayout>
<iosLocal:MyUIPickerView ItemsSource="{Binding Fruits}"
SelectedItem="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=SelectedItemChanged}" />
<androidLocal:MySpinner x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
ItemsSource="{Binding Fruits}"
SelectedObject="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=ItemSelected}" />
<winControls:ComboBox ItemsSource="{Binding Fruits}"
SelectedItem="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=SelectionChanged}" />
</StackLayout>
</ContentPage>

La página contiene un Label que muestra la fruta elegida por el usuario en un control nativo. El Label enlaza a la
SubclassedNativeControlsPageViewModel.SelectedFruit propiedad. El BindingContext de la página se establece en
una nueva instancia de la SubclassedNativeControlsPageViewModel clase en el archivo de código subyacente, con la
clase ViewModel que implementa el INotifyPropertyChanged interfaz.
La página también contiene una vista de selector nativo para cada plataforma. Cada vista nativa muestra la
colección de frutas enlazando su ItemSource propiedad a la SubclassedNativeControlsPageViewModel.Fruits
colección. Esto permite al usuario elegir una frutas, como se muestra en las capturas de pantalla siguiente:
En iOS y Android los selectores nativos usar métodos para configurar los controles. Por lo tanto, deben ser una
subclase estos selectores para exponer propiedades para que sean compatible con XAML. En la plataforma
Universal de Windows (UWP ), el ComboBox ya es compatible con XAML y, por lo que no requiere la creación de
subclases.
iOS
Las subclases de la implementación de iOS la UIPickerView vista y expone propiedades y un evento que se puede
consumir fácilmente desde XAML:
public class MyUIPickerView : UIPickerView
{
public event EventHandler<EventArgs> SelectedItemChanged;

public MyUIPickerView()
{
var model = new PickerModel();
model.ItemChanged += (sender, e) =>
{
if (SelectedItemChanged != null)
{
SelectedItemChanged.Invoke(this, e);
}
};
Model = model;
}

public IList<string> ItemsSource


{
get
{
var pickerModel = Model as PickerModel;
return (pickerModel != null) ? pickerModel.Items : null;
}
set
{
var model = Model as PickerModel;
if (model != null)
{
model.Items = value;
}
}
}

public string SelectedItem


{
get { return (Model as PickerModel).SelectedItem; }
set { }
}
}

El clase expone ItemsSource y SelectedItem propiedades y un SelectedItemChanged eventos. Un


MyUIPickerView
UIPickerView requiere una subyacente UIPickerViewModel data model, que obtiene acceso a la MyUIPickerView
propiedades y eventos. El UIPickerViewModel proporciona el modelo de datos la PickerModel clase:
class PickerModel : UIPickerViewModel
{
int selectedIndex = 0;
public event EventHandler<EventArgs> ItemChanged;
public IList<string> Items { get; set; }

public string SelectedItem


{
get
{
return Items != null && selectedIndex >= 0 && selectedIndex < Items.Count ? Items[selectedIndex] :
null;
}
}

public override nint GetRowsInComponent(UIPickerView pickerView, nint component)


{
return Items != null ? Items.Count : 0;
}

public override string GetTitle(UIPickerView pickerView, nint row, nint component)


{
return Items != null && Items.Count > row ? Items[(int)row] : null;
}

public override nint GetComponentCount(UIPickerView pickerView)


{
return 1;
}

public override void Selected(UIPickerView pickerView, nint row, nint component)


{
selectedIndex = (int)row;
if (ItemChanged != null)
{
ItemChanged.Invoke(this, new EventArgs());
}
}
}

El clase proporciona el almacenamiento subyacente para el MyUIPickerView (clase), a través de la


PickerModel
Items propiedad. Cada vez que el elemento seleccionado en el MyUIPickerView cambios, el Selected se ejecuta el
método, que actualiza el índice seleccionado y se activa el ItemChanged eventos. Esto garantiza que el
SelectedItem propiedad siempre devolverá el último elemento seleccionado por el usuario. Además, el
PickerModel clase invalida los métodos que se utilizan para configurar el MyUIPickerView instancia.

Android
Las subclases de implementación para Android el Spinner vista y expone propiedades y un evento que se puede
consumir fácilmente desde XAML:
class MySpinner : Spinner
{
ArrayAdapter adapter;
IList<string> items;

public IList<string> ItemsSource


{
get { return items; }
set
{
if (items != value)
{
items = value;
adapter.Clear();

foreach (string str in items)


{
adapter.Add(str);
}
}
}
}

public string SelectedObject


{
get { return (string)GetItemAtPosition(SelectedItemPosition); }
set
{
if (items != null)
{
int index = items.IndexOf(value);
if (index != -1)
{
SetSelection(index);
}
}
}
}

public MySpinner(Context context) : base(context)


{
ItemSelected += OnBindableSpinnerItemSelected;

adapter = new ArrayAdapter(context, Android.Resource.Layout.SimpleSpinnerItem);


adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
Adapter = adapter;
}

void OnBindableSpinnerItemSelected(object sender, ItemSelectedEventArgs args)


{
SelectedObject = (string)GetItemAtPosition(args.Position);
}
}

El MySpinner clase expone ItemsSource y SelectedObject propiedades y un ItemSelected eventos. Los elementos
mostrados por la MySpinner proporcionados clase por el Adapter asociado a la vista y los elementos se rellenan
en el Adapter cuando el ItemsSource propiedad se establece en primer lugar. Cada vez que el elemento
seleccionado en el MySpinner clase cambios, el OnBindableSpinnerItemSelected actualizaciones del controlador de
eventos el SelectedObject propiedad.

Resumen
En este artículo se muestra cómo consumir vistas nativas en archivos XAML de Xamarin.Forms. Se pueden
establecer las propiedades y los controladores de eventos en vistas nativas, y pueden interactuar con las vistas de
Xamarin.Forms.

Vínculos relacionados
NativeSwitch (ejemplo)
Forms2Native (ejemplo)
NativeViewInsideContentView (sample)
SubclassedNativeControls (ejemplo)
Formularios nativos
Pasar argumentos en XAML
Vistas nativas en C#
11/07/2019 • 12 minutes to read • Edit Online

descargar el ejemplo
Vistas nativas de iOS, Android y UWP pueden hacer referencia directamente desde las páginas de Xamarin.Forms
creadas con C#. En este artículo se muestra cómo agregar vistas nativas a un diseño de Xamarin.Forms creado con
C# y cómo reemplazar el diseño de vistas personalizadas para corregir su uso de la API de medición.

Información general
Cualquier control de Xamarin.Forms que permita Content debe establecerse, o que tenga un Children colección,
puede agregar vistas específicas de plataforma. Por ejemplo, un iOS UILabel pueden agregarse directamente a la
ContentView.Content propiedad, o a la StackLayout.Children colección. Sin embargo, tenga en cuenta que esta
funcionalidad requiere el uso de #if define en las soluciones de proyecto de Xamarin.Forms compartido y no
está disponible desde soluciones de la biblioteca estándar de .NET de Xamarin.Forms.
Las capturas de pantalla siguientes muestran específicos de la plataforma, las vistas que se han agregado a un
objeto Xamarin.Forms StackLayout :

La capacidad de agregar vistas específicas de plataforma a un diseño de Xamarin.Forms se habilita mediante dos
métodos de extensión en cada plataforma:
Add : agrega una vista específica de la plataforma para la Children colección de un diseño.
ToView : toma una vista específica de la plataforma y lo encapsula como un objeto Xamarin.Forms View que
se pueden establecer como el Content propiedad de un control.
Mediante estos métodos en un proyecto compartido Xamarin.Forms, es necesario importar el espacio de nombres
de Xamarin.Forms específicos de la plataforma adecuada:
iOS – Xamarin.Forms.Platform.iOS
Android – Xamarin.Forms.Platform.Android
Plataforma universal de Windows (UWP ) – Xamarin.Forms.Platform.UWP

Agregar vistas específicas de la plataforma en cada plataforma


Las secciones siguientes muestran cómo agregar vistas específicas de plataforma a un diseño de Xamarin.Forms
en cada plataforma.
iOS
En el ejemplo de código siguiente se muestra cómo agregar un UILabel a un StackLayout y un ContentView :

var uiLabel = new UILabel {


MinimumFontSize = 14f,
Lines = 0,
LineBreakMode = UILineBreakMode.WordWrap,
Text = originalText,
};
stackLayout.Children.Add (uiLabel);
contentView.Content = uiLabel.ToView();

El ejemplo supone que el stackLayout y contentView instancias se han creado previamente en XAML o C#.
Android
En el ejemplo de código siguiente se muestra cómo agregar un TextView a un StackLayout y un ContentView :

var textView = new TextView (MainActivity.Instance) { Text = originalText, TextSize = 14 };


stackLayout.Children.Add (textView);
contentView.Content = textView.ToView();

El ejemplo supone que el stackLayout y contentView instancias se han creado previamente en XAML o C#.
Plataforma universal de Windows
En el ejemplo de código siguiente se muestra cómo agregar un TextBlock a un StackLayout y un ContentView :

var textBlock = new TextBlock


{
Text = originalText,
FontSize = 14,
FontFamily = new FontFamily("HelveticaNeue"),
TextWrapping = TextWrapping.Wrap
};
stackLayout.Children.Add(textBlock);
contentView.Content = textBlock.ToView();

El ejemplo supone que el stackLayout y contentView instancias se han creado previamente en XAML o C#.

Reemplazar las mediciones de plataforma para las vistas personalizadas


Vistas personalizadas en cada plataforma implementan a menudo solo correctamente medida para el escenario de
diseño para el que se diseñaron. Por ejemplo, una vista personalizada que se han diseñado que solo ocupe la mitad
del ancho disponible del dispositivo. Sin embargo, después de que se comparten con otros usuarios, la vista
personalizada puede requerir hasta ocupar el ancho total disponible del dispositivo. Por lo tanto, puede ser
necesario reemplazar una implementación de medición de vistas personalizadas cuando se reutilizan en un diseño
de Xamarin.Forms. Por ese motivo, la Add y ToView métodos de extensión ofrecen invalidaciones que permiten a
los delegados de medida que se especifique, que pueden reemplazar el diseño de vista personalizada cuando se
agrega a un diseño de Xamarin.Forms.
Las siguientes secciones muestran cómo reemplazar el diseño de vistas personalizadas, para corregir su uso de la
API de medición.
iOS
El siguiente ejemplo de código muestra la CustomControl (clase), que hereda de UILabel :

public class CustomControl : UILabel


{
public override string Text {
get { return base.Text; }
set { base.Text = value.ToUpper (); }
}

public override CGSize SizeThatFits (CGSize size)


{
return new CGSize (size.Width, 150);
}
}

Una instancia de esta vista se agrega a un StackLayout , tal y como se muestra en el ejemplo de código siguiente:

var customControl = new CustomControl {


MinimumFontSize = 14,
Lines = 0,
LineBreakMode = UILineBreakMode.WordWrap,
Text = "This control has incorrect sizing - there's empty space above and below it."
};
stackLayout.Children.Add (customControl);

Sin embargo, dado que el CustomControl.SizeThatFits invalidación siempre devuelve un valor de 150 de alto, se
mostrará la vista con un espacio vacío por encima y debajo de él, como se muestra en la siguiente captura de
pantalla:

Una solución a este problema consiste en proporcionar un GetDesiredSizeDelegate implementación, como se


muestra en el ejemplo de código siguiente:
SizeRequest? FixSize (NativeViewWrapperRenderer renderer, double width, double height)
{
var uiView = renderer.Control;

if (uiView == null) {
return null;
}

var constraint = new CGSize (width, height);

// Let the CustomControl determine its size (which will be wrong)


var badRect = uiView.SizeThatFits (constraint);

// Use the width and substitute the height


return new SizeRequest (new Size (badRect.Width, 70));
}

Este método utiliza el ancho proporcionado por el CustomControl.SizeThatFits método, pero sustituye la altura de
150 por una altura de 70. Cuando el CustomControl instancia se agrega a la StackLayout , el FixSize método se
puede especificar como el GetDesiredSizeDelegate para corregir la medida incorrecta proporcionada por el
CustomControl clase:

stackLayout.Children.Add (customControl, FixSize);

El resultado en la vista personalizada que se muestra correctamente, sin espacio en blanco por encima y debajo de
él, como se muestra en la captura de pantalla siguiente:

Android
El siguiente ejemplo de código muestra la CustomControl (clase), que hereda de TextView :

public class CustomControl : TextView


{
public CustomControl (Context context) : base (context)
{
}

protected override void OnMeasure (int widthMeasureSpec, int heightMeasureSpec)


{
int width = MeasureSpec.GetSize (widthMeasureSpec);

// Force the width to half of what's been requested.


// This is deliberately wrong to demonstrate providing an override to fix it with.
int widthSpec = MeasureSpec.MakeMeasureSpec (width / 2, MeasureSpec.GetMode (widthMeasureSpec));

base.OnMeasure (widthSpec, heightMeasureSpec);


}
}
Una instancia de esta vista se agrega a un StackLayout , tal y como se muestra en el ejemplo de código siguiente:

var customControl = new CustomControl (MainActivity.Instance) {


Text = "This control has incorrect sizing - it doesn't occupy the available width of the device.",
TextSize = 14
};
stackLayout.Children.Add (customControl);

Sin embargo, dado que el CustomControl.OnMeasure invalidación siempre devuelve la mitad del ancho solicitado, la
vista se mostrarán ocupando sólo la mitad el ancho disponible del dispositivo, como se muestra en la captura de
pantalla siguiente:

Una solución a este problema consiste en proporcionar un GetDesiredSizeDelegate implementación, como se


muestra en el ejemplo de código siguiente:

SizeRequest? FixSize (NativeViewWrapperRenderer renderer, int widthConstraint, int heightConstraint)


{
var nativeView = renderer.Control;

if ((widthConstraint == 0 && heightConstraint == 0) || nativeView == null) {


return null;
}

int width = Android.Views.View.MeasureSpec.GetSize (widthConstraint);


int widthSpec = Android.Views.View.MeasureSpec.MakeMeasureSpec (
width * 2, Android.Views.View.MeasureSpec.GetMode (widthConstraint));
nativeView.Measure (widthSpec, heightConstraint);
return new SizeRequest (new Size (nativeView.MeasuredWidth, nativeView.MeasuredHeight));
}

Este método utiliza el ancho proporcionado por el CustomControl.OnMeasure método, pero la multiplica por dos.
Cuando el CustomControl instancia se agrega a la StackLayout , el FixSize método se puede especificar como el
GetDesiredSizeDelegate para corregir la medida incorrecta proporcionada por el CustomControl clase:

stackLayout.Children.Add (customControl, FixSize);

Esto da como resultado la vista personalizada se muestra correctamente, ocupan el ancho del dispositivo, como se
muestra en la captura de pantalla siguiente:

Plataforma universal de Windows


El siguiente ejemplo de código muestra la CustomControl (clase), que hereda de Panel :
public class CustomControl : Panel
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
"Text", typeof(string), typeof(CustomControl), new PropertyMetadata(default(string),
OnTextPropertyChanged));

public string Text


{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value.ToUpper()); }
}

readonly TextBlock textBlock;

public CustomControl()
{
textBlock = new TextBlock
{
MinHeight = 0,
MaxHeight = double.PositiveInfinity,
MinWidth = 0,
MaxWidth = double.PositiveInfinity,
FontSize = 14,
TextWrapping = TextWrapping.Wrap,
VerticalAlignment = VerticalAlignment.Center
};

Children.Add(textBlock);
}

static void OnTextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs


args)
{
((CustomControl)dependencyObject).textBlock.Text = (string)args.NewValue;
}

protected override Size ArrangeOverride(Size finalSize)


{
// This is deliberately wrong to demonstrate providing an override to fix it with.
textBlock.Arrange(new Rect(0, 0, finalSize.Width/2, finalSize.Height));
return finalSize;
}

protected override Size MeasureOverride(Size availableSize)


{
textBlock.Measure(availableSize);
return new Size(textBlock.DesiredSize.Width, textBlock.DesiredSize.Height);
}
}

Una instancia de esta vista se agrega a un StackLayout , tal y como se muestra en el ejemplo de código siguiente:

var brokenControl = new CustomControl {


Text = "This control has incorrect sizing - it doesn't occupy the available width of the device."
};
stackLayout.Children.Add(brokenControl);

Sin embargo, dado que el CustomControl.ArrangeOverride invalidación siempre devuelve la mitad del ancho
solicitado, se recortará la vista a la mitad el ancho disponible del dispositivo, como se muestra en la captura de
pantalla siguiente:
Una solución a este problema consiste en proporcionar un ArrangeOverrideDelegate implementación, al agregar la
vista para la StackLayout , tal y como se muestra en el ejemplo de código siguiente:

stackLayout.Children.Add(fixedControl, arrangeOverrideDelegate: (renderer, finalSize) =>


{
if (finalSize.Width <= 0 || double.IsInfinity(finalSize.Width))
{
return null;
}
var frameworkElement = renderer.Control;
frameworkElement.Arrange(new Rect(0, 0, finalSize.Width * 2, finalSize.Height));
return finalSize;
});

Este método utiliza el ancho proporcionado por el CustomControl.ArrangeOverride método, pero la multiplica por
dos. Esto da como resultado la vista personalizada se muestra correctamente, ocupan el ancho del dispositivo,
como se muestra en la captura de pantalla siguiente:

Resumen
En este artículo se explica cómo agregar vistas nativas a un diseño de Xamarin.Forms creado con C# y cómo
reemplazar el diseño de vistas personalizadas para corregir su uso de la API de medición.

Vínculos relacionados
NativeEmbedding (ejemplo)
Formularios nativos
Otras plataformas de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Xamarin.Forms es compatible con plataformas adicionales más allá de iOS, Android y Windows.

GTK
Xamarin.Forms tiene ahora soporte técnico de vista previa para aplicaciones de GTK #.

Mac
Xamarin.Forms tiene ahora la compatibilidad de versión preliminar para las aplicaciones de Mac OS.

Tizen
Tizen .NET permite crear aplicaciones .NET con Xamarin.Forms y Tizen .NET Framework.

WPF
Xamarin.Forms tiene ahora soporte técnico de vista previa para aplicaciones de Windows Presentation Foundation
(WPF ).
Configuración de la plataforma GTK #
11/07/2019 • 10 minutes to read • Edit Online

Xamarin.Forms tiene ahora soporte técnico de vista previa para aplicaciones de GTK #. GTK # es un Kit de
herramientas de interfaz gráfica de usuario que vincula el Kit de herramientas GTK + y de diversas bibliotecas
GNOME, lo que permite el desarrollo de GNOME totalmente nativa con aplicaciones de gráficos con Mono y.
NET. En este artículo se muestra cómo agregar un proyecto de GTK # a una solución de Xamarin.Forms.
Antes de empezar, cree una nueva solución de Xamarin.Forms o usar una solución de Xamarin.Forms existente,
por ejemplo, GameOfLife.

NOTE
Aunque en este artículo se centra en Agregar una aplicación de GTK # a una solución de Xamarin.Forms en VS2017 y Visual
Studio para Mac, también se puede realizar en MonoDevelop para Linux.

Agregar una aplicación GTK #


GTK # para macOS y Linux se instalan como parte de Mono. GTK # para .NET puede instalarse en Windows con el
GTK # instalador.
Visual Studio
Visual Studio para Mac
Siga estas instrucciones para agregar una aplicación GTK # que se ejecutará en el escritorio de Windows:
1. En Visual Studio 2019, haga doble clic en el nombre de la solución en el Explorador de soluciones y elija
Agregar > Nuevo proyecto... .
2. En el nuevo proyecto ventana a la izquierda, seleccione Visual C# y escritorio clásico de Windows. En
la lista de tipos de proyecto, elija biblioteca de clases (.NET Framework) y asegúrese de que el
Framework desplegable se establece en un mínimo de .NET Framework 4.7.
3. Escriba un nombre para el proyecto con un GTK extensión, por ejemplo GameOfLife.GTK. Haga clic en el
examinar botón, seleccione la carpeta que contiene la plataforma de otra proyectos y presione seleccionar
la carpeta. Esto colocará el proyecto de GTK + en el mismo directorio que los otros proyectos de la
solución.
Presione el Aceptar botón para crear el proyecto.
4. En el el Explorador de soluciones, a la derecha, haga clic en el nuevo proyecto GTK y seleccione
administrar paquetes de NuGet. Seleccione el examinar pestaña y busque Xamarin.Forms 3.0 o
posterior.

Seleccione el paquete y haga clic en el instalar botón.


5. Ahora busque el Xamarin.Forms.Platform.GTK paquete 3.0 o superior.

Seleccione el paquete y haga clic en el instalar botón.


6. En el el Explorador de soluciones, haga clic en el nombre de la solución y seleccione administrar
paquetes de NuGet para la solución. Seleccione el actualización ficha y el Xamarin.Forms paquete.
Seleccione todos los proyectos y actualizarlas con la misma versión de Xamarin.Forms que usado en el
proyecto de GTK +.
7. En el el Explorador de soluciones, haga doble clic en referencias en el proyecto de GTK +. En el
Administrador de referencias cuadro de diálogo, seleccione proyectos a la izquierda y activar la casilla
adyacente al proyecto de .NET estándar o compartido:
8. En el Administrador de referencias cuadro de diálogo, presione el examinar botón y vaya a la
C:\Program Files (x86)\GtkSharp\2.12\lib carpeta y seleccione el ATK sharp.dll, gdk sharp.dll, glade
sharp.dll, glib sharp.dll, gtk-dotnet.dll, gtk sharp.dll archivos.

Presione el Aceptar para agregar las referencias.


9. En el proyecto de GTK +, cambie el nombre Class1.cs a Program.cs.
10. En el proyecto de GTK +, edite el Program.cs archivo para que parezca al siguiente código:
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.GTK;

namespace GameOfLife.GTK
{
class MainClass
{
[STAThread]
public static void Main(string[] args)
{
Gtk.Application.Init();
Forms.Init();

var app = new App();


var window = new FormsWindow();
window.LoadApplication(app);
window.SetApplicationTitle("Game of Life");
window.Show();

Gtk.Application.Run();
}
}
}

Este código inicializa GTK # y Xamarin.Forms, crea una ventana de aplicación y ejecuta la aplicación.
11. En el el Explorador de soluciones, a la derecha, haga clic en el proyecto GTK y seleccione propiedades.
12. En el propiedades ventana, seleccione el aplicación pestaña y cambie el tipo de salida lista desplegable
para aplicación Windows.

13. En el el Explorador de soluciones, haga clic en el proyecto GTK y seleccione establecer como proyecto
de inicio. Presione F5 para ejecutar el programa con el depurador de Visual Studio en el escritorio de
Windows:
Pasos siguientes
Funcionalidad específica de plataforma
Puede determinar qué plataforma se está ejecutando en la aplicación de Xamarin.Forms en XAML o código. Esto
le permite cambiar las características del programa cuando se ejecuta en GTK #. En el código, compare el valor de
Device.RuntimePlatform con el Device.GTK constante (que es igual a la cadena "GTK"). Si hay una coincidencia, la
aplicación se ejecuta en GTK #.
En XAML, puede usar el OnPlatform para seleccionar un valor de propiedad específico de la plataforma:

<Button.TextColor>
<OnPlatform x:TypeArguments="Color">
<On Platform="iOS" Value="White" />
<On Platform="macOS" Value="White" />
<On Platform="Android" Value="Black" />
<On Platform="GTK" Value="Blue" />
</OnPlatform>
</Button.TextColor>

Icono de aplicación
Puede establecer el icono de la aplicación en el inicio:

window.SetApplicationIcon("icon.png");

Temas
Hay una amplia variedad de temas disponibles para GTK #, y se pueden usar desde una aplicación de
Xamarin.Forms:
GtkThemes.Init ();
GtkThemes.LoadCustomTheme ("Themes/gtkrc");

Formularios nativos
Formularios nativos permite Xamarin.Forms ContentPage -derivados de las páginas que será consumido por los
proyectos nativos, incluidos los proyectos de GTK #. Esto puede realizarse mediante la creación de una instancia de
la ContentPage -derivados de página y convertirlo en el nativo GTK # tipo usando el CreateContainer método de
extensión:

var settingsView = new SettingsView().CreateContainer();


vbox.PackEnd(settingsView, true, true, 0);

Para obtener más información acerca de los formularios nativos, vea formularios nativos.

Problemas
Se trata de una vista previa, por lo que debe esperar que no todo está lista para producción. Para el estado actual
de la implementación, consulte estado y para los problemas conocidos actuales, consulte problemas conocidos y
pendientes.
Configuración de la plataforma Mac
11/07/2019 • 5 minutes to read • Edit Online

Antes de empezar, cree (o usar una existente) proyecto de Xamarin.Forms. Solo puede agregar aplicaciones de
Mac con Visual Studio para Mac.

Agregar un proyecto de macOS a Xamarin.Forms vídeo

Adición de una aplicación de Mac


Siga estas instrucciones para agregar una aplicación de Mac que se ejecutará en macOS Sierra y El capitán de
macOS:
1. En Visual Studio para Mac, haga doble clic en la solución existente de Xamarin.Forms y elija Agregar >
Agregar nuevo proyecto...
2. En el nuevo proyecto ventana Elegir Mac > aplicación > aplicación Cocoa y presione siguiente.
3. Tipo de un nombre de la aplicación (y, opcionalmente, elija un nombre diferente para el elemento del
Dock), a continuación, presione siguiente.
4. Revise la configuración y presione crear. En estos pasos se muestran a continuación:

5. En el proyecto de Mac, haga doble clic en paquetes > Agregar paquetes... para agregar la Xamarin.Forms
NuGet. También debe actualizar los otros proyectos para usar la misma versión del paquete Xamarin.Forms
NuGet.
6. En el proyecto de Mac, haga doble clic en referencias y agregue una referencia al proyecto de
Xamarin.Forms (proyecto de biblioteca de proyecto compartido o .NET Standard).

7. Actualización Main.cs para inicializar el AppDelegate :

static class MainClass


{
static void Main(string[] args)
{
NSApplication.Init();
NSApplication.SharedApplication.Delegate = new AppDelegate(); // add this line
NSApplication.Main(args);
}
}

8. Actualización AppDelegate para inicializar Xamarin.Forms, crear una ventana y cargar la aplicación de
Xamarin.Forms (sin olvidarse de definir un adecuado Title ). Si tiene otras dependencias que deben
inicializarse, hacerlo aquí también.

using Xamarin.Forms;
using Xamarin.Forms.Platform.MacOS;
// also add a using for the Xamarin.Forms project, if the namespace is different to this file
...
[Register("AppDelegate")]
public class AppDelegate : FormsApplicationDelegate
{
NSWindow window;
public AppDelegate()
{
var style = NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Titled;

var rect = new CoreGraphics.CGRect(200, 1000, 1024, 768);


window = new NSWindow(rect, style, NSBackingStore.Buffered, false);
window.Title = "Xamarin.Forms on Mac!"; // choose your own Title here
window.TitleVisibility = NSWindowTitleVisibility.Hidden;
}

public override NSWindow MainWindow


{
get { return window; }
}

public override void DidFinishLaunching(NSNotification notification)


{
Forms.Init();
LoadApplication(new App());
base.DidFinishLaunching(notification);
}
}
9. Haga doble clic en Main.storyboard para editar en Xcode. Seleccione el ventana y desactive el
controlador inicial es casilla (Esto es porque el código anterior crea una ventana):

Puede editar el sistema de menú en el guión gráfico para quitar elementos no deseados.
10. Por último, agregue cualquier recurso local (p ej. archivos de imagen) de los proyectos existentes de
plataforma que son necesarios.
11. El proyecto de Mac ahora debe ejecutar el código de Xamarin.Forms en macOS.

Pasos siguientes
Aplicación de estilos
Con los cambios recientes realizados en OnPlatform ahora puede definir cualquier número de plataformas. Esto
incluye macOS.

<Button.TextColor>
<OnPlatform x:TypeArguments="Color">
<On Platform="iOS" Value="White"/>
<On Platform="macOS" Value="White"/>
<On Platform="Android" Value="Black"/>
</OnPlatform>
</Button.TextColor>

Tenga en cuenta también es posible que duplica en plataformas como esta: <On Platform="iOS, macOS" ...> .
Posición y tamaño de ventana
Puede ajustar el tamaño inicial y la ubicación de la ventana en la AppDelegate :

var rect = new CoreGraphics.CGRect(200, 1000, 1024, 768); // x, y, width, height

Problemas conocidos
Se trata de una vista previa, por lo que debe esperar que no todo está lista para producción. A continuación se
muestran algunas cosas que puede encontrarse con que agregue macOS a los proyectos:
No todos los paquetes de NuGet están listos para macOS
Es posible que algunas de las bibliotecas que usa aún no admite macOS. En este caso, deberá enviar una solicitud
al mantenedor del proyecto para agregarlo. Hasta que tengan soporte técnico, deberá buscar otras alternativas.
Características de Xamarin.Forms que faltan
No todas las características de Xamarin.Forms son completadas en esta versión preliminar. Para obtener más
información, consulte compatibilidad con la plataforma macOS estado en el Xamarin.Forms repositorio de
GitHub.

Vínculos relacionados
Xamarin.Mac
Tizen .NET
11/07/2019 • 2 minutes to read • Edit Online

Tizen .NET le permite desarrollar aplicaciones de Tizen para ejecutarse en dispositivos de Samsung, incluidos los
televisores, ponibles, dispositivos móviles y otros dispositivos de IoT.
Tizen .NET le permite crear aplicaciones .NET con Xamarin.Forms y Tizen .NET framework. Xamarin.Forms le
permite crear fácilmente interfaces de usuario, mientras que la API TizenFX proporciona interfaces para el
hardware que se encuentra en TV moderna, móvil, ponible y dispositivos IoT. Para obtener más información
acerca de .NET Tizen, consulte Introducción a la aplicación de .NET Tizen.

Primeros pasos
Antes de empezar a desarrollar aplicaciones .NET Tizen, primero debe configurar el entorno de desarrollo. Para
obtener más información, consulte instalar Visual Studio Tools para Tizen.
Para obtener información sobre cómo agregar proyecto Tizen .NET a una solución existente de Xamarin.Forms,
consulte crear su primera aplicación .NET de Tizen.

Documentación
Documentación de Xamarin.Forms – cómo compilar aplicaciones multiplataforma con C# y Xamarin.Forms.
Developer.tizen.org – documentación y los vídeos que le ayudarán a crear e implementar aplicaciones Tizen.

Muestras
Samsung mantiene una bifurcación de la ejemplos de Xamarin.Forms con proyectos Tizen agregados, y hay un
repositorio independiente Tizen-Csharp-Samples que contiene proyectos adicionales, incluido el ponible y
Demostraciones específicas de TV.
Configuración de la plataforma WPF
11/07/2019 • 6 minutes to read • Edit Online

Xamarin.Forms tiene ahora la compatibilidad de versión preliminar de Windows Presentation Foundation (WPF ).
En este artículo se muestra cómo agregar un proyecto de WPF a una solución de Xamarin.Forms.
Antes de empezar, cree una nueva solución de Xamarin.Forms en Visual Studio de 2019 o usar una solución de
Xamarin.Forms existente, por ejemplo, BoxViewClock. Las aplicaciones de WPF solo se pueden agregar a una
solución de Xamarin.Forms en Windows.

Agregar un proyecto de WPF a una aplicación de Xamarin.Forms con


Xamarin.University
Vídeo de compatibilidad de WPF con Xamarin.Forms 3.0

Agregar una aplicación WPF


Siga estas instrucciones para agregar una aplicación WPF que se ejecutará en los escritorios de Windows 7, 8 y
10:
1. En Visual Studio 2019, haga doble clic en el nombre de la solución en el el Explorador de soluciones y
elija Agregar > Nuevo proyecto... .
2. En el nuevo proyecto ventana a la izquierda, seleccione Visual C# y escritorio clásico de Windows. En
la lista de tipos de proyecto, elija aplicación de WPF (.NET Framework).
3. Escriba un nombre para el proyecto con un WPF extensión, por ejemplo, BoxViewClock.WPF. Haga clic
en el examinar botón, seleccione el BoxViewClock carpeta y presione seleccionar la carpeta. Esto
colocará el proyecto de WPF en el mismo directorio que los otros proyectos de la solución.
Haga clic en Aceptar para crear el proyecto.
4. En el el Explorador de soluciones, derecha, haga clic en el nuevo BoxViewClock.WPF del proyecto y
seleccione administrar paquetes de NuGet. Seleccione el examinar pestaña, haga clic en el incluir
versión preliminar casilla de verificación y busque Xamarin.Forms.

Seleccione paquete y haga clic en el instalar botón.


5. Ahora busque Xamarin.Forms.Platform.WPF empaquetar e instálela que también. Asegúrese de que el
paquete procede de Microsoft.
6. Haga clic en el nombre de la solución el el Explorador de soluciones y seleccione administrar paquetes
de NuGet para la solución. Seleccione el actualización ficha y el Xamarin.Forms paquete. Seleccione
todos los proyectos y actualizarlas con la misma versión de Xamarin.Forms:

7. En el proyecto WPF, haga doble clic en referencias. En el Administrador de referencias cuadro de


diálogo, seleccione proyectos a la izquierda y activar la casilla junto a la BoxViewClock proyecto:

8. Editar el MainWindow.xaml archivo del proyecto WPF. En el Window etiqueta, agregue una declaración
de espacio de nombres XML para el Xamarin.Forms.Platform.WPF ensamblado y espacio de nombres:
xmlns:wpf="clr-namespace:Xamarin.Forms.Platform.WPF;assembly=Xamarin.Forms.Platform.WPF"

Ahora, cambie el Window etiquetar a wpf:FormsApplicationPage . Cambiar el Title establecer con el


nombre de la aplicación, por ejemplo, BoxViewClock. El archivo XAML completado debería tener este
aspecto:

<wpf:FormsApplicationPage x:Class="BoxViewClock.WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wpf="clr-namespace:Xamarin.Forms.Platform.WPF;assembly=Xamarin.Forms.Platform.WPF"
xmlns:local="clr-namespace:BoxViewClock.WPF"
mc:Ignorable="d"
Title="BoxViewClock" Height="450" Width="800">
<Grid>

</Grid>
</wpf:FormsApplicationPage>

9. Editar el MainWindow.xaml.cs archivo del proyecto WPF. Agregue dos nuevos using directivas:

using Xamarin.Forms;
using Xamarin.Forms.Platform.WPF;

Cambie la clase base de MainWindow desde Window a FormsApplicationPage . Siguiendo el


InitializeComponent llamar, agregue las dos instrucciones siguientes:

Forms.Init();
LoadApplication(new BoxViewClock.App());

Excepto en los comentarios y sin usar using directivas, completo MainWindows.xaml.cs archivo debería
tener este aspecto:

using Xamarin.Forms;
using Xamarin.Forms.Platform.WPF;

namespace BoxViewClock.WPF
{
public partial class MainWindow : FormsApplicationPage
{
public MainWindow()
{
InitializeComponent();

Forms.Init();
LoadApplication(new BoxViewClock.App());
}
}
}

10. Haga clic en el proyecto WPF en el el Explorador de soluciones y seleccione establecer como proyecto
de inicio. Presione F5 para ejecutar el programa con el depurador de Visual Studio en el escritorio de
Windows:
Pasos siguientes
Funcionalidad específica de plataforma
Puede determinar qué plataforma se está ejecutando en la aplicación de Xamarin.Forms mediante código o XAML.
Esto le permite cambiar las características del programa cuando se ejecuta en WPF. En el código, compare el valor
de Device.RuntimePlatform con el Device.WPF constante (que es igual a la cadena "WPF"). Si hay una coincidencia,
la aplicación se ejecuta en WPF.
En XAML, puede usar el OnPlatform para seleccionar un valor de propiedad específico de la plataforma:

<Button.TextColor>
<OnPlatform x:TypeArguments="Color">
<On Platform="iOS" Value="White" />
<On Platform="macOS" Value="White" />
<On Platform="Android" Value="Black" />
<On Platform="WPF" Value="Blue" />
</OnPlatform>
</Button.TextColor>

Tamaño de ventana
Puede ajustar el tamaño inicial de la ventana de WPF MainWindow.xaml archivo:

Title="BoxViewClock" Height="450" Width="800"

Problemas
Se trata de una vista previa, por lo que debe esperar que no todo está lista para producción. No todos los paquetes
de NuGet para Xamarin.Forms están listos para WPF, y algunas características no ser totalmente operativo.
Funcionalidades específicas de plataforma
11/07/2019 • 11 minutes to read • Edit Online

descargar el ejemplo
Funcionalidades específicas de plataforma permiten utilizar la funcionalidad que solo está disponible en una
plataforma concreta, sin necesidad de implementar los representadores personalizados o los efectos.
El proceso para consumir una plataforma específica a través de XAML, o a través de la API fluida de código es
como sigue:
1. Agregar un xmlns declaración o using la directiva para el Xamarin.Forms.PlatformConfiguration espacio de
nombres.
2. Agregar un xmlns declaración o using la directiva para el espacio de nombres que contiene la funcionalidad
específica de la plataforma:
a. En iOS, se trata la Xamarin.Forms.PlatformConfiguration.iOSSpecific espacio de nombres.
b. En Android, se trata la Xamarin.Forms.PlatformConfiguration.AndroidSpecific espacio de nombres. Para
Android AppCompat, este es el Xamarin.Forms.PlatformConfiguration.AndroidSpecific.AppCompat espacio
de nombres.
c. En la plataforma Universal de Windows, se trata la Xamarin.Forms.PlatformConfiguration.WindowsSpecific
espacio de nombres.
3. Aplicar el específico de la plataforma de XAML, o de código con el On<T> API fluida. El valor de T puede ser el
iOS , Android , o Windows tipos a partir de la Xamarin.Forms.PlatformConfiguration espacio de nombres.

NOTE
Tenga en cuenta que intenta consumir un específico de la plataforma en una plataforma que no está disponible no dará
como resultado un error. En su lugar, el código se ejecutará sin el específico de la plataforma que se va a aplicar.

Funcionalidades específicas de plataforma consumen a través de la On<T> devueltos de la API de código fluent
IPlatformElementConfiguration objetos. Esto permite que varias funcionalidades específicas de plataforma que se
invocará en el mismo objeto con el método en cascada.
Para obtener más información sobre las características de plataforma proporciona Xamarin.Forms, consulte
funcionalidades específicas de plataforma de iOS, funcionalidades específicas de plataforma Android, y Windows
funcionalidades específicas de plataforma.

Creación de funcionalidades específicas de plataforma


Los fabricantes pueden crear sus propias funcionalidades específicas de plataforma con efectos. Un efecto
proporciona la funcionalidad específica, que, a continuación, se expone a través de una plataforma específica. El
resultado es un efecto que puede consumir más fácilmente a través de XAML y una API fluida de código.
El proceso de creación de una plataforma específica es como sigue:
1. Implementar la funcionalidad específica como un efecto. Para obtener más información, consulte crea un efecto.
2. Cree una clase específica de la plataforma que se va a exponer el efecto. Para obtener más información,
consulte creación de una clase específica de la plataforma.
3. En la clase específica de la plataforma, implemente una propiedad adjunta para permitir el específico de la
plataforma para consumirse a través de XAML. Para obtener más información, consulte agregar una propiedad
adjunta.
4. En la clase específica de la plataforma, implemente los métodos de extensión para permitir el específico de la
plataforma para consumirse a través de una API fluida de código. Para obtener más información, consulte
agregar métodos de extensión.
5. Modifique la implementación de efecto para que el efecto se aplica solo si se ha invocado el específico de la
plataforma en la misma plataforma que el efecto. Para obtener más información, consulte crea el efecto de.
El resultado de exponer un efecto como una plataforma específica es que el efecto se puede consumir más
fácilmente a través de XAML y una API fluida de código.

NOTE
Se prevé que los proveedores usará esta técnica para crear sus propias funcionalidades específicas de plataforma, para facilitar
su consumo por usuarios. Mientras que los usuarios pueden elegir crear sus propias funcionalidades específicas de
plataforma, se debe tener en cuenta que requiere más código que la creación y consumo de un efecto.

El aplicación de ejemplo muestra un Shadow específicos de la plataforma que se agrega una sombra al texto
mostrado por un Label control:

El aplicación de ejemplo implementa el Shadow específico de la plataforma en cada plataforma, para facilitar la
comprensión. Sin embargo, aparte de cada implementación de efecto específico de la plataforma, la
implementación de la clase de la sombra es idéntica en gran medida para cada plataforma. Por lo tanto, esta guía
se centra en la implementación de la clase de instantáneas y el efecto asociado en una sola plataforma.
Para obtener más información acerca de los efectos, vea personalización de controles con efectos.
Creación de una clase específica de la plataforma
Se crea una plataforma específica como un public static clase:

namespace MyCompany.Forms.PlatformConfiguration.iOS
{
public static Shadow
{
...
}
}

Las secciones siguientes describen la implementación de la Shadow efecto específico de la plataforma y asociado.
Agregar una propiedad adjunta
Una propiedad adjunta debe agregarse a la Shadow específicos de la plataforma para permitir el consumo a través
de XAML:

namespace MyCompany.Forms.PlatformConfiguration.iOS
{
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using FormsElement = Xamarin.Forms.Label;
public static class Shadow
{
const string EffectName = "MyCompany.LabelShadowEffect";

public static readonly BindableProperty IsShadowedProperty =


BindableProperty.CreateAttached("IsShadowed",
typeof(bool),
typeof(Shadow),
false,
propertyChanged: OnIsShadowedPropertyChanged);

public static bool GetIsShadowed(BindableObject element)


{
return (bool)element.GetValue(IsShadowedProperty);
}

public static void SetIsShadowed(BindableObject element, bool value)


{
element.SetValue(IsShadowedProperty, value);
}

...

static void OnIsShadowedPropertyChanged(BindableObject element, object oldValue, object newValue)


{
if ((bool)newValue)
{
AttachEffect(element as FormsElement);
}
else
{
DetachEffect(element as FormsElement);
}
}

static void AttachEffect(FormsElement element)


{
IElementController controller = element;
if (controller == null || controller.EffectIsAttached(EffectName))
{
return;
}
element.Effects.Add(Effect.Resolve(EffectName));
}

static void DetachEffect(FormsElement element)


{
IElementController controller = element;
if (controller == null || !controller.EffectIsAttached(EffectName))
{
return;
}

var toRemove = element.Effects.FirstOrDefault(e => e.ResolveId ==


Effect.Resolve(EffectName).ResolveId);
if (toRemove != null)
{
element.Effects.Remove(toRemove);
}
}
}
}

El IsShadowed propiedad adjunta se utiliza para agregar el MyCompany.LabelShadowEffect efecto y lo eliminará del
control que el Shadow clase se adjunta a. Esto adjunta la propiedad registra el OnIsShadowedPropertyChanged
método que se ejecutará cuando cambia el valor de la propiedad. A su vez, llama este método la AttachEffect o
DetachEffect método para agregar o quitar el efecto en función del valor de la IsShadowed propiedad adjunta. El
efecto se agrega o quita el control modificando el control Effects colección.

NOTE
Tenga en cuenta que el efecto se resuelve mediante la especificación de un valor que es una concatenación del nombre del
grupo de resolución y el identificador único que se especifica en la implementación del efecto. Para obtener más información,
consulte crea un efecto.

Para obtener más información sobre las propiedades adjuntas, consulte Attached Properties.
Adición de métodos de extensión
Métodos de extensión deben agregarse a la Shadow específicos de la plataforma para permitir el consumo a través
de una API fluida de código:

namespace MyCompany.Forms.PlatformConfiguration.iOS
{
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using FormsElement = Xamarin.Forms.Label;

public static class Shadow


{
...
public static bool IsShadowed(this IPlatformElementConfiguration<iOS, FormsElement> config)
{
return GetIsShadowed(config.Element);
}

public static IPlatformElementConfiguration<iOS, FormsElement> SetIsShadowed(this


IPlatformElementConfiguration<iOS, FormsElement> config, bool value)
{
SetIsShadowed(config.Element, value);
return config;
}
...
}
}

El IsShadowed y SetIsShadowed invocar la operación get de métodos de extensión y establecer los descriptores de
acceso para el IsShadowed propiedad adjunta, respectivamente. Cada método de extensión funciona en el
IPlatformElementConfiguration<iOS, FormsElement> type que especifica que se puede invocar el específico de la
plataforma en Label instancias de iOS.
Creación del efecto
El Shadow específicos de la plataforma agrega el MyCompany.LabelShadowEffect a un Label y lo quita. El siguiente
ejemplo de código muestra la LabelShadowEffect implementación para el proyecto de iOS:
[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(LabelShadowEffect), "LabelShadowEffect")]
namespace ShadowPlatformSpecific.iOS
{
public class LabelShadowEffect : PlatformEffect
{
protected override void OnAttached()
{
UpdateShadow();
}

protected override void OnDetached()


{
}

protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)


{
base.OnElementPropertyChanged(args);

if (args.PropertyName == Shadow.IsShadowedProperty.PropertyName)
{
UpdateShadow();
}
}

void UpdateShadow()
{
try
{
if (((Label)Element).OnThisPlatform().IsShadowed())
{
Control.Layer.CornerRadius = 5;
Control.Layer.ShadowColor = UIColor.Black.CGColor;
Control.Layer.ShadowOffset = new CGSize(5, 5);
Control.Layer.ShadowOpacity = 1.0f;
}
else if (!((Label)Element).OnThisPlatform().IsShadowed())
{
Control.Layer.ShadowOpacity = 0;
}
}
catch (Exception ex)
{
Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
}
}
}
}

El UpdateShadow método establece Control.Layer propiedades para crear la sombra, siempre que el IsShadowed
se establece la propiedad adjunta en true y siempre que el Shadow específico de la plataforma que se haya
invocado en la misma plataforma que el Efecto se implementa. Esta comprobación se realiza con el
OnThisPlatform método.

Si el Shadow.IsShadowed conectados cambia de valor de propiedad en tiempo de ejecución, las necesidades de


efecto para responder mediante la eliminación de la sombra. Por lo tanto, una versión reemplazada de la
OnElementPropertyChanged método se usa para responder al cambio de propiedad enlazable mediante una llamada
a la UpdateShadow método.
Para obtener más información acerca de cómo crear un efecto, consulte crea un efecto y pasar parámetros de
efecto como adjunta propiedades.
Consumo de la plataforma específica
El Shadow específicos de la plataforma se consumen en XAML estableciendo el Shadow.IsShadowed propiedad
adjunta un boolean valor:

<ContentPage xmlns:ios="clr-namespace:MyCompany.Forms.PlatformConfiguration.iOS" ...>


...
<Label Text="Label Shadow Effect" ios:Shadow.IsShadowed="true" ... />
...
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using MyCompany.Forms.PlatformConfiguration.iOS;

...

shadowLabel.On<iOS>().SetIsShadowed(true);

Vínculos relacionados
PlatformSpecifics (ejemplo)
ShadowPlatformSpecific (ejemplo)
funcionalidades específicas de plataforma de iOS
Funcionalidades específicas de plataforma Android
Funcionalidades específicas de plataforma de Windows
Personalización de controles con efectos
Propiedades asociadas
PlatformConfiguration API
Características de la plataforma Windows
11/07/2019 • 4 minutes to read • Edit Online

Desarrollar aplicaciones de Xamarin.Forms para plataformas de Windows requiere Visual Studio. El página
requisitos contiene más información sobre los requisitos previos.

Funcionalidades específicas de plataforma


Funcionalidades específicas de plataforma permiten utilizar la funcionalidad que solo está disponible en una
plataforma concreta, sin necesidad de implementar los representadores personalizados o los efectos.
La siguiente funcionalidad específica de la plataforma se proporciona para las vistas de Xamarin.Forms, páginas y
diseños en la plataforma Universal de Windows (UWP ):
Establecer una tecla de acceso para un VisualElement . Para obtener más información, consulte VisualElement
claves de acceso en Windows.
Deshabilitar el modo de color heredado en compatible VisualElement . Para obtener más información,
consulte VisualElement modo de Color heredado en Windows.
Se proporciona la siguiente funcionalidad específica de la plataforma para las vistas de Xamarin.Forms en UWP:
Detectar el orden de lectura del texto contenido en Entry , Editor , y Label instancias. Para obtener más
información, consulte InputView el orden de lectura en Windows.
Habilitar la compatibilidad de derivación de gestos en un ListView . Para obtener más información, consulte
ListView SelectionMode en Windows.
Habilitar un SearchBar para interactuar con el motor de corrección ortográfica. Para obtener más información,
consulte SearchBar Spell Check en Windows.
Habilitar un WebView para mostrar las alertas de JavaScript en un cuadro de diálogo de mensaje UWP. Para
obtener más información, consulte alertas de JavaScript de WebView en Windows.
Se proporciona la siguiente funcionalidad específica de la plataforma para las páginas de Xamarin.Forms para
UWP:
Contraer el MasterDetailPage barra de navegación. Para obtener más información, consulte MasterDetailPage
barra de navegación en Windows.
Opciones de ubicación de la barra de herramientas de configuración. Para obtener más información, consulte
posición de la barra de herramientas de página en Windows.
Habilitación de los iconos de página que se mostrará en un TabbedPage barra de herramientas. Para obtener
más información, consulte TabbedPage iconos en Windows.
Compatibilidad con la plataforma
Las plantillas de Xamarin.Forms disponibles en Visual Studio contienen un proyecto de plataforma Universal de
Windows (UWP ).

NOTE
Compatibilidad de Xamarin.Forms 1.x y 2.x Windows Phone 8 Silverlight, Windows Phone 8.1, y Windows 8.1 desarrollo de
aplicaciones. Sin embargo, estos tipos de proyecto han quedado desusados.

Introducción
Vaya a archivo > Nuevo > proyecto en Visual Studio y elija uno de los multiplataforma > aplicación vacía
(Xamarin.Forms) plantillas para comenzar.
Las soluciones de Xamarin.Forms anteriores o los creados en macOS, no tendrá todos los proyectos de Windows
enumerados anteriormente (pero deben agregarse manualmente). Si desea tener como destino la plataforma de
Windows no está en la solución, viste el instrucciones de instalación para agregar el deseado de Windows/s de
tipo de proyecto.

Muestras
Todos los ejemplos de libro de Charles Creating Mobile Apps with Xamarin.Forms se incluyen los proyectos de
plataforma Universal de Windows (para Windows 10).
El "Scott Hanselman" aplicación de demostración está disponible por separado y también incluye proyectos de
Apple Watch y Android Wear (con Xamarin.iOS y Xamarin.Android, respectivamente, Xamarin.Forms no se
ejecuta en estas plataformas).

Vínculos relacionados
Proyectos de instalación de Windows
Orden de lectura de InputView en Windows
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Esta plataforma Universal de Windows específicos de la plataforma permite que el orden de lectura (de izquierda a
derecha o de derecha a izquierda) de texto bidireccional en Entry , Editor y Label instancias que se ha
detectado dinámicamente. Se consume en XAML estableciendo el InputView.DetectReadingOrderFromContent (para
Entry y Editor instancias) o Label.DetectReadingOrderFromContent propiedad adjunta un boolean valor:

<ContentPage ...
xmlns:windows="clr-
namespace:Xamarin.Forms.PlatformConfiguration.WindowsSpecific;assembly=Xamarin.Forms.Core">
<StackLayout>
<Editor ... windows:InputView.DetectReadingOrderFromContent="true" />
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
...

editor.On<Windows>().SetDetectReadingOrderFromContent(true);

El Editor.On<Windows> método especifica que solo se ejecutan este específicos de la plataforma en la plataforma
Universal de Windows. El InputView.SetDetectReadingOrderFromContent método, en el
Xamarin.Forms.PlatformConfiguration.WindowsSpecific espacio de nombres, se usa para controlar si se detecta el
orden de lectura del contenido en el InputView . Además, el InputView.SetDetectReadingOrderFromContent método
puede utilizarse para alternar si se detecta el orden de lectura del contenido mediante una llamada a la
InputView.GetDetectReadingOrderFromContent método para devolver el valor actual:

editor.On<Windows>().SetDetectReadingOrderFromContent(!editor.On<Windows>
().GetDetectReadingOrderFromContent());

El resultado es que Entry , Editor ,y Label instancias pueden tener el orden de lectura de su contenido
dinámicamente detectado:
NOTE
A diferencia de la configuración de la FlowDirection propiedad, la lógica para las vistas que detectar el orden de lectura de
su contenido de texto no afectará a la alineación del texto dentro de la vista. En su lugar, ajusta el orden en el que se colocan
los bloques de texto bidireccional.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
WindowsSpecific API
ListView SelectionMode en Windows
11/07/2019 • 3 minutes to read • Edit Online

descargar el ejemplo
En la plataforma Universal de Windows, de forma predeterminada el Xamarin.Forms ListView usa nativo
ItemClick eventos para responder a interacción, en lugar de nativo Tapped eventos. Esto proporciona la
funcionalidad de accesibilidad para que el Narrador de Windows y el teclado pueden interactuar con el ListView .
Sin embargo, también presenta los gestos de tap dentro de la ListView no funciona.
Este controles específicos de la plataforma de plataforma Universal de Windows si los elementos de un ListView
pulse gestos, puede responder y, por tanto, si nativo ListView se activa el ItemClick o Tapped eventos. Se
consume en XAML estableciendo el ListView.SelectionMode propiedad adjunta a un valor de la
ListViewSelectionMode enumeración:

<ContentPage ...
xmlns:windows="clr-
namespace:Xamarin.Forms.PlatformConfiguration.WindowsSpecific;assembly=Xamarin.Forms.Core">
<StackLayout>
<ListView ... windows:ListView.SelectionMode="Inaccessible">
...
</ListView>
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
...

listView.On<Windows>().SetSelectionMode(ListViewSelectionMode.Inaccessible);

El ListView.On<Windows> método especifica que solo se ejecutan este específicos de la plataforma en la plataforma
Universal de Windows. El ListView.SetSelectionMode método, en el
Xamarin.Forms.PlatformConfiguration.WindowsSpecific espacio de nombres, se utiliza para controlar si los
elementos de un ListView puede responder pulse gestos, con el ListViewSelectionMode enumeración que
proporciona dos valores posibles:
Accessible : indica que el ListView desencadenará nativo ItemClick eventos para controlar la interacción y,
por lo tanto, proporcionar una funcionalidad de accesibilidad. Por lo tanto, el Narrador de Windows y el teclado
pueden interactuar con el ListView . Sin embargo, los elementos de la ListView no puede responder para
puntear gestos. Este es el comportamiento predeterminado para ListView instancias en la plataforma
Universal de Windows.
Inaccessible : indica que el ListView desencadenará nativo Tapped eventos para controlar la interacción. Por
lo tanto, los elementos de la ListView pulse gestos puede responder. Sin embargo, no hay ninguna
funcionalidad de accesibilidad y, por tanto, el Narrador de Windows y el teclado no pueden interactuar con el
ListView .
NOTE
El Accessible y Inaccessible modos de selección son mutuamente excluyentes y tendrá que elegir entre una accesible
ListView o un ListView que puede responder a los gestos de pulse.

Además, el GetSelectionMode método puede utilizarse para devolver actual ListViewSelectionMode .


El resultado es que un determinado ListViewSelectionMode se aplica a la ListView , que controla si los elementos
de la ListView pulse gestos, puede responder y, por tanto, si nativo ListView se activa el ItemClick o Tapped
eventos.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
WindowsSpecific API
Barra de navegación MasterDetailPage Windows
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Esta plataforma Universal de Windows específicos de la plataforma se usan para contraer la barra de navegación
en un MasterDetailPage y se consume en XAML estableciendo el MasterDetailPage.CollapseStyle y
MasterDetailPage.CollapsedPaneWidth propiedades adjuntas:

<MasterDetailPage ...
xmlns:windows="clr-
namespace:Xamarin.Forms.PlatformConfiguration.WindowsSpecific;assembly=Xamarin.Forms.Core"
windows:MasterDetailPage.CollapseStyle="Partial"
windows:MasterDetailPage.CollapsedPaneWidth="48">
...
</MasterDetailPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
...

page.On<Windows>().SetCollapseStyle(CollapseStyle.Partial).CollapsedPaneWidth(148);

El MasterDetailPage.On<Windows> método especifica que solo se ejecutarán este específicos de la plataforma en


Windows. El Page.SetCollapseStyle método, en el Xamarin.Forms.PlatformConfiguration.WindowsSpecific espacio
de nombres, se usa para especificar el estilo de contraer, con el CollapseStyle enumeración que proporciona dos
valores: Full y Partial . El MasterDetailPage.CollapsedPaneWidth método se utiliza para especificar el ancho de
una barra de navegación parcialmente contraído.
El resultado es que un determinado CollapseStyle se aplica a la MasterDetailPage instancia, con el ancho también
se especifica:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
WindowsSpecific API
Colocación de la barra de herramientas de página en
Windows
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Esta plataforma Universal de Windows específicos de la plataforma se usan para cambiar la ubicación de una
barra de herramientas en un Page y se consume en XAML estableciendo el Page.ToolbarPlacement propiedad
adjunta un valor de la ToolbarPlacement enumeración:

<TabbedPage ...
xmlns:windows="clr-
namespace:Xamarin.Forms.PlatformConfiguration.WindowsSpecific;assembly=Xamarin.Forms.Core"
windows:Page.ToolbarPlacement="Bottom">
...
</TabbedPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
...

page.On<Windows>().SetToolbarPlacement(ToolbarPlacement.Bottom);

El Page.On<Windows>método especifica que solo se ejecutarán este específicos de la plataforma en Windows. El


Page.SetToolbarPlacement método, en el Xamarin.Forms.PlatformConfiguration.WindowsSpecific espacio de
nombres, se usa para establecer la posición de la barra de herramientas, con el ToolbarPlacement que proporciona
la enumeración estos tres valores: Default , Top , y Bottom .
El resultado es que la selección de ubicación de la barra de herramientas especificada se aplica a la Page instancia:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
WindowsSpecific API
Proyectos de instalación de Windows
11/07/2019 • 6 minutes to read • Edit Online

Adición de nuevos proyectos de Windows a una solución existente de Xamarin.Forms


Las soluciones de Xamarin.Forms anteriores (o aquellos creados en macOS ) no tendrán los proyectos de
aplicaciones de plataforma Universal de Windows (UWP ). Por lo tanto, deberá agregar manualmente un
proyecto UWP para compilar una aplicación de Windows 10 (UWP ).

Agregar un Universal Windows Platform app


Visual Studio de 2019 en Windows 10 se recomienda para crear aplicaciones para UWP. Para obtener más
información acerca de la plataforma Universal de Windows, consulte Introducción a la plataforma Universal de
Windows.
UWP está disponible en Xamarin.Forms 2.1 y versiones posteriores, y xamarin.Forms.Maps para que se admite
en Xamarin.Forms 2.2 y versiones posteriores.
Compruebe el solución de problemas sección para obtener sugerencias útiles.
Siga estas instrucciones para agregar una aplicación para UWP que se ejecutará en teléfonos, tabletas y equipos
de escritorio de Windows 10:
1 . Haga doble clic en la solución y seleccione Agregar > Nuevo proyecto... y agregue un aplicación vacía
(Windows Universal) proyecto:

2 . En el nuevo proyecto de plataforma Universal de Windows cuadro de diálogo, seleccione las versiones
mínima y de destino de Windows 10 que se ejecutará la aplicación:

3 . Haga doble clic en el proyecto de UWP y seleccione administrar paquetes NuGet... y agregue el
Xamarin.Forms paquete. Asegúrese de que los otros proyectos de la solución también se actualizan a la misma
versión del paquete de Xamarin.Forms.
4 . Asegúrese de que el nuevo proyecto UWP se compilará el compilar > Administrador de configuración
ventana (Esto probablemente no habrá sucedido de forma predeterminada). Marca el compilar y implementar
cuadros para el proyecto Universal:
5 . Haga doble clic en el proyecto y seleccione Agregar > referencia y crear una referencia al proyecto de
aplicación de Xamarin.Forms (.NET Standard o proyectos compartidos).

6 . En el proyecto UWP, edite App.xaml.cs para incluir el Init llamada al método dentro de la OnLaunched
método alrededor de la línea 52:

// under this line


rootFrame.NavigationFailed += OnNavigationFailed;
// add this line
Xamarin.Forms.Forms.Init (e); // requires the `e` parameter

7 . En el proyecto UWP, edite MainPage.xaml quitando el Grid dentro de la Page elemento.


8 . En MainPage.xaml, agregue un nuevo xmlns entrada para Xamarin.Forms.Platform.UWP :

xmlns:forms="using:Xamarin.Forms.Platform.UWP"

9 . En MainPage.xaml, cambiar la raíz <Page elemento <forms:WindowsPage :

<forms:WindowsPage
...
xmlns:forms="using:Xamarin.Forms.Platform.UWP"
...
</forms:WindowsPage>

10 . En el proyecto UWP, edite MainPage.xaml.cs para quitar el : Page especificador de herencia para el
nombre de clase (puesto que ahora se heredará desde WindowsPage debido a los cambios realizados en el paso
anterior):

public sealed partial class MainPage // REMOVE ": Page"

11 . En MainPage.xaml.cs, agregue el LoadApplication llamar el MainPage constructor para iniciar la aplicación


de Xamarin.Forms:

// below this existing line


this.InitializeComponent();
// add this line
LoadApplication(new YOUR_NAMESPACE.App());

12 . Agregue los recursos locales (p ej. archivos de imagen) de los proyectos existentes de plataforma que son
necesarios.

Solución de problemas
"Excepción de invocación de destino" cuando se usa "Compilación con la cadena de herramientas nativas de
.NET"
Si su aplicación para UWP se hace referencia a varios ensamblados (por ejemplo un control de terceros
bibliotecas o su propia aplicación se divide en varias bibliotecas), no es posible que pueda Xamarin.Forms cargar
los objetos de dichos ensamblados (como representadores personalizados).
Esto puede ocurrir cuando se usa el compilar con cadena de herramientas .NET Native que es una opción
para aplicaciones UWP en el Propiedades > compilar > General ventana para el proyecto.
Puede solucionar este problema mediante una sobrecarga específica de UWP de la Forms.Init llamar
App.xaml.cs tal como se muestra en el código siguiente (debe reemplazar ClassInOtherAssembly con una clase
real hace referencia su código):

// You'll need to add `using System.Reflection;`


List<Assembly> assembliesToInclude = new List<Assembly>();

// Now, add in all the assemblies your app uses


assembliesToInclude.Add(typeof (ClassInOtherAssembly).GetTypeInfo().Assembly);

// Also do this for all your other 3rd party libraries


Xamarin.Forms.Forms.Init(e, assembliesToInclude);
// replaces Xamarin.Forms.Forms.Init(e);

Agregue una entrada para cada ensamblado que ha agregado como una referencia en el Explorador de
soluciones, ya sea a través de una referencia directa o NuGet.
Compilación nativa de .NET y de servicios de dependencia
Versiones de lanzamiento con .NET Native puede producir un error de compilación para resolver los servicios de
dependencia que se definen fuera el ejecutable de aplicación principal (como en un proyecto independiente o
biblioteca).
Use el DependencyService.Register<T>() método para registrar manualmente clases del servicio de dependencia.
Según el ejemplo anterior, agregue el método register similar al siguiente:

Xamarin.Forms.Forms.Init(e, assembliesToInclude);
Xamarin.Forms.DependencyService.Register<ClassInOtherAssembly>(); // add this
SearchBar ortográfica en Windows
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Esta plataforma Universal de Windows específicos de la plataforma permite una SearchBar para interactuar con el
motor de corrección ortográfica. Se consume en XAML estableciendo el SearchBar.IsSpellCheckEnabled propiedad
adjunta un boolean valor:

<ContentPage ...
xmlns:windows="clr-
namespace:Xamarin.Forms.PlatformConfiguration.WindowsSpecific;assembly=Xamarin.Forms.Core">
<StackLayout>
<SearchBar ... windows:SearchBar.IsSpellCheckEnabled="true" />
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
...

searchBar.On<Windows>().SetIsSpellCheckEnabled(true);

El SearchBar.On<Windows> método especifica que solo se ejecutan este específicos de la plataforma en la


plataforma Universal de Windows. El SearchBar.SetIsSpellCheckEnabled método, en el
Xamarin.Forms.PlatformConfiguration.WindowsSpecific espacio de nombres, se activa el corrector ortográfico y
desactiva. Además, el SearchBar.SetIsSpellCheckEnabled método puede utilizarse para activar o desactivar el
corrector ortográfico mediante una llamada a la SearchBar.GetIsSpellCheckEnabled método devuelva si el corrector
ortográfico está habilitado:

searchBar.On<Windows>().SetIsSpellCheckEnabled(!searchBar.On<Windows>().GetIsSpellCheckEnabled());

El resultado es que el texto escrito en el SearchBar puede spell comprobarse, con ortografía incorrecta que se
indica al usuario:
NOTE
El SearchBar clase en el Xamarin.Forms.PlatformConfiguration.WindowsSpecific también dispone de espacio de
nombres EnableSpellCheck y DisableSpellCheck métodos que pueden usarse para habilitar y deshabilitar el corrector
ortográfico en el SearchBar , respectivamente.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
WindowsSpecific API
Iconos de TabbedPage en Windows
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Esta plataforma Universal de Windows específicos de la plataforma permite a los iconos de página que se
mostrará en un TabbedPage barra de herramientas y proporciona la capacidad de especificar opcionalmente el
tamaño del icono. Se consume en XAML estableciendo el TabbedPage.HeaderIconsEnabled propiedad adjunta true
y, opcionalmente, estableciendo el TabbedPage.HeaderIconsSize propiedad adjunta un Size valor:

<TabbedPage ...
xmlns:windows="clr-
namespace:Xamarin.Forms.PlatformConfiguration.WindowsSpecific;assembly=Xamarin.Forms.Core"
windows:TabbedPage.HeaderIconsEnabled="true">
<windows:TabbedPage.HeaderIconsSize>
<Size>
<x:Arguments>
<x:Double>24</x:Double>
<x:Double>24</x:Double>
</x:Arguments>
</Size>
</windows:TabbedPage.HeaderIconsSize>
<ContentPage Title="Todo" IconImageSource="todo.png">
...
</ContentPage>
<ContentPage Title="Reminders" IconImageSource="reminders.png">
...
</ContentPage>
<ContentPage Title="Contacts" IconImageSource="contacts.png">
...
</ContentPage>
</TabbedPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
...

public class WindowsTabbedPageIconsCS : Xamarin.Forms.TabbedPage


{
public WindowsTabbedPageIconsCS()
{
On<Windows>().SetHeaderIconsEnabled(true);
On<Windows>().SetHeaderIconsSize(new Size(24, 24));

Children.Add(new ContentPage { Title = "Todo", IconImageSource = "todo.png" });


Children.Add(new ContentPage { Title = "Reminders", IconImageSource = "reminders.png" });
Children.Add(new ContentPage { Title = "Contacts", IconImageSource = "contacts.png" });
}
}

El TabbedPage.On<Windows> método especifica que solo se ejecutan este específicos de la plataforma en la


plataforma Universal de Windows. El TabbedPage.SetHeaderIconsEnabled método, en el
Xamarin.Forms.PlatformConfiguration.WindowsSpecific espacio de nombres, se usa para activar o desactivar los
iconos de encabezado. El TabbedPage.SetHeaderIconsSize método, opcionalmente, especifica el tamaño del icono
de encabezado con un Size valor.
Además, el TabbedPage clase en el Xamarin.Forms.PlatformConfiguration.WindowsSpecific espacio de nombres
también tiene un EnableHeaderIcons método que permite a los iconos de encabezado, un DisableHeaderIcons
método que deshabilita los iconos de encabezado, y un IsHeaderIconsEnabled método que devuelve un boolean
valor que indica si se habilitan los iconos de encabezado.
El resultado es esa página se pueden mostrar los iconos en un TabbedPage barra de herramientas, con el tamaño
de icono que se va a establecer opcionalmente al tamaño que desee:

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
WindowsSpecific API
Claves de acceso de VisualElement en Windows
11/07/2019 • 6 minutes to read • Edit Online

descargar el ejemplo
Teclas de acceso son métodos abreviados de teclado que mejoran la facilidad de uso y accesibilidad de las
aplicaciones en la plataforma Universal de Windows (UWP ) al proporcionar una manera intuitiva para los
usuarios a navegar rápidamente e interactuar con de interfaz de usuario visible la aplicación a través de un teclado
en lugar de a través de la interacción o un mouse. Son combinaciones de la tecla Alt y uno o más teclas
alfanuméricas, normalmente presionadas de manera secuencial. Métodos abreviados de teclado automáticamente
son compatibles con las claves de acceso que utilizan un carácter alfanumérico individual.
Sugerencias de teclas de acceso son flotantes distintivos que se muestra al lado de los controles que incluyen las
claves de acceso. Cada sugerencia de clave de acceso contiene las teclas alfanuméricas que activación el control
asociado. Cuando un usuario presiona la tecla Alt, se muestran las sugerencias de teclas de acceso.
Esta plataforma específica de UWP se usa para especificar una clave de acceso para un VisualElement . Se
consume en XAML estableciendo el VisualElement.AccessKey propiedad adjunta a un valor alfanumérico y,
opcionalmente, establezca el VisualElement.AccessKeyPlacement propiedad adjunta a un valor de la
AccessKeyPlacement enumeración, el VisualElement.AccessKeyHorizontalOffset propiedad adjunta un double y el
VisualElement.AccessKeyVerticalOffset propiedad adjunta a un double :

<TabbedPage ...
xmlns:windows="clr-
namespace:Xamarin.Forms.PlatformConfiguration.WindowsSpecific;assembly=Xamarin.Forms.Core">
<ContentPage Title="Page 1"
windows:VisualElement.AccessKey="1">
<StackLayout Margin="20">
...
<Switch windows:VisualElement.AccessKey="A" />
<Entry Placeholder="Enter text here"
windows:VisualElement.AccessKey="B" />
...
<Button Text="Access key F, placement top with offsets"
Margin="20"
Clicked="OnButtonClicked"
windows:VisualElement.AccessKey="F"
windows:VisualElement.AccessKeyPlacement="Top"
windows:VisualElement.AccessKeyHorizontalOffset="20"
windows:VisualElement.AccessKeyVerticalOffset="20" />
...
</StackLayout>
</ContentPage>
...
</TabbedPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:


using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
...

var page = new ContentPage { Title = "Page 1" };


page.On<Windows>().SetAccessKey("1");

var switchView = new Switch();


switchView.On<Windows>().SetAccessKey("A");
var entry = new Entry { Placeholder = "Enter text here" };
entry.On<Windows>().SetAccessKey("B");
...

var button4 = new Button { Text = "Access key F, placement top with offsets", Margin = new Thickness(20) };
button4.Clicked += OnButtonClicked;
button4.On<Windows>()
.SetAccessKey("F")
.SetAccessKeyPlacement(AccessKeyPlacement.Top)
.SetAccessKeyHorizontalOffset(20)
.SetAccessKeyVerticalOffset(20);
...

El VisualElement.On<Windows> método especifica que solo se ejecutan este específicos de la plataforma en la


plataforma Universal de Windows. El VisualElement.SetAccessKey método, en el
Xamarin.Forms.PlatformConfiguration.WindowsSpecific espacio de nombres, se usa para establecer el valor de clave
de acceso para el VisualElement . El VisualElement.SetAccessKeyPlacement método, opcionalmente, especifica la
posición que se usará para mostrar la sugerencia de clave de acceso, con el AccessKeyPlacement enumeración que
proporciona los siguientes valores posibles:
Auto : indica que la selección de ubicación de acceso keytips vendrá determinado por el sistema operativo.
Top : indica que la sugerencia de clave de acceso aparecerá por encima del borde superior de la
VisualElement .
Bottom : indica que la sugerencia de clave de acceso aparecerá debajo de la parte inferior de la VisualElement .
Right : indica que aparecerá la sugerencia de clave de acceso a la derecha del borde derecho de la
VisualElement .
Left : indica que aparecerá la sugerencia de clave de acceso a la izquierda del borde izquierdo de la
VisualElement .
Center : indica que la sugerencia de clave de acceso aparecen superpuesta en el centro de la VisualElement .

NOTE
Normalmente, el Auto colocación keytips es suficiente, lo que incluye compatibilidad con interfaces de usuario adaptable.

El VisualElement.SetAccessKeyHorizontalOffset y VisualElement.SetAccessKeyVerticalOffset pueden usarse


métodos de control más detallado de la ubicación de acceso keytips. El argumento para el
SetAccessKeyHorizontalOffset método indica el momento para mover la punta de clave de acceso hacia la
izquierda o derecha y el argumento para el SetAccessKeyVerticalOffset método indica la distancia que hay que
mover la sugerencia de clave de acceso hacia arriba o hacia abajo.

NOTE
Desplazamientos de sugerencia de clave de acceso no pueden establecerse cuando se establece la posición de clave de
acceso Auto .

Además, el GetAccessKey , GetAccessKeyPlacement , GetAccessKeyHorizontalOffset ,y GetAccessKeyVerticalOffset


pueden usarse métodos para recuperar el acceso de un valor y su ubicación de la clave.
El resultado es que se pueden mostrar sugerencias de teclas de acceso junto a cualquiera VisualElement
instancias que definen el acceso de las claves, presionando la tecla Alt:

Cuando un usuario activa una tecla de acceso presionando la tecla Alt, seguida por el acceso de clave, la acción
predeterminada para el VisualElement se ejecutará. Por ejemplo, cuando un usuario activa la clave de acceso en
un Switch , el Switch se alterna. Cuando un usuario activa la clave de acceso en un Entry , el Entry recibe el
foco. Cuando un usuario activa la clave de acceso en un Button , el controlador de eventos para el Clicked se
ejecuta el evento.
Para obtener más información acerca de las claves de acceso, consulte claves de acceso.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
WindowsSpecific API
Modo de Color VisualElement heredado en Windows
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Algunas de las vistas de Xamarin.Forms cuentan con un modo de color heredado. En este modo, cuando el
IsEnabled se establece la propiedad de la vista en false , la vista invalida los colores establecidos por el usuario
con los colores nativo predeterminado para el estado deshabilitado. Para hacia atrás compatibilidad, este modo
heredado de color permanece el comportamiento predeterminado para las vistas admitidas.
Esta plataforma Universal de Windows específicos de la plataforma deshabilita este modo heredado de color, por
lo que establecen los colores en una vista por el usuario permanece incluso cuando la vista está deshabilitada. Se
consume en XAML estableciendo el VisualElement.IsLegacyColorModeEnabled propiedad adjunta false :

<ContentPage ...
xmlns:windows="clr-
namespace:Xamarin.Forms.PlatformConfiguration.WindowsSpecific;assembly=Xamarin.Forms.Core">
<StackLayout>
...
<Editor Text="Enter text here"
TextColor="Blue"
BackgroundColor="Bisque"
windows:VisualElement.IsLegacyColorModeEnabled="False" />
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
...

_legacyColorModeDisabledEditor.On<Windows>().SetIsLegacyColorModeEnabled(false);

El VisualElement.On<Windows> método especifica que solo se ejecutarán este específicos de la plataforma en


Windows. El VisualElement.SetIsLegacyColorModeEnabled método, en el
Xamarin.Forms.PlatformConfiguration.WindowsSpecific espacio de nombres, se usa para controlar si se deshabilita el
modo de color heredado. Además, el VisualElement.GetIsLegacyColorModeEnabled método puede utilizarse para
devolver si está deshabilitado el modo de color heredado.
El resultado es que se puede deshabilitar el modo de color heredados, para que los colores establecidos en una
vista por el usuario permanezcan incluso cuando se deshabilita la vista:
NOTE
Al establecer un VisualStateGroup en una vista, se omite completamente el modo de color heredado. Para obtener más
información acerca de los estados visuales, vea Xamarin.Forms Visual State Manager.

Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
WindowsSpecific API
Alertas de JavaScript en WebView en Windows
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Este específicos de la plataforma permite una WebView para mostrar las alertas de JavaScript en un cuadro de
diálogo de mensaje UWP. Se consume en XAML estableciendo el WebView.IsJavaScriptAlertEnabled propiedad
adjunta un boolean valor:

<ContentPage ...
xmlns:windows="clr-
namespace:Xamarin.Forms.PlatformConfiguration.WindowsSpecific;assembly=Xamarin.Forms.Core">
<StackLayout>
<WebView ... windows:WebView.IsJavaScriptAlertEnabled="true" />
...
</StackLayout>
</ContentPage>

Como alternativa, pueden usarse desde C# mediante la API fluida:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
...

var webView = new Xamarin.Forms.WebView


{
Source = new HtmlWebViewSource
{
Html = @"<html><body><button onclick=""window.alert('Hello World from JavaScript');"">Click Me</button>
</body></html>"
}
};
webView.On<Windows>().SetIsJavaScriptAlertEnabled(true);

El WebView.On<Windows> método especifica que solo se ejecutan este específicos de la plataforma en la plataforma
Universal de Windows. El WebView.SetIsJavaScriptAlertEnabled método, en el
Xamarin.Forms.PlatformConfiguration.WindowsSpecific espacio de nombres, se usa para controlar si las alertas de
JavaScript están habilitadas. Además, el WebView.SetIsJavaScriptAlertEnabled método puede utilizarse para activar
o desactivar las alertas de JavaScript mediante una llamada a la IsJavaScriptAlertEnabled método para devolver
si están habilitadas:

_webView.On<Windows>().SetIsJavaScriptAlertEnabled(!_webView.On<Windows>().IsJavaScriptAlertEnabled());

El resultado es que se pueden mostrar las alertas de JavaScript en un cuadro de diálogo de mensaje UWP:
Vínculos relacionados
PlatformSpecifics (ejemplo)
Creación funcionalidades específicas de plataforma
WindowsSpecific API
Xamarin.Essentials
11/07/2019 • 4 minutes to read • Edit Online

Xamarin.Essentials brinda a los desarrolladores API multiplataformas para sus aplicaciones móviles.
Android, iOS y UWP ofrecen API de plataforma y sistema operativo únicas a las que los desarrolladores pueden
tener acceso desde C# mediante Xamarin. Xamarin.Essentials brinda una API multiplataforma única que funciona
con cualquier aplicación Xamarin.Forms, Android, iOS o UWP accesible desde código compartido, sin importar
cómo se creó la interfaz de usuario.

Introducción a Xamarin.Essentials
Siga la guía de introducción para instalar el paquete NuGet de Xamarin.Essentials a los proyectos nuevos o
existentes de Xamarin.Forms, Android, iOS o UWP.

Guías de características
Siga las guías para integrar estas características de Xamarin.Essentials en las aplicaciones:
Accelerometer: recupere los datos de aceleración del dispositivo en un espacio tridimensional.
App Information: conozca información sobre la aplicación.
Barometer: supervise los cambios de presión con el barómetro.
Battery: detecte fácilmente el nivel, origen y estado de la batería.
Clipboard: establezca o lea fácil y rápidamente texto en el Portapapeles.
Color Converters: métodos del asistente para System.Drawing.Color.
Compass: supervise los cambios en la brújula.
Connectivity: compruebe el estado de la conectividad y detecte cambios.
Detect Shake: detecte movimientos de agitación en el dispositivo.
Device Display Information: obtenga la orientación y las métricas de la pantalla del dispositivo.
Device Information: conozca información sobre el dispositivo de manera sencilla.
Email: envíe fácilmente mensajes de correo electrónico.
File System Helpers: guarde fácilmente archivos en los datos de la aplicación.
Flashlight: una manera sencilla de encender y apagar la linterna.
Geocoding: coordenadas y direcciones de código geográfico y de código geográfico inverso.
Geolocation: recupere la ubicación de GPS del dispositivo.
Gyroscope: haga seguimiento de la rotación alrededor de tres ejes primarios del dispositivo.
Launcher: permite que una aplicación abra un URI por el sistema.
Magnetometer: detecte la orientación del dispositivo respectivo del campo magnético de la Tierra.
MainThread: ejecute código en el subproceso principal de la aplicación.
Maps: abra la aplicación de mapas en una ubicación específica.
Open Browser: abra rápidamente y sin problemas un explorador en un sitio web específico.
Orientation Sensor: recupere la orientación del dispositivo en un espacio tridimensional.
Phone Dialer: abra el marcador telefónico.
Platform Extensions: métodos del asistente para convertir Rect, Size y Point.
Preferences: agregue rápida y sencillamente las preferencias persistentes.
Secure Storage: almacene datos de manera segura.
Share: envíe texto y URI de sitio web a otras aplicaciones.
SMS: cree un mensajes SMS para enviarlo.
Text-to-Speech: vocalice texto en el dispositivo.
Unit Converters: métodos del asistente para convertir unidades.
Version Tracking: haga seguimiento de las versiones de las aplicaciones y los números de compilación.
Vibrate: haga que el dispositivo vibre.

Solución de problemas
Busque ayuda si se encuentra con problemas.

Documentación de la API
Examine la documentación de la API para cada característica de Xamarin.Essentials.
Introducción a Xamarin.Essentials
11/07/2019 • 5 minutes to read • Edit Online

Xamarin.Essentials brinda una API multiplataforma única que funciona con cualquier aplicación iOS,
Android o UWP accesible desde código compartido, sin importar cómo se creó la interfaz de usuario.

Compatibilidad de la plataforma
Xamarin.Essentials admite las siguientes plataformas y sistemas operativos:

PLATAFORMA VERSIÓN

Android 4.4 (API 19) o versiones posteriores

iOS 10.0 o versiones posteriores

UWP 10.0.16299.0 o versiones posteriores

Instalación
Xamarin.Essentials está disponible como paquete NuGet que se puede agregar a cualquier proyecto
nuevo o existente con Visual Studio.
1. Descargue e instale Visual Studio con Visual Studio Tools para Xamarin.
2. Abra un proyecto existente o cree uno nuevo con la plantilla de aplicación vacía en Visual Studio
C# (Android, iPhone e iPad o multiplataforma). Importante: Si se agrega a un proyecto de UWP,
asegúrese de que en las propiedades del proyecto esté establecida la compilación 16299 u otra
posterior.
3. Agregue el paquete NuGet Xamarin.Essentials a cada proyecto:
Visual Studio
Visual Studio para Mac
En el panel del Explorador de soluciones, haga clic con el botón derecho en el nombre de la
solución y seleccione Administrar paquetes NuGet. Busque Xamarin.Essentials e instale el
paquete en TODOS los proyectos, incluidos Android, iOS, UWP y las bibliotecas de .NET
Standard.
4. Agregue una referencia a Xamarin.Essentials en cualquier clase de C# para hacer referencia a las
API.

using Xamarin.Essentials;

5. Xamarin.Essentials requiere una configuración específica de plataforma:


Android
iOS
UWP
La versión mínima de Android compatible con Xamarin.Essentials es la 4.4, que corresponde a un
nivel de API 19, pero la versión de destino de Android para compilar debe ser la 9.0,
correspondiente al nivel de API 28. (En Visual Studio, estas dos versiones se establecen en el
cuadro de diálogo Propiedades del proyecto correspondiente al proyecto de Android en la pestaña
Manifiesto de Android). En Visual Studio para Mac, se establecen en el cuadro de diálogo
Opciones del proyecto correspondiente al proyecto de Android, en la pestaña Aplicación de
Android).
Xamarin.Essentials instala la versión 28.0.0.1 de las bibliotecas de Xamarin.Android.Support que
necesita. Las demás bibliotecas de Xamarin.Android.Support que requiere la aplicación también se
deben actualizar a la versión 28.0.0.1 con el administrador de paquetes NuGet. Todas las
bibliotecas de Xamarin.Android.Support que la aplicación usa deben ser iguales y la versión debe
ser al menos 28.0.0.1. Consulte la página de solución de problemas si no puede agregar el paquete
NuGet de Xamarin.Essentials ni actualizar los paquetes NuGet de la solución.
En MainLauncher del proyecto Android o en cualquier Activity que se inicia, Xamarin.Essentials
se debe inicializar en el método OnCreate :

protected override void OnCreate(Bundle savedInstanceState) {


//...
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState); // add this line to your code,
it may also be called: bundle
//...

Para controlar los permisos en tiempo de ejecución de Android, Xamarin.Essentials debe recibir
cualquier OnRequestPermissionsResult . Agregue el código siguiente a todas las clases Activity :

public override void OnRequestPermissionsResult(int requestCode, string[] permissions,


[GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions,
grantResults);

base.OnRequestPermissionsResult(requestCode, permissions, grantResults);


}

6. Siga las guías de Xamarin.Essentials para poder copiar y pegar fragmentos de código para cada
característica.

Xamarin.Essentials : API multiplataformas para Mobile Apps


(video)

Otros recursos
Es recomendable que los desarrolladores que trabajan por primera vez con Xamarin visiten Introducción
al desarrollo de Xamarin.
Visite el repositorio GitHub de Xamarin.Essentials para ver el código fuente actual, qué viene más
adelante, ejecutar ejemplos y clonar el repositorio. Estaremos encantados de recibir cualquier
colaboración de la comunidad.
Examine la documentación de la API para conocer cada característica de Xamarin.Essentials.
Xamarin.Essentials: Acelerómetro
11/07/2019 • 4 minutes to read • Edit Online

La clase Accelerometer permite supervisar el sensor del acelerómetro del dispositivo, que indica la aceleración
del dispositivo en un espacio tridimensional.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Accelerometer
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La funcionalidad Accelerometer funciona mediante una llamada a los métodos Start y Stop para realizar
escuchas de los cambios realizados en la aceleración. Los cambios se enviarán a través del evento ReadingChanged .
A continuación le mostramos un ejemplo de uso:
public class AccelerometerTest
{
// Set speed delay for monitoring changes.
SensorSpeed speed = SensorSpeed.UI;

public AccelerometerTest()
{
// Register for reading changes, be sure to unsubscribe when finished
Accelerometer.ReadingChanged += Accelerometer_ReadingChanged;
}

void Accelerometer_ReadingChanged(object sender, AccelerometerChangedEventArgs e)


{
var data = e.Reading;
Console.WriteLine($"Reading: X: {data.Acceleration.X}, Y: {data.Acceleration.Y}, Z:
{data.Acceleration.Z}");
// Process Acceleration X, Y, and Z
}

public void ToggleAccelerometer()


{
try
{
if (Accelerometer.IsMonitoring)
Accelerometer.Stop();
else
Accelerometer.Start(speed);
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}

Las lecturas del acelerómetro se notifican en G. La G es una unidad de fuerza gravitacional igual a la ejercida por
el campo gravitatorio de la Tierra (9,81 m/s^2).
El sistema de coordenadas se define con respecto a la pantalla del teléfono en orientación predeterminada.
Cuando cambia la orientación de pantalla del dispositivo no se intercambian los ejes.
El eje X es horizontal y apunta a la derecha, el eje Y es vertical y apunta hacia arriba, y el eje Z apunta hacia el
exterior de la parte frontal de la pantalla. En este sistema, las coordenadas de detrás de la pantalla tienen valores
de Z negativo.
Ejemplos:
Cuando el dispositivo se encuentra plano sobre una mesa y se mueve desde el lado izquierdo hacia la
derecha, el valor de aceleración de X es positivo.
Cuando el dispositivo se encuentra plano sobre una mesa, el valor de aceleración es + 1,00 G o (+ 9,81
m/s^2), que corresponden a la aceleración del dispositivo (0 m/s^2) menos la fuerza de la gravedad (- 9,81
m/s^2) y normalizada en G.
Cuando el dispositivo se encuentra plano sobre una mesa y se mueve hacia el cielo con una aceleración de
A m/s^2, el valor de la aceleración es igual A + 9,81, que corresponde a la aceleración del dispositivo (+ A
m/s^2) menos la fuerza de la gravedad (- 9,81 m/s^2) y normalizada en G.
Velocidad de sensor
Más rápido: obtener los datos del sensor tan rápido como sea posible (no se garantiza la devolución en el
subproceso de interfaz de usuario).
Juego: velocidad adecuada para juegos (no se garantiza la devolución en el subproceso de interfaz de usuario).
Normal: velocidad predeterminada adecuada para los cambios de orientación de pantalla.
Interfaz de usuario: velocidad adecuada para la interfaz de usuario general.
Si no se garantiza la ejecución del controlador de eventos en el subproceso de interfaz de usuario y si el
controlador de eventos necesita tener acceso a elementos de la interfaz de usuario, use el método
MainThread.BeginInvokeOnMainThread para ejecutar ese código en el subproceso de interfaz de usuario.

API
Código fuente de Accelerometer
Documentación de API para Accelerometer

Vídeo relacionado

Encuentre más vídeos de Xamarin en Channel 9 y YouTube.


Xamarin.Essentials: Información de la aplicación:
11/07/2019 • 2 minutes to read • Edit Online

La clase AppInfo proporciona información sobre la aplicación.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de AppInfo
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Obtención de información de la aplicación:


La información siguiente se expone a través de la API:

// Application Name
var appName = AppInfo.Name;

// Package Name/Application Identifier (com.microsoft.testapp)


var packageName = AppInfo.PackageName;

// Application Version (1.0.0)


var version = AppInfo.VersionString;

// Application Build Number (1)


var build = AppInfo.BuildString;

Representación de la configuración de la aplicación


La clase AppInfo también puede mostrar una página de configuración mantenida por el sistema operativo para la
aplicación:

// Display settings page


AppInfo.ShowSettingsUI();

Esta página de configuración permite al usuario cambiar los permisos de la aplicación y realizar otras tareas
específicas de la plataforma.

Detalles de implementación de la plataforma


Android
iOS
UWP
La información sobre la aplicación se extrae de AndroidManifest.xml para estos campos:
Build: android:versionCode en el nodo manifest
Name - android:label en el nodo application
PackageName: package en el nodo manifest
VersionString: android:versionName en el nodo application

API
Código fuente de AppInfo
Documentación de API para AppInfo

Vídeo relacionado

Encuentre más vídeos de Xamarin en Channel 9 y YouTube.


Xamarin.Essentials: Barometer
11/07/2019 • 2 minutes to read • Edit Online

La clase Barometer permite supervisar el sensor del barómetro del dispositivo, que mide la presión.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Barometer
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Barometer funciona mediante una llamada a los métodos Start y Stop para escuchar los cambios en la lectura
de presión del barómetro en hectopascales. Los cambios se enviarán a través del evento ReadingChanged . A
continuación le mostramos un ejemplo de uso:
public class BarometerTest
{
// Set speed delay for monitoring changes.
SensorSpeed speed = SensorSpeed.UI;

public BarometerTest()
{
// Register for reading changes.
Barometer.ReadingChanged += Barometer_ReadingChanged;
}

void Barometer_ReadingChanged(object sender, BarometerChangedEventArgs e)


{
var data = e.Reading;
// Process Pressure
Console.WriteLine($"Reading: Pressure: {data.PressureInHectopascals} hectopascals");
}

public void ToggleBarometer()


{
try
{
if (Barometer.IsMonitoring)
Barometer.Stop();
else
Barometer.Start(speed);
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}

Velocidad de sensor
Más rápido: obtener los datos del sensor tan rápido como sea posible (no se garantiza la devolución en el
subproceso de interfaz de usuario).
Juego: velocidad adecuada para juegos (no se garantiza la devolución en el subproceso de interfaz de usuario).
Normal: velocidad predeterminada adecuada para los cambios de orientación de pantalla.
Interfaz de usuario: velocidad adecuada para la interfaz de usuario general.
Si no se garantiza la ejecución del controlador de eventos en el subproceso de interfaz de usuario y si el
controlador de eventos necesita tener acceso a elementos de la interfaz de usuario, use el método
MainThread.BeginInvokeOnMainThread para ejecutar ese código en el subproceso de interfaz de usuario.

Detalles de implementación de la plataforma


Android
iOS
UWP
Sin detalles de implementación específicos para la plataforma.
API
Código fuente de Barometer
Documentación de API para Barometer
Xamarin.Essentials: Batería
11/07/2019 • 5 minutes to read • Edit Online

La clase Battery le permite comprobar la información sobre la batería del dispositivo y supervisar los cambios.
Además, ofrece información sobre el estado de ahorro de energía del dispositivo, que indica si este se está
ejecutando en el modo de bajo consumo. Las aplicaciones deben evitar el procesamiento en segundo plano si el
estado de ahorro de energía del dispositivo está activado.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.
Para acceder a la funcionalidad de Battery, se requiere la siguiente configuración específica para la plataforma.
Android
iOS
UWP
El permiso Battery es necesario y se debe configurar en el proyecto Android. Se puede agregar de las siguientes
maneras:
Abra el archivo AssemblyInfo.cs de la carpeta Propiedades y agregue lo siguiente:

[assembly: UsesPermission(Android.Manifest.Permission.BatteryStats)]

O BIEN, actualice el manifiesto de Android:


Abra el archivo AndroidManifest.xml de la carpeta Propiedades y agregue lo siguiente dentro del nodo
manifest.

<uses-permission android:name="android.permission.BATTERY_STATS" />

O haga clic con el botón derecho en el proyecto de Android y abra las propiedades del proyecto. En Manifiesto
de Android, busque el área Permisos requeridos: y active el permiso Battery (Batería). Esto actualizará
automáticamente el archivo AndroidManifest.xml.

Uso de Battery
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Compruebe la información actual de la batería:


var level = Battery.ChargeLevel; // returns 0.0 to 1.0 or 1.0 when on AC or no battery.

var state = Battery.State;

switch (state)
{
case BatteryState.Charging:
// Currently charging
break;
case BatteryState.Full:
// Battery is full
break;
case BatteryState.Discharging:
case BatteryState.NotCharging:
// Currently discharging battery or not being charged
break;
case BatteryState.NotPresent:
// Battery doesn't exist in device (desktop computer)
case BatteryState.Unknown:
// Unable to detect battery state
break;
}

var source = Battery.PowerSource;

switch (source)
{
case BatteryPowerSource.Battery:
// Being powered by the battery
break;
case BatteryPowerSource.AC:
// Being powered by A/C unit
break;
case BatteryPowerSource.Usb:
// Being powered by USB cable
break;
case BatteryPowerSource.Wireless:
// Powered via wireless charging
break;
case BatteryPowerSource.Unknown:
// Unable to detect power source
break;
}

Cada vez que se cambia cualquiera de las propiedades de la batería, se desencadena un evento:

public class BatteryTest


{
public BatteryTest()
{
// Register for battery changes, be sure to unsubscribe when needed
Battery.BatteryInfoChanged += Battery_BatteryInfoChanged;
}

void Battery_BatteryInfoChanged(object sender, BatteryInfoChangedEventArgs e)


{
var level = e.ChargeLevel;
var state = e.State;
var source = e.PowerSource;
Console.WriteLine($"Reading: Level: {level}, State: {state}, Source: {source}");
}
}

Es posible poner los dispositivos que usan baterías en el modo de ahorro en caso de baja energía. A veces, los
dispositivos cambian automáticamente a este modo, por ejemplo, cuando la batería cae por debajo del 20 % de su
capacidad. El sistema operativo responde al modo de ahorro de energía reduciendo las actividades que tienden a
agotar la batería. Para ayudar, las aplicaciones pueden evitar el procesamiento en segundo plano u otras
actividades de alta potencia cuando el modo de ahorro de energía está activado.
También puede conocer el estado de ahorro de energía actual del dispositivo con la propiedad estática
Battery.EnergySaverStatus :

// Get energy saver status


var status = Battery.EnergySaverStatus;

Esta propiedad devuelve un miembro de la enumeración EnergySaverStatus , que es On , Off o Unknown . Si la


propiedad devuelve On , la aplicación debe evitar el procesamiento en segundo plano o cualquier otra actividad
que pueda tener un consumo energético grande.
La aplicación también debe instalar un controlador de eventos. La clase Battery expone un evento que se
desencadena cuando cambia el estado de ahorro de energía:

public class EnergySaverTest


{
public EnergySaverTest()
{
// Subscribe to changes of energy-saver status
Battery.EnergySaverStatusChanged += OnEnergySaverStatusChanged;
}

private void OnEnergySaverStatusChanged(EnergySaverStatusChangedEventArgs e)


{
// Process change
var status = e.EnergySaverStatus;
}
}

Si el estado de ahorro de energía cambia a On , la aplicación debe detener el procesamiento en segundo plano. Si
el estado cambia a Unknown o Off , la aplicación puede reanudar el procesamiento en segundo plano.

Diferencias entre plataformas


Android
iOS
UWP
No hay diferencias entre las plataformas.

API
Código fuente de Battery
Documentación de API para Battery

Vídeo relacionado
Encuentre más vídeos de Xamarin en Channel 9 y YouTube.
Xamarin.Essentials: Portapapeles
11/07/2019 • 2 minutes to read • Edit Online

La clase Clipboard permite copiar y pegar texto en el Portapapeles del sistema entre aplicaciones.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Clipboard
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Para comprobar si actualmente el Portapapeles tiene texto listo para pegar:

var hasText = Clipboard.HasText;

Para establecer texto en el Portapapeles:

await Clipboard.SetTextAsync("Hello World");

Para leer texto desde el Portapapeles:

var text = await Clipboard.GetTextAsync();

TIP
El acceso al Portapapeles debe realizarse en el subproceso de la interfaz de usuario principal. Consulte la API de MainThread
para ver cómo invocar métodos en el subproceso de la interfaz de usuario principal.

API
Código fuente de Clipboard
Documentación de API de Clipboard

Vídeo relacionado

Encuentre más vídeos de Xamarin en Channel 9 y YouTube.


Xamarin.Essentials: convertidores de color
11/07/2019 • 2 minutes to read • Edit Online

La clase ColorConverters de Xamarin.Essentials ofrece varios métodos auxiliares para System.Drawing.Color.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de los convertidores de color


Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Al trabajar con System.Drawing.Color , puede usar los convertidores integrados de Xamarin.Forms para crear un
color a partir de Hsl, Hex o UInt.

var blueHex = ColorConverters.FromHex("#3498db");


var blueHsl = ColorConverters.FromHsl(204, 70, 53);
var blueUInt = ColorConverers.FromUInt(3447003);

Uso de extensiones de color


Los método de extensión de System.Drawing.Color le permiten aplicar propiedades diferentes:

var blue = ColorConverters.FromHex("#3498db");

// Multiplies the current alpha by 50%


var blueWithAlpha = blue.MultiplyAlpha(.5f);

Hay varios métodos de extensión, por ejemplo:


ToUInt
MultiplyAlpha
WithHue
WithAlpha
WithSaturation
WithLuminosity

Uso de las extensiones de plataforma


Además, puede convertir System.Drawing.Color a la estructura de color específica de la plataforma. Estos métodos
solo se pueden llamar desde proyectos de UWP, iOS y Android.
var system = System.Drawing.Color.FromArgb(255, 52, 152, 219);

// Extension to convert to Android.Graphics.Color, UIKit.UIColor, or Windows.UI.Color


var platform = system.ToPlatformColor();

var platform = new Android.Graphics.Color(52, 152, 219, 255);

// Back to System.Drawing.Color
var system = platform.ToSystemColor();

El método ToSystemColor se aplica a Android.Graphics.Color, UIKit.UIColor y Windows.UI.Color.

API
Código fuente de los convertidores de color
Documentación sobre la API de los convertidores de color
Color fuente de las extensiones de color
Documentación sobre la API de las extensiones de color
Xamarin.Essentials: Brújula
11/07/2019 • 4 minutes to read • Edit Online

La clase Compass permite supervisar la dirección del norte magnético del dispositivo.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Compass
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La funcionalidad Compass funciona mediante una llamada a los métodos Start y Stop para realizar escuchas de
los cambios realizados en la brújula. Los cambios se enviarán a través del evento ReadingChanged . A continuación
se muestra un ejemplo:
public class CompassTest
{
// Set speed delay for monitoring changes.
SensorSpeed speed = SensorSpeed.UI;

public CompassTest()
{
// Register for reading changes, be sure to unsubscribe when finished
Compass.ReadingChanged += Compass_ReadingChanged;
}

void Compass_ReadingChanged(object sender, CompassChangedEventArgs e)


{
var data = e.Reading;
Console.WriteLine($"Reading: {data.HeadingMagneticNorth} degrees");
// Process Heading Magnetic North
}

public void ToggleCompass()


{
try
{
if (Compass.IsMonitoring)
Compass.Stop();
else
Compass.Start(speed);
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Some other exception has occurred
}
}
}

Velocidad de sensor
Más rápido: obtener los datos del sensor tan rápido como sea posible (no se garantiza la devolución en el
subproceso de interfaz de usuario).
Juego: velocidad adecuada para juegos (no se garantiza la devolución en el subproceso de interfaz de usuario).
Normal: velocidad predeterminada adecuada para los cambios de orientación de pantalla.
Interfaz de usuario: velocidad adecuada para la interfaz de usuario general.
Si no se garantiza la ejecución del controlador de eventos en el subproceso de interfaz de usuario y si el
controlador de eventos necesita tener acceso a elementos de la interfaz de usuario, use el método
MainThread.BeginInvokeOnMainThread para ejecutar ese código en el subproceso de interfaz de usuario.

Detalles de implementación de la plataforma


Android
Android no proporciona una API para recuperar la dirección de la brújula. El acelerómetro y el magnetómetro se
usan para calcular la dirección del norte magnético, lo que recomienda Google.
En casos poco habituales, es posible que vea resultados incoherentes porque sea necesario calibrar los sensores,
lo que implica mover el dispositivo describiendo un ocho. La mejor manera de hacerlo es abrir Google Maps,
pulsar en el punto de la ubicación y seleccionar Calibrar la brújula.
Tenga en cuenta que la ejecución simultánea de varios sensores desde la aplicación puede ajustar la velocidad del
sensor.

Filtro de paso bajo


Debido a la forma de calcular y actualizar los valores de la brújula de Android, es posible que sea necesario
suavizarlos. Se puede aplicar un filtro de paso bajo que calcula el promedio de los valores de seno y coseno de los
ángulos, y se puede activar si se sobrecarga el método Start , que acepta el parámetro bool applyLowPassFilter :

Compass.Start(SensorSpeed.UI, applyLowPassFilter: true);

Esto solo se aplica a la plataforma Android; el parámetro se ignora en iOS y UWP. Puede leer más información
aquí.

API
Código fuente de Compass
Documentación de API para Compass
Xamarin.Essentials: Conectividad
11/07/2019 • 4 minutes to read • Edit Online

La clase Connectivity permite supervisar los cambios en las condiciones de red del dispositivo, revisar el acceso
actual a la red y cómo está conectado actualmente.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.
Para acceder a la funcionalidad Connectivity, se requiere la siguiente configuración específica para la plataforma.
Android
iOS
UWP
El permiso AccessNetworkState es necesario y se debe configurar en el proyecto Android. Se puede agregar de las
siguientes maneras:
Abra el archivo AssemblyInfo.cs de la carpeta Propiedades y agregue lo siguiente:

[assembly: UsesPermission(Android.Manifest.Permission.AccessNetworkState)]

O BIEN, actualice el manifiesto de Android:


Abra el archivo AndroidManifest.xml de la carpeta Propiedades y agregue lo siguiente dentro del nodo
manifest.

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

O haga clic con el botón derecho en el proyecto de Android y abra las propiedades del proyecto. En Manifiesto
de Android, busque el área Permisos requeridos: y compruebe el permiso Access Network State (Estado de
red de acceso). Esto actualizará automáticamente el archivo AndroidManifest.xml.

Uso de Connectivity
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Compruebe el acceso de red actual:

var current = Connectivity.NetworkAccess;

if (current == NetworkAccess.Internet)
{
// Connection to internet is available
}
Acceso a la red cae en estas categorías:
Internet: acceso a Internet y local.
ConstrainedInternet: acceso limitado a Internet. Indica la conectividad cautiva del portal, donde se
proporciona acceso local a un portal web, pero el acceso a Internet requiere que se proporcionen credenciales
específicas a través de un portal.
Local: solo acceso a la red local.
None: sin conectividad disponible.
Unknown: no se puede determinar la conectividad de Internet.
Puede comprobar qué tipo de perfil de conexión el dispositivo usa de manera activa:

var profiles = Connectivity.ConnectionProfiles;


if (profiles.Contains(ConnectionProfile.WiFi))
{
// Active Wi-Fi connection.
}

Cada vez que el perfil de conexión o el acceso a la red cambia, puede recibir un evento cuando se desencadena:

public class ConnectivityTest


{
public ConnectivityTest()
{
// Register for connectivity changes, be sure to unsubscribe when finished
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
}

void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)


{
var access = e.NetworkAccess;
var profiles = e.ConnectionProfiles;
}
}

Limitaciones
Es importante tener en cuenta que puede que NetworkAccess informe Internet , pero no hay disponible acceso
total a la Web. Debido a cómo funciona la conectividad en cada plataforma, solo puede garantizar que hay
disponible una conexión. Por ejemplo, es posible que el dispositivo esté conectado a una red Wi-Fi, pero el
enrutador no está conectado a Internet. En esta instancia, puede que se indique que hay Internet, pero no hay
disponible ninguna conexión activa.

API
Código fuente de Connectivity
Documentación de API de Connectivity

Vídeo relacionado
Encuentre más vídeos de Xamarin en Channel 9 y YouTube.
Xamarin.Essentials: detección de agitaciones
11/07/2019 • 3 minutes to read • Edit Online

La clase Accelerometer permite supervisar el sensor del acelerómetro del dispositivo, que indica la aceleración
del dispositivo en un espacio tridimensional. Además, le permite registrar eventos que se realizarán cuando el
usuario agite el dispositivo.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de la detección de agitaciones


Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Para detectar la agitación del dispositivo, debe usar la funcionalidad Accelerometer mediante una llamada a los
métodos Start y Stop para realizar cambios en la aceleración y para detectar movimientos de agitación.
Siempre que se detecte agitación, se desencadenará un evento ShakeDetected . Se recomienda usar Game o faster
para SensorSpeed . A continuación le mostramos un ejemplo de uso:
public class DetectShakeTest
{
// Set speed delay for monitoring changes.
SensorSpeed speed = SensorSpeed.Game;

public DetectShakeTest()
{
// Register for reading changes, be sure to unsubscribe when finished
Accelerometer.ShakeDetected += Accelerometer_ShakeDetected ;
}

void Accelerometer_ShakeDetected (object sender, EventArgs e)


{
// Process shake event
}

public void ToggleAccelerometer()


{
try
{
if (Accelerometer.IsMonitoring)
Accelerometer.Stop();
else
Accelerometer.Start(speed);
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}

Velocidad de sensor
Más rápido: obtener los datos del sensor tan rápido como sea posible (no se garantiza la devolución en el
subproceso de interfaz de usuario).
Juego: velocidad adecuada para juegos (no se garantiza la devolución en el subproceso de interfaz de usuario).
Normal: velocidad predeterminada adecuada para los cambios de orientación de pantalla.
Interfaz de usuario: velocidad adecuada para la interfaz de usuario general.
Si no se garantiza la ejecución del controlador de eventos en el subproceso de interfaz de usuario y si el
controlador de eventos necesita tener acceso a elementos de la interfaz de usuario, use el método
MainThread.BeginInvokeOnMainThread para ejecutar ese código en el subproceso de interfaz de usuario.

Detalles de implementación
La API para la detección de agitaciones usa lecturas sin procesar del acelerómetro para calcular la aceleración.
Utiliza un mecanismo de cola simple para detectar si tres cuartos de los eventos recientes del acelerómetro se han
producido durante el último medio segundo. La aceleración se calcula añadiendo el cuadrado de las lecturas X, Y y
Z del acelerómetro y comparándolo con un umbral determinado.

API
Código fuente de Accelerometer
Documentación de API de Accelerometer

Vídeo relacionado

Encuentre más vídeos de Xamarin en Channel 9 y YouTube.


Xamarin.Essentials: Información de pantalla del
dispositivo
11/07/2019 • 2 minutes to read • Edit Online

La clase DeviceDisplay proporciona información sobre las métricas de la pantalla del dispositivo que determinan
cómo se ejecuta la aplicación. También puede solicitar que la pantalla no se apague mientras la aplicación se esté
ejecutando.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de DeviceDisplay
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Información de la pantalla principal


Además de información básica del dispositivo, la clase DeviceDisplay contiene información sobre la pantalla y la
orientación del dispositivo.

// Get Metrics
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;

// Orientation (Landscape, Portrait, Square, Unknown)


var orientation = mainDisplayInfo.Orientation;

// Rotation (0, 90, 180, 270)


var rotation = mainDisplayInfo.Rotation;

// Width (in pixels)


var width = mainDisplayInfo.Width;

// Height (in pixels)


var height = mainDisplayInfo.Height;

// Screen density
var density = mainDisplayInfo.Density;

La clase DeviceDisplay también expone un evento al que se puede suscribir para que se desencadene siempre
que cambie cualquier métrica de pantalla:
public class DisplayInfoTest
{
public DisplayInfoTest()
{
// Subscribe to changes of screen metrics
DeviceDisplay.MainDisplayInfoChanged += OnMainDisplayInfoChanged;
}

void OnMainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)


{
// Process changes
var displayInfo = e.DisplayInfo;
}
}

La clase DeviceDisplay expone una propiedad bool con el nombre KeepScreenOn . Esta propiedad puede
establecerse para que intente impedir que la pantalla del dispositivo se apague o bloquee.

public class KeepScreenOnTest


{
public void ToggleScreenLock()
{
DeviceDisplay.KeepScreenOn = !DeviceDisplay.KeepScreenOn;
}
}

Diferencias entre plataformas


Android
iOS
UWP
No hay diferencias.

API
Código fuente de DeviceDisplay
Documentación de API de DeviceDisplay
Xamarin.Essentials: Información del dispositivo
11/07/2019 • 2 minutes to read • Edit Online

La clase DeviceInfo proporciona información sobre el dispositivo en el que se ejecuta la aplicación.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de DeviceInfo
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La información siguiente se expone a través de la API:

// Device Model (SMG-950U, iPhone10,6)


var device = DeviceInfo.Model;

// Manufacturer (Samsung)
var manufacturer = DeviceInfo.Manufacturer;

// Device Name (Motz's iPhone)


var deviceName = DeviceInfo.Name;

// Operating System Version Number (7.0)


var version = DeviceInfo.VersionString;

// Platform (Android)
var platform = DeviceInfo.Platform;

// Idiom (Phone)
var idiom = DeviceInfo.Idiom;

// Device Type (Physical)


var deviceType = DeviceInfo.DeviceType;

Plataformas
DeviceInfo.Platformse correlaciona con una cadena de constante que se asigna al sistema operativo. Los valores
se pueden comprobar con el struct DevicePlatform :
DevicePlatform.iOS: iOS
DevicePlatform.Android: Android
DevicePlatform.UWP: UWP
DevicePlatform.Unknown: desconocido

Expresiones
DeviceInfo.Idiom se correlaciona con una cadena de constante que se asigna al tipo de dispositivo en el que se
ejecuta la aplicación. Los valores se pueden comprobar con el struct DeviceIdiom :
DeviceIdiom.Phone: teléfono
DeviceIdiom.Tablet: tableta
DeviceIdiom.Desktop: escritorio
DeviceIdiom.TV: TV
DeviceIdiom.Watch: reloj
DeviceIdiom.Unknown: desconocido

Tipo de dispositivo
DeviceInfo.DeviceType pone en correlación una enumeración para determinar si la aplicación se ejecuta en un
dispositivo físico o virtual. Un dispositivo virtual es un simulador o emulador.

Detalles de implementación de la plataforma


iOS
iOS no expone una API para que los desarrolladores obtengan el nombre del dispositivo iOS específico. En su
lugar, se devuelve un identificador de hardware, como iPhone10,6 que hace referencia al iPhone X. Apple no
proporciona una asignación de estos identificadores, pero se puede encontrar en The iPhone Wiki (una fuente no
oficial).

API
Código fuente de DeviceInfo
Documentación de API para DeviceInfo
Xamarin.Essentials: Correo electrónico
11/07/2019 • 3 minutes to read • Edit Online

La clase Email permite que una aplicación abra la aplicación de correo electrónico predeterminada con
información especificada incluido el asunto, el cuerpo y los destinatarios (PARA, CC, CCO ).

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

TIP
Para usar la API de correo electrónico en iOS, debe ejecutarla en un dispositivo físico, si no, se producirá una excepción.

Uso de Email
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Para usar la funcionalidad de Email se llama al método ComposeAsync con un valor EmailMessage que contiene
información sobre el correo electrónico:

public class EmailTest


{
public async Task SendEmail(string subject, string body, List<string> recipients)
{
try
{
var message = new EmailMessage
{
Subject = subject,
Body = body,
To = recipients,
//Cc = ccRecipients,
//Bcc = bccRecipients
};
await Email.ComposeAsync(message);
}
catch (FeatureNotSupportedException fbsEx)
{
// Email is not supported on this device
}
catch (Exception ex)
{
// Some other exception occurred
}
}
}

Diferencias entre plataformas


Android
iOS
UWP
No todos los clientes de correo electrónico para Android admiten Html . Puesto que no hay ninguna manera de
detectar este problema, recomendamos usar PlainText para enviar correos electrónicos.

Datos adjuntos

El envío de archivos por correo electrónico está disponible como versión preliminar experimental en
Xamarin.Essentials versión 1.1.0. Esta característica permite que una aplicación envíe archivos por correo
electrónico a través de clientes de correo electrónico en el dispositivo. Para habilitar esta característica, establezca
la siguiente propiedad en el código de inicio de la aplicación:

ExperimentalFeatures.Enable(ExperimentalFeatures.EmailAttachments);

Una vez que se haya habilitado la característica, se puede enviar por correo electrónico cualquier archivo.
Xamarin.Essentials detectará automáticamente el tipo de archivo (MIME ) y solicitará que el archivo se agregue
como datos adjuntos. Cada cliente de correo electrónico es diferente y podría admitir únicamente algunas
extensiones de archivo o ninguna en absoluto.
A continuación se muestra un ejemplo en el que se escribe texto en el disco y se agrega como datos adjuntos a un
correo electrónico:

var message = new EmailMessage


{
Subject = "Hello",
Body = "World",
};

var fn = "Attachment.txt";
var file = Path.Combine(FileSystem.CacheDirectory, fn);
File.WriteAllText(file, "Hello World");

message.Attachments.Add(new EmailAttachment(file));

await Email.ComposeAsync(message);

API
Código fuente de Email
Documentación de API para Email
Xamarin.Essentials: Aplicaciones auxiliares de sistema
de archivos
11/07/2019 • 3 minutes to read • Edit Online

La clase FileSystem contiene una serie de aplicaciones auxiliares para buscar la caché y los directorios de datos de
la aplicación y abrir archivos dentro del paquete de aplicación.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de aplicaciones auxiliares de sistema de archivos


Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Para obtener el directorio de la aplicación para almacenar datos en caché. Los datos en caché se pueden usar
para cualquier dato que se tenga que conservar más tiempo que los datos temporales, pero no deben ser datos
necesarios para que el funcionamiento sea correcto.

var cacheDir = FileSystem.CacheDirectory;

Para obtener el directorio de nivel superior de la aplicación para todos los archivos que no son archivos de datos
de usuario. Se realiza una copia de seguridad de estos archivos con el marco de sincronización del sistema
operativo. Vea Detalles de implementación de la plataforma a continuación.

var mainDir = FileSystem.AppDataDirectory;

Para abrir un archivo que se incluye en el paquete de aplicación:

using (var stream = await FileSystem.OpenAppPackageFileAsync(templateFileName))


{
using (var reader = new StreamReader(stream))
{
var fileContents = await reader.ReadToEndAsync();
}
}

Detalles de implementación de la plataforma


Android
iOS
UWP
CacheDirectory: devuelve el CacheDir del contexto actual.
AppDataDirectory: devuelve el FilesDir del contexto actual y se realiza una copia de seguridad mediante
Copia de seguridad automática a partir de la API 23 y versiones posteriores.
Agregue un archivo a la carpeta Activos del proyecto de Android y marque la acción de compilación como
AndroidAsset para usarla con OpenAppPackageFileAsync .

API
Código fuente de las aplicaciones auxiliares de sistema de archivos
Documentación de API para FileSystem
Xamarin.Essentials: Linterna
11/07/2019 • 3 minutes to read • Edit Online

La clase Flashlight tiene la capacidad de activar o desactivar el flash de la cámara del dispositivo y convertirlo en
una linterna.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.
Para acceder a la funcionalidad de Flashlight, se requiere la siguiente configuración específica para la plataforma.
Android
iOS
UWP
Los permisos Flashlight y Camera son obligatorios y se deben configurar en el proyecto de Android. Se puede
agregar de las siguientes maneras:
Abra el archivo AssemblyInfo.cs de la carpeta Propiedades y agregue lo siguiente:

[assembly: UsesPermission(Android.Manifest.Permission.Flashlight)]
[assembly: UsesPermission(Android.Manifest.Permission.Camera)]

O BIEN, actualice el manifiesto de Android:


Abra el archivo AndroidManifest.xml de la carpeta Propiedades y agregue lo siguiente dentro del nodo
manifest.

<uses-permission android:name="android.permission.FLASHLIGHT" />


<uses-permission android:name="android.permission.CAMERA" />

O haga clic con el botón derecho en el proyecto de Android y abra las propiedades del proyecto. En Manifiesto
de Android, busque el área Permisos necesarios: y active los permisos FLASHLIGHT (Linterna) y CAMERA
(Cámara). Esto actualizará automáticamente el archivo AndroidManifest.xml.
Mediante la adición de estos permisos Google Play filtrará automáticamente los dispositivos sin necesidad de
hardware específico. Para solucionarlo, agregue lo siguiente al archivo AssemblyInfo.cs del proyecto de Android:

[assembly: UsesFeature("android.hardware.camera", Required = false)]


[assembly: UsesFeature("android.hardware.camera.autofocus", Required = false)]

Uso de Flashlight
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La linterna se puede activar y desactivar a través de los métodos TurnOnAsync y TurnOffAsync :


try
{
// Turn On
await Flashlight.TurnOnAsync();

// Turn Off
await Flashlight.TurnOffAsync();
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
}
catch (PermissionException pEx)
{
// Handle permission exception
}
catch (Exception ex)
{
// Unable to turn on/off flashlight
}

Detalles de implementación de la plataforma


Android
iOS
UWP
La clase Flashlight se ha optimizado en función del sistema operativo del dispositivo.
Nivel de API 23 y superior
En los niveles de API más recientes, se usa el Modo Linterna para activar o desactivar la unidad de flash del
dispositivo.
Nivel de API 22 e inferior
Se crea una textura de la superficie de cámara para activar o desactivar el FlashMode de la unidad de la cámara.

API
Código fuente de Flashlight
Documentación de API para Flashlight
Xamarin.Essentials: Codificación geográfica
11/07/2019 • 3 minutes to read • Edit Online

La clase Geocoding proporciona API para geocodificar una marca de posición en coordenadas de posición e
invertir las coordenadas de código geográfico a una marca de posición.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.
Para acceder a la funcionalidad de Geocoding, se requiere la siguiente configuración específica para la
plataforma.
Android
iOS
UWP
No se requiere configuración adicional.

Uso de Geocoding
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Obtención de coordenadas de ubicación para una dirección:

try
{
var address = "Microsoft Building 25 Redmond WA USA";
var locations = await Geocoding.GetLocationsAsync(address);

var location = locations?.FirstOrDefault();


if (location != null)
{
Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude:
{location.Altitude}");
}
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Handle exception that may have occurred in geocoding
}

La altitud no siempre está disponible. Si no lo está, es posible que la propiedad Altitude sea null o que el valor
sea cero. Si lo está, el valor se expresa en metros sobre el nivel del mar.

Uso la geocodificación inversa


La geocodificación inversa es el proceso de obtener marcas de posición para un conjunto de coordenadas
existente:

try
{
var lat = 47.673988;
var lon = -122.121513;

var placemarks = await Geocoding.GetPlacemarksAsync(lat, lon);

var placemark = placemarks?.FirstOrDefault();


if (placemark != null)
{
var geocodeAddress =
$"AdminArea: {placemark.AdminArea}\n" +
$"CountryCode: {placemark.CountryCode}\n" +
$"CountryName: {placemark.CountryName}\n" +
$"FeatureName: {placemark.FeatureName}\n" +
$"Locality: {placemark.Locality}\n" +
$"PostalCode: {placemark.PostalCode}\n" +
$"SubAdminArea: {placemark.SubAdminArea}\n" +
$"SubLocality: {placemark.SubLocality}\n" +
$"SubThoroughfare: {placemark.SubThoroughfare}\n" +
$"Thoroughfare: {placemark.Thoroughfare}\n";

Console.WriteLine(geocodeAddress);
}
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Handle exception that may have occurred in geocoding
}

Distancia entre dos ubicaciones


Las clases Location y LocationExtensions definen métodos para calcular la distancia entre dos ubicaciones.
Consulte el artículo Xamarin.Essentials: Geolocalización para ver un ejemplo.

API
Código fuente de Geocoding
Documentación de API para Geocoding

Vídeo relacionado

Encuentre más vídeos de Xamarin en Channel 9 y YouTube.


Xamarin.Essentials: Geolocalización
11/07/2019 • 7 minutes to read • Edit Online

La clase Geolocation proporciona las API para recuperar las coordenadas de geolocalización actuales del
dispositivo.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.
Para acceder a la funcionalidad de Geolocation, se requiere la siguiente configuración específica para la
plataforma:
Android
iOS
UWP
Los permisos Coarse y Fine Location son requeridos y se deben configurar en el proyecto de Android. Además, si
la aplicación tiene como destino Android 5.0 (nivel de API 21) o versiones posteriores, debe declarar que la
aplicación usa las características de hardware en el archivo de manifiesto. Se puede agregar de las siguientes
maneras:
Abra el archivo AssemblyInfo.cs de la carpeta Propiedades y agregue lo siguiente:

[assembly: UsesPermission(Android.Manifest.Permission.AccessCoarseLocation)]
[assembly: UsesPermission(Android.Manifest.Permission.AccessFineLocation)]
[assembly: UsesFeature("android.hardware.location", Required = false)]
[assembly: UsesFeature("android.hardware.location.gps", Required = false)]
[assembly: UsesFeature("android.hardware.location.network", Required = false)]

O bien, actualice el manifiesto de Android:


Abra el archivo AndroidManifest.xml de la carpeta Propiedades y agregue lo siguiente dentro del nodo
manifest:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />


<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />

O bien, haga clic con el botón derecho en el proyecto de Android y abra las propiedades del proyecto. En
Manifiesto de Android, busque el área Permisos requeridos: y active los permisos
ACCESS_COARSE_LOCATION y ACCESS_FINE_LOCATION. Esto actualizará automáticamente el archivo
AndroidManifest.xml.

Uso de Geolocation
Agregue una referencia a Xamarin.Essentials en su clase:
using Xamarin.Essentials;

La API Geolocation también le pedirá permisos al usuario cuando sea necesario.


Puede obtener la última ubicación conocida del dispositivo mediante una llamada al método
GetLastKnownLocationAsync . A menudo, esto es más rápido que hacer una consulta completa, pero puede ser
menos preciso y probablemente devuelva null si no existe ninguna ubicación en caché.

try
{
var location = await Geolocation.GetLastKnownLocationAsync();

if (location != null)
{
Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude:
{location.Altitude}");
}
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
}
catch (PermissionException pEx)
{
// Handle permission exception
}
catch (Exception ex)
{
// Unable to get location
}

La altitud no siempre está disponible. Si no lo está, es posible que la propiedad Altitude sea null o que el valor
sea cero. Si lo está, el valor se expresa en metros sobre el nivel del mar.
Para consultar las coordenadas de ubicación del dispositivo actual, se puede usar GetLocationAsync . Es mejor
pasar un valor GeolocationRequest completo y CancellationToken , ya que se puede tardar algún tiempo en
obtener la ubicación del dispositivo.
try
{
var request = new GeolocationRequest(GeolocationAccuracy.Medium);
var location = await Geolocation.GetLocationAsync(request);

if (location != null)
{
Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude:
{location.Altitude}");
}
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
}
catch (PermissionException pEx)
{
// Handle permission exception
}
catch (Exception ex)
{
// Unable to get location
}

Precisión de la ubicación geográfica


En la tabla siguiente se describe la precisión por plataforma:
Mínima
PLATAFORMA DISTANCIA (EN METROS)

Android 500

iOS 3000

UWP 1000 - 5000

Bajo
PLATAFORMA DISTANCIA (EN METROS)

Android 500

iOS 1000

UWP 300 - 3000

Media (valor predeterminado )


PLATAFORMA DISTANCIA (EN METROS)

Android 100 - 500


PLATAFORMA DISTANCIA (EN METROS)

iOS 100

UWP 30-500

Alto
PLATAFORMA DISTANCIA (EN METROS)

Android 0 - 100

iOS 10

UWP <= 10

Óptima
PLATAFORMA DISTANCIA (EN METROS)

Android 0 - 100

iOS ~0

UWP <= 10

Detección de ubicaciones ficticias


Algunos dispositivos pueden devolver una ubicación ficticia desde el proveedor o desde una aplicación que
proporciona ubicaciones ficticias. Puede detectarlo usando IsFromMockProvider en cualquier Location .

var request = new GeolocationRequest(GeolocationAccuracy.Medium);


var location = await Geolocation.GetLocationAsync(request);

if (location != null)
{
if(location.IsFromMockProvider)
{
// location is from a mock provider
}
}

Distancia entre dos ubicaciones


Las clases Location y LocationExtensions definen métodos CalculateDistance que permiten calcular la distancia
entre dos ubicaciones geográficas. Esta distancia calculada no tiene en cuenta las carreteras ni otros caminos, y
simplemente es la distancia más corta entre los dos puntos a lo largo de la superficie de la Tierra, lo que también
se conoce como distancia ortodrómica o coloquialmente, "distancia a vuelo de pájaro".
Por ejemplo:

Location boston = new Location(42.358056, -71.063611);


Location sanFrancisco = new Location(37.783333, -122.416667);
double miles = Location.CalculateDistance(boston, sanFrancisco, DistanceUnits.Miles);
El constructor Location tiene argumentos de latitud y longitud en ese orden. Los valores de latitud positivos están
al norte del Ecuador, y los valores de longitud positivos están al este del meridiano de Greenwich. Use el
argumento final CalculateDistance para especificar millas o kilómetros. La clase UnitConverters también define
los métodos KilometersToMiles y MilesToKilometers para la conversión entre las dos unidades.

API
Código fuente de Geolocation
Documentación de API para Geolocation
Xamarin.Essentials: Giroscopio
11/07/2019 • 2 minutes to read • Edit Online

La clase Gyroscope permite supervisar el sensor de giroscopio del dispositivo, que es la rotación sobre los tres
ejes principales del dispositivo.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Gyroscope
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La funcionalidad Gyroscope funciona mediante una llamada a los métodos Start y Stop para realizar escuchas
de los cambios realizados en el giroscopio. Los cambios se enviarán a través del evento ReadingChanged en rad/s.
A continuación le mostramos un ejemplo de uso:
public class GyroscopeTest
{
// Set speed delay for monitoring changes.
SensorSpeed speed = SensorSpeed.UI;

public GyroscopeTest()
{
// Register for reading changes.
Gyroscope.ReadingChanged += Gyroscope_ReadingChanged;
}

void Gyroscope_ReadingChanged(object sender, GyroscopeChangedEventArgs e)


{
var data = e.Reading;
// Process Angular Velocity X, Y, and Z reported in rad/s
Console.WriteLine($"Reading: X: {data.AngularVelocity.X}, Y: {data.AngularVelocity.Y}, Z:
{data.AngularVelocity.Z}");
}

public void ToggleGyroscope()


{
try
{
if (Gyroscope.IsMonitoring)
Gyroscope.Stop();
else
Gyroscope.Start(speed);
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}

Velocidad de sensor
Más rápido: obtener los datos del sensor tan rápido como sea posible (no se garantiza la devolución en el
subproceso de interfaz de usuario).
Juego: velocidad adecuada para juegos (no se garantiza la devolución en el subproceso de interfaz de usuario).
Normal: velocidad predeterminada adecuada para los cambios de orientación de pantalla.
Interfaz de usuario: velocidad adecuada para la interfaz de usuario general.
Si no se garantiza la ejecución del controlador de eventos en el subproceso de interfaz de usuario y si el
controlador de eventos necesita tener acceso a elementos de la interfaz de usuario, use el método
MainThread.BeginInvokeOnMainThread para ejecutar ese código en el subproceso de interfaz de usuario.

API
Código fuente de Gyroscope
Documentación de API para Gyroscope
Xamarin.Essentials: Selector
11/07/2019 • 2 minutes to read • Edit Online

La clase Launcher permite que una aplicación abra un URI por el sistema. A menudo se usa al vincular en
profundidad en los esquemas de URI personalizados de otra aplicación. Si quiere abrir el explorador en un sitio
web, debe hacer referencia a la API Browser.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Launcher
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Para usar la funcionalidad Launcher, llame al método OpenAsync y pase un string o Uri para abrirla. Si quiere,
el método CanOpenAsync se puede usar para comprobar si el esquema de URI se puede administrar desde una
aplicación del dispositivo.

public class LauncherTest


{
public async Task OpenRideShareAsync()
{
var supportsUri = await Launcher.CanOpenAsync("lyft://");
if (supportsUri)
await Launcher.OpenAsync("lyft://ridetype?id=lyft_line");
}
}

Diferencias entre plataformas


Android
iOS
UWP
La tarea devuelta desde CanOpenAsync se completa de inmediato.

API
Código fuente de Launcher
Documentación de API para Launcher
Xamarin.Essentials: Magnetómetro
11/07/2019 • 2 minutes to read • Edit Online

La clase Magnetometer permite supervisar el sensor de magnetómetro del dispositivo, que indica la orientación
del dispositivo con respecto al campo magnético de la Tierra.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Magnetometer
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La funcionalidad Magnetometer funciona mediante una llamada a los métodos Start y Stop para realizar
escuchas de los cambios en el magnetómetro. Los cambios se enviarán a través del evento ReadingChanged . A
continuación le mostramos un ejemplo de uso:
public class MagnetometerTest
{
// Set speed delay for monitoring changes.
SensorSpeed speed = SensorSpeed.UI;

public MagnetometerTest()
{
// Register for reading changes.
Magnetometer.ReadingChanged += Magnetometer_ReadingChanged;
}

void Magnetometer_ReadingChanged(object sender, MagnetometerChangedEventArgs e)


{
var data = e.Reading;
// Process MagneticField X, Y, and Z
Console.WriteLine($"Reading: X: {data.MagneticField.X}, Y: {data.MagneticField.Y}, Z:
{data.MagneticField.Z}");
}

public void ToggleMagnetometer()


{
try
{
if (Magnetometer.IsMonitoring)
Magnetometer.Stop();
else
Magnetometer.Start(speed);
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}

Todos los datos se devuelven en µ (microteslas).

Velocidad de sensor
Más rápido: obtener los datos del sensor tan rápido como sea posible (no se garantiza la devolución en el
subproceso de interfaz de usuario).
Juego: velocidad adecuada para juegos (no se garantiza la devolución en el subproceso de interfaz de usuario).
Normal: velocidad predeterminada adecuada para los cambios de orientación de pantalla.
Interfaz de usuario: velocidad adecuada para la interfaz de usuario general.
Si no se garantiza la ejecución del controlador de eventos en el subproceso de interfaz de usuario y si el
controlador de eventos necesita tener acceso a elementos de la interfaz de usuario, use el método
MainThread.BeginInvokeOnMainThread para ejecutar ese código en el subproceso de interfaz de usuario.

API
Código fuente de Magnetometer
Documentación de API para Magnetometer
Xamarin.Essentials: MainThread
11/07/2019 • 5 minutes to read • Edit Online

La clase MainThread permite que las aplicaciones ejecuten código en el subproceso de ejecución principal y
determinen si un bloque de código determinado se ejecuta actualmente en el subproceso principal.

Fondo
La mayoría de los sistemas operativos, incluidos iOS, Android y Plataforma universal de Windows, usan un
modelo de un único subproceso para el código que participa en la interfaz de usuario. Este modelo resulta
necesario para serializar de manera adecuada los eventos de la interfaz de usuario, incluidas pulsaciones de
teclas y entradas táctiles. Con frecuencia, este subproceso se denomina el subproceso principal, el subproceso
de interfaz de usuario o el subproceso de UI. La desventaja que presenta este modelo es que todo el código
que accede a los elementos de la interfaz de usuario se deben ejecutar en el subproceso principal de la
aplicación.
Algunas veces, las aplicaciones deben usar eventos que llamar al controlador de eventos en un subproceso de
ejecución secundario. (Es posible que las clases Accelerometer , Compass , Gyroscope , Magnetometer y
OrientationSensor de Xamarin.Essentials devuelvan información en un subproceso secundario cuando se usa
con velocidades más rápidas). Si el controlador de eventos debe acceder a los elementos de la interfaz de
usuario, debe ejecutar ese código en el subproceso principal. La clase MainThread permite que la aplicación
ejecute este código en el subproceso principal.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de
que la biblioteca está correctamente instalada y configurada en los proyectos.

Ejecución de código en el subproceso principal


Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Para ejecutar código en el subproceso principal, llame al método MainThread.BeginInvokeOnMainThread estático.


El argumento es un objeto Action , que no es más que un método sin argumentos y sin valor devuelto:

MainThread.BeginInvokeOnMainThread(() =>
{
// Code to run on the main thread
});

También es posible definir un método independiente para el código que se debe ejecutar en el subproceso
principal:

void MyMainThreadCode()
{
// Code to run on the main thread
}
Luego, para ejecutar este método en el subproceso principal, haga referencia a él en el método
BeginInvokeOnMainThread :

MainThread.BeginInvokeOnMainThread(MyMainThreadCode);

NOTE
Xamarin.Forms tiene un método llamado Device.BeginInvokeOnMainThread(Action) que hace lo mismo que
MainThread.BeginInvokeOnMainThread(Action) . Si bien puede usar cualquier método en una aplicación de
Xamarin.Forms, considere si el código de llamada necesita o no una dependencia de Xamarin.Forms. Si no es así,
MainThread.BeginInvokeOnMainThread(Action) probablemente sea una mejor opción.

Determinación de si el código se ejecuta en el subproceso principal


La clase MainThread también permite que una aplicación determine si un bloque de código determinado se
ejecuta en el subproceso principal. La propiedad IsMainThread devuelve true si el código que llama a la
propiedad se ejecuta en el subproceso principal. Un programa puede usar esta propiedad para ejecutar código
diferente para el subproceso principal o secundario:

if (MainThread.IsMainThread)
{
// Code to run if this is the main thread
}
else
{
// Code to run if this is a secondary thread
}

Tal vez se pregunte si debe comprobar que el código se esté ejecutando en un subproceso secundario antes
de llamar a BeginInvokeOnMainThread , por ejemplo, de esta manera:

if (MainThread.IsMainThread)
{
MyMainThreadCode();
}
else
{
MainThread.BeginInvokeOnMainThread(MyMainThreadCode);
}

Quizás sospeche que esta comprobación puede mejorar el rendimiento si el bloque de código ya se ejecuta en
el subproceso principal.
Sin embargo, esta comprobación no es necesaria. Las implementaciones de plataforma de los
BeginInvokeOnMainThread mismos comprueban si la llamada se realiza en el subproceso principal. La
penalización de rendimiento si llama a BeginInvokeOnMainThread cuando no es realmente necesario es muy
pequeña.

API
Código fuente de MainThread
Documentación de API de MainThread
Xamarin.Essentials: Asignación
11/07/2019 • 3 minutes to read • Edit Online

La clase Map permite que una aplicación abra la aplicación de mapas instalada en una ubicación o marca de
posición específica.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Map
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Map funciona mediante una llamada al método OpenAsync con Location o Placemark abierto con
MapLaunchOptions opcional.

public class MapTest


{
public async Task NavigateToBuilding25()
{
var location = new Location(47.645160, -122.1306032);
var options = new MapLaunchOptions { Name = "Microsoft Building 25" };

await Map.OpenAsync(location, options);


}
}

Cuando se abre con Placemark , se requiere la siguiente información:


CountryName
AdminArea
Thoroughfare
Locality
public class MapTest
{
public async Task NavigateToBuilding25()
{
var placemark = new Placemark
{
CountryName = "United States",
AdminArea = "WA",
Thoroughfare = "Microsoft Building 25",
Locality = "Redmond"
};
var options = new MapLaunchOptions { Name = "Microsoft Building 25" };

await Map.OpenAsync(placemark, options);


}
}

Métodos de extensión.
Si ya tiene una referencia a Location o a Placemark , puede usar el método de extensión integrado OpenMapAsync
con MapLaunchOptions opcional:

public class MapTest


{
public async Task OpenPlacemarkOnMap(Placemark placemark)
{
await placemark.OpenMapAsync();
}
}

Modo de instrucciones
Si llama a OpenMapAsync sin MapLaunchOptions , el mapa se iniciará en la ubicación especificada. Si quiere, puede
hacer que se calcule una ruta de navegación desde la posición actual del dispositivo. Para ello, establezca
NavigationMode en MapLaunchOptions :

public class MapTest


{
public async Task NavigateToBuilding25()
{
var location = new Location(47.645160, -122.1306032);
var options = new MapLaunchOptions { NavigationMode = NavigationMode.Driving };

await Map.OpenAsync(location, options);


}
}

Diferencias entre plataformas


Android
iOS
UWP
NavigationMode admite Bicycling (Bicicleta), Driving (Automóvil) y Walking (A pie).

Detalles de implementación de la plataforma


Android
iOS
UWP
Android usa el esquema geo: de URI para iniciar la aplicación de mapas en el dispositivo. Esto podría pedirle al
usuario que seleccione de una aplicación existente que admite este esquema de URI. Xamarin.Essentials se probó
con Google Maps, que admite este esquema.

API
Código fuente de Map
Documentación de API para Map

Vídeo relacionado
Encuentre más vídeos de Xamarin en Channel 9 y YouTube.
Xamarin.Essentials: Explorador
11/07/2019 • 2 minutes to read • Edit Online

La clase Browser permite que una aplicación abra un vínculo web en el explorador optimizado preferido del
sistema o en el explorador externo.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Browser
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La funcionalidad Browser funciona mediante una llamada al método OpenAsync con Uri y BrowserLaunchMode .

public class BrowserTest


{
public async Task<bool> OpenBrowser(Uri uri)
{
return await Browser.OpenAsync(uri, BrowserLaunchMode.SystemPreferred);
}
}

Este método devuelve un valor después de que el usuario inicie el explorador, a veces sin que tenga que llegar a
cerrarlo. El resultado bool indica si el inicio ha sido correcto o no.

Personalización
Al usar el explorador preferido por el sistema, tiene disponibles varias opciones de personalización para iOS y
Android, por ejemplo, TitleMode (solo en Android), preferencias para las opciones de color para la Toolbar (en
iOS y Android) y Controls que se muestran (solo en iOS ).
Estas opciones se especifican usando BrowserLaunchOptions al llamar a OpenAsync .

await Browser.OpenAsync(uri, new BrowserLaunchOptions


{
LaunchMode = BrowserLaunchMode.SystemPreferred,
TitleMode = BrowserTitleMode.Show,
PreferredToolbarColor = Color.AliceBlue,
PreferredControlColor = Color.Violet
});
Detalles de implementación de la plataforma
Android
iOS
UWP
El modo de inicio determina cómo se inicia el explorador:

Preferencia del sistema


Se intentará usar las pestañas personalizadas de Chrome para cargar el URI y mantener el reconocimiento de la
navegación.

Externo
Se usará Intent para solicitar que se abra el URI a través del explorador normal del sistema.

API
Código fuente de Browser
Documentación de API para Browser

Vídeo relacionado

Encuentre más vídeos de Xamarin en Channel 9 y YouTube.


Xamarin.Essentials: OrientationSensor
11/07/2019 • 7 minutes to read • Edit Online

La clase OrientationSensor permite supervisar la orientación de un dispositivo en un espacio tridimensional.

NOTE
Esta clase se usa para determinar la orientación de un dispositivo en un espacio tridimensional. Si tiene que determinar si la
visualización de vídeo del dispositivo está en modo vertical u horizontal, use la propiedad Orientation del objeto
ScreenMetrics disponible en la clase DeviceDisplay .

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de OrientationSensor
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

OrientationSensor se habilita mediante una llamada al método Start para supervisar los cambios en la
orientación del dispositivo y se deshabilita al llamar al método Stop . Los cambios se enviarán a través del evento
ReadingChanged . Este es un uso de ejemplo:
public class OrientationSensorTest
{
// Set speed delay for monitoring changes.
SensorSpeed speed = SensorSpeed.UI;

public OrientationSensorTest()
{
// Register for reading changes, be sure to unsubscribe when finished
OrientationSensor.ReadingChanged += OrientationSensor_ReadingChanged;
}

void OrientationSensor_ReadingChanged(object sender, OrientationSensorChangedEventArgs e)


{
var data = e.Reading;
Console.WriteLine($"Reading: X: {data.Orientation.X}, Y: {data.Orientation.Y}, Z:
{data.Orientation.Z}, W: {data.Orientation.W}");
// Process Orientation quaternion (X, Y, Z, and W)
}

public void ToggleOrientationSensor()


{
try
{
if (OrientationSensor.IsMonitoring)
OrientationSensor.Stop();
else
OrientationSensor.Start(speed);
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}

Las lecturas de OrientationSensor se informan como un Quaternion que describe la orientación del dispositivo
en función de dos sistemas de coordenadas tridimensionales:
El dispositivo (generalmente un teléfono o tableta) tiene un sistema de coordenadas tridimensional con los
siguientes ejes:
El eje X positivo apunta a la derecha de la visualización en modo vertical.
El eje Y positivo apunta a la parte de arriba del dispositivo en modo horizontal.
El eje Z positivo apunta fuera de la pantalla.
El sistema de coordenadas tridimensional de la Tierra tiene estos ejes:
El eje X positivo es tangente a la superficie de la Tierra y apunta al este.
El eje Y positivo también es tangente a la superficie de la Tierra y apunta al norte.
El eje Z positivo es perpendicular a la superficie de la Tierra y apunta hacia arriba.
Quaternion describe la rotación del sistema de coordenadas del dispositivo en relación con el sistema de
coordenadas de la Tierra.
Un valor Quaternion está estrechamente relacionado con la rotación alrededor de un eje. Si un eje de rotación es
el vector normalizado (ax, ay, az) y el ángulo de rotación es Θ, los componentes (X, Y, Z, W ) del cuaternión son:
(ax·sin(Θ/2), ay·sin(Θ/2), az·sin(Θ/2), cos(Θ/2))
x y z

Estos son los sistemas de coordenadas de la derecha por lo que, con el pulgar de la mano derecha apuntando en
la dirección positiva del eje de rotación, la curva de los dedos indica la dirección de la rotación de los ángulos
positivos.
Ejemplos:
Cuando el dispositivo está pantalla arriba sobre una mesa, con la parte superior del mismo (en el modo
vertical) apuntando al norte, los dos sistemas de coordenadas están alineados. El valor Quaternion
representa el cuaternión de identidad (0, 0, 0, 1). Todas las rotaciones se pueden analizar en relación con
esta posición.
Cuando el dispositivo está pantalla arriba sobre una mesa y la parte superior del mismo (en el modo
vertical) apunta al oeste, el valor de Quaternion es (0, 0, 0.707, 0.707). El dispositivo se giró 90 grados
alrededor del eje Z de la Tierra.
Cuando el dispositivo se sostiene de manera vertical, con la parte superior (en el modo vertical) apuntando
al cielo y la parte posterior orientada al norte, es porque el dispositivo se giró 90 grados alrededor del eje
X. El valor de Quaternion es (0.707, 0, 0, 0.707).
Si el dispositivo se coloca de manera tal que el borde izquierdo esté sobre una mesa y la parte superior
apunte al norte, es porque el dispositivo se giró –90 grados alrededor del eje Y (o 90 grados alrededor del
eje Y negativo). El valor de Quaternion es (0, -0.707, 0, 0.707).

Velocidad de sensor
Más rápido: obtener los datos del sensor tan rápido como sea posible (no se garantiza la devolución en el
subproceso de interfaz de usuario).
Juego: velocidad adecuada para juegos (no se garantiza la devolución en el subproceso de interfaz de usuario).
Normal: velocidad predeterminada adecuada para los cambios de orientación de pantalla.
Interfaz de usuario: velocidad adecuada para la interfaz de usuario general.
Si no se garantiza la ejecución del controlador de eventos en el subproceso de interfaz de usuario y si el
controlador de eventos necesita tener acceso a elementos de la interfaz de usuario, use el método
MainThread.BeginInvokeOnMainThread para ejecutar ese código en el subproceso de interfaz de usuario.

API
Código fuente de OrientationSensor
Documentación de API de OrientationSensor
Xamarin.Essentials: Marcador telefónico
11/07/2019 • 2 minutes to read • Edit Online

La clase PhoneDialer permite que una aplicación abra un número de teléfono en el marcador telefónico.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso del marcador telefónico


Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La funcionalidad de marcador telefónico funciona mediante una llamada al método Open con un número de
teléfono con el que abrir el marcador. Cuando se solicita Open , la API intentará automáticamente dar formato al
número en función del código de país, si se especifica.

public class PhoneDialerTest


{
public void PlacePhoneCall(string number)
{
try
{
PhoneDialer.Open(number);
}
catch (ArgumentNullException anEx)
{
// Number was null or white space
}
catch (FeatureNotSupportedException ex)
{
// Phone Dialer is not supported on this device.
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}

API
Código fuente del marcador telefónico
Documentación de API para PhoneDialer
Xamarin.Essentials: extensiones de plataforma
11/07/2019 • 2 minutes to read • Edit Online

Xamarin.Essentials ofrece varios métodos de extensión de plataforma al trabajar con tipos de plataforma como
Rect, Size y Point. Esto significa que puede convertir la versión System de estos tipos a tipos específicos de iOS,
Android y UWP.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de las extensiones de plataforma


Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Todas las extensiones de plataforma solo se pueden llamar desde proyectos UWP, iOS o Android.
Punto

var system = new System.Drawing.Point(x, y);

// Convert to CoreGraphics.CGPoint, Android.Graphics.Point, and Windows.Foundation.Point


var platform = system.ToPlatformSize();

// Back to System.Drawing.Size
var system2 = platform.ToSystemSize();

Tamaño

var system = new System.Drawing.Size(width, height);

// Convert to CoreGraphics.CGSize, Android.Util.Size, and Windows.Foundation.Size


var platform = system.ToPlatformSize();

// Back to System.Drawing.Size
var system2 = platform.ToSystemSize();

Rectángulo

var system = new System.Drawing.Rectangle(x, y, width, height);

// Convert to CoreGraphics.CGRect, Android.Graphics.Rect, and Windows.Foundation.Rect


var platform = system.ToPlatformSize();

// Back to System.Drawing.Size
var system2 = platform.ToSystemSize();

API
Código fuente de los convertidores
Documentación sobre la API de los convertidores de puntos
Documentación sobre la API de los convertidores de rectángulos
Documentación sobre la API de los convertidores de tamaño
Xamarin.Essentials: Preferencias
11/07/2019 • 4 minutes to read • Edit Online

La clase Preferences ayuda a almacenar las preferencias de la aplicación en un almacén de clave y valor.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Preferences
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Para guardar un valor para una clave determinada en las preferencias:

Preferences.Set("my_key", "my_value");

Para recuperar un valor de las preferencias o un valor predeterminado si no se establece:

var myValue = Preferences.Get("my_key", "default_value");

Para quitar la clave de las preferencias:

Preferences.Remove("my_key");

Para quitar todas las preferencias:

Preferences.Clear();

Además de estos métodos, cada una toma un valor sharedName opcional que se puede usar para crear
contenedores adicionales para la preferencia. Lea los detalles de implementación de la plataforma a continuación.

Tipos de datos admitidos


En Preferences se admiten los tipos de datos siguientes:
bool
double
int
float
long
string
DateTime
Detalles de implementación
Los valores de DateTime se almacenan en un formato binario de 64 bits (entero largo) mediante dos métodos
definidos por la clase DateTime : El método ToBinary se usa para codificar el valor DateTime , mientras que el
método FromBinary descodifica el valor. Vea la documentación de estos métodos para obtener los ajustes que se
podrían realizar en los valores descodificados cuando se almacena un valor DateTime que no es de hora universal
coordinada (UTC ).

Detalles de implementación de la plataforma


Android
iOS
UWP
Todos los datos se almacenan en Preferencias compartidas. Si no se especifica ningún sharedName , se usan las
preferencias compartidas predeterminadas; en caso contrario, el nombre se usa para obtener una preferencia
compartida privada con el nombre especificado.

Persistencia
Al desinstalar la aplicación se quitarán todas las Preferencias. Hay una excepción, para las aplicaciones que se
destinan y se ejecutan en Android 6.0 (nivel de API 23) o versiones posteriores en las que se usa Copia de
seguridad automática. Esta característica está activada de forma predeterminada y conserva los datos de
aplicación, incluidas las preferencias compartidas, que son las que usa la API Preferences. Se puede
deshabilitar si se sigue la documentación de Google.

Limitaciones
Cuando se almacena una cadena, esta API está pensada para almacenar pequeñas cantidades de texto. El
rendimiento puede ser inferior si se intenta usar para almacenar grandes cantidades de texto.

API
Código fuente de Preferences
Documentación de API para Preferences

Vídeo relacionado
Encuentre más vídeos de Xamarin en Channel 9 y YouTube.
Xamarin.Essentials: Almacenamiento seguro
11/07/2019 • 8 minutes to read • Edit Online

La clase SecureStorage ayuda a almacenar pares de clave-valor sencillos de manera segura.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.
Para acceder a la funcionalidad SecureStorage, se requiere la siguiente configuración específica para la
plataforma:
Android
iOS
UWP

TIP
Copia de seguridad para aplicaciones es una característica de Android 6.0 (nivel de API 23) y versiones posteriores que crea
copias de seguridad de los datos de aplicación del usuario (preferencias compartidas, archivos en el almacenamiento interno
de la aplicación y otros archivos específicos). Los datos se restauran cuando se reinstala o instala una aplicación en un
dispositivo nuevo. Esto puede afectar a SecureStorage , que utiliza las preferencias compartidas de las que se creó una
copia de seguridad y que no se pueden descifrar cuando se realiza la restauración. Xamarin.Essentials controla
automáticamente este caso al quitar la clave para que se pueda restablecer, pero puede dar un paso adicional si deshabilita
Copia de seguridad automática.

Habilitación o deshabilitación de copia de seguridad


Puede elegir deshabilitar Copia de seguridad automática para toda la aplicación al establecer el valor
android:allowBackup en false en el archivo AndroidManifest.xml . Este enfoque solo se recomienda si planea
restaurar los datos de otra manera.

<manifest ... >


...
<application android:allowBackup="false" ... >
...
</application>
</manifest>

Copia de seguridad selectiva


Es posible configurar Copia de seguridad automática para deshabilitar la copia de seguridad de contenido
específico. Puede crear un conjunto de reglas personalizadas para excluir los elementos SecureStore de la copia
de seguridad.
1. Establezca el atributo android:fullBackupContent en AndroidManifest.xml:

<application ...
android:fullBackupContent="@xml/auto_backup_rules">
</application>
2. Cree un archivo XML denominado auto_backup_rules.xml en el directorio Resources/xml con la acción
de compilación AndroidResource. Luego, establezca este contenido que incluye todas las preferencias
compartidas, excepto SecureStorage :

<?xml version="1.0" encoding="utf-8"?>


<full-backup-content>
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="${applicationId}.xamarinessentials.xml"/>
</full-backup-content>

Uso de Secure Storage


Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Para guardar un valor para una clave determinada en el almacenamiento seguro:

try
{
await SecureStorage.SetAsync("oauth_token", "secret-oauth-token-value");
}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on device.
}

Para recuperar un valor desde el almacenamiento seguro:

try
{
var oauthToken = await SecureStorage.GetAsync("oauth_token");
}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on device.
}

NOTE
Si no hay ningún valor asociado con la clave solicitada, GetAsync devolverá null .

Para quitar una clave específica, llame a:

SecureStorage.Remove("oauth_token");

Para quitar todas las claves, llame a:

SecureStorage.RemoveAll();

Detalles de implementación de la plataforma


Android
iOS
UWP
Android KeyStore se usa para almacenar la clave de cifrado con la que se cifra el valor antes de guardarlo en
Preferencias compartidas con un nombre de archivo [ID -PAQUETE -APLICACIÓN ].xamarinessentials. La clave
(no una clave criptográfica, la clave para el valor) usada en el archivo de preferencias compartido es un hash MD5
de la clave pasada a las API SecureStorage .

Nivel de API 23 y superior


En los niveles de API más nuevos, una clave AES se obtiene de Android KeyStore y se usa con una cifra
AES/GCM/NoPadding para cifrar el valor antes de que se almacene en el archivo de preferencias compartidas.

Nivel de API 22 e inferior


En los niveles de API anteriores, Android KeyStore solo admite el almacenamiento de claves RSA, que se usa con
una cifra RSA/ECB/PKCS1Padding para cifrar una clave AES (generada de manera aleatoria en tiempo de
ejecución) y se almacena en el archivo de preferencias compartidas en la clave SecureStorageKey, si todavía no se
ha generado una.
SecureStorage usa la API Preferences y sigue la misma persistencia de datos que se describe en la
documentación sobre Preferencias. Si se actualiza un dispositivo desde Nivel de API 22 o inferior a Nivel de API
23 y superior, se seguirá usando este tipo de cifrado a menos que la aplicación se desinstale o se llame a
RemoveAll.

Limitaciones
Esta API está pensada para almacenar pequeñas cantidades de texto. El rendimiento puede ser lento si intenta
usarla para almacenar grandes cantidades de texto.

API
Código fuente de SecureStorage
Documentación de API de SecureStorage

Vídeo relacionado

Encuentre más vídeos de Xamarin en Channel 9 y YouTube.


Xamarin.Essentials: Compartir
11/07/2019 • 2 minutes to read • Edit Online

La clase Share permite que una aplicación comparta datos como texto y vínculos web con otras aplicaciones del
dispositivo.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Share
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Share funciona mediante una llamada al método RequestAsync con una carga de solicitud de datos que incluye
información para compartir con otras aplicaciones. Se pueden combinar texto y Uri, cada plataforma controlara el
filtrado basado en el contenido.

public class ShareTest


{
public async Task ShareText(string text)
{
await Share.RequestAsync(new ShareTextRequest
{
Text = text,
Title = "Share Text"
});
}

public async Task ShareUri(string uri)


{
await Share.RequestAsync(new ShareTextRequest
{
Uri = uri,
Title = "Share Web Link"
});
}
}

Interfaz de usuario para compartir con una aplicación externa que aparece cuando se realiza la solicitud:
Diferencias entre plataformas
Android
iOS
UWP
La propiedad Subject se usa para el asunto deseado de un mensaje.

Archivos

El uso compartido de archivos está disponible como versión preliminar experimental en Xamarin.Essentials
versión 1.1.0. Esta característica permite que una aplicación comparta archivos con otras aplicaciones del
dispositivo. Para habilitar esta característica, establezca la siguiente propiedad en el código de inicio de la
aplicación:

ExperimentalFeatures.Enable(ExperimentalFeatures.ShareFileRequest);

Una vez que se haya habilitado la característica, se puede compartir cualquier archivo. Xamarin.Essentials
detectará automáticamente el tipo de archivo (MIME ) y solicitará el uso compartido. Cada plataforma podría
admitir únicamente determinadas extensiones de archivo.
A continuación se muestra un ejemplo en el que se escribe texto en el disco y se comparte con otras aplicaciones:

var fn = "Attachment.txt";
var file = Path.Combine(FileSystem.CacheDirectory, fn);
File.WriteAllText(file, "Hello World");

await Share.RequestAsync(new ShareFileRequest


{
Title = Title,
File = new ShareFile(file)
});
API
Código fuente de Share
Documentación de API para Share

Vídeo relacionado
Encuentre más vídeos de Xamarin en Channel 9 y YouTube.
Xamarin.Essentials: SMS
11/07/2019 • 2 minutes to read • Edit Online

La clase SMS permite que una aplicación abra la aplicación SMS predeterminada con un mensaje especificado
para enviar un destinatario.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de SMS
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La funcionalidad SMS funciona mediante una llamada al método ComposeAsync , un SmsMessage que contiene el
destinatario del mensaje y el cuerpo del mismo, ambos opcionales.

public class SmsTest


{
public async Task SendSms(string messageText, string recipient)
{
try
{
var message = new SmsMessage(messageText, new []{ recipient });
await Sms.ComposeAsync(message);
}
catch (FeatureNotSupportedException ex)
{
// Sms is not supported on this device.
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}

Si quiere, puede pasar varios destinatarios a un SmsMessage :


public class SmsTest
{
public async Task SendSms(string messageText, string[] recipients)
{
try
{
var message = new SmsMessage(messageText, recipients);
await Sms.ComposeAsync(message);
}
catch (FeatureNotSupportedException ex)
{
// Sms is not supported on this device.
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}

API
Código fuente de SMS
Documentación de API para SMS
Xamarin.Essentials: Texto a voz
11/07/2019 • 3 minutes to read • Edit Online

La clase TextToSpeech permite que una aplicación utilice los motores de texto a voz para responder a texto del
dispositivo y también para consultar los idiomas disponibles que el motor puede admitir.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Text-to-Speech
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Text-to-Speech funciona mediante una llamada al método SpeakAsync con parámetros opcionales y de texto y se
devuelve una vez que finaliza la declaración.

public async Task SpeakNowDefaultSettings()


{
await TextToSpeech.SpeakAsync("Hello World");

// This method will block until utterance finishes.


}

public void SpeakNowDefaultSettings2()


{
TextToSpeech.SpeakAsync("Hello World").ContinueWith((t) =>
{
// Logic that will run after utterance finishes.

}, TaskScheduler.FromCurrentSynchronizationContext());
}

Este método toma un elemento CancellationToken opcional para detener la declaración una vez que se inicia.
CancellationTokenSource cts;
public async Task SpeakNowDefaultSettings()
{
cts = new CancellationTokenSource();
await TextToSpeech.SpeakAsync("Hello World", cancelToken: cts.Token);

// This method will block until utterance finishes.


}

// Cancel speech if a cancellation token exists & hasn't been already requested.
public void CancelSpeech()
{
if (cts?.IsCancellationRequested ?? true)
return;

cts.Cancel();
}

Text-to-Speech pondrá automáticamente en la cola las solicitudes de voz del mismo subproceso.

bool isBusy = false;


public void SpeakMultiple()
{
isBusy = true;
Task.Run(async () =>
{
await TextToSpeech.SpeakAsync("Hello World 1");
await TextToSpeech.SpeakAsync("Hello World 2");
await TextToSpeech.SpeakAsync("Hello World 3");
isBusy = false;
});

// or you can query multiple without a Task:


Task.WhenAll(
TextToSpeech.SpeakAsync("Hello World 1"),
TextToSpeech.SpeakAsync("Hello World 2"),
TextToSpeech.SpeakAsync("Hello World 3"))
.ContinueWith((t) => { isBusy = false; }, TaskScheduler.FromCurrentSynchronizationContext());
}

Configuración de voz
Para más información sobre cómo se responde el audio, SpeechOptions permite ajustar el volumen, el tono y la
configuración regional.

public async Task SpeakNow()


{
var settings = new SpeechOptions()
{
Volume = .75f,
Pitch = 1.0f
};

await TextToSpeech.SpeakAsync("Hello World", settings);


}

Los siguientes son los valores compatibles para estos parámetros:

PARÁMETRO MÍNIMA MÁXIMO

Tono 0 2.0
PARÁMETRO MÍNIMA MÁXIMO

Volumen 0 1.0

Configuraciones regionales de voz


Cada plataforma admite distintas configuraciones regionales para responder texto en distintos idiomas y acentos.
Las plataformas tienen distintos códigos y formas de especificar la configuración regional, que es la razón por la
que Xamarin.Essentials proporciona una clase Locale multiplataforma y una manera de hacer consultas con
GetLocalesAsync .

public async Task SpeakNow()


{
var locales = await TextToSpeech.GetLocalesAsync();

// Grab the first locale


var locale = locales.FirstOrDefault();

var settings = new SpeechOptions()


{
Volume = .75f,
Pitch = 1.0f,
Locale = locale
};

await TextToSpeech.SpeakAsync("Hello World", settings);


}

Limitaciones
No se garantiza la cola de declaraciones si se llama a través de varios subprocesos.
La reproducción de audio en segundo plano no se admite de manera oficial.

API
Código fuente de TextToSpeech
Documentación de TextToSpeech API
Xamarin.Essentials: convertidores de unidades
11/07/2019 • 2 minutes to read • Edit Online

La clase UnitConverters proporciona varios convertidores de unidades para ayudar a los desarrolladores que
usan Xamarin.Essentials.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de convertidores de unidades


Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

Todos los convertidores de unidades están disponibles mediante el uso de la clase estática UnitConverters de
Xamarin.Essentials. Por ejemplo, puede convertir fácilmente grados Fahrenheit a Celsius.

var celcius = UnitConverters.FahrenheitToCelsius(32.0);

Esta es una lista de las conversiones disponibles:


FahrenheitToCelsius
CelsiusToFahrenheit
CelsiusToKelvin
KelvinToCelsius
MilesToMeters
MilesToMeters
KilometersToMiles
DegreesToRadians
RadiansToDegrees
DegreesPerSecondToRadiansPerSecond
RadiansPerSecondToDegreesPerSecond
DegreesPerSecondToHertz
DegreesPerSecondToHertz
HertzToDegreesPerSecond
HertzToDegreesPerSecond
KilopascalsToHectopascals
HectopascalsToKilopascals
KilopascalsToPascals
HectopascalsToKilopascals
AtmospheresToPascals
PascalsToAtmospheres
CoordinatesToMiles
CoordinatesToKilometers

API
Código fuente de los convertidores de unidades
Documentación sobre la API de los convertidores de unidades
Xamarin.Essentials: Seguimiento de versiones
11/07/2019 • 2 minutes to read • Edit Online

La clase VersionTracking permite comprobar los números de versión y compilación de las aplicaciones además
de ver información adicional, por ejemplo si es la primera vez que se ha iniciado la aplicación o para la versión
actual, obtener información de la compilación anterior y mucho más.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.

Uso de Seguimiento de versiones


Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La primera vez que use la clase VersionTracking, se iniciará el seguimiento de la versión actual. Solo se debe
llamar a Track al principio de la aplicación cada vez que se cargue para asegurarse de que se realiza el
seguimiento de la información de versión actual:

VersionTracking.Track();

Después de la llamada inicial a Track , se puede leer la información de la versión:


// First time ever launched application
var firstLaunch = VersionTracking.IsFirstLaunchEver;

// First time launching current version


var firstLaunchCurrent = VersionTracking.IsFirstLaunchForCurrentVersion;

// First time launching current build


var firstLaunchBuild = VersionTracking.IsFirstLaunchForCurrentBuild;

// Current app version (2.0.0)


var currentVersion = VersionTracking.CurrentVersion;

// Current build (2)


var currentBuild = VersionTracking.CurrentBuild;

// Previous app version (1.0.0)


var previousVersion = VersionTracking.PreviousVersion;

// Previous app build (1)


var previousBuild = VersionTracking.PreviousBuild;

// First version of app installed (1.0.0)


var firstVersion = VersionTracking.FirstInstalledVersion;

// First build of app installed (1)


var firstBuild = VersionTracking.FirstInstalledBuild;

// List of versions installed (1.0.0, 2.0.0)


var versionHistory = VersionTracking.VersionHistory;

// List of builds installed (1, 2)


var buildHistory = VersionTracking.BuildHistory;

Detalles de implementación de la plataforma


Toda la información de la versión se almacena mediante la API Preferences de Xamarin.Essentials y se almacena
con un nombre de archivo de [ID -DEL -PAQUETE -DE -LA -APLICACIÓN ].xamarinessentials.versiontracking,
y sigue la misma persistencia de datos que se describe en la documentación de Preferences.

API
Código fuente de VersionTracking
Documentación de API para VersionTracking

Vídeo relacionado

Encuentre más vídeos de Xamarin en Channel 9 y YouTube.


Xamarin.Essentials: Vibración
11/07/2019 • 2 minutes to read • Edit Online

La clase Vibration permite iniciar y detener la funcionalidad de vibración durante un período de tiempo
determinado.

Primeros pasos
Para empezar a usar esta API, lea la guía de introducción para Xamarin.Essentials con el fin de asegurarse de que
la biblioteca está correctamente instalada y configurada en los proyectos.
Para acceder a la funcionalidad de Vibration, se requiere la siguiente configuración específica para la plataforma.
Android
iOS
UWP
El permiso Vibrate (Vibrar) es necesario y se debe configurar en el proyecto de Android. Se puede agregar de las
siguientes maneras:
Abra el archivo AssemblyInfo.cs de la carpeta Propiedades y agregue lo siguiente:

[assembly: UsesPermission(Android.Manifest.Permission.Vibrate)]

O BIEN, actualice el manifiesto de Android:


Abra el archivo AndroidManifest.xml de la carpeta Propiedades y agregue lo siguiente dentro del nodo
manifest.

<uses-permission android:name="android.permission.VIBRATE" />

O haga clic con el botón derecho en el proyecto de Android y abra las propiedades del proyecto. En Manifiesto
de Android, busque el área Permisos requeridos: y active el permiso VIBRATE (Vibrar). Esto actualizará
automáticamente el archivo AndroidManifest.xml.

Uso de Vibration
Agregue una referencia a Xamarin.Essentials en su clase:

using Xamarin.Essentials;

La funcionalidad de Vibration se puede solicitar para un período de tiempo concreto o el valor predeterminado de
500 milisegundos.
try
{
// Use default vibration length
Vibration.Vibrate();

// Or use specified time


var duration = TimeSpan.FromSeconds(1);
Vibration.Vibrate(duration);
}
catch (FeatureNotSupportedException ex)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Other error has occurred.
}

Se puede solicitar la cancelación de la vibración del dispositivo con el método Cancel :

try
{
Vibration.Cancel();
}
catch (FeatureNotSupportedException ex)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Other error has occurred.
}

Diferencias entre plataformas


Android
iOS
UWP
No hay diferencias entre las plataformas.

API
Código fuente de Vibration
Documentación de API para Vibration
Xamarin.Essentials: Solución de problemas
11/07/2019 • 2 minutes to read • Edit Online

Error: Se detectó un conflicto de versión para


Xamarin.Android.Support.Compat
El error siguiente se puede producir al actualizar paquetes NuGet (o agregar un paquete) nuevo con un proyecto
de Xamarin.Forms que usa Xamarin.Essentials:

NU1107: Version conflict detected for Xamarin.Android.Support.Compat. Reference the package directly from the
project to resolve this issue.
MyApp -> Xamarin.Essentials 1.1.0 -> Xamarin.Android.Support.CustomTabs 28.0.0.1 ->
Xamarin.Android.Support.Compat (= 28.0.0.1)
MyApp -> Xamarin.Forms 3.1.0.583944 -> Xamarin.Android.Support.v4 25.4.0.2 -> Xamarin.Android.Support.Compat
(= 25.4.0.2).

El problema es que no coinciden las dependencias de los dos paquetes NuGet. Esto se puede resolver agregando
de forma manual una versión específica de la dependencia (en este caso Xamarin.Android.Support.Compat)
que puede admitir los dos.
Para ello, agregue de forma manual el paquete NuGet que es el origen del conflicto y use la lista Versión para
seleccionar una versión específica. Actualmente, la versión 28.0.0.1 del paquete NuGet
Xamarin.Android.Support.Compat & Xamarin.Android.Support.Core.Util resolverá este error.
Consulte esta entrada de blog para obtener más información y ver un vídeo sobre cómo resolver el problema.
Si surge algún problema o encuentra un error, notifíquelo en el repositorio de Xamarin.Essentials de GitHub.
Almacenamiento de datos local de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Archivos
El control de archivos con Xamarin.Forms puede lograrse mediante código en una biblioteca de .NET Standard o
con recursos incrustados. En este artículo se explica cómo realizar operaciones de control de código compartido en
una aplicación de Xamarin.Forms.

Bases de datos locales


Xamarin.Forms admite aplicaciones de base de datos que usan el motor de base de datos de SQLite, lo que
permite cargar y guardar los objetos en código compartido. En este artículo se describe cómo las aplicaciones de
Xamarin.Forms pueden leer y escribir datos en una base de datos de SQLite local mediante SQLite.Net.
Control de archivos en Xamarin.Forms
11/07/2019 • 9 minutes to read • Edit Online

descargar el ejemplo
El control de archivos con Xamarin.Forms puede lograrse mediante código en una biblioteca de .NET Standard o
con recursos incrustados.

Información general
El código de Xamarin.Forms se ejecuta en varias plataformas, cada una con su propio sistema de archivos.
Anteriormente, esto significaba que las acciones de leer y escribir en archivos se realizaban con mayor facilidad
mediante las API de archivo nativas de cada plataforma. Como alternativa, los recursos incrustados son una
solución más sencilla para distribuir archivos de datos con una aplicación. Pero, con .NET Standard 2.0, se puede
compartir el código de acceso a archivos en bibliotecas de .NET Standard.
Para obtener información sobre cómo controlar archivos de imagen, vea la página Trabajar con imágenes.

Guardar y cargar archivos


Las clases System.IO pueden usarse para acceder al sistema de archivos en cada plataforma. La clase File le
permite crear, eliminar y leer archivos, mientras que la clase Directory le permite crear, eliminar o enumerar el
contenido de directorios. También puede usar la subclases Stream , que pueden proporcionar un mayor nivel de
control sobre las operaciones de archivo (como la compresión o la búsqueda de ubicaciones en un archivo).
Un archivo de texto se puede escribir con el método File.WriteAllText :

File.WriteAllText(fileName, text);

Un archivo de texto se puede leer con el método File.ReadAllText :

string text = File.ReadAllText(fileName);

Además, el método File.Exists determina si existe el archivo especificado:

bool doesExist = File.Exists(fileName);

La ruta del archivo en cada plataforma puede determinarse desde una biblioteca de .NET Standard mediante el
uso de un valor de la enumeración Environment.SpecialFolder como el primer argumento para el método
Environment.GetFolderPath . Después, esto puede combinarse con un nombre de archivo con el método
Path.Combine :

string fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),


"temp.txt");

Estas operaciones se demuestran en la aplicación de ejemplo, donde se incluye una página que guarda y carga
texto:
Cargar archivos insertados como recursos
Para insertar un archivo en un ensamblado de .NET Standard, cree o agregue un archivo y asegúrese de usar
esta acción de compilación: EmbeddedResource.
Visual Studio
Visual Studio para Mac

GetManifestResourceStream se usa para acceder al archivo incrustado mediante su identificador de recurso. De


forma predeterminada, el identificador de recurso es el nombre de archivo que tiene como prefijo el espacio de
nombres predeterminado para el proyecto donde se inserta (en este caso, el ensamblado es WorkingWithFiles y
el nombre de archivo es PCLTextResource.txt, por lo que el identificador de recurso es
WorkingWithFiles.PCLTextResource.txt ).

var assembly = IntrospectionExtensions.GetTypeInfo(typeof(LoadResourceText)).Assembly;


Stream stream = assembly.GetManifestResourceStream("WorkingWithFiles.PCLTextResource.txt");
string text = "";
using (var reader = new System.IO.StreamReader (stream)) {
text = reader.ReadToEnd ();
}

Después, la variable text puede usarse para mostrar el texto o, en cualquier caso, usarlo en el código. En esta
captura de pantalla de la aplicación de ejemplo, se muestra el texto representado en un control Label .
Cargar y deserializar código XML es igual de sencillo. En el código siguiente, se muestra un archivo XML que se
carga y se deserializa desde un recurso y, después, se enlaza a ListView para mostrarlo. El archivo XML contiene
una matriz de objetos Monkey (la clase se define en el código de ejemplo).

var assembly = IntrospectionExtensions.GetTypeInfo(typeof(LoadResourceText)).Assembly;


Stream stream = assembly.GetManifestResourceStream("WorkingWithFiles.PCLXmlResource.xml");
List<Monkey> monkeys;
using (var reader = new System.IO.StreamReader (stream)) {
var serializer = new XmlSerializer(typeof(List<Monkey>));
monkeys = (List<Monkey>)serializer.Deserialize(reader);
}
var listView = new ListView ();
listView.ItemsSource = monkeys;

Insertar en proyectos compartidos


Los proyectos compartidos también pueden contener archivos como recursos incrustados; pero, como el
contenido de un proyecto compartido se compila en los proyectos a los que se hace referencia, el prefijo usado
para los identificadores de recursos del archivo incrustado puede cambiar. Esto quiere decir que el identificador de
recurso para cada archivo incrustado puede ser distinto para cada plataforma.
Hay disponibles dos soluciones a este problema con los proyectos compartidos:
Sincronizar los proyectos: edite las propiedades del proyecto para cada plataforma con el fin de usar el
mismo nombre de ensamblado y el mismo espacio de nombres predeterminado. Después, este valor se puede
“codificar de forma rígida” como el prefijo para los identificadores de recursos incrustados en el proyecto
compartido.
Directivas de compilador #if: use directivas de compilador para establecer el prefijo de identificador de
recurso correcto y use ese valor para crear de forma dinámica el identificador de recurso correcto.
A continuación, se muestra un ejemplo de código de la segunda opción. Las directivas de compilador se usan para
seleccionar el prefijo del recurso codificado de forma rígida (que suele ser el mismo que el espacio de nombres
predeterminado del proyecto al que se hace referencia). Después, la variable resourcePrefix se usa para crear un
identificador de recurso válido al concatenarlo con el nombre de archivo del recurso incrustado.

#if __IOS__
var resourcePrefix = "WorkingWithFiles.iOS.";
#endif
#if __ANDROID__
var resourcePrefix = "WorkingWithFiles.Droid.";
#endif

Debug.WriteLine("Using this resource prefix: " + resourcePrefix);


// note that the prefix includes the trailing period '.' that is required
var assembly = IntrospectionExtensions.GetTypeInfo(typeof(SharedPage)).Assembly;
Stream stream = assembly.GetManifestResourceStream
(resourcePrefix + "SharedTextResource.txt");

Organización de recursos
En los ejemplos anteriores, se da por supuesto que el archivo está insertado en el directorio raíz del proyecto de la
biblioteca de .NET Standard, por lo que el identificador de recurso tendría el formato
EspacioDeNombres.NombreDeArchivo.Extensión, como WorkingWithFiles.PCLTextResource.txt y
WorkingWithFiles.iOS.SharedTextResource.txt .

Los recursos incrustados se pueden organizar en carpetas. Cuando un recurso incrustado se coloca en una
carpeta, el nombre de carpeta se convierte en parte del identificador de recurso (separado por puntos), de forma
que el formato del identificador de recurso se convierte en
EspacioDeNombres.Carpeta.NombreDeArchivo.Extensión. Al colocar los archivos usados en la aplicación
de ejemplo en una carpeta MyFolder, los identificadores de recurso correspondientes serían
WorkingWithFiles.MyFolder.PCLTextResource.txt y WorkingWithFiles.iOS.MyFolder.SharedTextResource.txt .

Depurar recursos incrustados


Como a veces es difícil comprender por qué no se carga un recurso específico, puede agregarse de forma
temporal el siguiente código de depuración a una aplicación para ayudar a confirmar que los recursos se han
configurado correctamente. En el panel Errores, se mostrarán todos los recursos incrustados conocidos en el
ensamblado específico para ayudar a depurar los problemas de carga de recursos.

using System.Reflection;
// ...
// use for debugging, not in released app code!
var assembly = IntrospectionExtensions.GetTypeInfo(typeof(SharedPage)).Assembly;
foreach (var res in assembly.GetManifestResourceNames()) {
System.Diagnostics.Debug.WriteLine("found resource: " + res);
}

Resumen
En este artículo, se muestran algunas operaciones de archivo sencillas para guardar y cargar texto en el dispositivo
y para cargar recursos incrustados. Con .NET Standard 2.0, se puede compartir el código de acceso a archivos en
bibliotecas de .NET Standard.

Vínculos relacionados
FilesSample
Xamarin.Forms Samples (Ejemplos de Xamarin.Forms)
Trabajar con el sistema de archivos en Xamarin.iOS
Bases de datos locales de Xamarin.Forms
11/07/2019 • 4 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Forms admite aplicaciones de base de datos que usan el motor de base de datos de SQLite, lo que
permite cargar y guardar los objetos en código compartido. En este artículo se describe cómo las aplicaciones de
Xamarin.Forms pueden leer y escribir datos en una base de datos de SQLite local mediante SQLite.Net.

Información general
Las aplicaciones de Xamarin.Forms pueden usar el paquete de NuGet SQLite.NET PCL para incorporar las
operaciones de base de datos en el código compartido haciendo referencia a las clases SQLite que se incluyen en
el NuGet. Las operaciones de base de datos se pueden definir en el proyecto de biblioteca de .NET Standard de la
solución de Xamarin.Forms.
La aplicación de ejemplo acompañante es una sencilla aplicación de lista de tareas. Las siguientes capturas de
pantalla muestran cómo el ejemplo aparece en cada plataforma:

Uso de SQLite
Para agregar compatibilidad de SQLite a una biblioteca de .NET Standard de Xamarin.Forms, use la función de
búsqueda de NuGet para buscar sqlite-net-pcl e instalar el paquete más reciente:
Hay una serie de paquetes de NuGet con nombres similares, el paquete correcto tiene estos atributos:
Creado por: Frank A. Krueger
Id.: sqlite-net-pcl
Vínculo de NuGet: sqlite-net-pcl

NOTE
Independientemente del nombre del paquete, utilice el paquete de NuGet sqlite-net-pcl incluso en los proyectos de .NET
Standard.

Una vez agregada la referencia, agregue una propiedad a la clase App que devuelve una ruta de acceso local para
almacenar la base de datos:

static TodoItemDatabase database;

public static TodoItemDatabase Database


{
get
{
if (database == null)
{
database = new TodoItemDatabase(
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"TodoSQLite.db3"));
}
return database;
}
}

El constructor TodoItemDatabase , que toma la ruta de acceso del archivo de base de datos como un argumento, se
muestra a continuación:

public TodoItemDatabase(string dbPath)


{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<TodoItem>().Wait();
}

La ventaja de exponer la base de datos como un singleton es que se crea una conexión de base de datos única que
se mantiene abierta mientras la aplicación se ejecuta, lo que evita el gasto de abrir y cerrar el archivo de base de
datos cada vez que se realiza una operación de base de datos.
El resto de la clase TodoItemDatabase contiene consultas de SQLite que se ejecutan entre plataformas. Debajo se
muestra el código de ejemplo de consulta (puede encontrar más información sobre la sintaxis en Uso de
SQLite.NET con Xamarin.iOS.
public Task<List<TodoItem>> GetItemsAsync()
{
return database.Table<TodoItem>().ToListAsync();
}

public Task<List<TodoItem>> GetItemsNotDoneAsync()


{
return database.QueryAsync<TodoItem>("SELECT * FROM [TodoItem] WHERE [Done] = 0");
}

public Task<TodoItem> GetItemAsync(int id)


{
return database.Table<TodoItem>().Where(i => i.ID == id).FirstOrDefaultAsync();
}

public Task<int> SaveItemAsync(TodoItem item)


{
if (item.ID != 0)
{
return database.UpdateAsync(item);
}
else {
return database.InsertAsync(item);
}
}

public Task<int> DeleteItemAsync(TodoItem item)


{
return database.DeleteAsync(item);
}

NOTE
La ventaja de usar la API de SQLite.Net asincrónica es que las operaciones de base de datos se mueven a subprocesos en
segundo plano. Además, no es necesario escribir código de control de simultaneidad adicional porque la API se encarga de
ello.

Resumen
Xamarin.Forms admite aplicaciones de base de datos que usan el motor de base de datos de SQLite, lo que
permite cargar y guardar los objetos en código compartido.
Este artículo se centra en el acceso a una base de datos SQLite mediante Xamarin.Forms. Para obtener más
información sobre cómo trabajar con SQLite.Net, vea la documentación de SQLite.NET en Android o SQLite.NET
en iOS.

Vínculos relacionados
Todo Sample (Ejemplo de tareas)
Xamarin.Forms Samples (Ejemplos de Xamarin.Forms)
Xamarin.Forms y servicios de Azure
11/07/2019 • 3 minutes to read • Edit Online

Usar una base de datos de documentos de Azure Cosmos DB en


Xamarin.Forms
Una base de datos de documentos de Azure Cosmos DB es una base de datos NoSQL que proporciona acceso de
baja latencia a los documentos JSON, que ofrece un servicio de base de datos rápido, alta disponibilidad y
escalabilidad para aplicaciones que requieren replicación global y escala sin problemas. En este artículo se explica
cómo usar la biblioteca de cliente estándar de .NET de Azure Cosmos DB para integrar una base de datos de
documentos de Azure Cosmos DB en una aplicación de Xamarin.Forms.

Enviar y recibir notificaciones de inserción con Azure Notification Hubs


y Xamarin.Forms
Azure Notification Hubs le permiten centralizar las notificaciones entre plataformas para que la aplicación de back-
end pueda comunicarse con un único centro. Azure Notification Hubs se encargará de distribuir las notificaciones
de inserción en varios proveedores de plataforma. En este artículo se explica cómo integrar Azure Notification
Hubs en una aplicación de Xamarin.Forms.

Store y acceder a los datos en Azure Storage de Xamarin.Forms


Azure Storage es una solución de almacenamiento en la nube escalable que puede utilizarse para almacenar datos
estructurados y no estructurados. En este artículo se muestra cómo usar Xamarin.Forms para almacenar datos
binarios y texto en el almacenamiento de Azure y cómo tener acceso a los datos.

Buscar datos con Azure Search y Xamarin.Forms


Azure Search es un servicio en la nube que proporciona la indización y consulta las capacidades de los datos
cargados. Esto quita los requisitos de infraestructura y la complejidad de algoritmo de búsqueda tradicionalmente
asociada con la implementación de la funcionalidad de búsqueda en una aplicación. En este artículo se muestra
cómo usar la biblioteca de Microsoft Azure Search para integrar la búsqueda de Azure en una aplicación de
Xamarin.Forms.

Azure Functions con Xamarin.Forms


En este artículo se muestra cómo crear su primera función de Azure que interactúa con Xamarin.Forms.

Azure SignalR Service con Xamarin.Forms


ASP.NET Core SignalR es un modelo de aplicación que simplifica el proceso de incorporación de comunicación en
tiempo real a las aplicaciones. Azure SignalR Service permite el rápido desarrollo e implementación de
aplicaciones escalables de SignalR. Este artículo y el ejemplo muestran cómo combinar Azure SignalR Service y
Azure Functions con Xamarin.Forms, para entregar mensajes en tiempo real a los clientes conectados.
Usar una base de datos de documentos de Azure
Cosmos DB en Xamarin.Forms
11/07/2019 • 15 minutes to read • Edit Online

descargar el ejemplo
Una base de datos de documentos de Azure Cosmos DB es una base de datos NoSQL que proporciona acceso de
baja latencia a los documentos JSON, que ofrece un servicio de base de datos rápido, alta disponibilidad y
escalabilidad para aplicaciones que requieren replicación global y escala sin problemas. En este artículo se explica
cómo usar la biblioteca de cliente estándar de .NET de Azure Cosmos DB para integrar una base de datos de
documentos de Azure Cosmos DB en una aplicación de Xamarin.Forms.

Vídeo de Microsoft Azure Cosmos DB


Una cuenta de base de datos de documento de Azure Cosmos DB se puede aprovisionar mediante una suscripción
de Azure. Cada cuenta de base de datos puede tener cero o más bases de datos. Una base de datos de documentos
en Azure Cosmos DB es un contenedor lógico para las colecciones de documentos y los usuarios.
Una base de datos de documentos de Azure Cosmos DB puede contener cero o más colecciones de documentos.
Cada colección de documentos puede tener un nivel de rendimiento diferente, lo que permite más rendimiento
que se especifique para las colecciones de acceso frecuente y menos rendimiento para las colecciones que se
accede con poca frecuencia.
Cada colección de documentos se compone de cero o más documentos JSON. Documentos en una colección
están libres de esquemas y, por lo que no es necesario compartir la misma estructura o campos. Cuando se
agregan documentos a una colección de documentos, Cosmos DB indexa automáticamente les y estén disponibles
para consulta.
Para fines de desarrollo, también se puede utilizar una base de datos de documento en un emulador. Usando el
emulador, las aplicaciones se pueden desarrollar y probar de forma local, sin crear una suscripción de Azure ni
incurrir en ningún gasto. Para obtener más información sobre el emulador, consulte desarrollar localmente con el
emulador de Azure Cosmos DB.
En este artículo y que acompaña a la aplicación de ejemplo, se muestra una aplicación de lista de tareas pendientes
donde las tareas se almacenan en una base de datos de documentos de Azure Cosmos DB. Para obtener más
información acerca de la aplicación de ejemplo, vea descripción del ejemplo.
Para obtener más información sobre Azure Cosmos DB, consulte el documentación de Azure Cosmos DB.

Programa de instalación
El proceso para integrar una base de datos de documentos de Azure Cosmos DB en una aplicación de
Xamarin.Forms es como sigue:
1. Cree una cuenta de Cosmos DB. Para obtener más información, consulte crear una cuenta de Azure Cosmos
DB.
2. Agregar el biblioteca de cliente de .NET Standard de Azure Cosmos DB paquete NuGet a los proyectos de
plataforma en la solución de Xamarin.Forms.
3. Agregar using directivas para el Microsoft.Azure.Documents , Microsoft.Azure.Documents.Client , y
Microsoft.Azure.Documents.Linq espacios de nombres a las clases que tendrá acceso a la cuenta de Cosmos DB.
Después de realizar estos pasos, la biblioteca de cliente estándar de .NET de Azure Cosmos DB puede utilizarse
para configurar y ejecutar solicitudes en la base de datos del documento.

NOTE
La biblioteca estándar de .NET de Azure Cosmos DB de cliente solo puede instalarse en los proyectos de plataforma y no en
un proyecto de biblioteca de clases portables (PCL). Por lo tanto, la aplicación de ejemplo es un proyecto de acceso
compartido (SAP) para evitar la duplicación de código. Sin embargo, la DependencyService clase puede usarse en un
proyecto PCL para invocar código de biblioteca de cliente estándar de .NET de Azure Cosmos DB incluido en proyectos
específicos de plataforma.

Consumo de la cuenta de Azure Cosmos DB


El DocumentClient tipo encapsula el punto de conexión, credenciales y directiva de conexión utilizada para tener
acceso a la cuenta de Azure Cosmos DB y se usa para configurar y ejecutar solicitudes en la cuenta. En el ejemplo
de código siguiente se muestra cómo crear una instancia de esta clase:

DocumentClient client = new DocumentClient(new Uri(Constants.EndpointUri), Constants.PrimaryKey);

Deben proporcionarse el Uri de Cosmos DB y la clave principal para el DocumentClient constructor. Estos pueden
obtenerse desde el Portal de Azure. Para obtener más información, consulte conectar a una cuenta de Azure
Cosmos DB.
Creación de una base de datos
Una base de datos de documento es un contenedor lógico para las colecciones de documentos y los usuarios y
puede ser creado en el Portal de Azure, o utilizando mediante programación el
DocumentClient.CreateDatabaseIfNotExistsAsync método:

public async Task CreateDatabase(string databaseName)


{
...
await client.CreateDatabaseIfNotExistsAsync(new Database
{
Id = databaseName
});
...
}

El CreateDatabaseIfNotExistsAsync método especifica un Database como un argumento de objeto con el Database


objeto que especifica el nombre de la base de datos como su Id propiedad. El CreateDatabaseIfNotExistsAsync
método crea la base de datos si no existe, o devuelve la base de datos si ya existe. Sin embargo, la aplicación de
ejemplo omite todos los datos devueltos por la CreateDatabaseIfNotExistsAsync método.

NOTE
El CreateDatabaseIfNotExistsAsync método devuelve un Task<ResourceResponse<Database>> objeto y el código de
estado de la respuesta pueden comprobarse para determinar si una base de datos se creó o se ha devuelto una base de
datos existente.

Creación de una colección de documentos


Una colección de documentos es un contenedor para documentos JSON y puede ser creado en el Portal de Azure,
o utilizando mediante programación el DocumentClient.CreateDocumentCollectionIfNotExistsAsync método:
public async Task CreateDocumentCollection(string databaseName, string collectionName)
{
...
// Create collection with 400 RU/s
await client.CreateDocumentCollectionIfNotExistsAsync(
UriFactory.CreateDatabaseUri(databaseName),
new DocumentCollection
{
Id = collectionName
},
new RequestOptions
{
OfferThroughput = 400
});
...
}

El CreateDocumentCollectionIfNotExistsAsync método requiere dos argumentos obligatorios: especifica un nombre


de base de datos como un Uri y un DocumentCollection objeto. El DocumentCollection objeto representa una
colección de documentos cuyo nombre se especifica con el Id propiedad. El
CreateDocumentCollectionIfNotExistsAsync método crea la colección de documentos si no existe, o devuelve la
colección de documentos si ya existe. Sin embargo, la aplicación de ejemplo omite todos los datos devueltos por la
CreateDocumentCollectionIfNotExistsAsync método.

NOTE
El CreateDocumentCollectionIfNotExistsAsync método devuelve un Task<ResourceResponse<DocumentCollection>>
objeto y el código de estado de la respuesta pueden comprobarse para determinar si una colección de documentos se creó o
se devuelve una colección de documentos existentes.

Opcionalmente, el CreateDocumentCollectionIfNotExistsAsync método también puede especificar un


RequestOptions objeto, que encapsula las opciones que se pueden especificar para las solicitudes emitidas a la
cuenta de Cosmos DB. El RequestOptions.OfferThroughput propiedad se utiliza para definir el nivel de rendimiento
de la colección de documentos y, en el ejemplo de aplicación, se establece en 400 unidades de solicitud por
segundo. Este valor se debe incrementar o disminuir dependiendo de si la colección con frecuencia o con poca
frecuencia accederá.

IMPORTANT
Tenga en cuenta que el CreateDocumentCollectionIfNotExistsAsync creará una nueva colección con un rendimiento
reservado, lo cual tiene implicaciones de precios.

Recuperación de documentos de la colección de documentos


El contenido de una colección de documentos puede obtenerse mediante la creación y ejecución de una consulta
de documento. Se crea una consulta de documento con el DocumentClient.CreateDocumentQuery método:
public async Task<List<TodoItem>> GetTodoItemsAsync()
{
...
var query = client.CreateDocumentQuery<TodoItem>(collectionLink)
.AsDocumentQuery();
while (query.HasMoreResults)
{
Items.AddRange(await query.ExecuteNextAsync<TodoItem>());
}
...
}

Esta consulta recupera todos los documentos de la colección especificada asincrónicamente y coloca los
documentos en un List<TodoItem> colección para su presentación.
El CreateDocumentQuery<T> método especifica un Uri argumento que representa la colección que se debe
consultar para los documentos. En este ejemplo, el collectionLink variable es un campo de nivel de clase que
especifica el Uri que representa la colección de documentos para recuperar documentos desde:

Uri collectionLink = UriFactory.CreateDocumentCollectionUri(Constants.DatabaseName, Constants.CollectionName);

El CreateDocumentQuery<T>método crea una consulta que se ejecuta de forma sincrónica y devuelve un


IQueryable<T> objeto. Sin embargo, el AsDocumentQuery método convierte el IQueryable<T> objeto a un
IDocumentQuery<T> objeto que se puede ejecutar de forma asincrónica. Se ejecuta la consulta asincrónica con el
IDocumentQuery<T>.ExecuteNextAsync método, que recupera la página siguiente de resultados de la base de datos de
documento, con el IDocumentQuery<T>.HasMoreResults propiedad que indica si hay resultados adicionales que va a
devolver la consulta.
Documentos se pueden filtrar en el servidor mediante la inclusión de un Where cláusula de la consulta, que se
aplica un predicado de filtrado a la consulta en la colección de documentos:

var query = client.CreateDocumentQuery<TodoItem>(collectionLink)


.Where(f => f.Done != true)
.AsDocumentQuery();

Esta consulta recupera todos los documentos de la colección cuya Done es igual a la propiedad false .
Insertar un documento en una colección de documentos
Documentos son contenido JSON de definidos por el usuario y se pueden insertar en una colección de
documentos con el DocumentClient.CreateDocumentAsync método:

public async Task SaveTodoItemAsync(TodoItem item, bool isNewItem = false)


{
...
await client.CreateDocumentAsync(collectionLink, item);
...
}

El CreateDocumentAsync método especifica un Uri argumento que representa la colección debe insertarse el
documento, y un object argumento que representa el documento que se va a insertar.
Reemplazar un documento en una colección de documentos
Se pueden reemplazar documentos en una colección de documentos con el DocumentClient.ReplaceDocumentAsync
método:
public async Task SaveTodoItemAsync(TodoItem item, bool isNewItem = false)
{
...
await client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(Constants.DatabaseName,
Constants.CollectionName, item.Id), item);
...
}

El ReplaceDocumentAsync método especifica un Uri argumento que representa el documento de la colección que
se debe reemplazar, y un object argumento que representa los datos de documento actualizado.
Eliminación de un documento de una colección de documentos
Se puede eliminar un documento de una colección de documentos con el DocumentClient.DeleteDocumentAsync
método:

public async Task DeleteTodoItemAsync(string id)


{
...
await client.DeleteDocumentAsync(UriFactory.CreateDocumentUri(Constants.DatabaseName,
Constants.CollectionName, id));
...
}

El DeleteDocumentAsync método especifica un Uri argumento que representa el documento en la colección que se
debe eliminar.
Eliminación de una colección de documentos
Se puede eliminar una colección de documentos de una base de datos con el
DocumentClient.DeleteDocumentCollectionAsync método:

await client.DeleteDocumentCollectionAsync(collectionLink);

El DeleteDocumentCollectionAsync método especifica un Uri argumento que representa la colección de


documentos que va a eliminar. Tenga en cuenta que al invocar este método, también se eliminarán los documentos
almacenados en la colección.
Eliminación de una base de datos
Se puede eliminar una base de datos de una cuenta de base de datos de Cosmos DB con la
DocumentClient.DeleteDatabaesAsync método:

await client.DeleteDatabaseAsync(UriFactory.CreateDatabaseUri(Constants.DatabaseName));

El DeleteDatabaseAsync método especifica un Uri argumento que representa la base de datos que va a eliminar.
Tenga en cuenta que al invocar este método también eliminará las colecciones de documentos almacenadas en la
base de datos y los documentos almacenados en las colecciones de documentos.

Resumen
En este artículo se explica cómo usar la biblioteca de cliente estándar de .NET de Azure Cosmos DB para integrar
una base de datos de documentos de Azure Cosmos DB en una aplicación de Xamarin.Forms. Una base de datos
de documentos de Azure Cosmos DB es una base de datos NoSQL que proporciona acceso de baja latencia a los
documentos JSON, que ofrece un servicio de base de datos rápido, alta disponibilidad y escalabilidad para
aplicaciones que requieren replicación global y escala sin problemas.
Vínculos relacionados
Lista de tareas de Azure Cosmos DB (ejemplo)
Documentación de Azure Cosmos DB
Biblioteca de cliente de Azure Cosmos DB .NET Standard
API de Azure Cosmos DB
Enviar y recibir notificaciones push con Azure
Notification Hubs y Xamarin.Forms
11/07/2019 • 29 minutes to read • Edit Online

descargar el ejemplo
Insertar información de entrega de notificaciones desde un sistema back-end en una aplicación móvil. Apple,
Google y otras plataformas tienen su propio servicio de notificación de inserción (PNS ). Azure Notification Hubs le
permiten centralizar las notificaciones entre plataformas para que la aplicación de back-end pueda comunicarse
con un único concentrador, que se encargará de distribuir las notificaciones para cada PNS específicos de la
plataforma.
Integrar Azure Notification Hubs en aplicaciones móviles siguiendo estos pasos:
1. Configurar los servicios de notificaciones Push y Azure Notification Hub.
2. Descubra cómo utilizar etiquetas y plantillas.
3. Crear una aplicación de Xamarin.Forms multiplataforma.
4. Configurar el proyecto nativo de Android para notificaciones de inserción.
5. Configurar el proyecto de iOS nativo para las notificaciones de inserción.
6. Probar las notificaciones mediante el centro de notificaciones de Azure.
7. Crear una aplicación de back-end para enviar notificaciones.

Configurar los servicios de notificaciones Push y centro de


notificaciones de Azure
La integración de Azure Notification Hubs con una aplicación móvil Xamarin.Forms es similar a la integración de
Azure Notification Hubs con una aplicación nativa de Xamarin. Configurar un aplicación FCM siguiendo los
pasos la consola de Firebase notificaciones Push en Xamarin.Android mediante Azure Notification Hubs. Complete
los pasos siguientes con el tutorial de Xamarin.Android:
1. Definir un nombre de paquete de Android, como com.xamarin.notifysample , que se usa en el ejemplo.
2. Descargar google-services.json desde la consola de Firebase. Este archivo se agregará a la aplicación Android
en un paso posterior.
3. Cree una instancia de Azure Notification Hub y asígnele un nombre. Este artículo y ejemplo, utilice
xdocsnotificationhub como nombre del centro.
4. Copie el FCM clave de servidor y guárdelo como el clave de API en Google (GCM o FCM ) en el centro de
notificaciones de Azure.
Captura de pantalla siguiente muestra la configuración de la plataforma de Google en el centro de notificaciones
de Azure:
Necesitará un equipo macOS para completar la configuración para dispositivos iOS. Configuración de APNS
siguiendo los pasos de la inicial en notificaciones de inserción a Xamarin.iOS mediante Azure Notification Hubs.
Complete los pasos siguientes con el tutorial de Xamarin.iOS:
1. Defina un identificador de lote de iOS. Este artículo y ejemplo, utilice com.xamarin.notifysample como el
identificador de paquete.
2. Cree un archivo de solicitud de firma de certificado (CSR ) y usarlo para generar un certificado de notificaciones
de inserción.
3. Cargue el certificado de notificaciones de inserción en Apple (APN ) en el centro de notificaciones de Azure.
Captura de pantalla siguiente muestra la configuración de la plataforma de Apple en el centro de notificaciones de
Azure:

Registrar las etiquetas y plantillas con el centro de notificaciones de


Azure
Centro de notificaciones de Azure requiere que las aplicaciones móviles para registrarse en el centro, definir
plantillas y suscribirse a las etiquetas. Registro vincula un identificador PNS específico de plataforma con un
identificador en el centro de notificaciones de Azure. Para obtener más información acerca de los registros, vea
administración de registros.
Las plantillas permiten a los dispositivos especificar plantillas de mensajes con parámetros. Por dispositivo, por la
etiqueta a la que se pueden personalizar los mensajes entrantes. Para más información acerca de las plantillas,
consulte plantillas.
Las etiquetas se pueden usar para suscribirse a categorías de mensaje como noticias, deportes y previsiones
meteorológicas. Por motivos de simplicidad, la aplicación de ejemplo define una plantilla predeterminada con un
solo parámetro llamado messageParam y llama una sola etiqueta default . En sistemas más complejos, las
etiquetas específicas de usuario pueden utilizarse para un usuario de mensajes entre los dispositivos para las
notificaciones personalizadas. Para más información acerca de las etiquetas, consulte expresiones de etiqueta y
enrutamiento.
Para recibir correctamente los mensajes, cada aplicación nativa debe realizar estos pasos:
1. Obtener un token o identificador de PNS de la plataforma de PNS.
2. Registrar el controlador de PNS con el centro de notificaciones de Azure.
3. Especificar una plantilla que contiene los mismos parámetros que los mensajes salientes.
4. Suscribirse a la etiqueta a la que apunta a los mensajes salientes.
Estos pasos se describen con más detalle para cada plataforma en la configurar la aplicación Android para las
notificaciones y para las notificaciones de dispositivos IOS secciones.

Funcionalidad de la aplicación de Xamarin.Forms


La aplicación de Xamarin.Forms de ejemplo muestra una lista de mensajes de notificación de inserción. Esto se
logra con la AddMessage método, que agrega el mensaje de notificación de inserción especificado a la interfaz de
usuario. Este método también impide que los mensajes duplicados se agreguen a la interfaz de usuario y se ejecuta
en el subproceso principal se puede llamar desde cualquier subproceso. En el código siguiente se muestra el
método AddMessage :

public void AddMessage(string message)


{
Device.BeginInvokeOnMainThread(() =>
{
if (messageDisplay.Children.OfType<Label>().Where(c => c.Text == message).Any())
{
// Do nothing, an identical message already exists
}
else
{
Label label = new Label()
{
Text = message,
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.Start
};
messageDisplay.Children.Add(label);
}
});
}

La aplicación de ejemplo contiene un AppConstants.cs archivo, que define propiedades utilizadas por los
proyectos de plataforma. Este archivo debe personalizarse con valores desde el centro de notificaciones de Azure.
El siguiente código muestra la AppConstants.cs archivo:
public static class AppConstants
{
public static string NotificationChannelName { get; set; } = "XamarinNotifyChannel";
public static string NotificationHubName { get; set; } = "< Insert your Azure Notification Hub name >";
public static string ListenConnectionString { get; set; } = "< Insert your
DefaultListenSharedAccessSignature >";
public static string DebugTag { get; set; } = "XamarinNotify";
public static string[] SubscriptionTags { get; set; } = { "default" };
public static string FCMTemplateBody { get; set; } = "{\"data\":{\"message\":\"$(messageParam)\"}}";
public static string APNTemplateBody { get; set; } = "{\"aps\":{\"alert\":\"$(messageParam)\"}}";
}

Personalizar los siguientes valores en AppConstants para conectar la aplicación de ejemplo en el centro de
notificaciones de Azure:
NotificationHubName : Utilice el nombre del centro de notificaciones de Azure que creó en el portal de Azure.
ListenConnectionString : Este valor se encuentra en el centro de notificaciones de Azure en las directivas de
acceso.
Captura de pantalla siguiente se muestra dónde se encuentran estos valores en el portal de Azure:

Configurar la aplicación para notificaciones de Android


Complete los pasos siguientes para configurar la aplicación Android para recibir y procesar las notificaciones:
1. Configurar Android nombre del paquete para que coincida con el nombre del paquete en la consola de
Firebase.
2. Instale los siguientes paquetes de NuGet para interactuar con Google Play, Firebase y Azure Notification Hubs:
a. Xamarin.GooglePlayServices.Base.
b. Xamarin.Firebase.Messaging.
c. Xamarin.Azure.NotificationHubs.Android.
3. Copia el google-services.json archivo que descargó durante la instalación FCM al proyecto y establezca la
acción de compilación en GoogleServicesJson .
4. Configurar AndroidManifest.xml para comunicarse con Firebase.
5. Registrar la aplicación con Firebase y centro de notificaciones de Azure mediante un FirebaseInstanceIdService
.
6. Procesar mensajes con un FirebaseMessagingService .
7. Incorporación de notificaciones entrantes a la interfaz de usuario de Xamarin.Forms.
NOTE
El GoogleServicesJson acción de compilación es parte de la Xamarin.GooglePlayServices.Base paquete NuGet. 2019 de
Visual Studio establece las acciones de compilación disponibles durante el inicio. Si no ve GoogleServicesJson como una
acción de compilación, reinicie Visual Studio de 2019 después de instalar los paquetes de NuGet.

Configurar el manifiesto de Android


El receiver elementos dentro de la application elemento permite que la aplicación se comunique con Firebase.
El uses-permission elementos que la aplicación pueda controlar los mensajes y registrar con Azure Notification
Hub. La completa AndroidManifest.xml debe ser similar al ejemplo siguiente:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1"


android:versionName="1.0" package="YOUR_PACKAGE_NAME" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<application android:label="Notification Hub Sample">
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
</application>
</manifest>

Registrarse con un FirebaseInstanceIdService personalizado


Firebase emite tokens que identifican de forma única un dispositivo en el PNS. Los tokens tienen una duración
larga, pero se actualizan ocasionalmente. Cuando un token se emitió o actualizó, la aplicación debe registrar su
nuevo token con el centro de notificaciones de Azure. El registro se controla mediante una instancia de una clase
que derive de FirebaseInstanceIdService .
En la aplicación de ejemplo FirebaseRegistrationService clase hereda de FirebaseInstanceIdService . Esta clase
tiene un IntentFilter que incluye com.google.firebase.INSTANCE_ID_EVENT , lo que permite al sistema de operativo
Android que llaman automáticamente a OnTokenRefresh cuando se emite un token por Firebase.
El código siguiente muestra personalizado FirebaseInstanceIdService desde la aplicación de ejemplo:
[Service]
[IntentFilter(new [] { "com.google.firebase.INSTANCE_ID_EVENT"})]
public class FirebaseRegistrationService : FirebaseInstanceIdService
{
public override void OnTokenRefresh()
{
string token = FirebaseInstanceId.Instance.Token;

// NOTE: logging the token is not recommended in production but during


// development it is useful to test messages directly from Firebase
Log.Info(AppConstants.DebugTag, $"Token received: {token}");

SendRegistrationToServer(token);
}

void SendRegistrationToServer(string token)


{
try
{
NotificationHub hub = new NotificationHub(AppConstants.NotificationHubName,
AppConstants.ListenConnectionString, this);

// register device with Azure Notification Hub using the token from FCM
Registration reg = hub.Register(token, AppConstants.SubscriptionTags);

// subscribe to the SubscriptionTags list with a simple template.


string pnsHandle = reg.PNSHandle;
var cats = string.Join(", ", reg.Tags);
var temp = hub.RegisterTemplate(pnsHandle, "defaultTemplate", AppConstants.FCMTemplateBody,
AppConstants.SubscriptionTags);
}
catch (Exception e)
{
Log.Error(AppConstants.DebugTag, $"Error registering device: {e.Message}");
}
}
}

El SendRegistrationToServer método en el FirebaseRegistrationClass registra el dispositivo con el centro de


notificaciones de Azure y se suscribe a las etiquetas con una plantilla. La aplicación de ejemplo define una etiqueta
única denominada default y llamar una plantilla con un solo parámetro messageParam en el AppConstants.cs
archivo. Para obtener más información sobre el registro, las etiquetas y plantillas, consulte registrar plantillas y
etiquetas con el centro de notificaciones de Azure
Procesar los mensajes con un FirebaseMessagingService
Los mensajes entrantes se enrutan a un FirebaseMessagingService instancia, donde se puede convertir en una
notificación local. El proyecto de Android en la aplicación de ejemplo contiene una clase denominada
FirebaseService que hereda de FirebaseMessagingService . Esta clase tiene un IntentFilter que incluye
com.google.firebase.MESSAGING_EVENT , lo que permite al sistema de operativo Android que llaman
automáticamente a OnMessageReceived cuando se recibe un mensaje de notificación de inserción.
El ejemplo siguiente se muestra la FirebaseService desde la aplicación de ejemplo:
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseService : FirebaseMessagingService
{
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
string messageBody = string.Empty;

if (message.GetNotification() != null)
{
messageBody = message.GetNotification().Body;
}

// NOTE: test messages sent via the Azure portal will be received here
else
{
messageBody = message.Data.Values.First();
}

// convert the incoming message to a local notification


SendLocalNotification(messageBody);

// send the incoming message directly to the MainPage


SendMessageToMainPage(messageBody);
}

void SendLocalNotification(string body)


{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
intent.PutExtra("message", body);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);

var notificationBuilder = new NotificationCompat.Builder(this)


.SetContentTitle("XamarinNotify Message")
.SetSmallIcon(Resource.Drawable.ic_launcher)
.SetContentText(body)
.SetAutoCancel(true)
.SetShowWhen(false)
.SetContentIntent(pendingIntent);

if (Build.VERSION.SdkInt >= BuildVersionCodes.O)


{
notificationBuilder.SetChannelId(AppConstants.NotificationChannelName);
}

var notificationManager = NotificationManager.FromContext(this);


notificationManager.Notify(0, notificationBuilder.Build());
}

void SendMessageToMainPage(string body)


{
(App.Current.MainPage as MainPage)?.AddMessage(body);
}
}

Los mensajes entrantes se convierten en una notificación local con el SendLocalNotification método. Este método
crea un nuevo Intent y coloca el mensaje contenido en el Intent como un string Extra . Cuando el usuario
pulsa la notificación local, si la aplicación está en primer plano o en segundo plano, el MainActivity se inicia y se
tiene acceso al contenido de mensaje a través de la Intent objeto.
La notificación local y Intent ejemplo requiere que el usuario realizar la acción de puntear en la notificación. Esto
es conveniente cuando el usuario debe tomar medidas antes de los cambios de estado de la aplicación. Sin
embargo, es posible que desee tener acceso a los datos del mensaje sin necesidad de una acción del usuario en
algunos casos. El ejemplo anterior también envía el mensaje directamente a la actual MainPage instancia con el
SendMessageToMainPage método. En producción, si implementa ambos métodos para un tipo de mensaje único, el
MainPage objeto obtendrán los mensajes duplicados si el usuario pulsa la notificación.

NOTE
La aplicación de Android solo recibirá notificaciones de inserción si se está ejecutando en segundo plano o el primer plano.
Para recibir notificaciones de inserción cuando el método main Activity es no se está ejecutando, debe implementar un
servicio, que está fuera del ámbito de este ejemplo. Para obtener más información, consulte crear servicios de Android

Incorporación de notificaciones entrantes a la interfaz de usuario de Xamarin.Forms


La MainActivity clase necesita obtener permiso para controlar las notificaciones y administrar los datos del
mensaje entrante. El código siguiente muestra la completa MainActivity implementación:

[Activity(Label = "NotificationHubSample", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher =


true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, LaunchMode =
LaunchMode.SingleTop)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;

base.OnCreate(savedInstanceState);

global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());

if (IsPlayServiceAvailable() == false)
{
throw new Exception("This device does not have Google Play Services and cannot receive push
notifications.");
}

CreateNotificationChannel();
}

protected override void OnNewIntent(Intent intent)


{
if (intent.Extras != null)
{
var message = intent.GetStringExtra("message");
(App.Current.MainPage as MainPage)?.AddMessage(message);
}

base.OnNewIntent(intent);
}

bool IsPlayServiceAvailable()
{
int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.Success)
{
if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
Log.Debug(AppConstants.DebugTag, GoogleApiAvailability.Instance.GetErrorString(resultCode));
else
{
Log.Debug(AppConstants.DebugTag, "This device is not supported");
}
return false;
}
return true;
}
}

void CreateNotificationChannel()
{
// Notification channels are new as of "Oreo".
// There is no need to create a notification channel on older versions of Android.
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelName = AppConstants.NotificationChannelName;
var channelDescription = String.Empty;
var channel = new NotificationChannel(channelName, channelName, NotificationImportance.Default)
{
Description = channelDescription
};

var notificationManager = (NotificationManager)GetSystemService(NotificationService);


notificationManager.CreateNotificationChannel(channel);
}
}
}

El Activity atributo establece la aplicación LaunchMode a SingleTop . Este modo de lanzamiento dice al SO
Android para permitir únicamente una sola instancia de esta actividad. Con este modo de lanzamiento, entrante
Intent datos se enrutan a la OnNewIntent método, que extrae datos del mensaje y lo envía a la MainPage instancia
a través de la AddMessage método. Si la aplicación utiliza un modo de inicio diferente, debe controlar Intent datos
de forma diferente.
El OnCreate método usa un método auxiliar llamado IsPlayServiceAvailable para asegurarse de que el
dispositivo es compatible con Google Play Services. Los emuladores o dispositivos que no son compatibles con
Google Play Services no pueden recibir notificaciones de inserción de Firebase.

Configurar notificaciones de iOS


Es el proceso para configurar la aplicación de iOS para recibir las notificaciones:
1. Configurar la identificador de paquete en el Info.plist archivo para que coincida con el valor utilizado en el
perfil de aprovisionamiento.
2. Agregar el habilitar las notificaciones de inserción opción a la Entitlements.plist archivo.
3. Agregar el Xamarin.Azure.NotificationHubs.iOS paquete NuGet al proyecto.
4. Registrarse para recibir notificaciones con APNS.
5. Registrar la aplicación con Azure Notification Hub y suscribirse a las etiquetas.
6. Incorporación de notificaciones de APNS para la interfaz de usuario de Xamarin.Forms.
La siguiente captura de pantalla muestra la habilitar las notificaciones de inserción opción seleccionada en el
Entitlements.plist archivo dentro de Visual Studio:

Registro de notificaciones con APNS


El FinishedLaunching método en el AppDelegate.cs archivo debe reemplazarse para registrar las notificaciones
remotas. Registro varía según la versión de iOS que se usa en el dispositivo. El proyecto de iOS en la aplicación de
ejemplo reemplaza el FinishedLaunching método para llamar a RegisterForRemoteNotifications tal como se
muestra en el ejemplo siguiente:

public override bool FinishedLaunching(UIApplication app, NSDictionary options)


{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());

base.FinishedLaunching(app, options);

RegisterForRemoteNotifications();

return true;
}

void RegisterForRemoteNotifications()
{
// register for remote notifications based on system version
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert |
UNAuthorizationOptions.Sound |
UNAuthorizationOptions.Sound,
(granted, error) =>
{
if (granted)
InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications);
});
}
else if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound,
new NSSet());

UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else
{
UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert |
UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
}

Registrar con Azure Notification Hub y suscribirse a las etiquetas


Cuando el dispositivo registrado correctamente para las notificaciones remotas durante el FinishedLaunching
método, llamará a iOS el RegisteredForRemoteNotifications método. Este método se debe invalidar para llevar a
cabo las siguientes acciones:
1. Crear una instancia de la SBNotificationHub .
2. Anular el registro de los registros existentes.
3. Registrar el dispositivo con el centro de notificaciones.
4. Suscríbase a etiquetas específicas con una plantilla.
Para obtener más información sobre el registro del dispositivo, plantillas y etiquetas, consulte registrar etiquetas y
plantillas con Azure Notification Hub. El código siguiente muestra el registro del dispositivo y plantillas:
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Hub = new SBNotificationHub(AppConstants.ListenConnectionString, AppConstants.NotificationHubName);

// update registration with Azure Notification Hub


Hub.UnregisterAllAsync(deviceToken, (error) =>
{
if (error != null)
{
Debug.WriteLine($"Unable to call unregister {error}");
return;
}

var tags = new NSSet(AppConstants.SubscriptionTags.ToArray());


Hub.RegisterNativeAsync(deviceToken, tags, (errorCallback) =>
{
if (errorCallback != null)
{
Debug.WriteLine($"RegisterNativeAsync error: {errorCallback}");
}
});

var templateExpiration =
DateTime.Now.AddDays(120).ToString(System.Globalization.CultureInfo.CreateSpecificCulture("en-US"));
Hub.RegisterTemplateAsync(deviceToken, "defaultTemplate", AppConstants.APNTemplateBody,
templateExpiration, tags, (errorCallback) =>
{
if (errorCallback != null)
{
if (errorCallback != null)
{
Debug.WriteLine($"RegisterTemplateAsync error: {errorCallback}");
}
}
});
});
}

NOTE
Registrar las notificaciones remotas puede producir errores en situaciones como sin conexión de red. Puede elegir invalidar el
FailedToRegisterForRemoveNotifications método para controlar el error de registro.

Incorporación de notificaciones de APNS para la interfaz de usuario de Xamarin.Forms


Cuando un dispositivo recibe una notificación remota, iOS llama a la ReceivedRemoteNotification método.
Mensaje entrante JSON se convierte en un NSDictionary objeto y el ProcessNotification método extrae los
valores del diccionario y los envía a Xamarin.Forms MainPage instancia. El ReceivedRemoteNotifications se invalida
el método para llamar a ProcessNotification tal como se muestra en el código siguiente:
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
ProcessNotification(userInfo, false);
}

void ProcessNotification(NSDictionary options, bool fromFinishedLaunching)


{
// make sure we have a payload
if (options != null && options.ContainsKey(new NSString("aps")))
{
// get the APS dictionary and extract message payload. Message JSON will be converted
// into a NSDictionary so more complex payloads may require more processing
NSDictionary aps = options.ObjectForKey(new NSString("aps")) as NSDictionary;
string payload = string.Empty;
NSString payloadKey = new NSString("alert");
if (aps.ContainsKey(payloadKey))
{
payload = aps[payloadKey].ToString();
}

if (!string.IsNullOrWhiteSpace(payload))
{
(App.Current.MainPage as MainPage)?.AddMessage(payload);
}

}
else
{
Debug.WriteLine($"Received request to process notification but there was no payload.");
}
}

Probar las notificaciones en el portal de Azure


Azure Notification Hubs permiten comprobar que la aplicación puede recibir mensajes de prueba. El envío de
prueba sección en el centro de notificaciones le permite elegir la plataforma de destino y enviar un mensaje.
Establecer el enviar a expresión de etiqueta a default enviará mensajes a las aplicaciones que han registrado
una plantilla para el default etiqueta. Al hacer clic en el enviar botón genera un informe que incluya el número
de dispositivos alcanzado con el mensaje. Captura de pantalla siguiente muestra una prueba de notificaciones de
Android en el portal de Azure:
Sugerencias para las pruebas
1. Al probar que una aplicación puede recibir notificaciones de inserción, debe usar un dispositivo físico. IOS y
Android dispositivos virtuales no pueden configurarse correctamente para recibir notificaciones de inserción.
2. La aplicación de Android de ejemplo registra su símbolo (token) y las plantillas de una vez cuando se emite el
token de Firebase. Durante las pruebas es posible que deba solicitar un nuevo token y vuelva a registrar con el
centro de notificaciones de Azure. Es la mejor manera de forzar esto limpiar el proyecto, elimine el bin y obj
carpetas y desinstalar la aplicación desde el dispositivo antes de volver a generar e implementar.
3. Muchas partes del flujo de notificaciones de inserción se ejecutan de forma asincrónica. Esto puede dar lugar a
que los puntos de interrupción no es el acierto o se vea afectado en un orden inesperado. Usar el registro de
dispositivo o de depuración para el seguimiento de la ejecución sin interrumpir el flujo de la aplicación. Filtrar el
registro de dispositivo Android mediante el DebugTag especificado en Constants .

Crear un distribuidor de notificación


Azure Notification Hubs permiten la aplicación de back-end enviar notificaciones a dispositivos entre plataformas.
El ejemplo muestra el envío de notificaciones con el NotificationDispatcher aplicación de consola. La aplicación
incluye el DispatcherConstants.cs archivo, que define las siguientes propiedades:

public static class DispatcherConstants


{
public static string[] SubscriptionTags { get; set; } = { "default" };
public static string NotificationHubName { get; set; } = "< Insert your Azure Notification Hub name >";
public static string FullAccessConnectionString { get; set; } = "< Insert your
DefaultFullSharedAccessSignature >";
}

Debe configurar el DispatcherConstants.cs para que coincida con la configuración del centro de notificaciones de
Azure. El valor de la SubscriptionTags propiedad debe coincidir con los valores utilizados en las aplicaciones
cliente. El propiedad es el nombre de la instancia de centro de notificaciones de Azure. El
NotificationHubName
FullAccessConnectionString propiedad es la clave de acceso se encuentra en el centro de notificaciones las
directivas de acceso. Captura de pantalla siguiente muestra la ubicación de la NotificationHubName y
FullAccessConnectionString propiedades en el portal de Azure:

La aplicación de consola recorre SubscriptionTags de valor y envía notificaciones a los suscriptores mediante una
instancia de la NotificationHubClient clase. El código siguiente muestra la aplicación de consola Program clase:
class Program
{
static int messageCount = 0;

static void Main(string[] args)


{
Console.WriteLine($"Press the spacebar to send a message to each tag in {string.Join(", ",
DispatcherConstants.SubscriptionTags)}");
WriteSeparator();
while (Console.ReadKey().Key == ConsoleKey.Spacebar)
{
SendTemplateNotificationsAsync().GetAwaiter().GetResult();
}
}

private static async Task SendTemplateNotificationsAsync()


{
NotificationHubClient hub =
NotificationHubClient.CreateClientFromConnectionString(DispatcherConstants.FullAccessConnectionString,
DispatcherConstants.NotificationHubName);
Dictionary<string, string> templateParameters = new Dictionary<string, string>();

messageCount++;

// Send a template notification to each tag. This will go to any devices that
// have subscribed to this tag with a template that includes "messageParam"
// as a parameter
foreach (var tag in DispatcherConstants.SubscriptionTags)
{
templateParameters["messageParam"] = $"Notification #{messageCount} to {tag} category
subscribers!";
try
{
await hub.SendTemplateNotificationAsync(templateParameters, tag);
Console.WriteLine($"Sent message to {tag} subscribers.");
}
catch (Exception ex)
{
Console.WriteLine($"Failed to send template notification: {ex.Message}");
}
}

Console.WriteLine($"Sent messages to {DispatcherConstants.SubscriptionTags.Length} tags.");


WriteSeparator();
}

private static void WriteSeparator()


{
Console.WriteLine("==========================================================================");
}
}

Cuando se ejecuta la aplicación de consola de ejemplo, puede presionar la barra espaciadora para enviar mensajes.
Los dispositivos que ejecutan el cliente deben recibir aplicaciones notificaciones numeradas, proporciona están
configurados correctamente.

Vínculos relacionados
Plantillas de notificación de inserción.
Administración de registros de dispositivo.
Enrutamiento y expresiones de etiqueta.
Tutorial de Xamarin.Android Azure Notification Hubs.
Tutorial de Xamarin.iOS Azure Notification Hubs.
Store y acceder a los datos en Azure Storage de
Xamarin.Forms
11/07/2019 • 18 minutes to read • Edit Online

descargar el ejemplo
Azure Storage es una solución de almacenamiento en la nube escalable que puede utilizarse para almacenar
datos estructurados y no estructurados. En este artículo se muestra cómo usar Xamarin.Forms para almacenar
datos binarios y texto en el almacenamiento de Azure y cómo tener acceso a los datos.
Azure Storage proporciona cuatro servicios de almacenamiento:
Almacenamiento de blobs. Un blob puede ser texto o datos binarios, como las copias de seguridad, las
máquinas virtuales, archivos multimedia o documentos.
Table Storage es un almacén de clave-atributo NoSQL.
Queue Storage es un servicio de mensajería para el procesamiento de flujo de trabajo y comunicación entre
servicios en la nube.
Almacenamiento de archivos ofrece almacenamiento compartido mediante el protocolo SMB.
Hay dos tipos de cuentas de almacenamiento:
Una cuenta de almacenamiento de uso general proporciona acceso a servicios de Azure Storage desde una sola
cuenta.
Una cuenta de almacenamiento de Blob es una cuenta de almacenamiento especializada para almacenar blobs.
Se recomienda este tipo de cuenta cuando necesite almacenar los datos blob.
En este artículo y que acompaña a la aplicación de ejemplo muestra cómo cargar archivos de texto e imágenes a
blob storage y descargarlos. Además, también se muestra al recuperar una lista de archivos desde blob storage y
eliminar archivos.
Para obtener más información sobre el almacenamiento de Azure, consulte Introducción al almacenamiento.

Introducción a Blob Storage


Almacenamiento de blobs consta de tres componentes, que se muestran en el diagrama siguiente:
Todo el acceso a Azure Storage es a través de una cuenta de almacenamiento. Una cuenta de almacenamiento
puede contener un número ilimitado de contenedores, y un contenedor puede almacenar un número ilimitado de
blobs, hasta alcanzar el límite de capacidad de la cuenta de almacenamiento.
Un blob es un archivo de cualquier tipo y tamaño. Azure Storage admite tres tipos diferentes de blob:
Los blobs en bloques están optimizados para streaming y almacenamiento de objetos en la nube y son una
buena opción para almacenar las copias de seguridad, archivos multimedia, documentos, etcetera. Los blobs en
bloques pueden ser hasta 195Gb de tamaño.
Anexar blobs son similares a los blobs en bloques, pero están optimizados para anexar las operaciones, como el
registro. Anexar blobs pueden ser hasta 195Gb de tamaño.
Los blobs en páginas están optimizados para operaciones frecuentes de lectura/escritura y se utilizan
normalmente para almacenar las máquinas virtuales y sus discos. Blobs en páginas pueden tener hasta 1Tb de
tamaño.

NOTE
Tenga en cuenta que las cuentas de almacenamiento de blobs admiten bloques y anexar blobs, pero no los blobs en páginas.

Un blob se carga en Azure Storage y descargado de Azure Storage, como una secuencia de bytes. Por lo tanto, los
archivos se deben convertir en una secuencia de bytes antes de la carga y convertido de nuevo a su representación
original después de la descarga.
Cada objeto que se almacena en el almacenamiento de Azure tiene una dirección URL única. El nombre de cuenta
de almacenamiento forma el subdominio de esa dirección y la combinación de nombre de dominio y subdominio
forms un extremo para la cuenta de almacenamiento. Por ejemplo, si la cuenta de almacenamiento se denomina
mystorageaccount, el extremo de blob predeterminado para la cuenta de almacenamiento es
https://mystorageaccount.blob.core.windows.net .

La dirección URL para tener acceso a un objeto en una cuenta de almacenamiento se crea anexando la ubicación
del objeto en la cuenta de almacenamiento para el punto de conexión. Por ejemplo, una dirección de blob tendrá el
formato https://mystorageaccount.blob.core.windows.net/mycontainer/myblob .

Programa de instalación
El proceso para integrar una cuenta de almacenamiento de Azure en una aplicación de Xamarin.Forms es como
sigue:
1. Cree una cuenta de almacenamiento. Para obtener más información, consulte crear una cuenta de
almacenamiento.
2. Agregar el biblioteca de cliente de Azure Storage a la aplicación de Xamarin.Forms.
3. Configurar la cadena de conexión de almacenamiento. Para obtener más información, consulte conectarse a
Azure Storage.
4. Agregar using directivas para el Microsoft.WindowsAzure.Storage y Microsoft.WindowsAzure.Storage.Blob
espacios de nombres a las clases que tendrá acceso a Azure Storage.

Conectarse a Azure Storage


Se debe autenticar cada solicitud realizada en los recursos de la cuenta de almacenamiento. Aunque los blobs se
pueden configurar para admitir la autenticación anónima, hay dos enfoques principales de que una aplicación
puede usar para autenticarse con una cuenta de almacenamiento:
Clave compartida. Este enfoque utiliza la clave de cuenta y el nombre de la cuenta de almacenamiento de Azure
para servicios de almacenamiento de acceso. Una cuenta de almacenamiento se asigna a dos claves privadas en
la creación que puede usarse para la autenticación de clave compartida.
Firma de acceso compartido. Se trata de un token que se puede anexar a una dirección URL que permite el
acceso delegado a un recurso de almacenamiento, con los permisos especifica, durante el período de tiempo
que sea válida.
Se pueden especificar las cadenas de conexión que incluya la información de autenticación necesaria para tener
acceso a recursos de Azure Storage desde una aplicación. Además, se puede configurar una cadena de conexión
para conectarse al emulador de almacenamiento de Azure desde Visual Studio.

NOTE
Azure Storage admite HTTP y HTTPS en una cadena de conexión. Sin embargo, se recomienda utilizar HTTPS.

Conectar con el emulador de almacenamiento de Azure


El emulador de almacenamiento de Azure proporciona un entorno local que emula el Azure blob, cola y tabla de
servicios para fines de desarrollo.
La siguiente cadena de conexión debe utilizarse para conectarse al emulador de almacenamiento de Azure:

UseDevelopmentStorage=true

Para obtener más información sobre el emulador de almacenamiento de Azure, consulte usar el emulador de
almacenamiento de Azure para desarrollo y pruebas.
Conectarse a Azure Storage mediante una clave compartida
El siguiente formato de cadena de conexión se debe usar para conectarse a Azure Storage con una clave
compartida:

DefaultEndpointsProtocol=[http|https];AccountName=myAccountName;AccountKey=myAccountKey

myAccountName se debe reemplazar por el nombre de la cuenta de almacenamiento y myAccountKey debe


reemplazarse por una de las dos claves de acceso de cuenta.

NOTE
Uso compartido cuando la autenticación de clave, el nombre de cuenta y la clave de cuenta se distribuirá a cada persona que
usa la aplicación, que proporcionará acceso de lectura/escritura completo a la cuenta de almacenamiento. Por lo tanto, use la
autenticación de clave compartida solo con fines de prueba y nunca distribuir claves a otros usuarios.

Conectarse a Azure Storage mediante una firma de acceso compartido


El siguiente formato de cadena de conexión se debe usar para conectarse a Azure Storage con una SAS:
BlobEndpoint=myBlobEndpoint;SharedAccessSignature=mySharedAccessSignature

myBlobEndpoint debe reemplazarse por la dirección URL del extremo de blob, y mySharedAccessSignature debe
reemplazarse por la SAS. La SAS proporciona el protocolo, el punto de conexión de servicio y las credenciales
para acceder al recurso.

NOTE
Se recomienda la autenticación de SAS para aplicaciones de producción. Sin embargo, en una aplicación de producción se
debe recuperar la SAS de un servicio back-end a petición, en lugar de que se incluye con la aplicación.
Para obtener más información sobre las firmas de acceso compartido, consulte utilizando firmas de acceso
compartido (SAS ).

Creación de un contenedor
El GetContainer método se usa para recuperar una referencia a un contenedor con nombre, que puede usarse
para recuperar los blobs del contenedor o a agregar al contenedor de blobs. El siguiente ejemplo de código
muestra la GetContainer método:

static CloudBlobContainer GetContainer(ContainerType containerType)


{
var account = CloudStorageAccount.Parse(Constants.StorageConnection);
var client = account.CreateCloudBlobClient();
return client.GetContainerReference(containerType.ToString().ToLower());
}

El CloudStorageAccount.Parse método analiza una cadena de conexión y devuelve un CloudStorageAccount


instancia que representa la cuenta de almacenamiento. Un CloudBlobClient , a continuación, se crea la instancia,
que se utiliza para recuperar los contenedores y blobs, mediante el CreateCloudBlobClient método. El
GetContainerReference método recupera el contenedor especificado como un CloudBlobContainer instancia antes
de que se devuelva al método de llamada. En este ejemplo, el nombre del contenedor es el ContainerType valor de
enumeración, puede convertida en una cadena en minúsculas.

NOTE
Los nombres de contenedor deben estar en minúsculas y deben empezar por una letra o un número. Además, solo puede
contener letras, números y guiones y debe tener entre 3 y 63 caracteres.

El GetContainer método se invoca de la siguiente manera:

var container = GetContainer(containerType);

El CloudBlobContainer instancia, a continuación, se puede usar para crear un contenedor si aún no existe:

await container.CreateIfNotExistsAsync();

De forma predeterminada, un contenedor recién creado es privado. Esto significa que se debe especificar una clave
de acceso de almacenamiento para recuperar los blobs del contenedor. Para obtener información acerca de cómo
realizar los blobs dentro de un público de contenedor, consulte crear un contenedor.

Cargar datos en un contenedor


El UploadFileAsync método se usa para cargar una secuencia de bytes de datos al almacenamiento de blobs y se
muestra en el ejemplo de código siguiente:
public static async Task<string> UploadFileAsync(ContainerType containerType, Stream stream)
{
var container = GetContainer(containerType);
await container.CreateIfNotExistsAsync();

var name = Guid.NewGuid().ToString();


var fileBlob = container.GetBlockBlobReference(name);
await fileBlob.UploadFromStreamAsync(stream);

return name;
}

Después de recuperar una referencia de contenedor, el método crea el contenedor si aún no existe. Un nuevo Guid
, a continuación, se crea para que actúe como un nombre de blob único y una referencia de blob en bloques se
recupera como una CloudBlockBlob instancia. El flujo de datos, a continuación, se carga en el blob mediante el
UploadFromStreamAsync método, que crea el blob si todavía no existe o lo sobrescribe si ya existe.

Antes de poder cargar un archivo con este método de almacenamiento de blobs, primero debe convertirse en una
secuencia de bytes. Esto se muestra en el ejemplo de código siguiente:

var byteData = Encoding.UTF8.GetBytes(text);


uploadedFilename = await AzureStorage.UploadFileAsync(ContainerType.Text, new MemoryStream(byteData));

El datos se convierten en una matriz de bytes que se encapsula como una secuencia que se pasa a la
text
UploadFileAsync método.

Descarga de datos de un contenedor


El GetFileAsync método se utiliza para descargar datos de blob de Azure Storage y se muestra en el ejemplo de
código siguiente:

public static async Task<byte[]> GetFileAsync(ContainerType containerType, string name)


{
var container = GetContainer(containerType);

var blob = container.GetBlobReference(name);


if (await blob.ExistsAsync())
{
await blob.FetchAttributesAsync();
byte[] blobBytes = new byte[blob.Properties.Length];

await blob.DownloadToByteArrayAsync(blobBytes, 0);


return blobBytes;
}
return null;
}

Después de recuperar una referencia de contenedor, el método recupera una referencia de blob para los datos
almacenados. Si existe el blob, sus propiedades se recuperan mediante el FetchAttributesAsync método. Se crea
una matriz de bytes del tamaño correcto y se descarga el blob como una matriz de bytes que se devuelve al
método de llamada.
Después de descargar los datos de blob de bytes, deben convertirse en su representación original. Esto se muestra
en el ejemplo de código siguiente:
var byteData = await AzureStorage.GetFileAsync(ContainerType.Text, uploadedFilename);
string text = Encoding.UTF8.GetString(byteData);

Se recupera la matriz de bytes de almacenamiento de Azure mediante el GetFileAsync método antes de que se
convierta a un UTF8 cadena codificada.

Mostrar datos en un contenedor


El GetFilesListAsync método se usa para recuperar una lista de los blobs almacenados en un contenedor y se
muestra en el ejemplo de código siguiente:

public static async Task<IList<string>> GetFilesListAsync(ContainerType containerType)


{
var container = GetContainer(containerType);

var allBlobsList = new List<string>();


BlobContinuationToken token = null;

do
{
var result = await container.ListBlobsSegmentedAsync(token);
if (result.Results.Count() > 0)
{
var blobs = result.Results.Cast<CloudBlockBlob>().Select(b => b.Name);
allBlobsList.AddRange(blobs);
}
token = result.ContinuationToken;
} while (token != null);

return allBlobsList;
}

Después de recuperar una referencia de contenedor, el método usa el contenedor ListBlobsSegmentedAsync


método para recuperar las referencias a los blobs dentro del contenedor. Los resultados devueltos por la
ListBlobsSegmentedAsync se enumeran método mientras el BlobContinuationToken instancia no es null . Cada
blob se transmite devueltos IListBlobItem a un CloudBlockBlob en pedidos acceso a la Name propiedad del blob,
antes de que sea el valor se agrega a la allBlobsList colección. Una vez el BlobContinuationToken instancia es
null , ha devuelto el último nombre de blob y ejecución sale del bucle.

Eliminar datos de un contenedor


El DeleteFileAsync método se usa para eliminar un blob de un contenedor y se muestra en el ejemplo de código
siguiente:

public static async Task<bool> DeleteFileAsync(ContainerType containerType, string name)


{
var container = GetContainer(containerType);
var blob = container.GetBlobReference(name);
return await blob.DeleteIfExistsAsync();
}

Después de recuperar una referencia de contenedor, el método recupera una referencia de blob para el blob
especificado. A continuación, se elimina el blob con el DeleteIfExistsAsync método.

Vínculos relacionados
Almacenamiento de Azure (ejemplo)
Introducción al almacenamiento
Cómo usar Blob Storage desde Xamarin
Uso de firmas de acceso compartido (SAS )
Windows Azure Storage (NuGet)
Buscar datos con Azure Search y Xamarin.Forms
11/07/2019 • 20 minutes to read • Edit Online

descargar el ejemplo
Azure Search es un servicio en la nube que proporciona la indización y consulta las capacidades de los datos
cargados. Esto quita los requisitos de infraestructura y la complejidad de algoritmo de búsqueda tradicionalmente
asociada con la implementación de la funcionalidad de búsqueda en una aplicación. En este artículo se muestra
cómo usar la biblioteca de Microsoft Azure Search para integrar la búsqueda de Azure en una aplicación de
Xamarin.Forms.

Información general
Los datos se almacenan en Azure Search como índices y documentos. Un índice es un almacén de datos que se
pueden buscar por el servicio Azure Search y es conceptualmente similar a una tabla de base de datos. Un
documento es una unidad única de datos en un índice de búsqueda y es conceptualmente similar a una fila de la
base de datos. Al cargar los documentos y enviar las consultas de búsqueda para Azure Search, las solicitudes se
realizan en un índice específico en el servicio de búsqueda.
Cada solicitud realizada a Azure Search debe incluir el nombre del servicio y una clave de API. Hay dos tipos de
clave de API:
Las claves de administración conceder derechos completos para todas las operaciones. Esto incluye la
administración del servicio, crear y eliminar orígenes de datos y los índices.
Claves de consulta conceder acceso de solo lectura a índices y documentos y debe usarse en aplicaciones que
emiten solicitudes de búsqueda.
La solicitud más comunes para Azure Search es ejecutar una consulta. Hay dos tipos de consulta que se pueden
enviar:
Un búsqueda consulta busca uno o más elementos en todos los campos de búsqueda en un índice. Las
consultas de búsqueda se crean con la sintaxis simplificada, o la sintaxis de consulta de Lucene. Para obtener
más información, consulte sintaxis de consulta Simple en Azure Search, y sintaxis de consulta de Lucene en
Azure Search.
Un filtro consulta evalúa una expresión booleana en todos los campos filtrables de un índice. Filtrar las
consultas se crean con un subconjunto del lenguaje de filtro de OData. Para obtener más información, consulte
sintaxis de expresiones de OData para Azure Search.
Las consultas de búsqueda y filtrar las consultas pueden utilizarse por separado o conjuntamente. Cuando se usan
juntas, la consulta de filtro se aplica primero a todo el índice y, a continuación, se realiza la consulta de búsqueda en
los resultados de la consulta de filtro.
Azure Search también admite recuperar sugerencias basadas en la entrada de búsqueda. Para obtener más
información, consulte sugerencia consultas.

Programa de instalación
El proceso de integración de Azure Search en una aplicación de Xamarin.Forms es como sigue:
1. Crear un servicio Azure Search. Para obtener más información, consulte crear un servicio de Azure Search
mediante el Portal de Azure.
2. Quitar Silverlight como una plataforma de destino de la biblioteca de clases portables (PCL ) de la solución de
Xamarin.Forms. Esto puede realizarse al cambiar el perfil PCL a cualquier perfil que admite el desarrollo
multiplataforma, pero no es compatible con Silverlight, como el perfil de 151 o 92.
3. Agregar el biblioteca de Microsoft Azure Search paquete NuGet al proyecto PCL en la solución de
Xamarin.Forms.
Después de realizar estos pasos, la API de biblioteca de Microsoft Search puede usarse para administrar los
orígenes de datos y los índices de búsqueda, cargar y administrar documentos y ejecutar consultas.

Crear el índice de búsqueda de Azure


Debe definir un esquema de índice que se asigna a la estructura de los datos que se va a buscar. Esto puede ser
complicado en el Portal de Azure, o utilizando mediante programación la SearchServiceClient clase. Esta clase
administra las conexiones con Azure Search y puede usarse para crear un índice. En el ejemplo de código siguiente
se muestra cómo crear una instancia de esta clase:

var searchClient =
new SearchServiceClient(Constants.SearchServiceName, new SearchCredentials(Constants.AdminApiKey));

El sobrecarga de constructor toma un nombre de servicio de búsqueda y un


SearchServiceClient
SearchCredentials objeto como argumentos, con el SearchCredentials ajuste objeto el clave de administración
para el servicio Azure Search. El clave de administración es necesario para crear un índice.

NOTE
Una sola SearchServiceClient instancia debe utilizarse en una aplicación para evitar que se abra demasiadas conexiones a
Azure Search.

Un índice está definido por el Index de objeto, como se muestra en el ejemplo de código siguiente:

static void CreateSearchIndex()


{
var index = new Index()
{
Name = Constants.Index,
Fields = new[]
{
new Field("id", DataType.String) { IsKey = true, IsRetrievable = true },
new Field("name", DataType.String) { IsRetrievable = true, IsFilterable = true, IsSortable = true,
IsSearchable = true },
new Field("location", DataType.String) { IsRetrievable = true, IsFilterable = true, IsSortable = true,
IsSearchable = true },
new Field("details", DataType.String) { IsRetrievable = true, IsFilterable = true, IsSearchable = true
},
new Field("imageUrl", DataType.String) { IsRetrievable = true }
},
Suggesters = new[]
{
new Suggester("nameSuggester", SuggesterSearchMode.AnalyzingInfixMatching, new[] { "name" })
}
};

searchClient.Indexes.Create(index);
}

El Index.Name propiedad debe establecerse en el nombre del índice y el Index.Fields propiedad debe
establecerse en una matriz de Field objetos. Cada Field instancia especifica un nombre, un tipo y las
propiedades, que especifican cómo se usa el campo. Estas propiedades incluyen:
IsKey : indica si el campo es la clave del índice. Solo un campo en el índice de tipo DataType.String , debe
designarse como campo de clave.
IsFacetable : indica si es posible llevar a cabo la navegación por facetas en este campo. El valor
predeterminado es false .
IsFilterable : indica si el campo se puede usar en las consultas de filtro. El valor predeterminado es false .
IsRetrievable : indica si se puede recuperar el campo en los resultados de búsqueda. El valor predeterminado
es true .
IsSearchable : indica si el campo está incluido en las búsquedas de texto completo. El valor predeterminado es
false .
IsSortable : indica si el campo se puede utilizar en OrderBy expresiones. El valor predeterminado es false .

NOTE
Cambio de un índice después de su implementación implica volver a generar y volver a cargar los datos.

Un Index objeto opcionalmente, puede especificar un Suggesters propiedad, que define los campos en el índice
que se usará para admitir Autocompletar o sugerencia consultas de búsqueda. El Suggesters propiedad debe
establecerse en una matriz de Suggester objetos que definen los campos que se utilizan para generar resultados
de la sugerencia de la búsqueda.
Después de crear el Index objeto, el índice se crea mediante una llamada a Indexes.Create en el
SearchServiceClient instancia.

NOTE
Al crear un índice de una aplicación que debe mantenerse con capacidad de respuesta, use el Indexes.CreateAsync
método.

Para obtener más información, consulte crear un índice de Azure Search mediante el SDK de .NET.

Al eliminar el índice de búsqueda de Azure


Se puede eliminar un índice mediante una llamada a Indexes.Delete en el SearchServiceClient instancia:

searchClient.Indexes.Delete(Constants.Index);

Cargar datos en el índice de búsqueda de Azure


Después de definir el índice, se pueden cargar datos a ella mediante uno de los dos modelos:
Modelo de extracción : periódicamente los datos se ingieren en Azure Cosmos DB, Azure SQL Database,
Azure Blob Storage o SQL Server hospedado en una máquina Virtual de Azure.
Modelo de inserción – datos se envían mediante programación al índice. Este es el modelo adoptado en este
artículo.
Un SearchIndexClient para importar datos en el índice, se debe crear la instancia. Esto puede realizarse mediante
una llamada a la SearchServiceClient.Indexes.GetClient método, como se muestra en el ejemplo de código
siguiente:
static void UploadDataToSearchIndex()
{
var indexClient = searchClient.Indexes.GetClient(Constants.Index);

var monkeyList = MonkeyData.Monkeys.Select(m => new


{
id = Guid.NewGuid().ToString(),
name = m.Name,
location = m.Location,
details = m.Details,
imageUrl = m.ImageUrl
});

var batch = IndexBatch.New(monkeyList.Select(IndexAction.Upload));


try
{
indexClient.Documents.Index(batch);
}
catch (IndexBatchException ex)
{
// Sometimes when the Search service is under load, indexing will fail for some
// documents in the batch. Compensating actions like delaying and retrying should be taken.
// Here, the failed document keys are logged.
Console.WriteLine("Failed to index some documents: {0}",
string.Join(", ", ex.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key)));
}
}

Datos que se importarán en el índice se empaquetan como un IndexBatch objeto, que encapsula una colección de
IndexAction objetos. Cada IndexAction instancia contiene un documento y una propiedad que indica qué acción
va a realizar en el documento de Azure Search. En el ejemplo de código anterior, el IndexAction.Upload acción que
se especifica, los resultados en el documento que se inserta en el índice si es nuevo, o reemplaza si ya existe. El
IndexBatch objeto, a continuación, se envía al índice mediante una llamada a la Documents.Index método en el
SearchIndexClient objeto. Para obtener información acerca de otras acciones de indexación, consulte decidir qué
acción de indexación para usar.

NOTE
Solo 1000 documentos pueden incluirse en una única solicitud de indexación.

Tenga en cuenta que en el ejemplo de código anterior, el monkeyList colección se crea como un objeto anónimo de
una colección de Monkey objetos. Esto crea los datos para el id campo y se resuelve como la asignación de
mayúsculas y minúsculas Pascal Monkey los nombres de campo de índice de búsqueda de nombres de propiedad
a mayúsculas y minúsculas. Como alternativa, esta asignación también puede realizarse agregando el
[SerializePropertyNamesAsCamelCase] atributo a la Monkey clase.

Para obtener más información, consulte cargar datos en Azure Search mediante el SDK de .NET.

Consultar el índice de búsqueda de Azure


Un SearchIndexClient para consultar un índice, se debe crear la instancia. Cuando una aplicación ejecuta
consultas, es conveniente seguir el principio de privilegios mínimos y crear un SearchIndexClient directamente,
pasando el clave de consulta como argumento. Esto garantiza que los usuarios tengan acceso de solo lectura a
índices y documentos. Este método se muestra en el ejemplo de código siguiente:
SearchIndexClient indexClient =
new SearchIndexClient(Constants.SearchServiceName, Constants.Index, new
SearchCredentials(Constants.QueryApiKey));

El SearchIndexClient sobrecarga de constructor toma un nombre de servicio de búsqueda, el nombre del índice y
un SearchCredentials objeto como argumentos, con el SearchCredentials ajuste objeto el clave de consulta para
el servicio Azure Search.
Consultas de búsqueda
El índice se puede consultar mediante una llamada a la Documents.SearchAsync método en el SearchIndexClient de
instancia, tal como se muestra en el ejemplo de código siguiente:

async Task AzureSearch(string text)


{
Monkeys.Clear();

var searchResults = await indexClient.Documents.SearchAsync<Monkey>(text);


foreach (SearchResult<Monkey> result in searchResults.Results)
{
Monkeys.Add(new Monkey
{
Name = result.Document.Name,
Location = result.Document.Location,
Details = result.Document.Details,
ImageUrl = result.Document.ImageUrl
});
}
}

El SearchAsync método toma un argumento de búsqueda de texto y un elemento opcional SearchParameters


objeto que se puede usar para refinar la consulta. Una consulta de búsqueda se especifica como el argumento de
texto de búsqueda, mientras que una consulta de filtro se puede especificar estableciendo el Filter propiedad de
la SearchParameters argumento. En el ejemplo de código siguiente se muestra que ambos tipos de consulta:

var parameters = new SearchParameters


{
Filter = "location ne 'China' and location ne 'Vietnam'"
};
var searchResults = await indexClient.Documents.SearchAsync<Monkey>(text, parameters);

Esta consulta de filtro se aplica a todo el índice y quita los documentos de los resultados donde el location campo
no es igual a China y no es igual a Vietnam. Después de filtrado, la consulta de búsqueda se realiza en los
resultados de la consulta de filtro.

NOTE
Para filtrar sin buscar, pasar * como el argumento de texto de búsqueda.

El SearchAsync método devuelve un DocumentSearchResult objeto que contiene los resultados de consulta. Este
objeto es de tipo enumerado con cada Document objeto se crea como un Monkey de objetos y agregado a la
Monkeys ObservableCollection para su presentación. Los siguientes capturas de pantalla show consulta resultados
de búsqueda de Azure Search:
Para obtener más información acerca de la búsqueda y filtrado, consulte consultas del índice de Azure Search con
el SDK de .NET.
Consultas de sugerencia
Azure Search permite a las sugerencias que se soliciten basándose en una consulta de búsqueda, mediante una
llamada a la Documents.SuggestAsync método en el SearchIndexClient instancia. Esto se muestra en el ejemplo de
código siguiente:
async Task AzureSuggestions(string text)
{
Suggestions.Clear();

var parameters = new SuggestParameters()


{
UseFuzzyMatching = true,
HighlightPreTag = "[",
HighlightPostTag = "]",
MinimumCoverage = 100,
Top = 10
};

var suggestionResults =
await indexClient.Documents.SuggestAsync<Monkey>(text, "nameSuggester", parameters);

foreach (var result in suggestionResults.Results)


{
Suggestions.Add(new Monkey
{
Name = result.Text,
Location = result.Document.Location,
Details = result.Document.Details,
ImageUrl = result.Document.ImageUrl
});
}
}

El SuggestAsync método toma un argumento de texto de búsqueda, el nombre del proveedor de sugerencias para
usar (que se define en el índice), y un elemento opcional SuggestParameters objeto que se puede usar para refinar
la consulta. El SuggestParameters instancia establece las propiedades siguientes:
UseFuzzyMatching : cuando se establece en true , Azure Search encontrará sugerencias incluso si hay un
carácter sustituido o no encontrado en el texto de búsqueda.
HighlightPreTag : la etiqueta a la que se antepone a resultados de la sugerencia.
HighlightPostTag : la etiqueta a la que se anexa a la sugerencia de visitas.
MinimumCoverage : representa el porcentaje del índice que debe estar cubierto por una consulta de sugerencia
para la consulta notifica un estado correcto. El valor predeterminado es 80.
Top : el número de sugerencias para recuperar. Debe ser un entero entre 1 y 100, con un valor predeterminado
de 5.
El efecto general es que se devolverán los primeros 10 resultados del índice con resaltado de referencias y los
resultados incluirán documentos que contengan los términos de búsqueda de la ortografía del mismo modo.
El SuggestAsync método devuelve un DocumentSuggestResult objeto que contiene los resultados de consulta. Este
objeto es de tipo enumerado con cada Document objeto se crea como un Monkey de objetos y agregado a la
Monkeys ObservableCollection para su presentación. Las capturas de pantalla siguientes muestran los resultados
de sugerencia obtenidos de Azure Search:
Tenga en cuenta que en la aplicación de ejemplo, el SuggestAsync método solo se invoca cuando el usuario
termina de escribir un término de búsqueda. Sin embargo, también puede usarse para admitir las consultas de
búsqueda de autocompletado ejecutando en cada keypress.

Resumen
En este artículo se muestra cómo usar la biblioteca de Microsoft Azure Search para integrar la búsqueda de Azure
en una aplicación de Xamarin.Forms. Azure Search es un servicio en la nube que proporciona la indización y
consulta las capacidades de los datos cargados. Esto quita los requisitos de infraestructura y la complejidad de
algoritmo de búsqueda tradicionalmente asociada con la implementación de la funcionalidad de búsqueda en una
aplicación.

Vínculos relacionados
Búsqueda de Azure (ejemplo)
Documentación de Azure Search
Biblioteca de Microsoft Azure Search
Introducción a Azure Functions
11/07/2019 • 2 minutes to read • Edit Online

descargar el ejemplo
Comience a crear su primera función de Azure que interactúa con Xamarin.Forms.
Visual Studio 2019
Visual Studio 2017
Visual Studio para Mac

Instrucciones paso a paso


Además del vídeo, puede seguir estas instrucciones para compilar su primera función mediante Visual Studio.

Vínculos relacionados
Documentos de Azure Functions
Implementación de una simple función de Azure con un cliente de Xamarin.Forms (ejemplo)
Azure SignalR Service con Xamarin.Forms
11/07/2019 • 19 minutes to read • Edit Online

descargar el ejemplo
ASP.NET Core SignalR es un modelo de aplicación que simplifica el proceso de incorporación de comunicación en
tiempo real a las aplicaciones. Azure SignalR Service permite el rápido desarrollo e implementación de
aplicaciones escalables de SignalR. Las funciones de Azure son los métodos de código sin servidor y de corta
duración que se pueden combinar con aplicaciones de forma escalable, controlado por eventos.
Este artículo y el ejemplo muestran cómo combinar Azure SignalR Service y Azure Functions con Xamarin.Forms,
para entregar mensajes en tiempo real a los clientes conectados.

Crear un servicio Azure SignalR y aplicación de Azure Functions


La aplicación de ejemplo consta de tres componentes principales: un centro de Azure SignalR Service, una
instancia de Azure Functions con dos funciones y una aplicación móvil que pueda enviar y recibir mensajes. Estos
componentes interactúan como sigue:
1. La aplicación móvil invoca un Negotiate función de Azure para obtener información sobre el centro de
SignalR.
2. La aplicación móvil usa la información de la negociación para registrarse con el centro de SignalR y forma de
una conexión.
3. Después del registro, la aplicación móvil envía mensajes a la hablar función de Azure.
4. El hablar función pasa el mensaje entrante para el centro de SignalR.
5. El centro de SignalR transmite el mensaje a todas las instancias de aplicación móvil conectada, incluido el
remitente original.

NOTE
El Negotiate y hablar funciones en la aplicación de ejemplo se pueden ejecutar localmente mediante Visual Studio de 2019
y las herramientas de Azure en tiempo de ejecución. Sin embargo, no se emula localmente Azure SignalR Service y resulta
difícil exponer hospedados localmente Azure Functions a dispositivos físicos o virtuales para las pruebas. Se recomienda
implementar las funciones de Azure a una instancia de la aplicación de Azure Functions, ya que esto permite que las pruebas
multiplataforma. Para obtener detalles de implementación, consulte implementar Azure Functions con Visual Studio de 2019.

Crear un servicio de Azure SignalR


Se puede crear un servicio de Azure SignalR eligiendo crear un recurso en la esquina superior izquierda de Azure
portal y buscar SignalR. Azure SignalR Service puede crearse en el nivel gratis. Azure SignalR Service debe estar
en sin servidor modo de servicio. Si opta por accidente el valor predeterminado o el modo de servicio clásico,
puede cambiar más adelante en las propiedades de Azure SignalR Service.
Captura de pantalla siguiente muestra la creación de un nuevo servicio de Azure SignalR:
Una vez creado, el claves sección de Azure SignalR Service contiene un cadena de conexión, que se usa para
conectar la aplicación de Azure Functions con el centro de SignalR. Captura de pantalla siguiente muestra dónde
encontrar la cadena de conexión de Azure SignalR Service:

Esta cadena de conexión se usa para implementar Azure Functions con Visual Studio de 2019.
Crear una aplicación de Azure Functions
Para probar la aplicación de ejemplo, debe crear una nueva aplicación de Azure Functions en Azure portal. Tome
nota de la nombre de la aplicación como esta dirección URL se utiliza en la aplicación de ejemplo Constants.cs
archivo. Captura de pantalla siguiente muestra la creación de una nueva aplicación de las funciones de Azure
denominado "xdocsfunctions":
Las funciones de Azure pueden implementarse en una instancia de la aplicación de Azure Functions de Visual
Studio de 2019. Las secciones siguientes describen la implementación de dos funciones en la aplicación de
ejemplo a una instancia de la aplicación de Azure Functions.
Crear Azure Functions en Visual Studio de 2019
La aplicación de ejemplo contiene una biblioteca de clases denominada ChatServer, que incluye dos funciones de
Azure sin servidor en archivos denominados Negotiate.cs y Talk.cs.
El Negotiatefunción responde a solicitudes web con un SignalRConnectionInfo objeto que contiene un
AccessToken propiedad y un Url propiedad. La aplicación móvil usa estos valores para registrarse con el centro
de SignalR. El código siguiente muestra el Negotiate función:

[FunctionName("Negotiate")]
public static SignalRConnectionInfo GetSignalRInfo(
[HttpTrigger(AuthorizationLevel.Anonymous,"get",Route = "negotiate")]
HttpRequest req,
[SignalRConnectionInfo(HubName = "simplechat")]
SignalRConnectionInfo connectionInfo)
{
return connectionInfo;
}

El Talk función responde a las solicitudes de HTTP POST que proporcionan un objeto de mensaje en el cuerpo
de POST. El cuerpo de POST se transforma en un SignalRMessage y se reenvía al centro de SignalR. El código
siguiente muestra el Talk función:
[FunctionName("Talk")]
public static async Task<IActionResult> Run(
[HttpTrigger(
AuthorizationLevel.Anonymous,
"post",
Route = "talk")]
HttpRequest req,
[SignalR(HubName = "simplechat")]
IAsyncCollector<SignalRMessage> questionR,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");

try
{
string json = await new StreamReader(req.Body).ReadToEndAsync();
dynamic obj = JsonConvert.DeserializeObject(json);

var name = obj.name.ToString();


var text = obj.text.ToString();

var jObject = new JObject(obj);

await questionR.AddAsync(
new SignalRMessage
{
Target = "newMessage",
Arguments = new[] { jObject }
});

return new OkObjectResult($"Hello {name}, your message was '{text}'");


}
catch (Exception ex)
{
return new BadRequestObjectResult("There was an error: " + ex.Message);
}
}

Para obtener más información acerca de las funciones de Azure y Azure Functions Apps, consulte documentación
de Azure Functions.
Implementación de Azure Functions con Visual Studio de 2019
2019 de Visual Studio le permite implementar las funciones en una aplicación de Azure Functions. Hospedado en
Azure functions facilitan las pruebas multiplataforma al proporcionar un punto de conexión de prueba accesible
para todos los dispositivos.
Haciendo clic en la aplicación de las funciones de ejemplo y elegir publicar inicia el cuadro de diálogo para
publicar las funciones en la aplicación de Azure Functions. Si ha seguido los pasos anteriores para configurar una
aplicación de función de Azure, puede elegir seleccionar existente para publicar las aplicaciones de ejemplo para
la aplicación de Azure Functions. Captura de pantalla siguiente muestra la publicación opciones del cuadro de
diálogo en Visual Studio de 2019:
Una vez que haya iniciado sesión con tu cuenta Microsoft, puede buscar y elegir la aplicación de Azure Functions
como el destino de publicación. Captura de pantalla siguiente muestra un ejemplo de aplicación de Azure
Functions en el cuadro de diálogo de publicación de Visual Studio de 2019:

Después de seleccionar una aplicación de Azure Functions se muestran la instancia, la dirección URL del sitio,
configuración y otra información sobre el destino de la aplicación de Azure Functions. Elija editar la
configuración de Azure App Service y escriba la cadena de conexión en el remoto campo. La cadena de
conexión está usando el Negotiate y hablar funciones para conectarse a Azure SignalR Service y está disponible
en el claves sección de Azure SignalR Servicio en el portal de Azure. Para obtener más información acerca de la
cadena de conexión, consulte crear un servicio de Azure SignalR .
Una vez haya escrito la cadena de conexión, haga clic en publicar para implementar las funciones a la aplicación
de Azure Functions. Una vez que haya finalizado, se enumerarán las funciones en la aplicación de Azure Functions
en Azure portal. Captura de pantalla siguiente muestra las funciones publicadas en el portal de Azure:
Integrar el servicio Azure SignalR con Xamarin.Forms
La integración entre Azure SignalR Service y la aplicación de Xamarin.Forms es una clase de servicio de SignalR
que se crea una instancia en el MainPage clase con controladores de eventos asignados a los tres eventos. Para
obtener más información acerca de estos controladores de eventos, consulte utilizar la clase de servicio de SignalR
en Xamarin.Forms.
La aplicación de ejemplo incluye un Constants.cs clase que debe personalizarse con el punto de conexión de
dirección URL de la aplicación de Azure Functions. Establezca el valor de la HostName propiedad a su dirección de
la aplicación de Azure Functions. El siguiente código muestra la Constants.cs propiedades con un ejemplo
HostName valor:

public static class Constants


{
public static string HostName { get; set; } = "https://example-functions-app.azurewebsites.net/";

// Used to differentiate message types sent via SignalR. This


// sample only uses a single message type.
public static string MessageName { get; set; } = "newMessage";

public static string Username


{
get
{
return $"{Device.RuntimePlatform} User";
}
}
}

NOTE
El Username propiedad en la aplicación de ejemplo Constants.cs archivo utiliza el dispositivo RuntimePlatform valor como
el nombre de usuario. Esto facilita probar dispositivos entre plataformas e identificar qué dispositivo está enviando el
mensaje. En una aplicación real, este valor sería un nombre de usuario único, recopilados durante un inicio de sesión de
seguridad o inicie sesión en proceso.

La clase de servicio SignalR


El SignalRService clase en el ChatClient proyecto en la aplicación de ejemplo muestra una implementación que
invoca las funciones en una aplicación de Azure Functions para conectarse a un servicio de Azure SignalR.
El SendMessageAsync método en el SignalRService clase se utiliza para enviar mensajes a los clientes conectados a
Azure SignalR Service. Este método realiza una solicitud HTTP POST a la hablar función hospedado en la
aplicación de Azure Functions, incluido un serializada Message objeto como la carga POST. El hablar función pasa
el mensaje al servicio Azure SignalR para difusión a todos los clientes. En el código siguiente se muestra el método
SendMessageAsync :

public async Task SendMessageAsync(string username, string message)


{
IsBusy = true;

var newMessage = new Message


{
Name = username,
Text = message
};

var json = JsonConvert.SerializeObject(newMessage);


var content = new StringContent(json, Encoding.UTF8, "application/json");
var result = await client.PostAsync($"{Constants.HostName}/api/talk", content);

IsBusy = false;
}

El ConnectAsync método en el SignalRService clase realiza una solicitud HTTP GET a la Negotiate función
hospedado en la aplicación de Azure Functions. El Negotiate función devuelve JSON que se deserializa en una
instancia de la NegotiateInfo clase. Una vez el NegotiateInfo se recupera el objeto, se usa para registrar
directamente con Azure SignalR Service mediante una instancia de la HubConnection clase.
SignalR de ASP.NET Core traduce los datos de entrada de la conexión abierta en mensajes y permite a los
desarrolladores definir tipos de mensajes y enlazar controladores de eventos a los mensajes entrantes por tipo. El
ConnectAsync método registra un controlador de eventos para el nombre de mensaje definido en la aplicación de
ejemplo Constants.cs archivo, que es "newMessage" de forma predeterminada.
En el código siguiente se muestra el método ConnectAsync :

public async Task ConnectAsync()


{
try
{
IsBusy = true;

string negotiateJson = await client.GetStringAsync($"{Constants.HostName}/api/negotiate");


NegotiateInfo negotiate = JsonConvert.DeserializeObject<NegotiateInfo>(negotiateJson);
HubConnection connection = new HubConnectionBuilder()
.WithUrl(negotiate.Url, options =>
{
options.AccessTokenProvider = async () => negotiate.AccessToken;
})
.Build();

connection.On<JObject>(Constants.MessageName, AddNewMessage);
await connection.StartAsync();

IsConnected = true;
IsBusy = false;

Connected?.Invoke(this, true, "Connection successful.");


}
catch (Exception ex)
{
ConnectionFailed?.Invoke(this, false, ex.Message);
}
}

El AddNewMessage método se enlaza como el controlador de eventos en el ConnectAsync del mensaje como se
muestra en el código anterior. Cuando se recibe un mensaje, el AddNewMessage se llama al método con los datos del
mensaje proporcionados como un JObject . El AddNewMessage método convierte el JObject a una instancia de la
Message clase y, a continuación, invoca el controlador para NewMessageReceived si uno se ha enlazado. En el código
siguiente se muestra el método AddNewMessage :

public void AddNewMessage(JObject message)


{
Message messageModel = new Message
{
Name = message.GetValue("name").ToString(),
Text = message.GetValue("text").ToString(),
TimeReceived = DateTime.Now
};

NewMessageReceived?.Invoke(this, messageModel);
}

Utilice la clase de servicio de SignalR en Xamarin.Forms


Uso de la clase de servicio SignalR en Xamarin.Forms se logra enlazando el SignalRService clase de eventos en el
MainPage clase de código subyacente.

El Connected eventos en el SignalRService clase se desencadena cuando una conexión SignalR se completó
correctamente. El ConnectionFailed eventos en el SignalRService clase se desencadena cuando se produce un
error en una conexión de SignalR. El SignalR_ConnectionChanged está enlazado el método controlador de eventos
para eventos en el MainPage constructor. Este controlador de eventos actualiza los Estados del botón Conectar y
envío basándose en la conexión success argumento y agrega el mensaje proporcionado por el evento a la cola de
chat mediante el AddMessage método. El código siguiente muestra el SignalR_ConnectionChanged método
controlador de eventos:

void SignalR_ConnectionChanged(object sender, bool success, string message)


{
connectButton.Text = "Connect";
connectButton.IsEnabled = !success;
sendButton.IsEnabled = success;

AddMessage($"Server connection changed: {message}");


}

El NewMessageReceived eventos en el SignalRService clase se desencadena cuando se recibe un mensaje nuevo de


Azure SignalR Service. El SignalR_NewMessageReceived método controlador de eventos está enlazado a la
NewMessageReceived eventos en el MainPage constructor. Este controlador de eventos convierte entrante Message
objeto en una cadena y lo agrega a la cola de chat mediante el AddMessage método. El código siguiente muestra el
SignalR_NewMessageReceived método controlador de eventos:

void SignalR_NewMessageReceived(object sender, Model.Message message)


{
string msg = $"{message.Name} ({message.TimeReceived}) - {message.Text}";
AddMessage(msg);
}

El AddMessage método agrega un nuevo mensaje como un Label objeto a la cola de chat. El AddMessage método
se suele denominar los controladores de eventos desde fuera el subproceso principal de la interfaz de usuario, por
lo que obliga a las actualizaciones de la interfaz de usuario que se produzca en el subproceso principal para evitar
excepciones. En el código siguiente se muestra el método AddMessage :
void AddMessage(string message)
{
Device.BeginInvokeOnMainThread(() =>
{
Label label = new Label
{
Text = message,
HorizontalOptions = LayoutOptions.Start,
VerticalOptions = LayoutOptions.Start
};

messageList.Children.Add(label);
});
}

Probar la aplicación
Puede probar la aplicación de chat de SignalR en iOS, Android y UWP siempre que tenga:
1. Crea un servicio de Azure SignalR.
2. Crea una aplicación de Azure Functions.
3. Personalizar el Constants.cs archivo con el punto de conexión de la aplicación de Azure Functions.
Una vez completados estos pasos y se ejecuta la aplicación, haga clic en el Connect botón forma una conexión con
Azure SignalR Service. Escribe un mensaje y haga clic en el enviar resultados de botón en mensajes que aparecen
en la cola de chat en cualquier conectado aplicaciones móviles.

Vínculos relacionados
Creación de aplicaciones móviles en tiempo real con Xamarin y SignalR
Introducción a SignalR
Introducción a Azure Functions
Documentación de Azure Functions
Ejemplo de chat de SignalR MVVM
Xamarin.Forms y Azure Cognitive Services
11/07/2019 • 2 minutes to read • Edit Online

Introducción
Microsoft Cognitive Services son un conjunto de API, SDK y servicios disponibles para los desarrolladores para
que sus aplicaciones más inteligentes mediante la adición de características como reconocimiento facial,
reconocimiento de voz y comprensión del lenguaje. En este artículo se proporciona una introducción a la aplicación
de ejemplo que muestra cómo invocar algunas de las API de Microsoft Cognitive Service desde las aplicaciones de
Xamarin.Forms.

Reconocimiento de voz
Microsoft Speech API es una API basada en la nube que proporciona algoritmos para procesar el lenguaje oral. En
este artículo se explica cómo usar la API de REST de reconocimiento de voz de Microsoft para convertir audio en
texto en una aplicación de Xamarin.Forms.

Corrector ortográfico
Bing Spell Check realiza contextual corrector ortográfico para el texto, que proporciona sugerencias de en línea
para palabras mal escritas. En este artículo se explica cómo usar Bing Spell Check REST API para corregir errores
ortográficos en una aplicación de Xamarin.Forms.

Traducción de texto
Microsoft Translator API puede usarse para traducir el texto de voz y a través de una API de REST. En este artículo
se explica cómo usar Microsoft Translator Text API para traducir texto de un idioma a otro en una aplicación de
Xamarin.Forms.

Reconocimiento de emociones
Face API toma una expresión facial de una imagen como entrada y devuelve los datos que incluyen los niveles de
confianza a través de un conjunto de emociones para cada cara de la imagen. En este artículo se explica cómo usar
Face API para que reconozca las emociones para evaluar una aplicación de Xamarin.Forms.
Introducción a Cognitive Services Xamarin.Forms y
Azure
11/07/2019 • 10 minutes to read • Edit Online

descargar el ejemplo
Microsoft Cognitive Services son un conjunto de API, SDK y servicios disponibles para los desarrolladores para
que sus aplicaciones más inteligentes mediante la adición de características como reconocimiento facial,
reconocimiento de voz y comprensión del lenguaje. En este artículo se proporciona una introducción a la
aplicación de ejemplo que muestra cómo invocar algunas de las API de Microsoft Cognitive Service.

Información general
El ejemplo adjunto es una aplicación de lista de tareas que proporciona funcionalidad para:
Ver una lista de tareas.
Agregar y editar tareas mediante el teclado en pantalla, o bien al realizar el reconocimiento de voz con la API de
voz de Microsoft. Para obtener más información acerca de cómo realizar el reconocimiento de voz, consulte
reconocimiento de voz con la API de Microsoft Speech.
Revisión ortográfica con Bing Spell Check API de tareas de comprobación. Para obtener más información,
consulte ortográfica con Bing Spell Check API.
Traducir las tareas del inglés al alemán mediante la API del traductor. Para obtener más información, consulte
traducción de texto mediante la API del traductor.
Eliminar las tareas.
Establece el estado de una tarea a 'listo'.
Valore la aplicación con reconocimiento de emociones, mediante Face API. Para obtener más información,
consulte mediante Face API de reconocimiento de emociones.
Las tareas se almacenan en una base de datos SQLite local. Para obtener más información sobre el uso de una
base de datos SQLite local, consulte trabajar con una base de datos Local.
El TodoListPage se muestra cuando se inicia la aplicación. Esta página muestra una lista de las tareas que se
almacenan en la base de datos local y permite al usuario para crear una nueva tarea o para clasificar la solicitud:
Se pueden crear nuevos elementos, haga clic en el + botón, que se desplaza a la TodoItemPage . Esta página
también se puede navegar a seleccionando una tarea:
El TodoItemPage permite crear, editar, revisar, tareas traducido, se guardan y se eliminan. El reconocimiento de voz
se puede usar para crear o editar una tarea. Esto se logra al presionar el botón de micrófono para iniciar la
grabación y presionando el botón mismo una segunda vez para detener la grabación, que envía la grabación a la
API de reconocimiento de voz de Bing.
Al hacer clic en el botón emoticones en el TodoListPage navega a la RateAppPage , que se usa para realizar el
reconocimiento de emociones en una imagen de una expresión facial:

El RateAppPage permite al usuario tomar una foto de su cara, que se envía a Face API con la emoción devuelta que
se muestran.

Descubra los componentes de aplicación


El proyecto de código compartido para la aplicación de ejemplo consta de cinco carpetas principales:

CARPETA PROPÓSITO

Modelos Contiene las clases de modelo de datos para la aplicación. Esto


incluye la TodoItem (clase), que modela un único elemento
de datos usados por la aplicación. La carpeta también incluye
las clases utilizadas para las respuestas JSON de modelo
devueltas desde diferentes Microsoft Cognitive Service APIs.

Repositorios Contiene el ITodoItemRepositoryinterfaz y


TodoItemRepository clase que se usan para realizar
operaciones de base de datos.
CARPETA PROPÓSITO

Servicios Contiene las interfaces y clases que se usan para tener acceso
a diferentes Microsoft Cognitive Service APIs, junto con
interfaces que se usan por el DependencyService clase para
buscar las clases que implementan las interfaces en los
proyectos de plataforma.

Utils Contiene el (clase), que es utilizado por el


Timer
AuthenticationService clase renovar un token de acceso
JWT cada 9 minutos.

Vistas Contiene las páginas de la aplicación.

El proyecto de código compartido también contiene algunos archivos importantes:

ARCHIVO PROPÓSITO

Constants.cs El Constants (clase), que especifica las claves de API y los


puntos de conexión de Microsoft Cognitive Service APIs que
se invocan. Las constantes de clave de API requieren
actualización para tener acceso a las distintas API de Cognitive
Service.

App.xaml.cs El App clase es responsable de crear instancias tanto la


primera página que se mostrará la aplicación en cada
plataforma, y el TodoManager clase que se usa para invocar
operaciones de base de datos.

Paquetes NuGet
La aplicación de ejemplo usa los siguientes paquetes NuGet:
Newtonsoft.Json : proporciona un marco JSON para. NET.
PCLStorage : proporciona un conjunto de archivos local las API de E/S de multiplataforma.
sqlite-net-pcl : proporciona almacenamiento de base de datos de SQLite.
Xam.Plugin.Media : proporciona las API de selección y toma de fotografía de multiplataforma.

Además, estos paquetes de NuGet también instalan sus propias dependencias.


Los datos del modelo
La aplicación de ejemplo usa el TodoItem clase para modelar los datos que se muestra y se almacenan en la base
de datos SQLite local. En el ejemplo de código siguiente se muestra la clase TodoItem :

public class TodoItem


{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public bool Done { get; set; }
}

El ID propiedad se utiliza para identificar de forma única cada TodoItem de instancia y está decorado con
atributos de SQLite que hacen que la propiedad de una clave principal de incremento automático de la base de
datos.
Invocar operaciones de base de datos
El TodoItemRepository clase implementa las operaciones de base de datos y una instancia de la clase se puede
acceder mediante el App.TodoManager propiedad. La TodoItemRepository clase proporciona los métodos siguientes
para invocar operaciones de base de datos:
GetAllItemsAsync : recupera todos los elementos de la base de datos SQLite local.
GetItemAsync : recupera un elemento especificado de la base de datos SQLite local.
SaveItemAsync : crea o actualiza un elemento de la base de datos SQLite local.
DeleteItemAsync : elimina el elemento especificado de la base de datos SQLite local.
Implementaciones de proyecto de plataforma
El Services carpeta en el proyecto de código compartido contiene la IFileHelper y IAudioRecorderService
interfaces que se usan por el DependencyService clase para buscar las clases que implementan las interfaces en los
proyectos de plataforma.
El IFileHelper interfaz se implementa mediante el FileHelper clase en cada proyecto de la plataforma. Esta clase
consta de un único método, GetLocalFilePath , que devuelve una ruta de acceso local para almacenar la base de
datos de SQLite.
El IAudioRecorderService interfaz se implementa mediante el AudioRecorderService clase en cada proyecto de la
plataforma. Esta clase consta de StartRecording , StopRecording y que admiten métodos, que use las API de
plataforma para grabar audio desde el micrófono del dispositivo y almacenan como un archivo wav. En iOS, el
AudioRecorderService usa el AVFoundation API para grabar audio. En Android, el AudioRecordService usa el
AudioRecord API para grabar audio. En la plataforma Universal de Windows ( UWP ), el AudioRecorderService usa
el AudioGraph API para grabar audio.
Invocar servicios cognitivos
La aplicación de ejemplo invoca los servicios cognitivos de Microsoft siguiente:
Microsoft Speech API. Para obtener más información, consulte reconocimiento de voz con la API de Microsoft
Speech.
Bing Spell Check API. Para obtener más información, consulte ortográfica con Bing Spell Check API.
Traducir API. Para obtener más información, consulte traducción de texto mediante la API del traductor.
Face API. Para obtener más información, consulte mediante Face API de reconocimiento de emociones.

Vínculos relacionados
Documentación de Microsoft Cognitive Services
Todo Cognitive Services (ejemplo)
Reconocimiento de voz con la API de Microsoft
Speech
11/07/2019 • 9 minutes to read • Edit Online

descargar el ejemplo
Microsoft Speech API es una API basada en la nube que proporciona algoritmos para procesar el lenguaje oral.
En este artículo se explica cómo usar la API de REST de reconocimiento de voz de Microsoft para convertir audio
en texto en una aplicación de Xamarin.Forms.

Información general
Microsoft Speech API tiene dos componentes:
Un reconocimiento de voz API para convertir las palabras habladas en texto. El reconocimiento de voz puede
realizarse a través de una API de REST, la biblioteca de cliente o la biblioteca de servicio.
Un texto a voz API para convertir texto en palabras habladas. Conversión de texto a voz se realiza a través de
una API de REST.
En este artículo se centra en realizar el reconocimiento de voz a través de la API de REST. Mientras que las
bibliotecas de cliente y el servicio admiten la devolución de resultados parciales, la API de REST solo puede
devolver un resultado de reconocimiento único, sin los resultados parciales.
Para usar la API de voz de Microsoft, se debe obtener una clave de API. Esto se puede obtener desde Azure
portal. Para obtener más información, consulte crear una cuenta de Cognitive Services en el portal de Azure.
Para obtener más información acerca de la API de voz de Microsoft, consulte documentación de Microsoft
Speech API.

Autenticación
Todas las solicitudes realizadas a la API de REST de voz de Microsoft requieren un token de acceso JSON Web
Token (JWT), que puede obtenerse desde el servicio de token de cognitive services en
https://api.cognitive.microsoft.com/sts/v1.0/issueToken . Se puede obtener un token mediante una solicitud
POST al servicio de token, especificando un Ocp-Apim-Subscription-Key encabezado que contiene la clave de API
como su valor.
En el ejemplo de código siguiente se muestra cómo solicitar un acceso token al servicio de token:
public AuthenticationService(string apiKey)
{
subscriptionKey = apiKey;
httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey);
}
...
async Task<string> FetchTokenAsync(string fetchUri)
{
UriBuilder uriBuilder = new UriBuilder(fetchUri);
uriBuilder.Path += "/issueToken";
var result = await httpClient.PostAsync(uriBuilder.Uri.AbsoluteUri, null);
return await result.Content.ReadAsStringAsync();
}

El token de acceso devuelta, que es texto Base64, tiene una fecha de expiración de 10 minutos. Por lo tanto, la
aplicación de ejemplo renueva el token de acceso cada 9 minutos.
Debe especificarse el token de acceso en cada API de REST de Microsoft Speech llamar como una Authorization
el prefijo con la cadena de encabezado Bearer , tal y como se muestra en el ejemplo de código siguiente:

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);

Un error pasar un token de acceso válido a la API de REST de Microsoft Speech provocará un error de 403
respuesta.

Realizar el reconocimiento de voz


El reconocimiento de voz se logra mediante una solicitud POST a la recognition API en
https://speech.platform.bing.com/speech/recognition/ . Una única solicitud no puede contener más de 10
segundos de audio, y la duración total de solicitudes no puede superar 14 segundos.
Contenido de audio se debe colocar en el cuerpo de POST de la solicitud en formato wav.
En la aplicación de ejemplo, el RecognizeSpeechAsync método invoca el proceso de reconocimiento de voz:

public async Task<SpeechResult> RecognizeSpeechAsync(string filename)


{
...

// Read audio file to a stream


var file = await PCLStorage.FileSystem.Current.LocalStorage.GetFileAsync(filename);
var fileStream = await file.OpenAsync(PCLStorage.FileAccess.Read);

// Send audio stream to Bing and deserialize the response


string requestUri = GenerateRequestUri(Constants.SpeechRecognitionEndpoint);
string accessToken = authenticationService.GetAccessToken();
var response = await SendRequestAsync(fileStream, requestUri, accessToken, Constants.AudioContentType);
var speechResult = JsonConvert.DeserializeObject<SpeechResult>(response);

fileStream.Dispose();
return speechResult;
}

Audio se registra en cada proyecto específico de plataforma como datos wav PCM y el RecognizeSpeechAsync
método usa el PCLStorage paquete NuGet para abrir el archivo de audio como una secuencia. Se recupera la
solicitud de reconocimiento de voz se genera el URI y un token de acceso al servicio de token. La solicitud de
reconocimiento de voz se registra en el recognition API, que devuelve una respuesta JSON que contiene el
resultado. La respuesta JSON se deserializa, con lo que se devuelve al método de llamada para su presentación.
Configurar el reconocimiento de voz
El proceso de reconocimiento de voz se puede configurar mediante la especificación de parámetros de consulta
HTTP:

string GenerateRequestUri(string speechEndpoint)


{
// To build a request URL, you should follow:
// https://docs.microsoft.com/azure/cognitive-services/speech/getstarted/getstartedrest
string requestUri = speechEndpoint;
requestUri += @"dictation/cognitiveservices/v1?";
requestUri += @"language=en-us";
requestUri += @"&format=simple";
System.Diagnostics.Debug.WriteLine(requestUri.ToString());
return requestUri;
}

La configuración principal realizada por el GenerateRequestUri método consiste en establecer la configuración


regional del contenido de audio. Para obtener una lista de las configuraciones regionales admitidas, consulte
idiomas admitidos.
Enviar la solicitud
El SendRequestAsync método realiza la solicitud POST a la API de REST de voz de Microsoft y devuelve la
respuesta:

async Task<string> SendRequestAsync(Stream fileStream, string url, string bearerToken, string contentType)
{
if (httpClient == null)
{
httpClient = new HttpClient();
}
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);

var content = new StreamContent(fileStream);


content.Headers.TryAddWithoutValidation("Content-Type", contentType);
var response = await httpClient.PostAsync(url, content);
return await response.Content.ReadAsStringAsync();
}

Este método crea la solicitud de POST:


Contiene la secuencia de audio en un StreamContent instancia, que proporciona contenido HTTP basado en
una secuencia.
Establecer el Content-Type encabezado de la solicitud a audio/wav; codec="audio/pcm"; samplerate=16000 .
Agregar el token de acceso a la Authorization encabezado, el prefijo con la cadena Bearer .

A continuación, se envía la solicitud POST a recognition API. La respuesta, a continuación, lee y devuelve al
método de llamada.
El recognition API enviará el código de estado HTTP 200 (OK) en la respuesta, siempre que la solicitud es válida,
lo que indica que la solicitud es correcta y que la información solicitada está en la respuesta. Para obtener una lista
de posibles respuestas de error, consulte Troubleshooting.
Procesamiento de la respuesta
La respuesta de API se devuelve en formato JSON, con el texto reconocido que esté incluido en la name etiqueta.
Los siguientes datos JSON muestran un mensaje de respuesta correcta típico:
{
"RecognitionStatus":"Success",
"DisplayText":"Go shopping tomorrow.",
"Offset":16000000,
"Duration":17100000
}

En la aplicación de ejemplo, la respuesta JSON se deserializa en un SpeechResult instancia, con lo que se


devuelve al método de llamada para su presentación, como se muestra en las capturas de pantalla siguiente:

Resumen
En este artículo se explica cómo usar la API de REST de voz de Microsoft para convertir audio en texto en una
aplicación de Xamarin.Forms. Además de realizar el reconocimiento de voz, Speech API de Microsoft también
puede convertir texto en palabras habladas.

Vínculos relacionados
Documentación de Microsoft Speech API.
Consumir un servicio Web RESTful
Todo Cognitive Services (ejemplo)
La revisión ortográfica con Bing Spell Check API
11/07/2019 • 9 minutes to read • Edit Online

descargar el ejemplo
Bing Spell Check realiza contextual corrector ortográfico para el texto, que proporciona sugerencias de en línea
para palabras mal escritas. En este artículo se explica cómo usar Bing Spell Check REST API para corregir errores
ortográficos en una aplicación de Xamarin.Forms.

Información general
Bing Spell Check REST API tiene dos modos de funcionamiento y un modo debe especificarse al realizar una
solicitud a la API:
Spell corrige un texto breve (de hasta 9 palabras) sin realizar ningún cambio de mayúsculas y minúsculas.
Proof corrige texto largo, proporciona correcciones de mayúsculas y minúsculas y puntuación básica y
suprime correcciones agresivas.
Para usar Bing Spell Check API, se debe obtener una clave de API. Esto puede obtenerse en pruebe Cognitive
Services
Para obtener una lista de los idiomas admitidos por Bing Spell Check API, consulte idiomas admitidos. Para
obtener más información acerca de Bing Spell Check API, consulte documentación de Bing Spell comprobar.

Autenticación
Cada solicitud realizada a Bing Spell Check API necesita una clave de API que se debe especificar como el valor
de la Ocp-Apim-Subscription-Key encabezado. En el ejemplo de código siguiente se muestra cómo agregar la clave
de API para el Ocp-Apim-Subscription-Key encabezado de una solicitud:

public BingSpellCheckService()
{
httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", Constants.BingSpellCheckApiKey);
}

Error al pasar una clave de API válida a Bing Spell Check API se producirá un error en la 401 respuesta.

Realizar la revisión ortográfica


Corrector ortográfico puede lograrse mediante una solicitud GET o POST a la SpellCheck API en
https://api.cognitive.microsoft.com/bing/v7.0/SpellCheck . Al realizar una solicitud GET, el texto que se puede
comprobar la ortografía se envía como un parámetro de consulta. Al realizar una solicitud POST, se envía el texto
que se puede comprobar la ortografía en el cuerpo de solicitud. Las solicitudes GET se limitan a corrector
ortográfico 1500 caracteres debido a la limitación de longitud de cadena de parámetro de consulta. Por lo tanto,
normalmente se deben realizar las solicitudes POST a menos que las cadenas cortas son que se va a comprobar
la ortografía.
En la aplicación de ejemplo, el SpellCheckTextAsync método invoca el proceso de revisión ortográfica:
public async Task<SpellCheckResult> SpellCheckTextAsync(string text)
{
string requestUri = GenerateRequestUri(Constants.BingSpellCheckEndpoint, text, SpellCheckMode.Spell);
var response = await SendRequestAsync(requestUri);
var spellCheckResults = JsonConvert.DeserializeObject<SpellCheckResult>(response);
return spellCheckResults;
}

El SpellCheckTextAsync método genera un URI de solicitud y, a continuación, envía la solicitud para el SpellCheck
API, que devuelve una respuesta JSON que contiene el resultado. La respuesta JSON se deserializa, con lo que se
devuelve al método de llamada para su presentación.
Configurar el corrector ortográfico
El proceso de revisión ortográfica puede configurarse mediante la especificación de parámetros de consulta
HTTP:

string GenerateRequestUri(string spellCheckEndpoint, string text, SpellCheckMode mode)


{
string requestUri = spellCheckEndpoint;
requestUri += string.Format("?text={0}", text); // text to spell check
requestUri += string.Format("&mode={0}", mode.ToString().ToLower()); // spellcheck mode - proof or spell
return requestUri;
}

Este método establece el texto que se puede comprobar la ortografía y el modo de comprobación de corrección
ortográfica.
Para obtener más información acerca de Bing Spell Check REST API, consulte referencia de Spell Check API v7.
Enviar la solicitud
El SendRequestAsync método realiza la solicitud GET a Bing Spell Check REST API y devuelve la respuesta:

async Task<string> SendRequestAsync(string url)


{
var response = await httpClient.GetAsync(url);
return await response.Content.ReadAsStringAsync();
}

Este método envía la solicitud GET a la SpellCheck API con la URL de solicitud especifica el texto que se deben
traducir y el modo de comprobación de corrección ortográfica. La respuesta, a continuación, lee y devuelve al
método de llamada.
El SpellCheck API enviará el código de estado HTTP 200 (OK) en la respuesta, siempre que la solicitud es válida,
lo que indica que la solicitud es correcta y que la información solicitada está en la respuesta. Para obtener una lista
de objetos de respuesta, consulte objetos de respuesta.
Procesamiento de la respuesta
Se devuelve la respuesta de API en formato JSON. Los siguientes datos JSON muestran el mensaje de respuesta
para el texto mal escrito Go shappin tommorow :
{
"_type":"SpellCheck",
"flaggedTokens":[
{
"offset":3,
"token":"shappin",
"type":"UnknownToken",
"suggestions":[
{
"suggestion":"shopping",
"score":1
}
]
},
{
"offset":11,
"token":"tommorow",
"type":"UnknownToken",
"suggestions":[
{
"suggestion":"tomorrow",
"score":1
}
]
}
],
"correctionType":"High"
}

El flaggedTokens matriz contiene una matriz de palabras del texto que se han marcado como no se ha escrito
correctamente o que están gramaticalmente incorrecto. La matriz estará vacía si no hay errores ortográficos o
gramaticales se encuentran. Las etiquetas dentro de la matriz son:
offset – un desplazamiento de base cero desde el principio de la cadena de texto a la palabra que se marcó.
token : la palabra en la cadena de texto que no se ha escrito correctamente o no es correcta gramaticalmente.
type : el tipo de error que provocó la palabra que se marcará. Hay dos valores posibles: RepeatedToken y
UnknownToken .
suggestions : una matriz de palabras que se corregirá el error de ortografía o gramática. La matriz está
formada por un suggestion y un score , lo que indica el nivel de confianza de que la corrección sugerida es
correcta.
En la aplicación de ejemplo, la respuesta JSON se deserializa en un SpellCheckResult instancia, con lo que se
devuelve al método de llamada para su presentación. El siguiente ejemplo de código muestra cómo el
SpellCheckResult instancia se procesa para su presentación:

var spellCheckResult = await bingSpellCheckService.SpellCheckTextAsync(TodoItem.Name);


foreach (var flaggedToken in spellCheckResult.FlaggedTokens)
{
TodoItem.Name = TodoItem.Name.Replace(flaggedToken.Token,
flaggedToken.Suggestions.FirstOrDefault().Suggestion);
}

Este código recorre el FlaggedTokens colección y reemplaza cualquier mal escrita o palabras gramaticalmente
incorrectas en el texto de origen con la primera sugerencia. Las capturas de pantalla siguientes se muestran antes
y después de la revisión ortográfica:
NOTE
El ejemplo anterior usa Replace por motivos de simplicidad, pero a través de una gran cantidad de texto podría
reemplazar el token erróneo. La API proporciona el offset valor que debe usarse en aplicaciones de producción para
identificar la ubicación correcta en el texto de origen para realizar una actualización.

Resumen
En este artículo se explica cómo usar Bing Spell Check REST API para corregir errores ortográficos en una
aplicación de Xamarin.Forms. Bing Spell Check realiza contextual corrector ortográfico para el texto, que
proporciona sugerencias de en línea para palabras mal escritas.

Vínculos relacionados
Documentación de Bing Spell Check
Consumir un servicio Web RESTful
Todo Cognitive Services (ejemplo)
Referencia de Bing Spell Check API v7
Traducción de texto mediante la API del traductor
11/07/2019 • 8 minutes to read • Edit Online

descargar el ejemplo
Microsoft Translator API puede usarse para traducir el texto de voz y a través de una API de REST. En este
artículo se explica cómo usar Microsoft Translator Text API para traducir texto de un idioma a otro en una
aplicación de Xamarin.Forms.

Información general
La API del traductor tiene dos componentes:
Una API de REST para traducir texto de un idioma en el texto de otro idioma de traducción de texto. La API
detecta automáticamente el idioma del texto que se ha enviado antes de trasladarlo.
Una API de REST para transcriba la voz de un idioma en el texto de otro idioma de traducción de voz. La API
también integra capacidades de texto a voz para hablar de vuelta el texto traducido.
En este artículo se centra en traducir el texto de un idioma a otro utilizando Translator Text API.
Para usar Translator Text API, se debe obtener una clave de API. Esto puede obtenerse en cómo suscribirse a
Microsoft Translator Text API.
Para obtener más información acerca de Microsoft Translator Text API, consulte documentación de Translator Text
API.

Autenticación
Cada solicitud realizada a Translator Text API requiere un token de acceso JSON Web Token (JWT), que puede
obtenerse desde el servicio de token de cognitive services en
https://api.cognitive.microsoft.com/sts/v1.0/issueToken . Se puede obtener un token mediante una solicitud
POST al servicio de token, especificando un Ocp-Apim-Subscription-Key encabezado que contiene la clave de API
como su valor.
En el ejemplo de código siguiente se muestra cómo solicitar un acceso token al servicio de token:

public AuthenticationService(string apiKey)


{
subscriptionKey = apiKey;
httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey);
}
...
async Task<string> FetchTokenAsync(string fetchUri)
{
UriBuilder uriBuilder = new UriBuilder(fetchUri);
uriBuilder.Path += "/issueToken";
var result = await httpClient.PostAsync(uriBuilder.Uri.AbsoluteUri, null);
return await result.Content.ReadAsStringAsync();
}

El token de acceso devuelta, que es texto Base64, tiene una fecha de expiración de 10 minutos. Por lo tanto, la
aplicación de ejemplo renueva el token de acceso cada 9 minutos.
Debe especificarse el token de acceso en cada Translator Text API llamar como una Authorization el prefijo con la
cadena de encabezado Bearer , tal y como se muestra en el ejemplo de código siguiente:

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);

Para obtener más información sobre el servicio de token de cognitive services, consulte API de Token de
autenticación.

Realizar la traducción de texto


Traducción de texto se puede lograr mediante una solicitud GET a la translate API en
https://api.microsofttranslator.com/v2/http.svc/translate . En la aplicación de ejemplo, el TranslateTextAsync
método invoca el proceso de traducción de texto:

public async Task<string> TranslateTextAsync(string text)


{
...
string requestUri = GenerateRequestUri(Constants.TextTranslatorEndpoint, text, "en", "de");
string accessToken = authenticationService.GetAccessToken();
var response = await SendRequestAsync(requestUri, accessToken);
var xml = XDocument.Parse(response);
return xml.Root.Value;
}

El TranslateTextAsync método genera un URI de solicitud y recupera un token de acceso al servicio de token. A
continuación, se envía la solicitud de traducción de texto a la translate API, que devuelve una respuesta XML
que contiene el resultado. Se analiza la respuesta XML y se devuelve el resultado de la traducción al método de
llamada para su presentación.
Para obtener más información acerca de las API de REST de traducción de texto, consulte Microsoft Translator
Text API.
Configurar la traducción de texto
El proceso de traducción de texto se puede configurar mediante la especificación de parámetros de consulta
HTTP:

string GenerateRequestUri(string endpoint, string text, string to)


{
string requestUri = endpoint;
requestUri += string.Format("?text={0}", Uri.EscapeUriString(text));
requestUri += string.Format("&to={0}", to);
return requestUri;
}

Este método establece el texto que se deben traducir y el idioma para traducir el texto para. Para obtener una lista
de los idiomas compatibles con Microsoft Translator, consulte idiomas admitidos en Microsoft Translator Text API.

NOTE
Si una aplicación necesita saber qué idioma está el texto, el Detect API se puede llamar para detectar el idioma de la
cadena de texto.

Enviar la solicitud
El SendRequestAsync método realiza la solicitud GET a la API de REST de traducción de texto y devuelve la
respuesta:

async Task<string> SendRequestAsync(string url, string bearerToken)


{
if (httpClient == null)
{
httpClient = new HttpClient();
}
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);

var response = await httpClient.GetAsync(url);


return await response.Content.ReadAsStringAsync();
}

Este método basa la solicitud GET mediante la adición de token de acceso para el Authorization encabezado, el
prefijo con la cadena Bearer . A continuación, se envía la solicitud GET a la translate API con la URL de solicitud
especifica el texto que se deben traducir y el idioma para traducir el texto a. La respuesta, a continuación, lee y
devuelve al método de llamada.
El translate API enviará el código de estado HTTP 200 (OK) en la respuesta, siempre que la solicitud es válida,
lo que indica que la solicitud es correcta y que la información solicitada está en la respuesta. Para obtener una lista
de posibles respuestas de error, vea los mensajes de respuesta en obtener traducir.
Procesamiento de la respuesta
La respuesta de API se devuelve en formato XML. Los siguientes datos XML muestran un mensaje de respuesta
correcta típico:

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Morgen kaufen gehen ein</string>

En la aplicación de ejemplo, se analiza la respuesta XML en un XDocument instancia, con el valor de la raíz XML
que se devuelve al método de llamada para su presentación, como se muestra en las capturas de pantalla
siguiente:
Resumen
En este artículo se explica cómo usar Microsoft Translator Text API traducir texto de un idioma al texto de otro
idioma en una aplicación de Xamarin.Forms. Además de traducir el texto, Microsoft Translator API también
pueden transcribir voz de un idioma en el texto de otro idioma.

Vínculos relacionados
Documentación de Translator Text API.
Consumir un servicio Web RESTful
Todo Cognitive Services (ejemplo)
Microsoft Translator Text API.
Reconocimiento de emociones con la Face API
11/07/2019 • 10 minutes to read • Edit Online

descargar el ejemplo
Face API toma una expresión facial de una imagen como entrada y devuelve los datos que incluyen los niveles de
confianza a través de un conjunto de emociones para cada cara de la imagen. En este artículo se explica cómo
usar Face API para que reconozca las emociones para evaluar una aplicación de Xamarin.Forms.

Información general
Face API puede realizar la detección de emociones para detectar la ira, desprecio, asco, miedo, felicidad,
neutralidad, tristeza y sorpresa, en una expresión facial. Universalmente y culturas estas emociones se comunican
a través de las mismas expresiones faciales básicas. Además de devolver un resultado de emociones para una
expresión facial, Face API puede también devuelve un rectángulo de selección de caras detectadas. Tenga en
cuenta que se debe obtener una clave de API para usar Face API. Esto puede obtenerse en pruebe Cognitive
Services.
Reconocimiento de emociones puede realizarse a través de una biblioteca de cliente y una API de REST. En este
artículo se centra en realizar el reconocimiento de emociones a través de la API de REST. Para obtener más
información acerca de la API de REST, consulte Face API de REST.
Face API puede usarse también para reconocer las expresiones faciales de personas en vídeo y puede devolver un
resumen de sus emociones. Para obtener más información, consulte cómo analizar vídeos en tiempo real.
Para obtener más información acerca de Face API, consulte Face API.

Autenticación
Cada solicitud realizada a Face API necesita una clave de API que se debe especificar como el valor de la
Ocp-Apim-Subscription-Key encabezado. En el ejemplo de código siguiente se muestra cómo agregar la clave de
API para el Ocp-Apim-Subscription-Key encabezado de una solicitud:

public FaceRecognitionService()
{
_client = new HttpClient();
_client.DefaultRequestHeaders.Add("ocp-apim-subscription-key", Constants.FaceApiKey);
}

Error al pasar una clave de API válida para Face API producirá un error en la 401 respuesta.

Realizar el reconocimiento de emociones


Reconocimiento de emociones se realiza mediante una solicitud POST que contiene una imagen a la detect API
en https://[location].api.cognitive.microsoft.com/face/v1.0 , donde [location]] es la región utiliza para
obtener la clave de API. Los parámetros de solicitud opcionales son:
returnFaceId – Si se deben devolver faceIds de las caras detectadas. El valor predeterminado es true .
returnFaceLandmarks – Si se deben devolver los puntos de referencia de las caras detectadas. El valor
predeterminado es false .
returnFaceAttributes : si desea analizar y volver a especifican uno o más atributos faciales. Los atributos de
cara admitidos incluyen age , gender , headPose , smile , facialHair , glasses , emotion , hair , makeup ,
occlusion , accessories , blur , exposure , y noise . Tenga en cuenta que el análisis de atributos de cara tiene
costo adicional de cálculo y el tiempo.
Contenido de la imagen se debe colocar en el cuerpo de la solicitud POST como una dirección URL o datos
binarios.

NOTE
Formatos de archivo de imagen admitidos son JPEG, PNG, GIF y BMP, y el tamaño de archivo permitido es de 1KB a 4MB.

En la aplicación de ejemplo, el proceso de reconocimiento de emociones se invoca mediante una llamada a la


DetectAsync método:

Face[] faces = await _faceRecognitionService.DetectAsync(photoStream, true, false, new FaceAttributeType[] {


FaceAttributeType.Emotion });

Esta llamada al método especifica la secuencia que contiene los datos de imagen, que se deben devolver faceIds,
que no deben devolverse faciales, y que se debe analizar la emoción de la imagen. También especifica que los
resultados se devolverán como una matriz de Face objetos. A su vez, el DetectAsync método invoca el detect
API de REST que realiza el reconocimiento de emociones:

public async Task<Face[]> DetectAsync(Stream imageStream, bool returnFaceId, bool returnFaceLandmarks,


IEnumerable<FaceAttributeType> returnFaceAttributes)
{
var requestUrl =
$"{Constants.FaceEndpoint}/detect?returnFaceId={returnFaceId}" +
"&returnFaceLandmarks={returnFaceLandmarks}" +
"&returnFaceAttributes={GetAttributeString(returnFaceAttributes)}";
return await SendRequestAsync<Stream, Face[]>(HttpMethod.Post, requestUrl, imageStream);
}

Este método genera un URI de solicitud y, a continuación, envía la solicitud para el detect API a través de la
SendRequestAsync método.

NOTE
Debe usar la misma región en las llamadas de Face API que utilizó para obtener las claves de suscripción. Por ejemplo, si ha
obtenido las claves de suscripción desde el westus región, que será el punto de conexión de detección de caras
https://westus.api.cognitive.microsoft.com/face/v1.0/detect .

Enviar la solicitud
El SendRequestAsync método realiza la solicitud POST a Face API y devuelve el resultado como un Face matriz:
async Task<TResponse> SendRequestAsync<TRequest, TResponse>(HttpMethod httpMethod, string requestUrl,
TRequest requestBody)
{
var request = new HttpRequestMessage(httpMethod, Constants.FaceEndpoint);
request.RequestUri = new Uri(requestUrl);
if (requestBody != null)
{
if (requestBody is Stream)
{
request.Content = new StreamContent(requestBody as Stream);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
}
else
{
// If the image is supplied via a URL
request.Content = new StringContent(JsonConvert.SerializeObject(requestBody, s_settings),
Encoding.UTF8, "application/json");
}
}

HttpResponseMessage responseMessage = await _client.SendAsync(request);


if (responseMessage.IsSuccessStatusCode)
{
string responseContent = null;
if (responseMessage.Content != null)
{
responseContent = await responseMessage.Content.ReadAsStringAsync();
}
if (!string.IsNullOrWhiteSpace(responseContent))
{
return JsonConvert.DeserializeObject<TResponse>(responseContent, s_settings);
}
return default(TResponse);
}
else
{
...
}
return default(TResponse);
}

Si la imagen se proporciona a través de una secuencia, el método basa la solicitud POST ajustando el flujo de
imagen en un StreamContent instancia, que proporciona contenido HTTP basado en una secuencia. Como
alternativa, si la imagen se proporciona a través de una dirección URL, el método crea la solicitud POST
ajustando la dirección URL en un StringContent instancia, que proporciona contenido HTTP basado en una
cadena.
A continuación, se envía la solicitud POST a detect API. La respuesta es leer, se deserializa y se devuelve al
método de llamada.
El detect API enviará el código de estado HTTP 200 (OK) en la respuesta, siempre que la solicitud es válida, lo
que indica que la solicitud es correcta y que la información solicitada está en la respuesta. Para obtener una lista
de posibles respuestas de error, consulte Face API de REST.
Procesamiento de la respuesta
Se devuelve la respuesta de API en formato JSON. Los siguientes datos JSON muestran un mensaje de
respuesta correcta típico que proporciona los datos solicitados por la aplicación de ejemplo:
[
{
"faceId":"8a1a80fe-1027-48cf-a7f0-e61c0f005051",
"faceRectangle":{
"top":192,
"left":164,
"width":339,
"height":339
},
"faceAttributes":{
"emotion":{
"anger":0.0,
"contempt":0.0,
"disgust":0.0,
"fear":0.0,
"happiness":1.0,
"neutral":0.0,
"sadness":0.0,
"surprise":0.0
}
}
}
]

Un mensaje de respuesta correcta se compone de una matriz de entradas de cara ordenados según el tamaño del
rectángulo de cara en orden descendente, mientras que una respuesta vacía no indica ningún caras detectadas.
Cada uno de ellos reconoce cara incluye una serie de atributos de cara opcional, que se especifican mediante el
returnFaceAttributes argumento para el DetectAsync método.

En la aplicación de ejemplo, la respuesta JSON se deserializa en una matriz de Face objetos. Al interpretar los
resultados de Face API, las emociones detectadas se deben interpretar como la emoción con la puntuación más
alta, como las puntuaciones se normalizan para que sumen uno. Por lo tanto, la aplicación de ejemplo muestra la
emoción reconocida con la puntuación más alta para la cara detectada más grande en la imagen. Esto se consigue
con el código siguiente:

emotionResultLabel.Text = faces.FirstOrDefault().FaceAttributes.Emotion.ToRankedList().FirstOrDefault().Key;

Captura de pantalla siguiente muestra el resultado del proceso de reconocimiento de emociones en la aplicación
de ejemplo:
Resumen
En este artículo se explica cómo usar Face API para que reconozca las emociones para evaluar una aplicación de
Xamarin.Forms. Face API toma una expresión facial de una imagen como entrada y devuelve los datos que
incluyen la confianza entre un conjunto de emociones para cada cara de la imagen.

Vínculos relacionados
Face API.
Todo Cognitive Services (ejemplo)
Face API de REST
Xamarin.Forms y servicios Web
11/07/2019 • 2 minutes to read • Edit Online

Introducción
En este artículo se ofrece un tutorial de la aplicación de ejemplo de Xamarin.Forms que muestra cómo comunicar
con los servicios web diferentes. Los temas tratados incluyen la Anatomía de la aplicación, las páginas, el modelo
de datos y la invocación de operaciones del servicio web.

Consumir un servicio Web ASP.NET (ASMX)


Los servicios Web ASP.NET (ASMX) proporcionan la capacidad para crear servicios web que envían mensajes a
través de HTTP mediante el protocolo Simple de acceso a objetos (SOAP ). SOAP es un protocolo independiente de
la plataforma y lenguaje para crear y obtener acceso a servicios web. Los consumidores de un servicio de ASMX
no es necesario saber nada acerca de la plataforma, el modelo de objetos o el lenguaje de programación usado
para implementar el servicio. Sólo necesitan entender cómo enviar y recibir mensajes SOAP. En este artículo se
muestra cómo consumir un servicio web ASMX desde una aplicación de Xamarin.Forms.

Consumir un servicio Web de Windows Communication Foundation


(WCF)
WCF es el marco unificado de Microsoft para crear aplicaciones orientadas a servicios. Permite a los
desarrolladores crear aplicaciones distribuidas seguras, confiables, transacciones e interoperables. Existen
diferencias entre los servicios Web de ASP.NET (ASMX) y WCF, pero es importante comprender que WCF admite
las mismas funcionalidades que proporciona ASMX: los mensajes SOAP a través de HTTP. En este artículo se
muestra cómo consumir un servicio SOAP de WCF desde una aplicación de Xamarin.Forms.

Consumir un servicio Web RESTful


Representational State Transfer (REST) es un estilo de arquitectura para la creación de servicios web. Solicitudes
REST se realizan a través de HTTP utilizando los mismos verbos HTTP que los exploradores web que se usan para
recuperar las páginas web y para enviar datos a los servidores. En este artículo se muestra cómo consumir un
servicio web de RESTful desde una aplicación de Xamarin.Forms.
Introducción a servicios Web de Xamarin.Forms
11/07/2019 • 8 minutes to read • Edit Online

descargar el ejemplo
En este tema se ofrece un tutorial de la aplicación de ejemplo de Xamarin.Forms que muestra cómo comunicar
con los servicios web diferentes. Aunque cada servicio web utiliza una aplicación de ejemplo independiente, todos
son funcionalmente similares y comparten clases comunes.
La aplicación de lista de tareas pendientes de ejemplo que se describe a continuación se usa para demostrar
cómo obtener acceso a diferentes tipos de back-ends de web service con Xamarin.Forms. Proporciona
funcionalidad para:
Ver una lista de tareas.
Agregar, editar y eliminar tareas.
Establece el estado de una tarea a 'listo'.
Diga a los campos de nombre y las notas de la tarea.
En todos los casos, las tareas se almacenan en un back-end que se accede a través de un servicio web.
Cuando se inicia la aplicación, se muestra una página que enumera las tareas que se recuperan desde el servicio
web y permite al usuario crear una nueva tarea. Al hacer clic en una tarea desplaza a la aplicación a una segunda
página, donde la tarea puede se puede editar, se guardan, eliminan y habla. A continuación se muestra la
aplicación final:
Cada tema de esta guía proporciona un vínculo de descarga a un diferentes versión de la aplicación que se
muestra un tipo específico de back-end de web service. Descargue el código de ejemplo correspondiente en la
página de cada estilo de servicio web.

Descubra los componentes de aplicación


El proyecto de código compartido para cada aplicación de ejemplo consta de tres carpetas principales:

CARPETA PROPÓSITO

Datos Contiene las clases e interfaces utilizadas para administrar


elementos de datos y comunicarse con el servicio web. Como
mínimo, esto incluye la TodoItemManager (clase), que se
expone a través de una propiedad en el App clase para
invocar las operaciones del servicio web.

Modelos Contiene las clases de modelo de datos para la aplicación.


Como mínimo, esto incluye la TodoItem (clase), que modela
un único elemento de datos usados por la aplicación. La
carpeta puede incluir también las clases adicionales que se
usa para modelar los datos de usuario.

Vistas Contiene las páginas de la aplicación. Esto suele estar


compuesto por el TodoListPage y TodoItemPage clases y
las clases adicionales que se usa para realizar la autenticación.

El proyecto de código compartido para cada aplicación también consta de un número de archivos importantes:

ARCHIVO PROPÓSITO

Constants.cs El Constants (clase), que especifica las constantes utilizadas


por la aplicación para comunicarse con el servicio web. Estas
constantes requieren actualización para tener acceso a su
personal de back-end servicio creado en un proveedor.

ITextToSpeech.cs El ITextToSpeech interfaz, que especifica que el Speak


método debe proporcionarse mediante cualquier clase de
implementación.
ARCHIVO PROPÓSITO

Todo.cs El App clase que es responsable de crear instancias tanto la


primera página que se mostrará la aplicación en cada
plataforma, y el TodoItemManager clase que se utiliza para
invocar las operaciones del servicio web.

Páginas de vista
La mayoría de las aplicaciones de ejemplo contienen al menos dos páginas:
TodoListPage : esta página muestra una lista de TodoItem instancias y un icono de marca si la TodoItem.Done
propiedad es true . Al hacer clic en un elemento se desplaza a la TodoItemPage . Además, se pueden crear
nuevos elementos, haga clic en el + símbolos.
TodoItemPage : esta página muestra los detalles para el seleccionado TodoItem y permite que se puede
editar, guardar, elimina y habla.
Además, algunas aplicaciones de ejemplo contienen páginas adicionales que se usan para administrar el proceso
de autenticación de usuario.
Los datos del modelo
Cada aplicación de ejemplo usa el TodoItem clase para modelar los datos que se muestran y se envía al servicio
web para el almacenamiento. En el ejemplo de código siguiente se muestra la clase TodoItem :

public class TodoItem


{
public string ID { get; set; }
public string Name { get; set; }
public string Notes { get; set; }
public bool Done { get; set; }
}

El ID propiedad se utiliza para identificar de forma única cada TodoItem de instancia y cada servicio web sirve
para identificar los datos que se va a actualizar o eliminar.
Invocar operaciones de servicio web
Se tiene acceso a las operaciones del servicio Web a través de la TodoItemManager clase y una instancia de la clase
se pueden acceder mediante el App.TodoManager propiedad. La TodoItemManager clase proporciona los métodos
siguientes para invocar las operaciones del servicio web:
GetTasksAsync : este método se usa para rellenar el ListView control en el TodoListPage con el TodoItem
instancias recuperados del servicio web.
SaveTaskAsync : este método se utiliza para crear o actualizar un TodoItem instancia del servicio web.
DeleteTaskAsync : este método se usa para eliminar un TodoItem instancia del servicio web.
Además, algunas aplicaciones de ejemplo contienen métodos adicionales en el TodoItemManager (clase), que se
usan para administrar el proceso de autenticación de usuario.
En lugar de invocar las operaciones del servicio web directamente, el TodoItemManager métodos invocan métodos
en una clase dependiente que se inserta en la TodoItemManager constructor. Por ejemplo, una aplicación de
ejemplo inserta la RestService clase en el TodoItemManager constructor para proporcionar la implementación
que usa las API de REST para acceder a los datos.

Vínculos relacionados
ASMX (ejemplo)
WCF (ejemplo)
REST (ejemplo)
Consumir un servicio Web ASP.NET (ASMX)
11/07/2019 • 12 minutes to read • Edit Online

descargar el ejemplo
ASMX proporciona la capacidad de crear servicios web que envían mensajes mediante el protocolo Simple de
acceso de objetos (SOAP ). SOAP es un protocolo independiente de la plataforma y lenguaje para crear y obtener
acceso a servicios web. Los consumidores de un servicio de ASMX no es necesario saber nada acerca de la
plataforma, el modelo de objetos o el lenguaje de programación usado para implementar el servicio. Sólo
necesitan entender cómo enviar y recibir mensajes SOAP. En este artículo se muestra cómo consumir un servicio
de ASMX SOAP desde una aplicación de Xamarin.Forms.
Un mensaje SOAP es un documento XML que contiene los siguientes elementos:
Un elemento raíz denominado sobres que identifica el documento XML como un mensaje SOAP.
Opcional encabezado elemento que contiene información específica de la aplicación, como datos de
autenticación. Si el encabezado elemento está presente debe ser el primer elemento secundario de la sobres
elemento.
Obligatoria cuerpo elemento que contiene el mensaje SOAP, diseñado para el destinatario.
Opcional error elemento que se usa para indicar los mensajes de error. Si el error elemento está presente, debe
ser un elemento secundario de la cuerpo elemento.
SOAP puede operar en muchos protocolos de transporte, incluidos HTTP, SMTP, TCP y UDP. Sin embargo, un
servicio de ASMX solo puede funcionar a través de HTTP. La plataforma Xamarin es compatible con las
implementaciones estándar de SOAP 1.1 a través de HTTP, y esto incluye compatibilidad con muchas de las
configuraciones estándar de servicio ASMX.
Este ejemplo incluye las aplicaciones móviles que se ejecutan en dispositivos físicos o emulados y un servicio de
ASMX que proporciona métodos para obtener, agregar, editar y eliminar datos. Cuando se ejecutan las
aplicaciones móviles, se conectan al servicio ASMX alojado localmente como se muestra en la captura de pantalla
siguiente:
NOTE
En iOS 9 y versiones posteriores, App Transport Security (ATS) exige que las conexiones seguras entre los recursos de internet
(por ejemplo, el servidor back-end de la aplicación) y la aplicación, lo que impide la divulgación accidental de información
confidencial. Puesto que ATS está habilitada de forma predeterminada en las aplicaciones compiladas para iOS 9, todas las
conexiones estará sujeto a los requisitos de seguridad ATS. Si las conexiones no cumplen estos requisitos, se producirá un
error con una excepción. Se puede optar por en ATS de si no es posible usar la HTTPS del protocolo y proteger la
comunicación de los recursos de internet. Esto puede lograrse mediante la actualización de la aplicación Info.plist archivo.
Para obtener más información, consulte App Transport Security.

Consumir el servicio web


El servicio ASMX proporciona las siguientes operaciones:

OPERACIÓN DESCRIPCIÓN PARÁMETROS

GetTodoItems Obtener una lista de tareas pendientes

CreateTodoItem Crear una nueva tarea pendiente Un documento XML serializado


TodoItem

EditTodoItem Actualizar una tarea pendiente Un documento XML serializado


TodoItem

DeleteTodoItem Eliminar una tarea pendiente Un documento XML serializado


TodoItem

Para obtener más información sobre el modelo de datos utilizado en la aplicación, consulte los datos de modelado.

Crear al proxy TodoService


Una clase de proxy, llamada TodoService , extiende SoapHttpClientProtocol y proporciona métodos para
comunicarse con el servicio ASMX a través de HTTP. El proxy se genera mediante la adición de una referencia
web a cada proyecto específico de plataforma de 2019 de Visual Studio o Visual Studio 2017. La referencia web
genera métodos y eventos para cada acción definida en el documento de lenguaje de descripción de servicios Web
(WSDL ) del servicio.
Por ejemplo, el GetTodoItemsacción de servicio da como resultado un GetTodoItemsAsync método y un
GetTodoItemsCompleted eventos en el servidor proxy. El método generado tiene un tipo de valor devuelto void e
invoca el GetTodoItems acción en el elemento primario SoapHttpClientProtocol clase. Cuando el método invocado
recibe una respuesta del servicio, se activa el GetTodoItemsCompleted eventos y proporciona los datos de respuesta
dentro del evento Result propiedad.

Crear la implementación ISoapService


Para habilitar el proyecto compartido y multiplataforma trabajar con el servicio, el ejemplo define el ISoapService
interfaz, que sigue el modelo de programación de tareas asincrónica en C# . Cada plataforma implementa el
ISoapService para exponer el proxy específico de la plataforma. El ejemplo usa TaskCompletionSource objetos para
exponer el proxy como una interfaz de tarea asincrónica. Detalles sobre el uso de TaskCompletionSource se
encuentran en las implementaciones de cada tipo de acción en las secciones siguientes.
El ejemplo SoapService :
1. Crea una instancia de la TodoService como una instancia de nivel de clase
2. Crea una colección denominada Items almacenar TodoItem objetos
3. Especifica un punto de conexión personalizado para el elemento opcional Url propiedad en el TodoService

public class SoapService : ISoapService


{
ASMXService.TodoService todoService;
public List<TodoItem> Items { get; private set; } = new List<TodoItem>();

public SoapService ()
{
todoService = new ASMXService.TodoService ();
todoService.Url = Constants.SoapUrl;
...
}
}

Crear objetos de transferencia de datos


La aplicación de ejemplo usa el TodoItem clase para modelar los datos. Para almacenar un TodoItem elemento en
el servicio web debe convertirse primero en el proxy generado TodoItem tipo. Esto se consigue mediante la
ToASMXServiceTodoItem método, como se muestra en el ejemplo de código siguiente:

ASMXService.TodoItem ToASMXServiceTodoItem (TodoItem item)


{
return new ASMXService.TodoItem {
ID = item.ID,
Name = item.Name,
Notes = item.Notes,
Done = item.Done
};
}

Este método crea un nuevo ASMService.TodoItem de instancia y establece cada propiedad a la propiedad idéntica
desde el TodoItem instancia.
De forma similar, cuando se recuperan datos desde el servicio web, se debe convertir desde el proxy generado
TodoItem tipo a un TodoItem instancia. Esto se consigue con la FromASMXServiceTodoItem método, como se
muestra en el ejemplo de código siguiente:

static TodoItem FromASMXServiceTodoItem (ASMXService.TodoItem item)


{
return new TodoItem {
ID = item.ID,
Name = item.Name,
Notes = item.Notes,
Done = item.Done
};
}

Este método recupera los datos desde el proxy generado TodoItem escriba y lo establece en recién creado
TodoItem instancia.

Recuperar datos
El ISoapService interfaz espera el RefreshDataAsync método devuelva un Task con la colección de elementos. Sin
embargo, el TodoService.GetTodoItemsAsync método devuelve void. Para satisfacer el patrón de interfaz, debe
llamar a GetTodoItemsAsync , espere a que el GetTodoItemsCompleted eventos se activan y rellene la colección. Esto
permite devolver una colección válida a la interfaz de usuario.
El ejemplo siguiente crea un nuevo TaskCompletionSource , comienza la llamada asincrónica en el RefreshDataAsync
método y espera el Task proporcionada por el TaskCompletionSource . Cuando el
TodoService_GetTodoItemsCompleted se invoca el controlador de eventos rellena el Items colección y las
actualizaciones de la TaskCompletionSource :
public class SoapService : ISoapService
{
TaskCompletionSource<bool> getRequestComplete = null;
...

public SoapService()
{
...
todoService.GetTodoItemsCompleted += TodoService_GetTodoItemsCompleted;
}

public async Task<List<TodoItem>> RefreshDataAsync()


{
getRequestComplete = new TaskCompletionSource<bool>();
todoService.GetTodoItemsAsync();
await getRequestComplete.Task;
return Items;
}

private void TodoService_GetTodoItemsCompleted(object sender, ASMXService.GetTodoItemsCompletedEventArgs


e)
{
try
{
getRequestComplete = getRequestComplete ?? new TaskCompletionSource<bool>();

Items = new List<TodoItem>();


foreach (var item in e.Result)
{
Items.Add(FromASMXServiceTodoItem(item));
}
getRequestComplete?.TrySetResult(true);
}
catch (Exception ex)
{
Debug.WriteLine(@"\t\tERROR {0}", ex.Message);
}
}

...
}

Para obtener más información, consulte modelo de programación asincrónica y TPL y la programación tradicional
de .NET Framework asincrónicas.
Crear o editar datos
Al crear o editar datos, se debe implementar la ISoapService.SaveTodoItemAsync método. Este método detecta si el
TodoItem es un elemento nuevo o actualizado y llama al método apropiado en el todoService objeto. El
CreateTodoItemCompleted y EditTodoItemCompleted también se deberían implementar controladores de eventos
para que sepa cuándo el todoService ha recibido una respuesta desde el servicio ASMX (estos se pueden
combinar en un único controlador puesto que realizan la misma operación). El ejemplo siguiente muestra las
implementaciones de controlador de interfaz y eventos, así como el TaskCompletionSource objeto usado para
funcionar de forma asincrónica:
public class SoapService : ISoapService
{
TaskCompletionSource<bool> saveRequestComplete = null;
...

public SoapService()
{
...
todoService.CreateTodoItemCompleted += TodoService_SaveTodoItemCompleted;
todoService.EditTodoItemCompleted += TodoService_SaveTodoItemCompleted;
}

public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)


{
try
{
var todoItem = ToASMXServiceTodoItem(item);
saveRequestComplete = new TaskCompletionSource<bool>();
if (isNewItem)
{
todoService.CreateTodoItemAsync(todoItem);
}
else
{
todoService.EditTodoItemAsync(todoItem);
}
await saveRequestComplete.Task;
}
catch (SoapException se)
{
Debug.WriteLine("\t\t{0}", se.Message);
}
catch (Exception ex)
{
Debug.WriteLine("\t\tERROR {0}", ex.Message);
}
}

private void TodoService_SaveTodoItemCompleted(object sender,


System.ComponentModel.AsyncCompletedEventArgs e)
{
saveRequestComplete?.TrySetResult(true);
}

...
}

Eliminar datos
Eliminación de datos requiere una implementación similar. Definir un TaskCompletionSource , implemente un
controlador de eventos y el ISoapService.DeleteTodoItemAsync método:
public class SoapService : ISoapService
{
TaskCompletionSource<bool> deleteRequestComplete = null;
...

public SoapService()
{
...
todoService.DeleteTodoItemCompleted += TodoService_DeleteTodoItemCompleted;
}

public async Task DeleteTodoItemAsync (string id)


{
try
{
deleteRequestComplete = new TaskCompletionSource<bool>();
todoService.DeleteTodoItemAsync(id);
await deleteRequestComplete.Task;
}
catch (SoapException se)
{
Debug.WriteLine("\t\t{0}", se.Message);
}
catch (Exception ex)
{
Debug.WriteLine("\t\tERROR {0}", ex.Message);
}
}

private void TodoService_DeleteTodoItemCompleted(object sender,


System.ComponentModel.AsyncCompletedEventArgs e)
{
deleteRequestComplete?.TrySetResult(true);
}

...
}

Probar el servicio web


Dispositivos físicos o emulados con un servicio hospedado localmente para probar requiere la configuración
personalizada de IIS, direcciones de punto de conexión y las reglas de firewall en su lugar. Para obtener más
detalles sobre cómo configurar el entorno de prueba, consulte el configurar el acceso remoto a IIS Express. La
única diferencia entre las pruebas de WCF y ASMX es el número de puerto de la TodoService.

Vínculos relacionados
TodoASMX (ejemplo)
IAsyncResult
Consumir un servicio Web de Windows
Communication Foundation (WCF)
11/07/2019 • 24 minutes to read • Edit Online

descargar el ejemplo
WCF es el marco unificado de Microsoft para crear aplicaciones orientadas a servicios. Permite a los
desarrolladores crear aplicaciones distribuidas seguras, confiables, transacciones e interoperables. En este artículo
se muestra cómo consumir un servicio de Protocolo Simple de acceso de objetos (SOAP ) de WCF desde una
aplicación de Xamarin.Forms.
WCF describe un servicio con una variedad de contratos diferentes incluidas:
Los contratos de datos : definir las estructuras de datos que forman la base para el contenido dentro de un
mensaje.
Contratos de mensaje – redactar mensajes de los contratos de datos existente.
Contratos de error : permitir que los errores de SOAP personalizados que se especifique.
Contratos de servicio : especificar las operaciones que admiten servicios y los mensajes necesarios para
interactuar con cada operación. También especifique ningún comportamiento de error personalizado que se
puede asociar con las operaciones en cada servicio.
Existen diferencias entre los servicios Web de ASP.NET (ASMX) y WCF, pero WCF admite las mismas
funcionalidades que proporciona ASMX: los mensajes SOAP a través de HTTP. Para obtener más información
sobre cómo usar un servicio de ASMX, consulte consumir servicios de Web de ASP.NET (ASMX) .

IMPORTANT
La compatibilidad con la plataforma de Xamarin para WCF se limita a los mensajes codificados en texto SOAP a través de
HTTP/HTTPS mediante la BasicHttpBinding clase.
Compatibilidad con WCF requiere el uso de herramientas solo está disponibles en un entorno de Windows para generar al
proxy y hospedar el TodoWCFService. Compilar y probar la aplicación de iOS requiere implementar el TodoWCFService en un
equipo Windows o como un servicio web de Azure.
Aplicaciones nativas de Xamarin Forms suelen compartan el código con una biblioteca de clases .NET Standard. Sin embargo,
.NET Core no admite actualmente WCF para que el proyecto compartido debe ser una biblioteca de clases Portable heredada.
Para obtener información sobre la compatibilidad WCF en .NET Core, vea selección entre .NET Core y .NET Framework para
aplicaciones de servidor.

La solución de aplicación de ejemplo incluye un servicio WCF que se puede ejecutar localmente y se muestra en la
captura de pantalla siguiente:
NOTE
En iOS 9 y versiones posteriores, App Transport Security (ATS) exige que las conexiones seguras entre los recursos de internet
(por ejemplo, el servidor back-end de la aplicación) y la aplicación, lo que impide la divulgación accidental de información
confidencial. Puesto que ATS está habilitada de forma predeterminada en las aplicaciones compiladas para iOS 9, todas las
conexiones estará sujeto a los requisitos de seguridad ATS. Si las conexiones no cumplen estos requisitos, se producirá un
error con una excepción.
Se puede optar por en ATS de si no es posible usar la HTTPS del protocolo y proteger la comunicación de los recursos de
internet. Esto puede lograrse mediante la actualización de la aplicación Info.plist archivo. Para obtener más información,
consulte App Transport Security.

Consumir el servicio web


El servicio WCF proporciona las siguientes operaciones:

OPERACIÓN DESCRIPCIÓN PARÁMETROS

GetTodoItems Obtener una lista de tareas pendientes

CreateTodoItem Crear una nueva tarea pendiente Un documento XML serializado


TodoItem

EditTodoItem Actualizar una tarea pendiente Un documento XML serializado


TodoItem

DeleteTodoItem Eliminar una tarea pendiente Un documento XML serializado


TodoItem
Para obtener más información sobre el modelo de datos utilizado en la aplicación, consulte los datos de modelado.
Un proxy debe generarse para consumir un servicio WCF, que permite que la aplicación para conectarse al
servicio. El proxy se construye por consumir los metadatos del servicio que definen los métodos y la configuración
del servicio asociado. Estos metadatos se muestran en forma de un documento de lenguaje de descripción de
servicios Web (WSDL ) que se genera el servicio web. El proxy puede compilarse mediante el Microsoft WCF Web
Service Reference Provider en Visual Studio 2017 para agregar una referencia de servicio para el servicio web en
una biblioteca de .NET Standard. Una alternativa para crear al proxy con el Microsoft WCF Web Service Reference
Provider en Visual Studio 2017 consiste en utilizar ServiceModel Metadata Utility Tool (svcutil.exe). Para obtener
más información, consulte ServiceModel Metadata Utility Tool (Svcutil.exe).
Las clases de proxy generadas proporcionan métodos para consumir los servicios web que usan el patrón de
diseño del modelo de programación asincrónica (APM ). En este patrón, una operación asincrónica se implementa
como dos métodos denominados BeginOperationName y EndOperationName, que comienzan y terminan la
operación asincrónica.
El BeginOperationName método comienza la operación asincrónica y devuelve un objeto que implementa el
IAsyncResult interfaz. Después de llamar a BeginOperationName, una aplicación puede seguir ejecutando
instrucciones en el subproceso de llamada mientras lleva a la operación asincrónica en un subproceso ThreadPool.
Para cada llamada a BeginOperationName, también debe llamar la aplicación EndOperationName para obtener
los resultados de la operación. El valor devuelto de EndOperationName es el mismo tipo devuelto por el método
de servicio web sincrónico. Por ejemplo, el EndGetTodoItems método devuelve una colección de TodoItem
instancias. El EndOperationName método también incluye un IAsyncResult parámetro que se debe establecer en
la instancia devuelta por la llamada correspondiente a la BeginOperationName método.
Task Parallel Library (TPL ) puede simplificar el proceso de consumo de un par de métodos begin/end APM al
encapsular las operaciones asincrónicas en la misma Task objeto. Esta encapsulación proporciona varias
sobrecargas de los TaskFactory.FromAsync método.
Para obtener más información acerca de APM vea modelo de programación asincrónica y TPL y la programación
tradicional de .NET Framework asincrónicas en MSDN.
Crear el objeto TodoServiceClient
La clase de proxy generado proporciona el TodoServiceClient (clase), que se usa para comunicarse con el servicio
WCF a través de HTTP. Proporciona funcionalidad para invocar métodos de servicio web como operaciones
asincrónicas de un URI identifican la instancia de servicio. Para obtener más información acerca de las operaciones
asincrónicas, vea información general de soporte técnico de Async.
El TodoServiceClient instancia se declara en el nivel de clase para que se encuentra el objeto para siempre y
cuando la aplicación necesita consumir el servicio WCF, tal como se muestra en el ejemplo de código siguiente:

public class SoapService : ISoapService


{
ITodoService todoService;
...

public SoapService ()
{
todoService = new TodoServiceClient (
new BasicHttpBinding (),
new EndpointAddress (Constants.SoapUrl));
}
...
}

El TodoServiceClient instancia está configurada con una dirección de extremo y la información de enlace. Un
enlace se utiliza para especificar el transporte, codificación y detalles protocolares requeridos para que las
aplicaciones y servicios para comunicarse entre sí. El BasicHttpBinding especifica que se enviarán los mensajes
codificados en texto SOAP a través del protocolo de transporte HTTP. Especificación de una dirección de punto de
conexión permite a la aplicación para conectarse a distintas instancias del servicio WCF, siempre que hay varias
instancias publicadas.
Para obtener más información sobre la configuración de la referencia de servicio, consulte configuración de la
referencia de servicio.
Crear objetos de transferencia de datos
La aplicación de ejemplo usa el TodoItem clase para modelar los datos. Para almacenar un TodoItem elemento en
el servicio web debe convertirse primero en el proxy generado TodoItem tipo. Esto se consigue mediante la
ToWCFServiceTodoItem método, como se muestra en el ejemplo de código siguiente:

TodoWCFService.TodoItem ToWCFServiceTodoItem (TodoItem item)


{
return new TodoWCFService.TodoItem
{
ID = item.ID,
Name = item.Name,
Notes = item.Notes,
Done = item.Done
};
}

Este método simplemente crea un nuevo TodoWCFService.TodoItem de instancia y establece cada propiedad a la
propiedad idéntica desde el TodoItem instancia.
De forma similar, cuando se recuperan datos desde el servicio web, se debe convertir desde el proxy generado
TodoItem tipo a un TodoItem instancia. Esto se consigue con la FromWCFServiceTodoItem método, como se muestra
en el ejemplo de código siguiente:

static TodoItem FromWCFServiceTodoItem (TodoWCFService.TodoItem item)


{
return new TodoItem
{
ID = item.ID,
Name = item.Name,
Notes = item.Notes,
Done = item.Done
};
}

Este método simplemente recupera los datos desde el proxy generado TodoItem escriba y lo establece en recién
creado TodoItem instancia.
Recuperar datos
El TodoServiceClient.BeginGetTodoItems y TodoServiceClient.EndGetTodoItems métodos se usan para llamar a la
GetTodoItems operación proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un
Task de objeto, como se muestra en el ejemplo de código siguiente:
public async Task<List<TodoItem>> RefreshDataAsync ()
{
...
var todoItems = await Task.Factory.FromAsync <ObservableCollection<TodoWCFService.TodoItem>> (
todoService.BeginGetTodoItems,
todoService.EndGetTodoItems,
null,
TaskCreationOptions.None);

foreach (var item in todoItems)


{
Items.Add (FromWCFServiceTodoItem (item));
}
...
}

El Task.Factory.FromAsync método crea un Task que se ejecuta el TodoServiceClient.EndGetTodoItems método una


vez el TodoServiceClient.BeginGetTodoItems método se completa, con el null parámetro que indica que no hay
datos se pasan a la BeginGetTodoItems delegar. Por último, el valor de la TaskCreationOptions enumeración
especifica que debe usarse el comportamiento predeterminado para la creación y ejecución de tareas.
El TodoServiceClient.EndGetTodoItems método devuelve un ObservableCollection de TodoWCFService.TodoItem
instancias, que, a continuación, se convierte en un List de TodoItem instancias para su presentación.
Crear datos
El TodoServiceClient.BeginCreateTodoItem y TodoServiceClient.EndCreateTodoItem métodos se usan para llamar a la
CreateTodoItem operación proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un
Task de objeto, como se muestra en el ejemplo de código siguiente:

public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)


{
...
var todoItem = ToWCFServiceTodoItem (item);
...
await Task.Factory.FromAsync (
todoService.BeginCreateTodoItem,
todoService.EndCreateTodoItem,
todoItem,
TaskCreationOptions.None);
...
}

El Task.Factory.FromAsync método crea un Task que se ejecuta el TodoServiceClient.EndCreateTodoItem método


una vez el TodoServiceClient.BeginCreateTodoItem método se completa, con el todoItem parámetro que se va a los
datos que se pasan a la BeginCreateTodoItem delegado para especificar el TodoItem que va a crear el servicio web.
Por último, el valor de la TaskCreationOptions enumeración especifica que debe usarse el comportamiento
predeterminado para la creación y ejecución de tareas.
El servicio web inicia una FaultException si se produce un error al crear el TodoItem , que se controla mediante la
aplicación.
actualizar datos
El TodoServiceClient.BeginEditTodoItem y TodoServiceClient.EndEditTodoItem métodos se usan para llamar a la
EditTodoItem operación proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un
Task de objeto, como se muestra en el ejemplo de código siguiente:
public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)
{
...
var todoItem = ToWCFServiceTodoItem (item);
...
await Task.Factory.FromAsync (
todoService.BeginEditTodoItem,
todoService.EndEditTodoItem,
todoItem,
TaskCreationOptions.None);
...
}

El Task.Factory.FromAsync método crea un Task que se ejecuta el TodoServiceClient.EndEditTodoItem método una


vez el TodoServiceClient.BeginCreateTodoItem método se completa, con el todoItem parámetro que se va a los
datos que se pasan a la BeginEditTodoItem delegado para especificar el TodoItem actualizarse por el servicio web.
Por último, el valor de la TaskCreationOptions enumeración especifica que debe usarse el comportamiento
predeterminado para la creación y ejecución de tareas.
El servicio web inicia una FaultException si se produce un error al buscar o actualizar el TodoItem , que se controla
mediante la aplicación.
Eliminar datos
El TodoServiceClient.BeginDeleteTodoItem y TodoServiceClient.EndDeleteTodoItem métodos se usan para llamar a la
DeleteTodoItem operación proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un
Task de objeto, como se muestra en el ejemplo de código siguiente:

public async Task DeleteTodoItemAsync (string id)


{
...
await Task.Factory.FromAsync (
todoService.BeginDeleteTodoItem,
todoService.EndDeleteTodoItem,
id,
TaskCreationOptions.None);
...
}

El Task.Factory.FromAsync método crea un Task que se ejecuta el TodoServiceClient.EndDeleteTodoItem método


una vez el TodoServiceClient.BeginDeleteTodoItem método se completa, con el id parámetro que se va a los datos
que se pasan a la BeginDeleteTodoItem delegado para especificar el TodoItem va a eliminar el servicio web. Por
último, el valor de la TaskCreationOptions enumeración especifica que debe usarse el comportamiento
predeterminado para la creación y ejecución de tareas.
El servicio web inicia una FaultException si se produce un error al buscar o eliminar el TodoItem , que se controla
mediante la aplicación.

Configurar el acceso remoto a IIS Express


En Visual Studio 2017 o Visual Studio de 2019, debería poder probar la aplicación para UWP en un equipo sin
ninguna configuración adicional. Probar los clientes iOS y Android puede requerir que los pasos adicionales en
esta sección. Consulte conéctese a servicios Web locales de los emuladores de Android y simuladores de iOS para
obtener más información.
De forma predeterminada, IIS Express solo responderá a las solicitudes para localhost . Los dispositivos remotos
(por ejemplo, un dispositivo Android, un iPhone o incluso un simulador) no tendrán acceso a un servicio WCF
local. Necesitará saber la dirección IP de estación de trabajo de Windows 10 en la red local. Para este ejemplo,
suponga que la estación de trabajo tiene la dirección IP 192.168.1.143 . Los pasos siguientes explican cómo
configurar Windows 10 e IIS Express para que acepte conexiones remotas y conectarse al servicio desde un
dispositivo físico o virtual:
1. Agregar una excepción al Firewall de Windows. Debe abrir un puerto a través de Firewall de Windows
que las aplicaciones de la subred pueden usar para comunicarse con el servicio WCF. Cree una regla de
entrada abrir puerto 49393 en el firewall. Desde un símbolo del sistema administrativo, ejecute este
comando:

netsh advfirewall firewall add rule name="TodoWCFService" dir=in protocol=tcp localport=49393


profile=private remoteip=localsubnet action=allow

2. Configurar IIS Express para las conexiones remotas acepte. Puede configurar IIS Express editando el
archivo de configuración de IIS Express en [directorio de la
solución].vs\config\applicationhost.config. Buscar el site elemento con el nombre TodoWCFService .
Debería ser similar al siguiente XML:

<site name="TodoWCFService" id="2">


<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="C:\Users\tom\TodoWCF\TodoWCFService\TodoWCFService" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:49393:localhost" />
</bindings>
</site>

Deberá agregar dos binding elementos para abrir el puerto 49393 para el tráfico externo y el emulador de
Android. El enlace utiliza un [IP address]:[port]:[hostname] formato que especifica cómo IIS Express
responderá a las solicitudes. Las solicitudes externas tendrán nombres de host que se deben especificar
como un binding . Agregue el siguiente código XML para el bindings elemento, reemplazando la dirección
IP con su propia dirección IP:

<binding protocol="http" bindingInformation="*:49393:192.168.1.143" />


<binding protocol="http" bindingInformation="*:49393:127.0.0.1" />

Después de los cambios del bindings elemento debe ser similar al siguiente:

<site name="TodoWCFService" id="2">


<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="C:\Users\tom\TodoWCF\TodoWCFService\TodoWCFService" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:49393:localhost" />
<binding protocol="http" bindingInformation="*:49393:192.168.1.143" />
<binding protocol="http" bindingInformation="*:49393:127.0.0.1" />
</bindings>
</site>

IMPORTANT
De forma predeterminada, IIS Express no aceptará las conexiones de orígenes externos por motivos de seguridad.
Para habilitar las conexiones procedentes de dispositivos remotos deben ejecutar IIS Express con permisos
administrativos. La manera más fácil de hacerlo es ejecutar Visual Studio 2017 con permisos administrativos. Esto
iniciará IIS Express con permisos administrativos al ejecutar el TodoWCFService.
Con estos pasos completados, debe ser capaz de ejecutar el TodoWCFService y conectarse desde otros
dispositivos de la subred. Puede probar esto mediante la ejecución de la aplicación y visitar
http://localhost:49393/TodoService.svc . Si se produce un solicitud incorrecta error al visitar esa dirección
URL, su bindings puede ser incorrecto en la configuración de IIS Express (la solicitud está llegando a IIS
Express, pero se ha rechazado). Si se produce un error diferente, es posible que no se está ejecutando la
aplicación o el firewall está configurado incorrectamente.
Para permitir que IIS Express mantener la ejecución y ofrecer el servicio, desactive la editar y continuar
opción las propiedades del proyecto > Web > depuradores.
3. Personalizar el punto de conexión de dispositivos usan para tener acceso al servicio. Este paso
implica la configuración de la aplicación cliente, que se ejecuta en un dispositivo físico o emulado, para
acceder al servicio WCF.
El emulador de Android usa un proxy interno que impide que el emulador de obtener acceso directamente a
la máquina host localhost dirección. En su lugar, la dirección 10.0.2.2 en el emulador se enruta a
localhost en el equipo host a través de un proxy interno. Estas solicitudes procesadas por el proxy tendrá
127.0.0.1 como nombre de host en el encabezado de solicitud, que es la razón por la que creó el enlace de
IIS Express para este nombre de host en los pasos anteriores.
IOS Simulator se ejecuta en un equipo Mac compilar host, incluso si usas el remoto de iOS Simulator para
Windows. Las solicitudes de red desde el simulador tendrá su dirección IP de la estación de trabajo en la red
local como el nombre de host (en este ejemplo tiene 192.168.1.143 , pero la dirección IP real
probablemente serán diferente). Se trata de por qué creó el enlace de IIS Express para este nombre de host
en los pasos anteriores.
Asegúrese del SoapUrl propiedad en el Constants.cs archivo en el proyecto TodoWCF (Portable) tienen
valores que son correctos para la red:

public static string SoapUrl


{
get
{
var defaultUrl = "http://localhost:49393/TodoService.svc";

if (Device.RuntimePlatform == Device.Android)
{
defaultUrl = "http://10.0.2.2:49393/TodoService.svc";
}
else if (Device.RuntimePlatform == Device.iOS)
{
defaultUrl = "http://192.168.1.143:49393/TodoService.svc";
}

return defaultUrl;
}
}

Una vez que haya configurado el Constants.cs con los puntos de conexión adecuadas, debe ser capaz de
conectarse a la TodoWCFService que se ejecuta en la estación de trabajo de Windows 10 desde dispositivos
físicos o virtuales.

Vínculos relacionados
TodoWCF (ejemplo)
Cómo: Crear a un cliente de Windows Communication Foundation
ServiceModel Metadata Utility Tool (svcutil.exe)
Consumir un servicio Web RESTful
11/07/2019 • 15 minutes to read • Edit Online

descargar el ejemplo
Integrar un servicio web en una aplicación es un escenario común. En este artículo se muestra cómo consumir un
servicio web de RESTful desde una aplicación de Xamarin.Forms.
Representational State Transfer (REST) es un estilo de arquitectura para la creación de servicios web. Solicitudes
REST se realizan a través de HTTP utilizando los mismos verbos HTTP que los exploradores web que se usan
para recuperar las páginas web y para enviar datos a los servidores. Los verbos son:
OBTENER : esta operación se usa para recuperar datos desde el servicio web.
POST : esta operación se usa para crear un nuevo elemento de datos en el servicio web.
COLOCAR : esta operación se usa para actualizar un elemento de datos en el servicio web.
REVISIÓN : esta operación se usa para actualizar un elemento de datos en el servicio web con la descripción
de un conjunto de instrucciones sobre cómo se debe modificar el elemento. No se utiliza este verbo en la
aplicación de ejemplo.
ELIMINAR : esta operación se usa para eliminar un elemento de datos en el servicio web.
Se llama a las API de RESTful API que se adhieren a REST del servicio Web y se definen mediante:
Un URI base.
Métodos HTTP, como GET, POST, PUT, PATCH o DELETE.
Tipo de medio de los datos, como JavaScript Object Notation (JSON ).
Servicios web rESTful normalmente utilizan mensajes JSON para devolver datos al cliente. JSON es un formato
de intercambio de datos basado en texto que genera cargas compactas, lo que reducción los requisitos de ancho
de banda al enviar datos. La aplicación de ejemplo usa el código abierto biblioteca de NewtonSoft JSON.NET
para serializar y deserializar los mensajes.
La simplicidad de REST ha ayudado a hacer el método principal para tener acceso a servicios web en aplicaciones
móviles.
Cuando se ejecuta la aplicación de ejemplo, se conectará a un servicio REST hospedado localmente, como se
muestra en la captura de pantalla siguiente:
NOTE
En iOS 9 y versiones posteriores, App Transport Security (ATS) exige que las conexiones seguras entre los recursos de
internet (por ejemplo, el servidor back-end de la aplicación) y la aplicación, lo que impide la divulgación accidental de
información confidencial. Puesto que ATS está habilitada de forma predeterminada en las aplicaciones compiladas para iOS 9,
todas las conexiones estará sujeto a los requisitos de seguridad ATS. Si las conexiones no cumplen estos requisitos, se
producirá un error con una excepción.
Se puede optar por en ATS de si no es posible usar el HTTPS protocolo y proteger la comunicación de los recursos de
internet. Esto puede lograrse mediante la actualización de la aplicación Info.plist archivo. Para obtener más información,
consulte App Transport Security.

Consumo del servicio Web


El servicio REST está escrito con ASP.NET Core y proporciona las siguientes operaciones:

OPERACIÓN MÉTODO HTTP URI RELATIVO PARÁMETROS

Obtener una lista de tareas GET / API/todoitems /


pendientes

Crear una nueva tarea EXPONER / API/todoitems / TodoItem en formato JSON


pendiente

Actualizar una tarea PUT / API/todoitems / TodoItem en formato JSON


pendiente

Eliminar una tarea pendiente SUPRIMIR /api/todoitems/{id}

La mayoría de los URI incluyen el TodoItem ID en la ruta de acceso. Por ejemplo, para eliminar la TodoItem cuyo
identificador es 6bb8a868-dba1-4f1a-93b7-24ebce87e243 , el cliente envía una solicitud DELETE
http://hostname/api/todoitems/6bb8a868-dba1-4f1a-93b7-24ebce87e243 . Para obtener más información acerca del
modelo de datos usado en la aplicación de ejemplo, vea los datos de modelado.
Cuando el marco API Web recibe una solicitud enruta la solicitud a una acción. Estas acciones son métodos
públicos simplemente en el TodoItemsController clase. El marco de trabajo usa una tabla de enrutamiento para
determinar qué acción va a invocar en respuesta a una solicitud, que se muestra en el ejemplo de código
siguiente:

config.Routes.MapHttpRoute(
name: "TodoItemsApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { controller="todoitems", id = RouteParameter.Optional }
);

La tabla de enrutamiento contiene una plantilla de ruta, y cuando el marco API Web recibe una solicitud HTTP,
intenta coincidir con el URI en la plantilla de ruta en la tabla de enrutamiento. Si una coincidencia de ruta no se
encuentra que el cliente recibe un error 404 (no encontrado). Si se encuentra una ruta coincidente, Web API
selecciona el controlador y la acción siguiente:
Para buscar el controlador, Web API agrega "controller" en el valor de la {controller } variable.
Para buscar la acción, API Web examina el método HTTP y examina las acciones de controlador que se
decoran con el mismo método HTTP como un atributo.
El {id } variable de marcador de posición se asigna a un parámetro de acción.
El servicio REST usa la autenticación básica. Para obtener más información, consulte autenticar un servicio web
RESTful. Para obtener más información sobre el enrutamiento de ASP.NET Web API, consulte Routing in
ASP.NET Web API en el sitio Web ASP.NET. Para obtener más información acerca de cómo crear el servicio
REST mediante ASP.NET Core, consulte crear servicios back-end para aplicaciones móviles nativas.
La HttpClient clase se utiliza para enviar y recibir solicitudes a través de HTTP. Proporciona funcionalidad para
enviar solicitudes HTTP y recibir respuestas HTTP de un URI identifica el recurso. Cada solicitud se envía como
una operación asincrónica. Para obtener más información acerca de las operaciones asincrónicas, vea información
general de soporte técnico de Async.
La HttpResponseMessage clase representa un mensaje de respuesta HTTP recibido del servicio web después de
realizar una solicitud HTTP. Contiene información acerca de la respuesta, incluido el código de estado, los
encabezados y cualquier cuerpo. El HttpContent clase representa el cuerpo HTTP y encabezados de contenido,
como Content-Type y Content-Encoding . El contenido se puede leer utilizando cualquiera de los ReadAs métodos,
como ReadAsStringAsync y ReadAsByteArrayAsync , dependiendo del formato de los datos.
Crear el objeto HTTPClient
El HttpClient instancia se declara en el nivel de clase para que se encuentra el objeto para siempre y cuando la
aplicación necesita para realizar solicitudes HTTP, como se muestra en el ejemplo de código siguiente:

public class RestService : IRestService


{
HttpClient _client;
...

public RestService ()
{
_client = new HttpClient ();
}
...
}

Recuperar datos
El HttpClient.GetAsync método se utiliza para enviar la solicitud GET al servicio web especificado por el
identificador URI y, a continuación, recibir la respuesta del servicio web, como se muestra en el ejemplo de código
siguiente:

public async Task<List<TodoItem>> RefreshDataAsync ()


{
...
var uri = new Uri (string.Format (Constants.TodoItemsUrl, string.Empty));
...
var response = await _client.GetAsync (uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync ();
Items = JsonConvert.DeserializeObject <List<TodoItem>> (content);
}
...
}

El servicio REST envía un código de estado HTTP en el HttpResponseMessage.IsSuccessStatusCode propiedad para


indicar si la solicitud HTTP se realizó correctamente o no. Para esta operación el resto servicio envía el código de
estado HTTP 200 (OK) en la respuesta, lo que indica que la solicitud es correcta y que la información solicitada en
la respuesta.
Si la operación HTTP se realizó correctamente, se lee el contenido de la respuesta, para su presentación. El
HttpResponseMessage.Content propiedad representa el contenido de la respuesta HTTP y el
HttpContent.ReadAsStringAsync método escribe asincrónicamente el contenido HTTP en una cadena. Este
contenido, a continuación, se convierte de JSON a un List de TodoItem instancias.
Creación de datos
El HttpClient.PostAsync método se usa para enviar la solicitud POST al servicio web especificado por el
identificador URI y, a continuación, para recibir la respuesta del servicio web, como se muestra en el ejemplo de
código siguiente:

public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)


{
var uri = new Uri (string.Format (Constants.TodoItemsUrl, string.Empty));

...
var json = JsonConvert.SerializeObject (item);
var content = new StringContent (json, Encoding.UTF8, "application/json");

HttpResponseMessage response = null;


if (isNewItem)
{
response = await _client.PostAsync (uri, content);
}
...

if (response.IsSuccessStatusCode)
{
Debug.WriteLine (@"\tTodoItem successfully saved.");

}
...
}

El TodoItem instancia se convierte en una carga JSON para enviar al servicio web. Esta carga, a continuación, se
inserta en el cuerpo de contenido HTTP que se enviará al servicio web antes de realizar la solicitud con el
PostAsync método.

El servicio REST envía un código de estado HTTP en el HttpResponseMessage.IsSuccessStatusCode propiedad para


indicar si la solicitud HTTP se realizó correctamente o no. Las respuestas comunes para esta operación son:
201 (creado) : la solicitud dio como resultado un nuevo recurso que se creó antes de que se envió la
respuesta.
400 (solicitud incorrecta) : el servidor no entiende la solicitud.
409 (conflicto) : la solicitud no puede llevarse a cabo debido a un conflicto en el servidor.
Actualizar datos
El HttpClient.PutAsync método se utiliza para enviar la solicitud PUT al servicio web especificado por el
identificador URI y, a continuación, recibir la respuesta del servicio web, como se muestra en el ejemplo de código
siguiente:

public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)


{
...
response = await _client.PutAsync (uri, content);
...
}

La operación de la PutAsync es idéntico al método el PostAsync método que se usa para crear datos en el
servicio web. Sin embargo, se diferencian las posibles respuestas enviadas desde el servicio web.
El servicio REST envía un código de estado HTTP en el HttpResponseMessage.IsSuccessStatusCode propiedad para
indicar si la solicitud HTTP se realizó correctamente o no. Las respuestas comunes para esta operación son:
204 (sin contenido) : se ha procesado correctamente la solicitud y la respuesta está intencionadamente en
blanco.
400 (solicitud incorrecta) : el servidor no entiende la solicitud.
404 (no encontrado) : el recurso solicitado no existe en el servidor.
Eliminar datos
El HttpClient.DeleteAsync método se utiliza para enviar la solicitud DELETE al servicio web especificado por el
identificador URI y, a continuación, recibir la respuesta del servicio web, como se muestra en el ejemplo de código
siguiente:

public async Task DeleteTodoItemAsync (string id)


{
var uri = new Uri (string.Format (Constants.TodoItemsUrl, id));
...
var response = await _client.DeleteAsync (uri);
if (response.IsSuccessStatusCode)
{
Debug.WriteLine (@"\tTodoItem successfully deleted.");
}
...
}

El servicio REST envía un código de estado HTTP en el HttpResponseMessage.IsSuccessStatusCode propiedad para


indicar si la solicitud HTTP se realizó correctamente o no. Las respuestas comunes para esta operación son:
204 (sin contenido) : se ha procesado correctamente la solicitud y la respuesta está intencionadamente en
blanco.
400 (solicitud incorrecta) : el servidor no entiende la solicitud.
404 (no encontrado) : el recurso solicitado no existe en el servidor.

Vínculos relacionados
Creación de servicios back-end para aplicaciones móviles nativas
TodoREST (ejemplo)
HttpClient
Autenticación del servicio Web de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Autenticar un servicio Web de RESTful


HTTP es compatible con el uso de varios mecanismos de autenticación para controlar el acceso a los recursos.
Autenticación básica proporciona acceso a recursos solo a los clientes que tienen las credenciales correctas. En este
artículo se explica cómo usar la autenticación básica para proteger el acceso a recursos de servicio web RESTful.

Autenticar a los usuarios con un proveedor de identidades


Xamarin.Auth es un SDK multiplataforma para autenticar a los usuarios y sus cuentas de almacenamiento. Incluye
los autenticadores de OAuth que proporcionan compatibilidad para consumir los proveedores de identidades
como Microsoft, Google, Facebook y Twitter. Este artículo explica cómo usar Xamarin.Auth para administrar el
proceso de autenticación en una aplicación de Xamarin.Forms.

Autenticar a los usuarios con Azure Active Directory B2C


Azure B2C de Active Directory es una solución de administración de identidades en la nube para aplicaciones
móviles y web orientadas al consumidor. En este artículo se explica cómo usar la biblioteca de autenticación de
Microsoft (MSAL ) y Azure Active Directory B2C para integrar la administración de identidades de consumidor en
una aplicación de Xamarin.Forms.

Autenticar a los usuarios con una base de datos de documentos de


Azure Cosmos DB y Xamarin.Forms
Azure bases de datos de documento de Cosmos DB admiten colecciones con particiones, que pueden abarcar
varios servidores y particiones, al tiempo que admite almacenamiento ilimitado y rendimiento. En este artículo se
explica cómo combinar el control de acceso con las colecciones con particiones, para que un usuario sólo puede
tener acceso a sus propios documentos en una aplicación de Xamarin.Forms.
Autenticar un servicio Web de RESTful
11/07/2019 • 7 minutes to read • Edit Online

HTTP es compatible con el uso de varios mecanismos de autenticación para controlar el acceso a los recursos.
Autenticación básica proporciona acceso a recursos solo a los clientes que tienen las credenciales correctas. En
este artículo se muestra cómo usar la autenticación básica para proteger el acceso a recursos de servicio web
RESTful.

NOTE
En iOS 9 y versiones posteriores, App Transport Security (ATS) exige que las conexiones seguras entre los recursos de
internet (por ejemplo, el servidor back-end de la aplicación) y la aplicación, lo que impide la divulgación accidental de
información confidencial. Puesto que ATS está habilitada de forma predeterminada en las aplicaciones compiladas para iOS 9,
todas las conexiones estará sujeto a los requisitos de seguridad ATS. Si las conexiones no cumplen estos requisitos, se
producirá un error con una excepción. Se puede optar por en ATS de si no es posible usar la HTTPS del protocolo y
proteger la comunicación de los recursos de internet. Esto puede lograrse mediante la actualización de la aplicación
Info.plist archivo. Para obtener más información, consulte App Transport Security.

Autenticar a los usuarios a través de HTTP


Autenticación básica es el mecanismo de autenticación más sencillo compatible con HTTP e implica al cliente que
envía el nombre de usuario y la contraseña como texto no cifrado en base64 codificado. Funciona como se indica
a continuación:
Si un servicio web recibe una solicitud para un recurso protegido, se rechaza la solicitud con un código de
estado HTTP 401 (acceso denegado) y establece el encabezado de respuesta WWW -Authenticate, tal como se
muestra en el diagrama siguiente:

Si un servicio web recibe una solicitud para un recurso protegido, con el Authorization encabezado
correctamente establecido, el web servicio responde con un código de estado HTTP 200, lo que indica que la
solicitud es correcta y que la información solicitada está en la respuesta. Este escenario se muestra en el
diagrama siguiente:
NOTE
Sólo debe utilizarse la autenticación básica a través de una conexión HTTPS. Cuando se utiliza en una conexión HTTP, el
Authorization encabezado puede descodificar fácilmente si un atacante captura el tráfico HTTP.

Especificar la autenticación básica en una solicitud Web


Uso de la autenticación básica se especifica como sigue:
1. La cadena "Basic" se agrega a la Authorization encabezado de la solicitud.
2. El nombre de usuario y la contraseña se combinan en una cadena con el formato "nombreDeUsuario:
contraseña", que es, a continuación, codificado en base64 y agregado a la Authorization encabezado de la
solicitud.
Por lo tanto, con un nombre de usuario de 'XamarinUser' y una contraseña de 'XamarinPassword', el encabezado
se convierte en:

Authorization: Basic WGFtYXJpblVzZXI6WGFtYXJpblBhc3N3b3Jk

El HttpClient clase puede establecer el valor de encabezado en el


Authorization
HttpClient.DefaultRequestHeaders.Authorization propiedad. Dado que el HttpClient instancia existe en varias
solicitudes, el Authorization encabezado sólo es necesario establecer una vez, en lugar de cuando realizar cada
solicitud, tal como se muestra en el ejemplo de código siguiente:

public class RestService : IRestService


{
HttpClient _client;
...

public RestService ()
{
var authData = string.Format ("{0}:{1}", Constants.Username, Constants.Password);
var authHeaderValue = Convert.ToBase64String (Encoding.UTF8.GetBytes (authData));

_client = new HttpClient ();


_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue ("Basic", authHeaderValue);
}
...
}

A continuación, cuando se realiza una solicitud a una operación de servicio web se firma la solicitud con el
Authorization encabezado, que indica si el usuario tiene permiso para invocar la operación.

NOTE
Si bien este código almacena las credenciales como constantes, no se almacenan en un formato no seguro en una aplicación
publicada. El Xamarith.Auth NuGet proporciona funcionalidad para almacenar las credenciales de forma segura. Para obtener
más información, consulte almacenar y recuperar información de cuenta en los dispositivos.

Procesamiento en el lado del servidor de encabezado de autorización


El servicio REST debe decorar cada acción con el [BasicAuthentication] atributo. Este atributo se usa para
analizar el Authorization encabezado y determinar si las credenciales con codificación base64 válidas
comparándolos con los valores almacenados en Web.config. Aunque este enfoque es adecuado para un servicio
de ejemplo, requieren la extensión para un servicio web de acceso público.
En el módulo de autenticación básica utilizadas por IIS, los usuarios se autentican con sus credenciales de
Windows. Por lo tanto, los usuarios deben tener cuentas en el dominio del servidor. Sin embargo, el modelo de
autenticación básica se puede configurar para permitir la autenticación personalizada, donde las cuentas de
usuario se autenticarán un origen externo, como una base de datos. Para obtener más información, consulte
autenticación básica en ASP.NET Web API en el sitio Web ASP.NET.

NOTE
Autenticación básica no se diseñó para administrar el cierre de sesión. Por lo tanto, el enfoque de autenticación básica
estándar para el cierre de sesión es para finalizar la sesión.

Vínculos relacionados
Consumir un servicio web RESTful
HttpClient
Autenticar a los usuarios con un proveedor de
identidades
11/07/2019 • 26 minutes to read • Edit Online

descargar el ejemplo
Xamarin.Auth es un SDK multiplataforma para autenticar a los usuarios y sus cuentas de almacenamiento.
Incluye los autenticadores de OAuth que proporcionan compatibilidad para consumir los proveedores de
identidades como Microsoft, Google, Facebook y Twitter. Este artículo explica cómo usar Xamarin.Auth para
administrar el proceso de autenticación en una aplicación de Xamarin.Forms.
OAuth es un estándar abierto para la autenticación y permite un propietario del recurso notificar a un proveedor
de recursos que se debe conceder permiso a un tercero para tener acceso a su información sin compartir la
identidad de los propietarios de recursos. Un ejemplo de esto sería la habilitación de un usuario notificar a un
proveedor de identidades (por ejemplo, Google, Microsoft, Facebook o Twitter) que se debe conceder permiso a
una aplicación para acceder a sus datos, sin compartir la identidad del usuario. Normalmente se usa como un
enfoque para los usuarios para iniciar sesión en sitios Web y aplicaciones mediante un proveedor de identidades,
pero sin exponer su contraseña para el sitio Web o aplicación.
Una descripción general del flujo de autenticación al consumir un proveedor de identidades de OAuth es como
sigue:
1. La aplicación navega en un explorador a una dirección URL de proveedor de identidad.
2. El proveedor de identidades controla la autenticación de usuario y devuelve un código de autorización a la
aplicación.
3. La aplicación intercambia el código de autorización para un token de acceso del proveedor de identidades.
4. La aplicación utiliza el token de acceso para acceder a las API en el proveedor de identidades, como una API
para solicitar datos de usuario básica.
La aplicación de ejemplo muestra cómo usar Xamarin.Auth para implementar un flujo de autenticación nativa con
Google. Mientras se usa Google como proveedor de identidades en este tema, el enfoque es igualmente aplicable
a otros proveedores de identidades. Para obtener más información sobre la autenticación mediante el punto de
conexión de Google OAuth 2.0, consulte utilizando OAuth2.0 para el acceso a Google APIs en el sitio Web de
Google.

NOTE
En iOS 9 y versiones posteriores, App Transport Security (ATS) exige que las conexiones seguras entre los recursos de
internet (por ejemplo, el servidor back-end de la aplicación) y la aplicación, lo que impide la divulgación accidental de
información confidencial. Puesto que ATS está habilitada de forma predeterminada en las aplicaciones compiladas para iOS 9,
todas las conexiones estará sujeto a los requisitos de seguridad ATS. Si las conexiones no cumplen estos requisitos, se
producirá un error con una excepción. Se puede optar por en ATS de si no es posible usar la HTTPS del protocolo y
proteger la comunicación de los recursos de internet. Esto puede lograrse mediante la actualización de la aplicación
Info.plist archivo. Para obtener más información, consulte App Transport Security.

Uso de Xamarin.Auth para autenticar a los usuarios


Xamarin.Auth admite dos enfoques para que las aplicaciones interactúan con el punto de conexión de autorización
del proveedor de identidades:
1. Uso de una vista web incrustado. Aunque esto ha sido una práctica común, ya no se recomienda por las
razones siguientes:
La aplicación que hospeda la vista web puede tener acceso a las credenciales del usuario autenticación
completa, no solo la concesión de autorización de OAuth que fue diseñada para la aplicación. Esto
infringe el principio de privilegio mínimo, ya que la aplicación tiene acceso a las credenciales más
eficaces que requiere, aumentando potencialmente la superficie de ataque de la aplicación.
La aplicación host podría capturar los nombres de usuario y contraseñas, automáticamente enviar
formularios y omitir el consentimiento del usuario y copie las cookies de sesión y usarlos para realizar
acciones autenticadas como usuario.
Vistas web incrustadas no compartan el estado de autenticación con otras aplicaciones o el explorador
web del dispositivo, solicitar al usuario que inicie sesión para cada solicitud de autorización que se
considera una experiencia de usuario inferiores.
Algunos puntos de conexión de autorización tomar medidas para detectar y bloquear las solicitudes de
autorización que procedan de vistas web.
2. Utilizando el explorador web del dispositivo, que es el enfoque recomendado. Utilizando el explorador del
dispositivo para las solicitudes de OAuth mejora la facilidad de uso de una aplicación, como los usuarios
solo necesitan iniciar sesión en el proveedor de identidad una vez por dispositivo, mejora las tasas de
conversión de flujos de inicio de sesión y la autorización en la aplicación. El explorador del dispositivo
también proporciona seguridad mejorada, como las aplicaciones pueden inspeccionar y modificar el
contenido en una vista web, pero no el contenido que se muestra en el explorador. Este es el enfoque
adoptado en este artículo y aplicación de ejemplo.
En el siguiente diagrama se muestra una descripción general de cómo la aplicación de ejemplo utiliza
Xamarin.Auth para autenticar a los usuarios y recuperar sus datos básicos:

La aplicación realiza una solicitud de autenticación para usar Google el OAuth2Authenticator clase. Se devuelve
una respuesta de autenticación, una vez que el usuario se ha autenticado correctamente con Google a través de su
página de inicio de sesión, que incluye un token de acceso. A continuación, la aplicación realiza una solicitud a
Google para datos de usuario básica, mediante el OAuth2Request (clase), con el token de acceso que se incluye en
la solicitud.
Programa de instalación
Debe crearse un proyecto de consola de API de Google para integrar el inicio de sesión de Google con una
aplicación de Xamarin.Forms. Esto se puede lograr de la siguiente manera:
1. Vaya a la consola de API de Google sitio Web e inicie sesión con credenciales de cuenta de Google.
2. En la lista desplegable proyecto, seleccione un proyecto existente o crear uno nuevo.
3. En la barra lateral en "Administrador de API", seleccione credenciales, a continuación, seleccione el ficha de
pantalla de consentimiento de OAuth. Elija un dirección de correo electrónico, especifique un nombre
de producto que se muestra a los usuariosy presione guardar.
4. En el credenciales ficha, seleccione el crear credenciales desplegable lista y elija Id. de cliente de OAuth.
5. En tipo de aplicación, seleccione la plataforma que se ejecutará la aplicación móvil (iOS o Android).
6. Rellene los detalles necesarios y seleccione el crear botón.
NOTE
Un identificador de cliente permite que una aplicación tener acceso a habilitado APIs Google y para las aplicaciones móviles
es único para una sola plataforma. Por lo tanto, un identificador de cliente OAuth debe crearse para cada plataforma que
va a usar inicio de sesión de Google.

Después de realizar estos pasos, Xamarin.Auth puede utilizarse para iniciar un flujo de autenticación de OAuth2
con Google.
Crear y configurar un autenticador
De Xamarin.Auth OAuth2Authenticator clase es responsable de controlar el flujo de autenticación de OAuth. En el
ejemplo de código siguiente se muestra la creación de instancias de la OAuth2Authenticator clase al realizar la
autenticación mediante el explorador web del dispositivo:

var authenticator = new OAuth2Authenticator(


clientId,
null,
Constants.Scope,
new Uri(Constants.AuthorizeUrl),
new Uri(redirectUri),
new Uri(Constants.AccessTokenUrl),
null,
true);

La OAuth2Authenticator clase requiere un número de parámetros, que son los siguientes:


Id. de cliente : Esto identifica al cliente que realiza la solicitud y se puede recuperar desde el proyecto en el
consola de API de Google.
Secreto de cliente : debe ser null o string.Empty .
Ámbito : Esto identifica a la API de acceso que está solicitando la aplicación y el valor informa a la pantalla de
consentimiento que se muestra al usuario. Para obtener más información sobre los ámbitos, consulte solicitud
de autorización de API en el sitio Web de Google.
Autorizar a la dirección URL : identifica la dirección URL donde se obtendrán el código de autorización de.
Dirección URL de redireccionamiento : identifica la dirección URL donde se enviará la respuesta. El valor
de este parámetro debe coincidir con uno de los valores que aparece en el credenciales pestaña para el
proyecto en el Google Developers Console.
Dirección AccessToken Url : identifica la dirección URL usada para solicitar tokens de acceso una vez
obtenido un código de autorización.
GetUserNameAsync Func : opcional Func que se usará para recuperar el nombre de usuario de la cuenta de
forma asincrónica después de que se ha autenticado correctamente.
Usar la interfaz de usuario nativa : un boolean valor que indica si se debe usar el explorador web del
dispositivo para realizar la solicitud de autenticación.
Configurar controladores de eventos de autenticación
Antes de presentar la interfaz de usuario, un controlador de eventos para el OAuth2Authenticator.Completed evento
debe estar registrado, como se muestra en el ejemplo de código siguiente:

authenticator.Completed += OnAuthCompleted;

Este evento se desencadenará cuando el usuario se autentica correctamente o cancela el inicio de sesión.
Opcionalmente, un controlador de eventos para el OAuth2Authenticator.Error también se pueden registrar
eventos.
Presentar la interfaz de usuario de inicio de sesión
La interfaz de usuario de inicio de sesión se puede presentar al usuario mediante el uso de un presentador de
inicio de sesión de Xamarin.Auth, lo que debe inicializarse en cada proyecto de la plataforma. El ejemplo de código
siguiente muestra cómo inicializar un presentador de inicio de sesión en el AppDelegate clase en el proyecto de
iOS:

global::Xamarin.Auth.Presenters.XamarinIOS.AuthenticationConfiguration.Init();

El ejemplo de código siguiente muestra cómo inicializar un presentador de inicio de sesión en el MainActivity
clase en el proyecto de Android:

global::Xamarin.Auth.Presenters.XamarinAndroid.AuthenticationConfiguration.Init(this, bundle);

El proyecto de biblioteca estándar. NET, a continuación, puede invocar el presentador de inicio de sesión como
sigue:

var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();


presenter.Login(authenticator);

Tenga en cuenta que el argumento para el Xamarin.Auth.Presenters.OAuthLoginPresenter.Login método es el


OAuth2Authenticator instancia. Cuando el Login se invoca el método, la interfaz de usuario de inicio de sesión se
presenta al usuario en una pestaña de explorador web del dispositivo, que se muestra en las capturas de pantalla
siguiente:
Procesa la dirección URL de redireccionamiento
Después de que el usuario completa el proceso de autenticación, el control se devuelve a la aplicación desde la
pestaña del explorador web. Esto se logra mediante el registro de un esquema de dirección URL personalizado
para la dirección URL de redireccionamiento que se devuelve desde el proceso de autenticación y, a continuación,
detectar y controlar la dirección URL personalizada, una vez que se envía.
Al elegir un esquema de dirección URL personalizado para asociar a una aplicación, las aplicaciones deben usar
un esquema basado en un nombre bajo su control. Esto puede lograrse mediante el nombre del identificador de
lote en iOS y el nombre del paquete en Android y, a continuación, invirtiéndola para realizar el esquema de
dirección URL. Sin embargo, algunos proveedores de identidades, como Google, asignan identificadores de
cliente basados en nombres de dominio, que, a continuación, se puede deshacer y utilizados como esquema de
dirección URL. Por ejemplo, si Google crea un identificador de cliente de
902730282010-ks3kd03ksoasioda93jldas93jjj22kr.apps.googleusercontent.com , el esquema de dirección URL será
com.googleusercontent.apps.902730282010-ks3kd03ksoasioda93jldas93jjj22kr . Tenga en cuenta que solo un único /
puede aparecer después del componente de esquema. Por lo tanto, es un ejemplo completo de una dirección URL
de redireccionamiento utilizando una combinación de dirección URL personalizada
com.googleusercontent.apps.902730282010-ks3kd03ksoasioda93jldas93jjj22kr:/oauth2redirect .

Cuando el explorador web recibe una respuesta del proveedor de identidad que contiene una combinación de
dirección URL personalizada, intenta cargar la dirección URL, que se producirá un error. En su lugar, el esquema
de dirección URL personalizado se notifica al sistema operativo al generar un evento. El sistema operativo, a
continuación, se comprueba para esquemas registrados, y si encuentra uno, el sistema operativo se inicia la
aplicación que registra el esquema y enviarlo en la dirección URL de redireccionamiento.
El mecanismo para registrar un esquema de dirección URL personalizado con el sistema operativo y el esquema
de control es específico para cada plataforma.
iOS
En iOS, se registra una combinación de dirección URL personalizada en Info.plist, tal y como se muestra en la
captura de pantalla siguiente:

El identificador valor puede ser cualquier cosa y el rol valor debe establecerse en Visor. El esquemas Url valor,
que comienza con com.googleusercontent.apps , puede obtenerse desde el identificador de cliente de iOS para el
proyecto en consola de API de Google.
Cuando el proveedor de identidades completa la solicitud de autorización, redirige a la dirección URL de
redireccionamiento de la aplicación. Dado que la dirección URL usa un esquema personalizado, da como
resultado de iniciar la aplicación de iOS, pasando la dirección URL como un parámetro de inicio, donde se
procesó la OpenUrl la invalidación de la aplicación AppDelegate (clase), que se muestra en el ejemplo de código
siguiente:

public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)


{
// Convert NSUrl to Uri
var uri = new Uri(url.AbsoluteString);

// Load redirectUrl page


AuthenticationState.Authenticator.OnPageLoading(uri);

return true;
}

El OpenUrl método convierte la dirección URL recibida desde un NSUrl a .NET Uri , antes de procesar la
dirección URL de redireccionamiento con el OnPageLoading método para un público OAuth2Authenticator objeto.
Esto hace que Xamarin.Auth para cerrar la pestaña del explorador web y analizar los datos recibidos de OAuth.
Android
En Android, se registra un esquema de dirección URL personalizado mediante la especificación de un
IntentFilter atributo el Activity que va a controlar el esquema. Cuando el proveedor de identidades completa
la solicitud de autorización, redirige a la dirección URL de redireccionamiento de la aplicación. Como la dirección
URL utiliza un esquema personalizado, da como resultado de iniciar la aplicación de Android, pasando la dirección
URL como un parámetro de inicio, donde se procesó la OnCreate método de la Activity registrado para
controlar el esquema de dirección URL personalizado. El ejemplo de código siguiente muestra la clase de la
aplicación de ejemplo que controla el esquema de dirección URL personalizado:
[Activity(Label = "CustomUrlSchemeInterceptorActivity", NoHistory = true, LaunchMode = LaunchMode.SingleTop )]
[IntentFilter(
new[] { Intent.ActionView },
Categories = new [] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataSchemes = new [] { "<insert custom URL here>" },
DataPath = "/oauth2redirect")]
public class CustomUrlSchemeInterceptorActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);

// Convert Android.Net.Url to Uri


var uri = new Uri(Intent.Data.ToString());

// Load redirectUrl page


AuthenticationState.Authenticator.OnPageLoading(uri);

Finish();
}
}

El DataSchemes propiedad de la IntentFilter debe establecerse en el identificador de cliente invertido que se


obtiene del identificador de cliente Android para el proyecto en consola de API de Google.
El OnCreate método convierte la dirección URL recibida desde un Android.Net.Url a .NET Uri , antes de
procesar la dirección URL de redireccionamiento con el OnPageLoading método para un público
OAuth2Authenticator objeto. Esto hace que Xamarin.Auth cerrar la pestaña del explorador web y analizar los datos
recibidos de OAuth.

IMPORTANT
En Android, se utiliza Xamarin.Auth el CustomTabs API para comunicarse con el sistema operativo y el explorador web. Sin
embargo, no se garantiza que un CustomTabs explorador compatible se instalará en el dispositivo del usuario.

Examinar la respuesta de OAuth


Después de analizar los datos recibidos de OAuth, Xamarin.Auth, se producirá la OAuth2Authenticator.Completed
eventos. En el controlador de eventos para este evento, el AuthenticatorCompletedEventArgs.IsAuthenticated
propiedad puede usarse para identificar si la autenticación se realizó correctamente, tal como se muestra en el
ejemplo de código siguiente:

async void OnAuthCompleted(object sender, AuthenticatorCompletedEventArgs e)


{
...
if (e.IsAuthenticated)
{
...
}
}

Los datos recopilados de una autenticación correcta están disponibles en el


AuthenticatorCompletedEventArgs.Account propiedad. Esto incluye un token de acceso, que se puede usar para
firmar las solicitudes de datos a una API proporcionada por el proveedor de identidades.
Realizar solicitudes de datos
Después de la aplicación obtiene un token de acceso, se utiliza para realizar una solicitud para el
https://www.googleapis.com/oauth2/v2/userinfo API para solicitar datos de usuario básica del proveedor de
identidades. Esta solicitud se realiza con de Xamarin.Auth OAuth2Request (clase), que representa una solicitud que
se autentica utilizando una cuenta que se recuperan de un OAuth2Authenticator de instancia, tal como se muestra
en el ejemplo de código siguiente:

// UserInfoUrl = https://www.googleapis.com/oauth2/v2/userinfo
var request = new OAuth2Request ("GET", new Uri (Constants.UserInfoUrl), null, e.Account);
var response = await request.GetResponseAsync ();
if (response != null)
{
string userJson = response.GetResponseText ();
var user = JsonConvert.DeserializeObject<User> (userJson);
}

Así como el método HTTP y la dirección URL de API, el OAuth2Request instancia especifica también una Account
instancia que contiene el token de acceso que firma la solicitud a la dirección URL especificada por el
Constants.UserInfoUrl propiedad. El proveedor de identidades, a continuación, devuelve los datos básicos de
usuario como una respuesta JSON, incluidos el nombre de los usuarios y la dirección de correo electrónico,
siempre que se reconoce como válido el token de acceso. La respuesta JSON, a continuación, se lee y
deserializarse la user variable.
Para obtener más información, consulte una llamada a una API de Google en el portal de desarrolladores de
Google.
Almacenar y recuperar información de cuenta en los dispositivos
Almacena de forma segura Xamarin.Auth Account objetos en una cuenta de almacén para que las aplicaciones no
tienen siempre que vuelva a autenticar a los usuarios. El AccountStore clase es responsable de almacenar
información de la cuenta y está respaldado por servicios de la cadena de claves en iOS y el KeyStore clase en
Android.
El siguiente ejemplo de código muestra cómo un Account objeto se guarda de forma segura:

AccountStore.Create ().Save (e.Account, Constants.AppName);

Guardado de las cuentas se identifican mediante una clave compuesta por la cuenta Username propiedad y un
identificador de servicio, que es una cadena que se utiliza cuando capturando cuentas desde el almacén de
cuentas. Si un Account guardado anteriormente, una llamada a la Save método nuevo sobrescribirá.
Account los objetos para un servicio se puede recuperar mediante una llamada a la FindAccountsForService
método, como se muestra en el ejemplo de código siguiente:

var account = AccountStore.Create ().FindAccountsForService (Constants.AppName).FirstOrDefault();

El FindAccountsForService método devuelve un IEnumerable colección de Account objetos, con el primer


elemento de la colección que se establece como la cuenta coincidente.

Resumen
En este artículo se explica cómo usar Xamarin.Auth para administrar el proceso de autenticación en una aplicación
de Xamarin.Forms. Xamarin.Auth proporciona el OAuth2Authenticator y OAuth2Request clases que son usadas por
las aplicaciones de Xamarin.Forms para consumir los proveedores de identidades como Microsoft, Google,
Facebook y Twitter.

Vínculos relacionados
OAuthNativeFlow (ejemplo)
OAuth 2.0 para aplicaciones nativas
Uso de OAuth2.0 para tener acceso a las API de Google
Xamarin.Auth (NuGet)
Xamarin.Auth (GitHub)
Autenticar a los usuarios con Azure Active Directory
B2C
11/07/2019 • 17 minutes to read • Edit Online

descargar el ejemplo
Azure B2C de Active Directory proporciona administración de identidad en la nube para aplicaciones móviles y
web orientadas al consumidor. En este artículo se muestra cómo usar Azure Active Directory B2C para integrar la
administración de identidad en una aplicación móvil con la biblioteca de autenticación de Microsoft.

Información general
Azure B2C de Active Directory (ADB2C ) es un servicio de administración de identidades para aplicaciones
orientadas al consumidor. Permite que los usuarios inicien sesión su aplicación con sus cuentas sociales existentes
o las credenciales personalizadas, como correo electrónico o nombre de usuario y contraseña. Las cuentas de
credenciales personalizadas se conocen como local cuentas.
El proceso para integrar el servicio de administración de identidades de Azure Active Directory B2C en una
aplicación móvil es como sigue:
1. Crear a un inquilino de Azure Active Directory B2C
2. Registrar la aplicación móvil con el inquilino de Azure Active Directory B2C
3. Crear directivas de registro e inicio de sesión y ha olvidado flujos de usuario de contraseña
4. Use la biblioteca de autenticación de Microsoft (MSAL ) para iniciar un flujo de trabajo de autenticación con el
inquilino de Azure Active Directory B2C.

NOTE
Azure B2C de Active Directory admite varios proveedores de identidades, incluidos Microsoft, GitHub, Facebook, Twitter y
mucho más. Para obtener más información sobre las capacidades de Azure Active Directory B2C, consulte documentación de
Azure Active Directory B2C.
Biblioteca de autenticación de Microsoft admite varias arquitecturas de aplicaciones y plataformas. Para obtener información
sobre las capacidades MSAL, consulte biblioteca de autenticación de Microsoft en GitHub.

Configurar a un inquilino de Azure Active Directory B2C


Para ejecutar el proyecto de ejemplo, debe crear a un inquilino de Azure Active Directory B2C. Para obtener más
información, consulte crear un inquilino de Azure Active Directory B2C en Azure portal.
Una vez creado un inquilino, necesitará el nombredeinquilino y Id. de inquilino para configurar la aplicación
móvil. El Id. de inquilino y el nombre se definen por el dominio generado al crear la dirección URL del inquilino. Si
la dirección URL de inquilino generada es https://contoso20190410tenant.onmicrosoft.com/ el Id. de inquilino es
contoso20190410tenant.onmicrosoft.com y nombredeinquilino es contoso20190410tenant . Busque el dominio del
inquilino en Azure portal, haga clic en el filtro de directorio y suscripción en el menú superior. Captura de
pantalla siguiente muestra el directorio de Azure y el botón de filtro de suscripción y el dominio del inquilino:
En el proyecto de ejemplo, edite el Constants.cs archivo para establecer el tenantName y tenantId campos. El
código siguiente muestra cómo se deben establecer estos valores si el dominio del inquilino es
https://contoso20190410tenant.onmicrosoft.com/ , reemplace estos valores con los valores desde el portal:

public static class Constants


{
static readonly string tenantName = "contoso20190410tenant";
static readonly string tenantId = "contoso20190410tenant.onmicrosoft.com";
...
}

Registrar la aplicación móvil con Azure Active Directory B2C


Una aplicación móvil debe registrarse con el inquilino antes de que se puede conectar y autenticar a los usuarios.
El proceso de registro asigna un único Id. de aplicación a la aplicación y un dirección URL de
redireccionamiento que dirige las respuestas a la aplicación tras la autenticación. Para obtener más información,
consulte Azure Active Directory B2C: Registrar la aplicación. Necesitará saber la Id. de aplicación asignado a la
aplicación, que aparece después del nombre de la aplicación en la vista de propiedades. Captura de pantalla
siguiente muestra dónde encontrar el identificador de aplicación:

Espera que la biblioteca de autenticación de Microsoft la dirección URL de redireccionamiento para la


aplicación sea su Id. de aplicación como prefijo el texto "msal" y seguido por un punto de conexión denominado
"auth". Si el identificador de aplicación es "1234abcd", la dirección URL completa debe ser msal1234abcd://auth .
Asegúrese de que la aplicación se ha habilitado la Native client configuración y crear un URI de
redireccionamiento personalizado mediante su identificador de aplicación, como se muestra en la siguiente
captura de pantalla:
La dirección URL que se usará más adelante en el Android ApplicationManifest.xml y el iOS Info.plist.
En el proyecto de ejemplo, edite el Constants.cs archivo para establecer el clientId campo a su Id. de
aplicación. El código siguiente muestra cómo se debe establecer este valor si es su identificador de aplicación
1234abcd :

public static class Constants


{
static readonly string tenantName = "contoso20190410tenant";
static readonly string tenantId = "contoso20190410tenant.onmicrosoft.com";
static readonly string clientId = "1234abcd";
...
}

Crear directivas de registro e inicio de sesión y olvidar las directivas de


contraseñas
Una directiva es una experiencia de los usuarios seguir para completar una tarea como la creación de una cuenta o
restablecer una contraseña. Una directiva también especifica el contenido de los tokens que recibe la aplicación
cuando el usuario vuelve de la experiencia. Debe configurar directivas para tanto la cuenta de registro e inicio de
sesión y restablecimiento de contraseña. Azure tiene directivas integradas que simplifican la creación de directivas
comunes. Para obtener más información, consulte Azure Active Directory B2C: Las directivas integradas.
Cuando haya completado la configuración de directiva, debe tener dos directivas en el flujos de usuario
(directivas) vista en el portal de Azure. Captura de pantalla siguiente muestra dos directivas configuradas en el
portal de Azure:
En el proyecto de ejemplo, edite el Constants.cs archivo para establecer el policySignin y policyPassword
campos para reflejar los nombres que eligió durante la configuración de directiva:

public static class Constants


{
static readonly string tenantName = "contoso20190410tenant";
static readonly string tenantId = "contoso20190410tenant.onmicrosoft.com";
static readonly string clientId = "1234abcd";
static readonly string policySignin = "B2C_1_signupsignin1";
static readonly string policyPassword = "B2C_1_passwordreset";
...
}

Usar la biblioteca de autenticación de Microsoft (MSAL) para la


autenticación
El paquete NuGet de biblioteca de autenticación de Microsoft (MSAL ) debe agregarse el recurso compartido, un
proyecto .NET Standard y los proyectos de plataforma en una solución de Xamarin.Forms. MSAL incluye un
PublicClientApplicationBuilder clase que crea un objeto que cumple con la IPublicClientApplication interfaz.
MSAL utiliza With cláusulas para proporcionar parámetros adicionales a los métodos de autenticación y el
constructor.
En el proyecto de ejemplo, el código subyacente para App.xaml define las propiedades estáticas denominadas
AuthenticationClient y UIParent y crea una instancia de la AuthenticationClient objeto en el constructor. El
WithIosKeychainSecurityGroup cláusula proporciona un nombre de grupo de seguridad para las aplicaciones de
iOS. El WithB2CAuthority cláusula proporciona el valor predeterminado autoridad, o la directiva, que se usará
para autenticar a los usuarios. En el ejemplo siguiente se muestra cómo crear una instancia de la
PublicClientApplication :
public partial class App : Application
{
public static IPublicClientApplication AuthenticationClient { get; private set; }

public static object UIParent { get; set; } = null;

public App()
{
InitializeComponent();

AuthenticationClient = PublicClientApplicationBuilder.Create(Constants.ClientId)
.WithIosKeychainSecurityGroup(Constants.IosKeychainSecurityGroups)
.WithB2CAuthority(Constants.AuthoritySignin)
.Build();

MainPage = new NavigationPage(new LoginPage());


}

...

El OnAppearing controlador de eventos en el LoginPage.xaml.cs código detrás de las llamadas


AcquireTokenSilentAsync para actualizar el token de autenticación para los usuarios que han iniciado sesión antes.
El proceso de autenticación se redirige a la LogoutPage si se realiza correctamente y no realiza ninguna acción en
caso de error. El ejemplo siguiente muestra el proceso de reautenticación silenciosa en OnAppearing :

public partial class LoginPage : ContentPage


{
...

protected override async void OnAppearing()


{
try
{
// Look for existing account
IEnumerable<IAccount> accounts = await App.AuthenticationClient.GetAccountsAsync();

AuthenticationResult result = await App.AuthenticationClient


.AcquireTokenSilent(Constants.Scopes, accounts.FirstOrDefault())
.ExecuteAsync();

await Navigation.PushAsync(new LogoutPage(result));


}
catch
{
// Do nothing - the user isn't logged in
}
base.OnAppearing();
}

...
}

El OnLoginButtonClicked controlador de eventos (que se desencadena cuando se hace clic en el botón de inicio de
sesión) llamadas AcquireTokenAsync . La biblioteca MSAL automáticamente abre el Explorador de dispositivos
móviles y navega a la página de inicio de sesión. La URL de inicio de sesión, denominada una autoridad, es una
combinación del nombre de inquilino y las directivas se definen en el Constants.cs archivo. Si el usuario elige el
olvidó la opción de contraseña, se devuelven a la aplicación con una excepción, que inicia la olvidó la experiencia de
contraseña. El ejemplo siguiente muestra el proceso de autenticación:
public partial class LoginPage : ContentPage
{
...

async void OnLoginButtonClicked(object sender, EventArgs e)


{
AuthenticationResult result;
try
{
result = await App.AuthenticationClient
.AcquireTokenInteractive(Constants.Scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(App.UIParent)
.ExecuteAsync();

await Navigation.PushAsync(new LogoutPage(result));


}
catch (MsalException ex)
{
if (ex.Message != null && ex.Message.Contains("AADB2C90118"))
{
result = await OnForgotPassword();
await Navigation.PushAsync(new LogoutPage(result));
}
else if (ex.ErrorCode != "authentication_canceled")
{
await DisplayAlert("An error has occurred", "Exception message: " + ex.Message, "Dismiss");
}
}
}

...
}

El OnForgotPassword método es similar al proceso de inicio de sesión, pero implementa una directiva
personalizada. OnForgotPassword utiliza una sobrecarga diferente del AcquireTokenAsync , que le permite
proporcionar un determinado autoridad. El ejemplo siguiente muestra cómo especificar un personalizado
autoridad al adquirir un token:

public partial class LoginPage : ContentPage


{
...
async Task<AuthenticationResult> OnForgotPassword()
{
try
{
return await App.AuthenticationClient
.AcquireTokenInteractive(Constants.Scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(App.UIParent)
.WithB2CAuthority(Constants.AuthorityPasswordReset)
.ExecuteAsync();
}
catch (MsalException)
{
// Do nothing - ErrorCode will be displayed in OnLoginButtonClicked
return null;
}
}
}

La última pieza de autenticación es el proceso de cierre de sesión. El OnLogoutButtonClicked método se llama


cuando el usuario presiona el botón de cierre de sesión. Recorre en bucle todas las cuentas y garantiza que se han
invalidado sus tokens. El ejemplo siguiente muestra la implementación de cierre de sesión:

public partial class LogoutPage : ContentPage


{
...
async void OnLogoutButtonClicked(object sender, EventArgs e)
{
IEnumerable<IAccount> accounts = await App.AuthenticationClient.GetAccountsAsync();

while (accounts.Any())
{
await App.AuthenticationClient.RemoveAsync(accounts.First());
accounts = await App.AuthenticationClient.GetAccountsAsync();
}

await Navigation.PopAsync();
}
}

iOS
En iOS, el esquema de dirección URL personalizado que se registró con Azure Active Directory B2C debe estar
registrado en Info.plist. MSAL espera que el esquema de dirección URL para adherirse a un modelo específico, se
ha descrito anteriormente en registrar la aplicación móvil con Azure Active Directory B2C. Captura de pantalla
siguiente muestra el esquema de dirección URL personalizado en Info.plist.

MSAL también requiere derechos de la cadena de claves en iOS, registrados en el Entitilements.plist, tal y como
se muestra en la captura de pantalla siguiente:

Cuando Azure Active Directory B2C se completa la solicitud de autorización, redirige a la URL de
redireccionamiento registrados. Da como resultado el esquema de dirección URL personalizado en iOS iniciando
la aplicación móvil y pasarle la dirección URL como un parámetro de inicio, donde se procesó la OpenUrl la
invalidación de la aplicación AppDelegate clase y devuelve el control de la experiencia de MSAL. El OpenUrl
implementación se muestra en el ejemplo de código siguiente:

using Microsoft.Identity.Client;

namespace TodoAzure.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
...
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url);
return base.OpenUrl(app, url, options);
}
}
}

Android
En Android, el esquema de dirección URL personalizado que se registró con Azure Active Directory B2C debe
estar registrado en el AndroidManifest.xml. MSAL espera que el esquema de dirección URL para adherirse a un
modelo específico, se ha descrito anteriormente en registrar la aplicación móvil con Azure Active Directory B2C. El
ejemplo siguiente muestra el esquema de dirección URL personalizado en el AndroidManifest.xml.

<?xml version="1.0" encoding="utf-8"?>


<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1"
android:versionName="1.0" package="com.xamarin.adb2cauthorization">
<uses-sdk android:minSdkVersion="15" />
<application android:label="ADB2CAuthorization">
<activity android:name="microsoft.identity.client.BrowserTabActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="INSERT_URI_SCHEME_HERE" android:host="auth" />"
</intent-filter>
</activity>"
</application>
</manifest>

El MainActivity clase debe modificarse para proporcionar la UIParent objeto a la aplicación durante la OnCreate
llamar. Cuando Azure Active Directory B2C se completa la solicitud de autorización, se redirige a la combinación
de dirección URL registrada desde el AndroidManifest.xml. Da como resultado el esquema URI registrado en
Android que llama a la OnActivityResult método con la dirección URL como un parámetro de inicio, donde se
procesó la SetAuthenticationContinuationEventArgs método.
public class MainActivity : FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;

base.OnCreate(bundle);

Forms.Init(this, bundle);
LoadApplication(new App());
App.UIParent = this;
}

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)


{
base.OnActivityResult(requestCode, resultCode, data);
AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode,
data);
}
}

Plataforma universal de Windows


No se requiere ninguna configuración adicional para usar MSAL en la plataforma Universal de Windows

Ejecución del proyecto


Ejecute la aplicación en un dispositivo físico o virtual. Pulsar el inicio de sesión botón debe abrir el explorador y
navegue a una página donde puede iniciar sesión o crear una cuenta. Después de completar el proceso de inicio de
sesión, debería volver a la página de cierre de sesión de la aplicación. Captura de pantalla siguiente muestra el
inicio de sesión de usuario en la pantalla que se ejecutan en iOS y Android:

Vínculos relacionados
AzureADB2CAuth (ejemplo)
Azure Active Directory B2C
Biblioteca de autenticación de Microsoft
Documentación de la biblioteca de autenticación de Microsoft
Autenticar a los usuarios con una base de datos de
documentos de Azure Cosmos DB y Xamarin.Forms
12/07/2019 • 23 minutes to read • Edit Online

Descargar el ejemplo
Azure bases de datos de documento de Cosmos DB admiten colecciones con particiones, que pueden abarcar
varios servidores y particiones, al tiempo que admite almacenamiento ilimitado y rendimiento. En este artículo se
explica cómo combinar el control de acceso con las colecciones con particiones, para que un usuario sólo puede
tener acceso a sus propios documentos en una aplicación de Xamarin.Forms.

Información general
Una clave de partición debe especificarse al crear una colección con particiones y documentos con la misma clave
de partición se almacenarán en la misma partición. Por lo tanto, especificar la identidad del usuario como una clave
de partición dará como resultado de una colección con particiones que sólo van a almacenar documentos de ese
usuario. Esto también garantiza que la base de datos de documentos de Azure Cosmos DB se escalará a medida el
número de usuarios y aumentar los elementos.
Se debe conceder acceso a cualquier colección, y el modelo de control de acceso de API de SQL define dos tipos
de construcciones de acceso:
Las claves maestras habilitar el acceso administrativo completo a todos los recursos dentro de una cuenta de
Cosmos DB y se crean cuando se crea una cuenta de Cosmos DB.
Los tokens de recursos capturan la relación entre el usuario de una base de datos y el permiso el usuario tiene
para un recurso específico de Cosmos DB, como una colección o un documento.
Exponer una clave maestra, abre una cuenta de Cosmos DB a la posibilidad de un uso negligente o
malintencionado. Sin embargo, los tokens de recursos de Azure Cosmos DB proporcionan un mecanismo seguro
para permitir que los clientes leer, escribir y eliminar recursos específicos en una cuenta de Azure Cosmos DB
según los permisos concedidos.
Un enfoque típico para solicitar, generar y entregar tokens de recursos a una aplicación móvil es usar a un agente
de token de recurso. El siguiente diagrama muestra una descripción general de cómo la aplicación de ejemplo usa
a un agente de tokens de recursos para administrar el acceso a los datos de la base de datos de documento:
La resource token broker es un servicio de API Web de nivel intermedio, hospedado en Azure App Service, que
tiene la clave maestra de la cuenta de Cosmos DB. La aplicación de ejemplo usa a la resource token broker para
administrar el acceso a la base de datos de documento como sigue:
1. En el inicio de sesión, la aplicación de Xamarin.Forms se pone en contacto con Azure App Service para iniciar
un flujo de autenticación.
2. Azure App Service realiza un flujo de autenticación de OAuth con Facebook. Una vez que se complete el flujo
de autenticación, la aplicación de Xamarin.Forms recibe un token de acceso.
3. La aplicación de Xamarin.Forms utiliza el token de acceso para solicitar un token de recurso de la resource
token broker.
4. La resource token broker usa el token de acceso para solicitar la identidad del usuario de Facebook. La
identidad del usuario, a continuación, se usa para solicitar un token de recurso de Cosmos DB, que se usa para
conceder acceso de lectura/escritura a la colección con particiones del usuario autenticado.
5. La aplicación de Xamarin.Forms utiliza el token de recurso para acceder directamente a los recursos de Cosmos
DB con los permisos definidos por el token de recurso.

NOTE
Cuando expira el token de recurso, las solicitudes de base de datos de documento posterior recibirá una excepción 401 no
autorizado. En este momento, las aplicaciones de Xamarin.Forms deben volver a establecer la identidad y solicitar un nuevo
token de recurso.

Para obtener más información acerca de la partición de Cosmos DB, consulte partición y escalado en Azure
Cosmos DB. Para obtener más información acerca del control de acceso de Cosmos DB, consulte protección del
acceso a datos de Cosmos DB y control de acceso en la API de SQL.

Programa de instalación
El proceso para integrar a la resource token broker en una aplicación de Xamarin.Forms es como sigue:
1. Cree una cuenta de Cosmos DB que se va a usar el control de acceso. Para obtener más información, consulte
Cosmos DB configuración.
2. Cree un Azure App Service para hospedar a la resource token broker. Para obtener más información, consulte
configuración de Azure App Service.
3. Creación de una aplicación de Facebook para realizar la autenticación. Para obtener más información, consulte
Facebook App Configuration.
4. Configurar el servicio de aplicación de Azure para llevar a cabo la autenticación con Facebook fácil. Para
obtener más información, consulte configuración de autenticación de Azure App Service.
5. Configure la aplicación de ejemplo de Xamarin.Forms para comunicarse con Azure App Service y Cosmos DB.
Para obtener más información, consulte configuración de la aplicación de Xamarin.Forms.
Configuración de Azure Cosmos DB
El proceso de creación de una cuenta de Cosmos DB que se va a usar el control de acceso es como sigue:
1. Cree una cuenta de Cosmos DB. Para obtener más información, consulte crear una cuenta de Azure Cosmos
DB.
2. En la cuenta de Cosmos DB, crear una nueva colección denominada UserItems , especificar una clave de
partición de /userid .
Configuración de Azure App Service
El proceso para hospedar a la resource token broker en Azure App Service es como sigue:
1. En el portal de Azure, cree una nueva aplicación web de App Service. Para obtener más información,
consulte crear una aplicación web en un entorno de App Service.
2. En el portal de Azure, abra la hoja de configuración de la aplicación de la aplicación web y agregue la
siguiente configuración:
accountUrl : el valor debe ser la dirección URL de cuenta de Cosmos DB en la hoja de claves de la
cuenta de Cosmos DB.
accountKey : el valor debe ser la clave maestra para Cosmos DB (principal o secundaria) en la hoja de
claves de la cuenta de Cosmos DB.
databaseId : el valor debe ser el nombre de la base de datos de Cosmos DB.
collectionId : el valor debe ser el nombre de la colección de Cosmos DB (en este caso, UserItems ).
hostUrl : el valor debe ser la dirección URL de la aplicación web en la hoja de información general de la
cuenta de servicio de aplicación.
Captura de pantalla siguiente muestra esta configuración:

3. Publicar la solución de resource token broker en la aplicación web de Azure App Service.
Configuración de la aplicación de Facebook
El proceso de creación de una aplicación de Facebook para realizar la autenticación es el siguiente:
1. Creación de una aplicación de Facebook. Para obtener más información, consulte registrar y configurar una
aplicación en el Centro para desarrolladores de Facebook.
2. Agregar el producto de inicio de sesión de Facebook a la aplicación. Para obtener más información, consulte
agregar inicio de sesión de Facebook a la aplicación o sitio Web en el Centro para desarrolladores de Facebook.
3. Configurar el inicio de sesión de Facebook como sigue:
Habilitar el inicio de sesión de cliente OAuth.
Habilitar el inicio de sesión de OAuth de Web.
Establece el URI para el URI de la aplicación web de App Service, de redirección de OAuth válido con
/.auth/login/facebook/callback anexado.
Captura de pantalla siguiente muestra esta configuración:

Para obtener más información, consulte registrar la aplicación con Facebook.


Configuración de autenticación de Azure App Service
El proceso de configuración de autenticación fácil de App Service es como sigue:
1. En el Portal de Azure, vaya a la aplicación web de App Service.
2. En el Portal de Azure, abra la autenticación / hoja de autorización y realizar la configuración siguiente:
Autenticación de App Service debe estar activada.
La acción que se realizará cuando una solicitud no está autenticada debe establecerse en inicio de
sesión con Facebook.
Captura de pantalla siguiente muestra esta configuración:

La aplicación web de App Service también debe configurarse para comunicarse con la aplicación de Facebook para
habilitar el flujo de autenticación. Esto puede realizarse seleccionando el proveedor de identidades de Facebook, y
especificando el Id. de aplicación y secreto de la aplicación los valores de la configuración de la aplicación de
Facebook en el Centro para desarrolladores de Facebook. Para obtener más información, consulte Facebook
agregar información a la aplicación.
Configuración de la aplicación de Xamarin.Forms
El proceso para configurar la aplicación de ejemplo de Xamarin.Forms es como sigue:
1. Abra la solución Xamarin.Forms.
2. Abra Constants.cs y actualice los valores de las siguientes constantes:
EndpointUri : el valor debe ser la dirección URL de cuenta de Cosmos DB en la hoja de claves de la
cuenta de Cosmos DB.
DatabaseName : el valor debe ser el nombre de la base de datos del documento.
CollectionName : el valor debe ser el nombre de la colección de base de datos de documentos (en este
caso, UserItems ).
ResourceTokenBrokerUrl : el valor debe ser la dirección URL de la aplicación de web resource token
broker en la hoja de información general de la cuenta de servicio de aplicación.

Iniciando el inicio de sesión


La aplicación de ejemplo inicia el proceso de inicio de sesión mediante el uso de Xamarin.Auth para redirigir un
explorador a una URL de proveedor de identidades, como se muestra en el ejemplo de código siguiente:

var auth = new Xamarin.Auth.WebRedirectAuthenticator(


new Uri(Constants.ResourceTokenBrokerUrl + "/.auth/login/facebook"),
new Uri(Constants.ResourceTokenBrokerUrl + "/.auth/login/done"));

Esto hace que un flujo de autenticación de OAuth que se inicie entre Azure App Service y Facebook, que muestra la
página de inicio de sesión de Facebook:
Se puede cancelar el inicio de sesión presionando el cancelar botón en iOS o presionando el volver botón en
Android, en cuyo caso permanece sin autenticar el usuario y la interfaz de usuario del proveedor de identidad es
Quita de la pantalla.
Para obtener más información acerca de Xamarin.Auth, consulte la autenticación de usuarios con un proveedor de
identidades.

Obtener un Token de recurso


Después de autenticarse correctamente, el WebRedirectAuthenticator.Completed desencadena el evento. En el
ejemplo de código siguiente se muestra cómo controlar este evento:

auth.Completed += async (sender, e) =>


{
if (e.IsAuthenticated && e.Account.Properties.ContainsKey("token"))
{
var easyAuthResponseJson = JsonConvert.DeserializeObject<JObject>(e.Account.Properties["token"]);
var easyAuthToken = easyAuthResponseJson.GetValue("authenticationToken").ToString();

// Call the ResourceBroker to get the resource token


using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Add("x-zumo-auth", easyAuthToken);
var response = await httpClient.GetAsync(Constants.ResourceTokenBrokerUrl + "/api/resourcetoken/");
var jsonString = await response.Content.ReadAsStringAsync();
var tokenJson = JsonConvert.DeserializeObject<JObject>(jsonString);
resourceToken = tokenJson.GetValue("token").ToString();
UserId = tokenJson.GetValue("userid").ToString();

if (!string.IsNullOrWhiteSpace(resourceToken))
{
client = new DocumentClient(new Uri(Constants.EndpointUri), resourceToken);
...
}
...
}
}
};

El resultado de una autenticación correcta es un token de acceso, que está disponible


AuthenticatorCompletedEventArgs.Account propiedad. El token de acceso se extrae y se utiliza en una solicitud GET
en del resource token broker resourcetoken API.
El resourcetoken API usa el token de acceso para solicitar la identidad del usuario de Facebook, que sirve para
solicitar un token de recurso de Cosmos DB. Si ya existe un documento de permiso válido para el usuario en la
base de datos de documento, se recupera y se devuelve un documento JSON que contiene el token de recurso a la
aplicación de Xamarin.Forms. Si no existe un documento de permiso válido para el usuario, se crea un usuario y un
permiso en la base de datos de documento y el token de recurso se extraen el documento de permiso y se
devuelve a la aplicación de Xamarin.Forms en un documento JSON.

NOTE
Un usuario de base de datos de documento es un recurso asociado con una base de datos de documento, y cada base de
datos puede contener cero o más usuarios. Un permiso de base de datos de documento es un recurso asociado con un
usuario de base de datos de documento y cada usuario puede contener cero o más permisos. Un recurso de permiso
proporciona acceso a un token de seguridad que requiere que el usuario cuando se intenta tener acceso a un recurso como
un documento.

Si el resourcetoken API se completa correctamente, enviará el código de estado HTTP 200 (OK) en la respuesta,
junto con un documento JSON que contiene el token de recurso. Los siguientes datos JSON muestran un mensaje
de respuesta correcta típico:

{
"id": "John Smithpermission",
"token":
"type=resource&ver=1&sig=zx6k2zzxqktzvuzuku4b7y==;a74aukk99qtwk8v5rxfrfz7ay7zzqfkbfkremrwtaapvavw2mrvia4umbi/7
iiwkrrq+buqqrzkaq4pp15y6bki1u//zf7p9x/aefbvqvq3tjjqiffurfx+vexa1xarxkkv9rbua9ypfzr47xpp5vmxuvzbekkwq6txme0xxxb
jhzaxbkvzaji+iru3xqjp05amvq1r1q2k+qrarurhmjzah/ha0evixazkve2xk1zu9u/jpyf1xrwbkxqpzebvqwma+hyyaazemr6qx9uz9be==
;",
"expires": 4035948,
"userid": "John Smith"
}

El WebRedirectAuthenticator.Completed controlador de eventos lee la respuesta desde el resourcetoken API y


extrae el token de recurso y el identificador de usuario. El token de recurso, a continuación, se pasa como
argumento a la DocumentClient constructor, que encapsula el punto de conexión, credenciales y directiva de
conexión utilizada para acceder a Cosmos DB y se usa para configurar y ejecutar solicitudes en Cosmos DB. El
token de recurso se envía con cada solicitud para acceder directamente a un recurso e indica que se concede
acceso de lectura/escritura a la colección con particiones de los usuarios autenticados.

Recuperación de documentos
Recuperar documentos que pertenecen sólo al usuario autenticado puede lograrse mediante la creación de una
consulta de documento que incluye el identificador del usuario como clave de partición y se muestra en el ejemplo
de código siguiente:

var query = client.CreateDocumentQuery<TodoItem>(collectionLink,


new FeedOptions
{
MaxItemCount = -1,
PartitionKey = new PartitionKey(UserId)
})
.Where(item => !item.Id.Contains("permission"))
.AsDocumentQuery();
while (query.HasMoreResults)
{
Items.AddRange(await query.ExecuteNextAsync<TodoItem>());
}

La consulta recupera todos los documentos que pertenecen al usuario autenticado, de la colección especificada,
asincrónicamente y los coloca en un List<TodoItem> colección para su presentación.
El CreateDocumentQuery<T> método especifica un Uri argumento que representa la colección que se debe
consultar documentos, y un FeedOptions objeto. La FeedOptions objeto especifica que se puede devolver un
número ilimitado de elementos por la consulta y el identificador del usuario como clave de partición. Esto
garantiza que se devuelven únicamente los documentos en la colección del usuario con particiones en el resultado.

NOTE
Tenga en cuenta que los documentos de permiso, que son creados por la resource token broker, se almacenan en la misma
colección de documentos que los documentos creados por la aplicación de Xamarin.Forms. Por lo tanto, la consulta de
documento contiene un Where cláusula que se aplica un predicado de filtrado a la consulta en la colección de documentos.
Esta cláusula garantiza que los documentos de permiso no se devuelven de la colección de documentos.

Para obtener más información acerca de cómo recuperar documentos desde una colección de documentos,
consulte recuperar documentos de colección de documentos.

Inserción de documentos
Antes de insertar un documento en una colección de documentos, el TodoItem.UserId propiedad debe actualizarse
con el valor utilizado como clave de partición, como se muestra en el ejemplo de código siguiente:

item.UserId = UserId;
await client.CreateDocumentAsync(collectionLink, item);

Esto garantiza que se insertará el documento en la colección del usuario con particiones.
Para obtener más información sobre cómo insertar un documento en una colección de documentos, consulte
insertar documentos en una colección de documentos.

Eliminación de documentos
El valor de clave de partición debe especificarse al eliminar un documento de una colección con particiones, como
se muestra en el ejemplo de código siguiente:

await client.DeleteDocumentAsync(UriFactory.CreateDocumentUri(Constants.DatabaseName,
Constants.CollectionName, id),
new RequestOptions
{
PartitionKey = new PartitionKey(UserId)
});

Esto garantiza que Cosmos DB sabe que particiones colección para eliminar el documento de.
Para obtener más información sobre cómo eliminar un documento de una colección de documentos, consulte
eliminar un documento de una colección de documentos.

Resumen
En este artículo se explica cómo combinar el control de acceso con las colecciones con particiones, para que un
usuario sólo puede tener acceso a sus propios documentos de base de datos de documento en una aplicación de
Xamarin.Forms. Especificar la identidad del usuario como una clave de partición garantiza que una colección con
particiones solo puede almacenar documentos de ese usuario.

Vínculos relacionados
Todo Azure Cosmos DB Auth (ejemplo)
Consumo de una base de datos de documentos de Cosmos Azure DB
Protección del acceso a datos de Azure Cosmos DB
Control de acceso en la API de SQL.
Cómo crear particiones y escalado en Azure Cosmos DB
Biblioteca de cliente de Azure Cosmos DB
API de Azure Cosmos DB
Implementación y pruebas de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

Publicación de aplicaciones de iOS


Las aplicaciones de iOS se pueden distribuir a través del App Store de Apple, internamente y mediante canales a
medida.

Publicación de aplicaciones de Android


Las aplicaciones de Android se pueden distribuir a través de diversos canales, como el correo electrónico, un
servidor web privado, Google Play o Amazon Appstore para Android.

Publicación de aplicaciones de la Plataforma universal de Windows


Las aplicaciones para UWP se pueden distribuir a través de la Microsoft Store, la carga lateral del paquete de
aplicación directamente en un dispositivo o la instalación web.

Publicación de aplicaciones de Mac


Las aplicaciones de Mac se puede distribuir a través del Mac App Store o directamente.

Rendimiento
Existen muchas técnicas para aumentar el rendimiento de aplicaciones de Xamarin.Forms. En conjunto, estas
técnicas pueden reducir considerablemente la cantidad de trabajo que está realizando una CPU y la cantidad de
memoria consumida por una aplicación.

Pruebas automatizadas con Visual Studio App Center


App Center Test es un servicio de automatización de pruebas para aplicaciones móviles nativas e híbridas. También
se conoce como Test Cloud.
Introducción a la distribución de aplicaciones
Xamarin.iOS
11/07/2019 • 5 minutes to read • Edit Online

Este documento contiene información general sobre las técnicas de distribución que están disponibles para las
aplicaciones Xamarin.iOS y actúa como un punto de partida a documentos más detallados sobre el tema.
Una vez que se ha desarrollado una aplicación de Xamarin.iOS, el siguiente paso del ciclo de vida de desarrollo de
software es distribuirla a los usuarios, como se muestra en la sección destacada del siguiente diagrama:

Apple proporciona los siguientes métodos para distribuir una aplicación de iOS, que son compatibles con
Xamarin.iOS:
1. La App Store
2. Interna (Enterprise)
3. Ad Hoc
Todos estos escenarios requieren que las aplicaciones se aprovisionen mediante el correspondiente perfil de
aprovisionamiento. Los perfiles de aprovisionamiento son archivos que contienen información de firma de código,
así como la identidad de la aplicación y el mecanismo de distribución previsto. También contienen información
sobre en qué dispositivos se puede implementar la aplicación para la distribución que no se realice a través del
App Store.

Distribución a través del App Store


IMPORTANT
Apple ha comunicado que, a partir de marzo de 2019, las aplicaciones y actualizaciones que se envíen al App Store deberán
haberse compilado con el SDK de iOS 12.1 o posterior, incluido en Xcode 10.1 y versiones posteriores. Las aplicaciones
también deberán admitir los tamaños de pantalla del iPhone XS y el iPad Pro de 12.9".

Se trata de la forma principal mediante la que se distribuyen las aplicaciones de iOS a los consumidores en
dispositivos iOS. Todas las aplicaciones que se envían a la App Store requieren la aprobación de Apple.
Las aplicaciones se envían a la App Store a través de un portal llamado iTunes Connect. La guía Configurar la
aplicación en iTunes Connect proporciona más información sobre cómo configurar y usar este portal para
preparar una aplicación de Xamarin.iOS para su publicación en el App Store.
Es importante tener en cuenta que solo los desarrolladores que pertenecen al Programa para desarrolladores
de Apple tienen acceso a iTunes Connect. Los miembros del Programa para desarrolladores empresariales de
Apple no tienen acceso.
Para obtener más información, visite la guía Distribución a través del App Store.

Distribución interna
A veces denominada Distribución empresarial, la distribución interna permite a los miembros del Programa para
desarrolladores empresariales de Apple distribuir aplicaciones internamente a otros miembros de la misma
organización. La distribución interna tiene las ventajas de no requerir una revisión de la App Store y no tener
ningún límite en el número de dispositivos en los que se puede instalar una aplicación. Sin embargo, es importante
tener en cuenta que los miembros del Programa para desarrolladores empresariales de Apple no tienen
acceso a iTunes Connect y, por lo tanto, el licenciatario es responsable de distribuir la aplicación.
Para obtener más información sobre cómo configurar y cómo distribuir una aplicación de forma interna, consulte
la Guía de distribución interna.

Distribución ad hoc
Los usuarios pueden probar las aplicaciones de Xamarin.iOS a través de la distribución ad hoc, que está disponible
tanto en el Programa para desarrolladores de Apple y el Programa para desarrolladores empresariales de
Apple, y permite realizar la prueba en un máximo de 100 dispositivos iOS. El mejor caso de uso para la
distribución ad hoc es la distribución dentro de una empresa cuando iTunes Connect no es una opción.
Para obtener más información sobre cómo configurar y cómo distribuir una aplicación de forma interna, consulte
la Guía de distribución ad hoc.

Resumen
Este artículo le da una breve introducción a los mecanismos de distribución que están disponibles para las
aplicaciones de Xamarin.iOS. Introduce la iTunes App Store, la implementación interna y ad hoc, y proporciona
vínculos a información más detallada.

Vínculos relacionados
Distribución a través del App Store
Configuración de una aplicación en iTunes Connect
Publicación en App Store
Distribución interna
Distribución ad hoc
Archivo iTunesMetadata.plist
Compatibilidad con IPA
Solución de problemas
Publicar una aplicación
11/07/2019 • 6 minutes to read • Edit Online

Después de crear una gran aplicación, la gente querrá usarla. En este artículo se describen los pasos implicados en
la distribución pública de una aplicación creada con Xamarin.Android a través de canales como el correo
electrónico, un servidor web privado, Google Play o la Tienda Apps de Amazon para Android.

Información general
El paso final en el desarrollo de una aplicación de Xamarin.Android es publicar la aplicación. La publicación es el
proceso de compilación de una aplicación de Xamarin.Android para que esté lista para que los usuarios la instalen
en sus dispositivos, e implica dos tareas esenciales:
Preparar la publicación: se crea una versión de lanzamiento de la aplicación que se puede implementar en
dispositivos Android (consulte Preparar una aplicación para su lanzamiento para más información sobre la
preparación de lanzamiento).
Distribución: la versión de lanzamiento de una aplicación estará disponible a través de uno o varios
canales de distribución.
El siguiente diagrama ilustra los pasos de publicación de una aplicación de Xamarin.Android:

Como puede observarse en el diagrama anterior, la preparación es la misma independientemente del método de
distribución que se utilice. Hay varias formas de poner una aplicación de Android a disposición de los usuarios:
A través de un sitio web: una aplicación Xamarin.Android puede estar disponible para su descarga en un sitio
web, desde el que los usuarios pueden instalarla haciendo clic en un vínculo.
Por correo electrónico: los usuarios pueden instalar una aplicación Xamarin.Android desde su correo
electrónico. La aplicación se instalará cuando se abra el archivo adjunto con un dispositivo Android.
A través de una tienda: existen varias tiendas de aplicaciones para la distribución, como Google Play o la
Tienda Apps de Amazon para Android.
Utilizar una tienda establecida es la manera más común para publicar una aplicación, ya que proporciona la mayor
cobertura de mercado y el máximo control sobre la distribución. Sin embargo, publicar una aplicación a través de
una tienda requiere un esfuerzo adicional.
Varios canales pueden distribuir una aplicación de Xamarin.Android simultáneamente. Por ejemplo, una aplicación
puede publicarse en Google Play, la Tienda Apps de Amazon para Android y también se puede descargar desde un
servidor web.
Los otros dos métodos de distribución (descarga o correo electrónico) son más útiles para un subconjunto de
usuarios, como un entorno empresarial o una aplicación que solo está destinada a un conjunto de usuarios
pequeño o controlado. La distribución a través de un servidor y de correo electrónico también son modelos de
publicación más sencillos, que requieren menos preparación para publicar una aplicación.
El programa de distribución de aplicaciones de Amazon Mobile permite a los desarrolladores de aplicaciones
móviles distribuir y vender sus aplicaciones en Amazon. Los usuarios pueden descubrir y comprar aplicaciones en
sus dispositivos Android mediante la aplicación Tienda Apps de Amazon. A continuación aparece una captura de
pantalla de la Tienda Apps de Amazon en un dispositivo Android:
Google Play es, posiblemente, la tienda más completa y popular para aplicaciones Android. Google Play permite a
los usuarios detectar, descargar, evaluar y pagar por aplicaciones haciendo clic en un solo icono en su dispositivo o
su equipo. Google Play también proporciona herramientas para ayudar en el análisis de ventas y las tendencias del
mercado y controlar qué dispositivos y usuarios pueden descargar una aplicación. A continuación aparece una
captura de pantalla de Google Play en un dispositivo Android:
En esta sección se muestra cómo cargar la aplicación en una tienda, como Google Play, junto con el material
promocional adecuado. Se describen los archivos de expansión del APK y se ofrece información general
conceptual sobre qué son y cómo funcionan. También se describen los servicios de Licencias de Google. Por
último, se presentan medios alternativos de distribución, incluido el uso de un servidor web HTTP, la distribución
por correo electrónico simple y Amazon Appstore para Android.

Vínculos relacionados
HelloWorldPublishing (ejemplo)
Creación de proceso
Vinculación
Obtener una clave de API de Google Maps
Firma de aplicaciones
Publicación en Google Play
Licencias de aplicaciones de Google
Android.Play.ExpansionLibrary
Portal de distribución de aplicaciones móviles
Preguntas más frecuentes sobre la distribución de aplicaciones móviles de Amazon
Publicación de aplicaciones Xamarin.Mac en el Mac
App Store
11/07/2019 • 3 minutes to read • Edit Online

Información general
Las aplicaciones de Xamarin.Mac pueden distribuirse de dos maneras diferentes:
Developer ID: las aplicaciones firmadas con un Developer ID se distribuyen fuera del App Store, pero
Gatekeeper las reconoce y les da permiso para que se instalen.
Mac App Store: las aplicaciones deben tener un paquete de instalación, y la aplicación y el programa de
instalación deben estar firmados para enviarse al Mac App Store.
En este documento se explica cómo utilizar Visual Studio para Mac y Xcode para configurar una cuenta de
desarrollador de Apple y configurar un proyecto de Xamarin.Mac para cada tipo de implementación.

Programa para desarrolladores de Mac


Al unirse al Programa para desarrolladores de Mac, el desarrollador tendrá la opción de hacerlo como una persona
o una empresa, como se muestra en la captura de pantalla siguiente:
Elija el tipo de inscripción correcto para su situación.

NOTE
Las selecciones realizadas aquí afectarán al modo en que algunas pantallas aparecen al configurar una cuenta de
desarrollador. Las descripciones y las capturas de pantalla de este documento se realizan desde la perspectiva de una cuenta
de desarrollador personal. En una empresa, algunas opciones solo estarán disponibles para los usuarios del Administrador
del equipo.

Certificados e identificadores
En esta guía se describe el proceso de creación de los certificados y los identificadores necesarios para publicar
una aplicación Xamarin.Mac.
Crear un perfil de aprovisionamiento
En esta guía se describe el proceso de creación de los perfiles de aprovisionamiento necesarios para publicar una
aplicación Xamarin.Mac.
Configuración de aplicaciones de Mac
En esta guía se describe el proceso de configuración de una aplicación Xamarin.Mac para su publicación.
Firmar con Developer ID
En esta guía se explican los pasos para firmar una aplicación Xamarin.Mac con Developer ID para su publicación.
Paquete para el Mac App Store
En esta guía se describe el proceso de creación de una aplicación Xamarin.Mac para su publicación en el Mac App
Store.
Cargar en el Mac App Store
En esta guía se describe el proceso de carga de una aplicación Xamarin.Mac para su publicación en el Mac App
Store.

Vínculos relacionados
Instalación
Ejemplo de Hello, Mac
Identificador del desarrollador y equipo selector
Rendimiento de Xamarin.Forms
11/07/2019 • 20 minutes to read • Edit Online

Existen muchas técnicas para aumentar el rendimiento de aplicaciones de Xamarin.Forms. En conjunto, estas
técnicas pueden reducir considerablemente la cantidad de trabajo que está realizando una CPU y la cantidad de
memoria consumida por una aplicación. En este artículo se describen y se explican estas técnicas.

Evolve 2016: Optimizar el rendimiento de las aplicaciones con Xamarin.Forms

Información general
El mal rendimiento de una aplicación se manifiesta de muchas formas. Puede hacer que parezca que una
aplicación deja de responder, puede ocasionar un desplazamiento lento y puede reducir la duración de la batería.
La optimización del rendimiento conlleva mucho más que la mera implementación de código eficaz. También debe
tenerse en cuenta la experiencia de rendimiento de la aplicación del usuario. Por ejemplo, asegurarse de que las
operaciones se ejecuten sin evitar que el usuario realice otras actividades puede ayudar a mejorar su experiencia.
Existen varias técnicas para aumentar el rendimiento, y la percepción de rendimiento, de una aplicación de
Xamarin.Forms. Son los siguientes:
Habilitar el compilador XAML
Elegir el diseño correcto
Habilitar la compresión de diseño
Usar representadores rápidos
Reducir enlaces innecesarios
Optimizar el rendimiento de diseño
Optimizar el rendimiento de ListView
Optimizar los recursos de imagen
Reducir el tamaño del árbol visual
Reducir el tamaño del diccionario de recursos de aplicación
Usar el patrón de representador personalizado

NOTE
Antes de leer este artículo, debería leer Rendimiento multiplataforma, donde se explican técnicas no específicas de una
plataforma para mejorar el uso de memoria y el rendimiento de las aplicaciones compiladas con la plataforma Xamarin.

Habilitar el compilador XAML


XAML se puede compilar de forma opcional directamente en lenguaje intermedio (IL ) con el compilador XAML
(XAMLC ). XAMLC ofrece una serie de ventajas:
Realiza la comprobación en tiempo de compilación de XAML, notificando al usuario de los errores.
Reduce parte del tiempo de carga y creación de instancias para los elementos XAML.
Facilita reducir el tamaño de archivo del ensamblado final al dejar de incluir archivos .xaml.
XAMLC está deshabilitado de forma predeterminada para garantizar la compatibilidad con versiones anteriores.
Pero se puede habilitar en el nivel de ensamblado y el nivel de clase. Para más información, vea Compilación de
XAML.

Elegir el diseño correcto


Un diseño capaz de mostrar varios elementos secundarios, pero que solo tiene un elemento secundario, es poco
rentable. Por ejemplo, el siguiente ejemplo de código muestra un StackLayout con un único elemento secundario:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DisplayImage.HomePage">
<ContentPage.Content>
<StackLayout>
<Image Source="waterfront.jpg" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

Es un desperdicio y el elemento StackLayout debería eliminarse, como se muestra en el ejemplo de código


siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DisplayImage.HomePage">
<ContentPage.Content>
<Image Source="waterfront.jpg" />
</ContentPage.Content>
</ContentPage>

Además, no intente reproducir el aspecto de un diseño específico mediante combinaciones de otros diseños, dado
que como resultado se realizarían cálculos de diseño innecesarios. Por ejemplo, no intente reproducir un diseño
Grid mediante una combinación de instancias StackLayout . El ejemplo de código siguiente muestra un ejemplo
de esta práctica incorrecta:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Details.HomePage"
Padding="0,20,0,0">
<ContentPage.Content>
<StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Name:" />
<Entry Placeholder="Enter your name" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Age:" />
<Entry Placeholder="Enter your age" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Occupation:" />
<Entry Placeholder="Enter your occupation" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Address:" />
<Entry Placeholder="Enter your address" />
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>

Es una pérdida de tiempo porque se realizan cálculos de diseño innecesarios. En su lugar, el diseño deseado puede
lograrse mejor mediante un Grid , como se muestra en el ejemplo de código siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Details.HomePage"
Padding="0,20,0,0">
<ContentPage.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Label Text="Name:" />
<Entry Grid.Column="1" Placeholder="Enter your name" />
<Label Grid.Row="1" Text="Age:" />
<Entry Grid.Row="1" Grid.Column="1" Placeholder="Enter your age" />
<Label Grid.Row="2" Text="Occupation:" />
<Entry Grid.Row="2" Grid.Column="1" Placeholder="Enter your occupation" />
<Label Grid.Row="3" Text="Address:" />
<Entry Grid.Row="3" Grid.Column="1" Placeholder="Enter your address" />
</Grid>
</ContentPage.Content>
</ContentPage>

Habilitar la compresión de diseño


La compresión de diseño quita diseños especificados del árbol visual, en un intento de mejorar el rendimiento de
la representación de la página. La ventaja de rendimiento que esto ofrece varía según la complejidad de una
página, la versión del sistema operativo que se va a usar y el dispositivo en el que se ejecuta la aplicación. Sin
embargo, las mejoras de rendimiento más importantes se apreciarán en los dispositivos más antiguos. Para
obtener más información, vea Layout Compression (Compresión de diseño).

Usar representadores rápidos


Los representadores rápidos reducen la inflación y los costos de representación de controles de Xamarin.Forms en
Android mediante la reducción de la jerarquía de control nativo resultante. Además, esto mejora el rendimiento
porque crea menos objetos, lo cual a su vez genera un árbol visual menos complejo y menos uso de memoria.
Para obtener más información, consulte Fast Renderers (Representadores rápidos).

Reducir enlaces innecesarios


No use enlaces para el contenido que se puede establecer fácilmente de forma estática. No hay ninguna ventaja
en enlazar datos que no necesitan ser enlazados, ya que los enlaces no son rentables. Por ejemplo, establecer
Button.Text = "Accept" tiene una menor sobrecarga que enlazar Button.Text a una propiedad string de
ViewModel propiedad con valor "Accept".

Optimizar el rendimiento del diseño


Xamarin.Forms 2 presentó un motor de diseño optimizado que afecta a las actualizaciones de diseño. Para
obtener el mejor rendimiento posible, siga estas instrucciones:
Reduzca la profundidad de las jerarquías de diseño especificando valores de propiedad Margin , lo que permite
la creación de diseños con menos vistas ajustadas. Para más información, vea Márgenes y relleno.
Cuando se usa un Grid , intente asegurarse de establecer en tamaño Auto el menor número posible de filas y
columnas. Cada fila o columna de tamaño automático hará que el motor de diseño tenga que realizar cálculos
de diseño adicionales. En su lugar, use filas y columnas de tamaño fijo si es posible. Como alternativa,
establezca las filas y columnas para ocupar una cantidad proporcional de espacio con el valor de enumeración
GridUnitType.Star , siempre que el árbol primario siga estas directrices de diseño.
No establezca las propiedades VerticalOptions y HorizontalOptions de un diseño a menos que sea necesario.
Los valores predeterminados de LayoutOptions.Fill y LayoutOptions.FillAndExpand permiten la optimización
de diseño óptima. Cambiar estas propiedades tiene un costo y consume memoria, incluso cuando se
establecen en los valores predeterminados.
Evite el uso de un RelativeLayout siempre que sea posible. Como resultado, la CPU tendrá que realizar mucho
más trabajo.
Cuando se usa un AbsoluteLayout , evite el uso de la propiedad AbsoluteLayout.AutoSize siempre que sea
posible.
Cuando se usa un StackLayout , asegúrese de que solo un elemento secundario se establece en
LayoutOptions.Expands . Esta propiedad garantiza que el elemento secundario especificado ocupa el mayor
espacio que el StackLayout puede asignarle y es poco rentable realizar estos cálculos más de una vez.
No llame a ninguno de los métodos de la clase Layout , dado que provocan que se realicen cálculos de diseño
costosos. En su lugar, es probable que se pueda obtener el comportamiento de diseño deseado estableciendo
las propiedades TranslationX y TranslationY . Como alternativa, cree una subclase de la clase Layout<View>
para lograr el comportamiento de diseño deseado.
No actualice ninguna instancia Label con más frecuencia de la necesaria, dado que el cambio de tamaño de la
etiqueta puede producir que se vuelva a calcular la pantalla completa.
No establezca la propiedad Label.VerticalTextAlignment a menos sea necesario.
Establezca el LineBreakMode de las instancias Label en NoWrap siempre que sea posible.

Optimizar el rendimiento de ListView


Cuando se usa un control ListView hay una serie de experiencias del usuario que deben optimizarse:
Inicialización: el intervalo de tiempo que comienza cuando se crea el control y que termina cuando los
elementos se muestran en pantalla.
Desplazamiento: la capacidad de desplazarse por la lista y garantizar que la interfaz de usuario no se retrasa
con los gestos de toque.
Interacción para agregar, eliminar y seleccionar elementos.
El control ListView requiere una aplicación para proporcionar datos y plantillas de celda. La forma de conseguirlo
tendrá un gran impacto en el rendimiento del control. Para más información, vea Rendimiento de ListView.

Optimizar los recursos de imagen


Mostrar recursos de imagen puede aumentar considerablemente la superficie de memoria de la aplicación. Por
tanto, solo se deberían crear cuando fuera necesario y deberían liberarse en cuanto la aplicación no los necesitara.
Por ejemplo, si una aplicación muestra una imagen mediante la lectura de sus datos desde una secuencia,
asegúrese de que esa secuencia se crea solo cuando sea necesario y que se libera cuando ya no es necesaria. Esto
se consigue mediante la creación de la secuencia cuando se crea la página, o cuando se desencadena el evento
Page.Appearing y, después, mediante la eliminación de la secuencia cuando se desencadena el evento
Page.Disappearing .

Al descargar una imagen para mostrar con el método ImageSource.FromUri , guarde en la memoria caché la
imagen descargada asegurándose de que la propiedad UriImageSource.CachingEnabled está establecida en true .
Para más información, vea Trabajar con imágenes.
Para más información, vea Optimizar los recursos de imagen.

Reducir el tamaño del árbol visual


Reducir el número de elementos en una página hará que la página se procese más rápido. Hay dos técnicas
principales para conseguir esto. La primera es ocultar los elementos que no están visibles. La propiedad
IsVisible de cada elemento determina si el elemento debe ser parte del árbol visual o no. Por tanto, si un
elemento no es visible porque está oculto detrás de otros elementos, quite el elemento o establezca su propiedad
IsVisible en false .

La segunda técnica es quitar los elementos innecesarios. Por ejemplo, en el ejemplo de código siguiente se
muestra un diseño de página que muestra una serie de elementos Label :

<ContentPage.Content>
<StackLayout>
<StackLayout Padding="20,20,0,0">
<Label Text="Hello" />
</StackLayout>
<StackLayout Padding="20,20,0,0">
<Label Text="Welcome to the App!" />
</StackLayout>
<StackLayout Padding="20,20,0,0">
<Label Text="Downloading Data..." />
</StackLayout>
</StackLayout>
</ContentPage.Content>

Se puede mantener el mismo diseño de página con un número reducido de elementos, como se muestra en el
ejemplo de código siguiente:

<ContentPage.Content>
<StackLayout Padding="20,20,0,0" Spacing="25">
<Label Text="Hello" />
<Label Text="Welcome to the App!" />
<Label Text="Downloading Data..." />
</StackLayout>
</ContentPage.Content>

Reducir el tamaño del diccionario de recursos de aplicación


Todos los recursos que se usan en la aplicación se deben almacenar en el diccionario de recursos de la aplicación
para evitar la duplicación. Esto facilita reducir la cantidad de código XAML que tiene que analizarse en la
aplicación. El siguiente ejemplo de código muestra el recurso HeadingLabelStyle , que se usa en toda la aplicación,
por lo que se define en el diccionario de recursos de la aplicación:
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Resources.App">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="HeadingLabelStyle" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="FontSize" Value="Large" />
<Setter Property="TextColor" Value="Red" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

Pero el código de XAML que es específico de una página no debería incluirse en el diccionario de recursos de la
aplicación, dado que los recursos se analizarán en el inicio de la aplicación en lugar de cuando los solicite una
página. Si una página que no es la página de inicio usa un recurso, se debe colocar en el diccionario de recursos
para esa página, para así reducir el código de XAML que se analiza cuando se inicia la aplicación. El siguiente
ejemplo de código muestra el recurso HeadingLabelStyle , que solo se usa en una página, por lo que se define en
el diccionario de recursos de la página:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Test.HomePage"
Padding="0,20,0,0">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="HeadingLabelStyle" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="FontSize" Value="Large" />
<Setter Property="TextColor" Value="Red" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
...
</ContentPage.Content>
</ContentPage>

Para más información sobre los dominios de aplicación, vea Working with Styles .

Usar el patrón de presentador personalizado


La mayoría de las clases de representador exponen el método OnElementChanged , que se llama cuando se crea un
control personalizado de Xamarin.Forms para representar el control nativo correspondiente. Las clases de
representador personalizadas, en cada clase de representador específica de la plataforma, invalidan después este
método para crear instancias y personalizar el control nativo. El método SetNativeControl se usa para crear una
instancia del control nativo y este método también asignará la referencia de control a la propiedad Control .
Aun así, en algunos casos se puede llamar al método OnElementChanged varias veces. Por lo tanto, para evitar
pérdidas de memoria que pueden afectar al rendimiento, debe tener cuidado al crear una instancia de un nuevo
control nativo. El enfoque que usar al crear instancias de un nuevo control nativo en un presentador personalizado
se muestra en el ejemplo de código siguiente:
protected override void OnElementChanged (ElementChangedEventArgs<NativeListView> e)
{
base.OnElementChanged (e);

if (e.OldElement != null) {
// Unsubscribe from event handlers and cleanup any resources
}

if (e.NewElement != null) {
if (Control == null) {
// Instantiate the native control
}
// Configure the control and subscribe to event handlers
}
}

Solo se debe crear una instancia de un nuevo control nativo una vez, cuando la propiedad Control es null .
Además, solo se debe crear, configurar el control y suscribir los controladores de eventos cuando se adjunta el
presentador personalizado a un nuevo elemento de Xamarin.Forms. De forma similar, solo se debe cancelar la
suscripción de los controladores de eventos que se han suscrito cuando cambia el elemento al que está asociado
el presentador. Adoptar este enfoque facilita crear un presentador personalizado de rendimiento eficaz que no
sufra pérdidas de memoria.

IMPORTANT
El método SetNativeControl solo se debe llamar si e.NewElement no es null .

Para más información sobre presentadores personalizados, vea Personalización de controles en cada plataforma.

Resumen
En este artículo se han descrito y explicado técnicas para aumentar el rendimiento de las aplicaciones de
Xamarin.Forms. En conjunto, estas técnicas pueden reducir considerablemente la cantidad de trabajo que está
realizando una CPU y la cantidad de memoria consumida por una aplicación.

Vínculos relacionados
Rendimiento multiplataforma
Rendimiento de ListView
Representadores rápidos
Compresión de diseño
XamlCompilation
XamlCompilationOptions
Conceptos avanzados y funcionamiento interno
11/07/2019 • 2 minutes to read • Edit Online

Resolución de dependencias
En este artículo se explica cómo insertar un método de resolución de dependencia en Xamarin.Forms para que el
contenedor de inserción de dependencia de la aplicación tiene control sobre la creación y duración de los
representadores personalizados, los efectos, y DependencyService implementaciones.

Representadores rápidos
Este artículo presentan a los representadores rápidos, que reducen la inflación y los costos de representación de un
control de Xamarin.Forms en Android mediante la reducción de la jerarquía de control nativo resultante.

.NET Standard
En este artículo se explica cómo convertir una aplicación de Xamarin.Forms para usar .NET Standard 2.0.
Resolución de dependencias en Xamarin.Forms
11/07/2019 • 19 minutes to read • Edit Online

descargar el ejemplo
En este artículo se explica cómo insertar un método de resolución de dependencia en Xamarin.Forms para que el
contenedor de inserción de dependencia de la aplicación tiene control sobre la creación y duración de los
representadores personalizados, efectos y las implementaciones de DependencyService. Los ejemplos de código
en este artículo se toman de la resolución de dependencia mediante contenedores ejemplo.
En el contexto de una aplicación de Xamarin.Forms que usa el patrón Model-View -ViewModel (MVVM ), un
contenedor de inserción de dependencia puede utilizarse para registrar y resolver los modelos de vista y para
registrar servicios e insertarlas en modelos de vista. Durante la creación del modelo de vista, el contenedor
inserta las dependencias que son necesarias. Si no se han creado esas dependencias, el contenedor crea y
resuelve primero las dependencias. Para obtener más información sobre la inserción de dependencias, incluidos
ejemplos de inyección de dependencias en los modelos de vista, consulte inserción de dependencias.
Control sobre la creación y duración de los tipos de proyectos de la plataforma tradicionalmente se realiza
mediante Xamarin.Forms, que usa el Activator.CreateInstance método para crear instancias de los
representadores personalizados, los efectos, y DependencyService implementaciones. Lamentablemente, esto
limita el control del desarrollador sobre la creación y duración de estos tipos y la capacidad de insertar
dependencias en ellos. Este comportamiento puede modificarse mediante la inserción de un método de
resolución de dependencia en Xamarin.Forms que controla cómo se crearán los tipos: contenedor de inserción de
dependencia de la aplicación, o Xamarin.Forms. Sin embargo, tenga en cuenta que no hay ningún requisito para
insertar un método de resolución de dependencia en Xamarin.Forms. Xamarin.Forms continuará crear y
administrar la duración de los tipos de proyectos de la plataforma, si no se ha insertado un método de resolución
de dependencia.

NOTE
Aunque en este artículo se centra en la inyección de un método de resolución de dependencia en Xamarin.Forms que
resuelve los tipos registrados mediante un contenedor de inserción de dependencia, también es posible insertar un método
de resolución de dependencia que usa los métodos de fábrica para resolver tipos registrados. Para obtener más
información, consulte el resolución de dependencia mediante métodos de generador ejemplo.

Inserción de un método de resolución de dependencia


El DependencyResolver clase proporciona la capacidad para insertar un método de resolución de dependencia en
Xamarin.Forms, utilizando el ResolveUsing método. A continuación, cuando Xamarin.Forms tiene una instancia
de un tipo determinado, el método de resolución de dependencia tiene la oportunidad para proporcionar la
instancia. Si el método de resolución de dependencia devuelve null para un tipo solicitado, Xamarin.Forms
vuelve a intentar crear el tipo de instancia de sí misma mediante el Activator.CreateInstance método.
El ejemplo siguiente muestra cómo establecer el método de resolución de dependencia con el ResolveUsing
método:
using Autofac;
using Xamarin.Forms.Internals;
...

public partial class App : Application


{
// IContainer and ContainerBuilder are provided by Autofac
static IContainer container;
static readonly ContainerBuilder builder = new ContainerBuilder();

public App()
{
...
DependencyResolver.ResolveUsing(type => container.IsRegistered(type) ? container.Resolve(type) :
null);
...
}
...
}

En este ejemplo, el método de resolución de dependencia se establece en una expresión lambda que usa el
contenedor de inserción de dependencia de Autofac para resolver los tipos que se han registrado con el
contenedor. En caso contrario, null se devolverán, lo que dará como resultado en Xamarin.Forms al intentar
resolver el tipo.

NOTE
La API de un contenedor de inserción de dependencia es el contenedor específica. Los ejemplos de código en este artículo
usan Autofac como un contenedor de inserción de dependencia, que proporciona el IContainer y ContainerBuilder
tipos. Contenedores de inserción de dependencia alternativo podrían usarse por igual, pero usaría distintas API que se
presentan aquí.

Tenga en cuenta que no hay ningún requisito para establecer el método de resolución de dependencia durante el
inicio de la aplicación. Se puede establecer en cualquier momento. La única restricción es que necesita saber
sobre el método de resolución de dependencia en el momento en que la aplicación intenta consumir tipos
almacenados en el contenedor de inserción de dependencia Xamarin.Forms. Por lo tanto, si hay servicios en el
contenedor de inserción de dependencias que requiera la aplicación durante el inicio, el método de resolución de
dependencia deberá establecerse al principio de ciclo de vida de la aplicación. De forma similar, si el contenedor
de inserción de dependencia administra la creación y duración de una determinada Effect , deberá conocer el
método de resolución de dependencia antes de intentar crear una vista de Xamarin.Forms que usa Effect .

WARNING
Al registrar y resolver los tipos con un contenedor de inserción de dependencia, estos tienen un costo por uso del
contenedor de la reflexión para la creación de cada tipo, especialmente si se está reconstruyendo dependencias para cada
navegación de páginas en la aplicación de rendimiento. Si hay muchas o pocas dependencias, puede aumentar
significativamente el costo de la creación.

Registro de tipos
Los tipos deben registrarse en el contenedor de inserción de dependencia antes de que pueda resolver a través
del método de resolución de dependencia. El ejemplo de código siguiente muestra los métodos de registro que
expone la aplicación de ejemplo en el App (clase), para el contenedor Autofac:
using Autofac;
using Autofac.Core;
...

public partial class App : Application


{
static IContainer container;
static readonly ContainerBuilder builder = new ContainerBuilder();
...

public static void RegisterType<T>() where T : class


{
builder.RegisterType<T>();
}

public static void RegisterType<TInterface, T>() where TInterface : class where T : class, TInterface
{
builder.RegisterType<T>().As<TInterface>();
}

public static void RegisterTypeWithParameters<T>(Type param1Type, object param1Value, Type param2Type,


string param2Name) where T : class
{
builder.RegisterType<T>()
.WithParameters(new List<Parameter>()
{
new TypedParameter(param1Type, param1Value),
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == param2Type && pi.Name == param2Name,
(pi, ctx) => ctx.Resolve(param2Type))
});
}

public static void RegisterTypeWithParameters<TInterface, T>(Type param1Type, object param1Value, Type


param2Type, string param2Name) where TInterface : class where T : class, TInterface
{
builder.RegisterType<T>()
.WithParameters(new List<Parameter>()
{
new TypedParameter(param1Type, param1Value),
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == param2Type && pi.Name == param2Name,
(pi, ctx) => ctx.Resolve(param2Type))
}).As<TInterface>();
}

public static void BuildContainer()


{
container = builder.Build();
}
...
}

Cuando una aplicación utiliza un método de resolución de dependencia para resolver los tipos de un contenedor,
los registros de tipo normalmente se realizan desde proyectos de la plataforma. Esto permite que los proyectos
de plataforma registrar los tipos de representadores personalizados, los efectos, y DependencyService
implementaciones.
Después de registro del tipo desde un proyecto de la plataforma, el IContainer objeto debe compilarse, que se
logra mediante una llamada a la BuildContainer método. Este método invoca de Autofac Build método en el
ContainerBuilder instancia, que crea un nuevo contenedor de inyección de dependencia que contiene los
registros que se han realizado.
En las secciones siguientes, un Logger clase que implementa el ILogger interfaz se inyecta en constructores de
clase. El Logger clase registro simple implementa funcionalidad utilizando el Debug.WriteLine método y se usa
para demostrar cómo los servicios se pueden insertar en los representadores personalizados, los efectos, y
DependencyService implementaciones.

Registrar a los representadores personalizados


La aplicación de ejemplo incluye una página que reproduce vídeos de web, cuyo origen XAML se muestra en el
ejemplo siguiente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:video="clr-namespace:FormsVideoLibrary"
...>
<video:VideoPlayer Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4" />
</ContentPage>

El VideoPlayer vista se implementa en cada plataforma mediante una VideoPlayerRenderer (clase), que
proporciona la funcionalidad para reproducir el vídeo. Para obtener más información sobre estas clases de
representador personalizado, consulte implementa un Reproductor de vídeo.
En iOS y la plataforma Universal de Windows (UWP ), el VideoPlayerRenderer clases tienen el siguiente
constructor, que requiere un ILogger argumento:

public VideoPlayerRenderer(ILogger logger)


{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

En todas las plataformas, realizar el registro de tipo con el contenedor de inserción de dependencia mediante el
RegisterTypes método, que se invoca antes de la plataforma de carga de la aplicación con el
LoadApplication(new App()) método. El ejemplo siguiente se muestra el RegisterTypes método en la plataforma
de iOS:

void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterType<FormsVideoLibrary.iOS.VideoPlayerRenderer>();
App.BuildContainer();
}

En este ejemplo, el tipo concreto está registrado a través de una asignación con el tipo de interfaz y el
Logger
VideoPlayerRenderer tipo se registrado directamente sin una asignación de interfaz. Cuando el usuario navega a
la página que contiene el VideoPlayer vista, se invocará el método de resolución de dependencia para resolver el
VideoPlayerRenderer tipo desde el contenedor de inserción de dependencia, que también se resolver e insertar el
Logger escriba en el VideoPlayerRenderer constructor.

El constructor en la plataforma Android es un poco más complicado, ya que requiere un


VideoPlayerRenderer
Context argumento además el ILogger argumento:

public VideoPlayerRenderer(Context context, ILogger logger) : base(context)


{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

El ejemplo siguiente se muestra el RegisterTypes método en la plataforma Android:


void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterTypeWithParameters<FormsVideoLibrary.Droid.VideoPlayerRenderer>
(typeof(Android.Content.Context), this, typeof(ILogger), "logger");
App.BuildContainer();
}

En este ejemplo, el App.RegisterTypeWithParameters método registra el VideoPlayerRenderer con el contenedor de


inserción de dependencia. El método de registro garantiza que el MainActivity instancia se insertará como el
Context argumento y que la Logger se insertará el tipo como el ILogger argumento.

Efectos de registro
La aplicación de ejemplo incluye una página que usa un toque seguimiento efecto arrastrar BoxView instancias
alrededor de la página. El Effect se agrega a la BoxView con el código siguiente:

var boxView = new BoxView { ... };


var touchEffect = new TouchEffect();
boxView.Effects.Add(touchEffect);

El TouchEffect clase es un RoutingEffect que se implementa en cada plataforma mediante una TouchEffect
clase que tiene un PlatformEffect . La plataforma TouchEffect clase proporciona la funcionalidad para arrastrar
el BoxView alrededor de la página. Para obtener más información sobre estas clases efecto, consulte invocar
eventos de efectos.
En todas las plataformas, la TouchEffect clase tiene el siguiente constructor, que requiere un ILogger
argumento:

public TouchEffect(ILogger logger)


{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

En todas las plataformas, realizar el registro de tipo con el contenedor de inserción de dependencia mediante el
RegisterTypes método, que se invoca antes de la plataforma de carga de la aplicación con el
LoadApplication(new App()) método. El ejemplo siguiente se muestra el RegisterTypes método en la plataforma
Android:

void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterType<TouchTracking.Droid.TouchEffect>();
App.BuildContainer();
}

En este ejemplo, el Logger tipo concreto está registrado a través de una asignación con el tipo de interfaz y el
TouchEffect tipo se registrado directamente sin una asignación de interfaz. Cuando el usuario navega a la página
que contiene un BoxView instancia que tiene el TouchEffect conectadas a ella, se invocará el método de
resolución de dependencia para resolver la plataforma TouchEffect tipo a partir de la dependencia contenedor
de inserción, que también se resolver e insertar el Logger escriba en el TouchEffect constructor.
Registrar las implementaciones de DependencyService
La aplicación de ejemplo incluye una página que usa DependencyService implementaciones en cada plataforma
para permitir al usuario elegir una foto de la biblioteca de imágenes del dispositivo. El IPhotoPicker interfaz
define la funcionalidad que se implementa mediante el DependencyService implementaciones y se muestra en el
ejemplo siguiente:

public interface IPhotoPicker


{
Task<Stream> GetImageStreamAsync();
}

En cada proyecto de la plataforma, el PhotoPicker la clase implementa la IPhotoPicker interfaz mediante las API
de plataforma. Para obtener más información acerca de estos servicios de dependencia, consulte seleccionar una
foto de la biblioteca de imágenes.
En iOS y UWP, la PhotoPicker clases tienen el siguiente constructor, que requiere un ILogger argumento:

public PhotoPicker(ILogger logger)


{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

En todas las plataformas, realizar el registro de tipo con el contenedor de inserción de dependencia mediante el
RegisterTypes método, que se invoca antes de la plataforma de carga de la aplicación con el
LoadApplication(new App()) método. El ejemplo siguiente se muestra el RegisterTypes método en UWP:

void RegisterTypes()
{
DIContainerDemo.App.RegisterType<ILogger, Logger>();
DIContainerDemo.App.RegisterType<IPhotoPicker, Services.UWP.PhotoPicker>();
DIContainerDemo.App.BuildContainer();
}

En este ejemplo, el Logger tipo concreto está registrado a través de una asignación con el tipo de interfaz y el
PhotoPicker tipo también se registra a través de una asignación de interfaz.

El PhotoPicker constructor en la plataforma Android es un poco más complicado, ya que requiere un Context
argumento además el ILogger argumento:

public PhotoPicker(Context context, ILogger logger)


{
_context = context ?? throw new ArgumentNullException(nameof(context));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

El ejemplo siguiente se muestra el RegisterTypes método en la plataforma Android:

void RegisterTypes()
{
App.RegisterType<ILogger, Logger>();
App.RegisterTypeWithParameters<IPhotoPicker, Services.Droid.PhotoPicker>(typeof(Android.Content.Context),
this, typeof(ILogger), "logger");
App.BuildContainer();
}

En este ejemplo, el App.RegisterTypeWithParameters método registra el PhotoPicker con el contenedor de


inserción de dependencia. El método de registro garantiza que el MainActivity instancia se insertará como el
Context argumento y que la Logger se insertará el tipo como el ILogger argumento.
Cuando el usuario navega a la página de selección de fotos y elige para seleccionar una foto, la
OnSelectPhotoButtonClicked controlador se ejecuta:

async void OnSelectPhotoButtonClicked(object sender, EventArgs e)


{
...
var photoPickerService = DependencyService.Resolve<IPhotoPicker>();
var stream = await photoPickerService.GetImageStreamAsync();
if (stream != null)
{
image.Source = ImageSource.FromStream(() => stream);
}
...
}

Cuando el DependencyService.Resolve<T> se invoca el método, se invocará el método de resolución de


dependencia para resolver el PhotoPicker tipo desde el contenedor de inserción de dependencia, que también se
resolver e insertar el Logger tipo en el PhotoPicker constructor.

NOTE
El Resolve<T> método se debe usar al resolver un tipo de contenedor de inserción de dependencia de la aplicación a
través de la DependencyService .

Vínculos relacionados
Resolución de dependencia mediante contenedores (ejemplo)
Inserción de dependencias
Implementación de un reproductor de vídeo
Invocación de eventos de efectos
Seleccionar una foto de la biblioteca de imágenes
Representadores rápidos de Xamarin.Forms
11/07/2019 • 3 minutes to read • Edit Online

Tradicionalmente, la mayoría de los representadores de control original en Android se compone de dos vistas:
Control nativo, como un Button o TextView .
Un contenedor ViewGroup que controla algunas de las otras tareas, administración de gestos y el trabajo de
diseño.
Sin embargo, este enfoque tiene una implicación del rendimiento que dos vistas se crean para cada control
lógico, lo que resulta en un árbol visual más compleja que requiere más memoria y más capacidad de
procesamiento para representar en la pantalla.
Los representadores rápidos reducen la inflación y los costos de representación de un control de Xamarin.Forms
en una sola vista. Por lo tanto, en lugar de crear dos vistas y agregarlos al árbol de la vista, se crea uno solo. Esto
mejora el rendimiento porque crea menos objetos, lo que significa que a su vez un árbol de vista menos
complejo, y menos uso de memoria (que también tiene como resultado menos pausas de la colección de
elementos no utilizados).
Representadores rápidos están disponibles para los siguientes controles de Xamarin.Forms en Android:
Button
Image
Label
Frame

Funcionalmente, estos representadores rápidos no son distintos a los representadores heredados.


Xamarin.Forms 4.0 y versiones posteriores, todas las aplicaciones destinadas a FormsAppCompatActivity usará
estos representadores rápidos de forma predeterminada. Los representadores para todos los controles nuevos,
incluidos ImageButton y CollectionView , utilice el enfoque de representador rápida.
Mejoras de rendimiento al usar a representadores rápidos variarán para cada aplicación, en función de la
complejidad del diseño. Por ejemplo, son posibles mejoras de rendimiento de x2 al desplazarse a través de un
ListView que contienen miles de filas de datos, donde se realizan las celdas de cada fila de controles que usar
representadores rápidos, que da como resultado visiblemente desplazamiento más suave.

NOTE
Los representadores personalizados pueden crearse para representadores rápidos utilizando el mismo enfoque que se usan
para los representadores heredados. Para obtener más información, consulte Custom Renderers (Representadores
personalizados).

Compatibilidad con versiones anteriores


Representadores rápidos pueden reemplazarse con los métodos siguientes:
1. Habilitación de los representadores heredados agregando la siguiente línea de código para su
MainActivity clase antes de llamar a Forms.Init :

Forms.SetFlags("UseLegacyRenderers");
2. Uso de los representadores personalizados que tienen como destino a los representadores heredados. Los
representadores personalizados existentes seguirán funcionando con los representadores heredados.
3. Especificar otro View.Visual , tales como Material , que usa distintos representadores. Para obtener más
información acerca de Material Visual, consulte Xamarin.Forms Material Visual.

Vínculos relacionados
Representadores personalizados
2.0 compatibilidad con .NET standard en
Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

En este artículo se explica cómo convertir una aplicación de Xamarin.Forms para usar .NET Standard 2.0.
Estándar de .NET es una especificación de API de .NET que va a estar disponible en todas las implementaciones.
NET. Resulta más fácil compartir código entre aplicaciones de escritorio, aplicaciones móviles, juegos y servicios
de nube aunando las API idénticas a las diferentes plataformas. Para obtener información acerca de las
plataformas compatibles con el estándar. NET, vea soporte de implementación de .NET.
Bibliotecas de .NET standard son el reemplazo de las bibliotecas de clases portables (PCL ). Sin embargo, una
biblioteca que tenga como destino .NET Standard sigue siendo una PCL y se conoce como una PCL basadas en
.NET Standard. Algunos perfiles de PCL se asignan a las versiones de .NET Standard, y para los perfiles que tienen
una asignación, los tipos de dos biblioteca podrán hacen referencia entre sí. Para obtener más información,
consulte compatibilidad con PCL.
2.4 de Xamarin.Forms permite que las aplicaciones de Xamarin.Forms para el destino .NET Standard 2.0
reemplazando la PCL con una biblioteca .NET Standard 2.0. Esto puede lograrse como sigue:
Asegúrese de .NET Core 2.0 está instalado.
Actualización de la solución de Xamarin.Forms para usar Xamarin.Forms 2.4 o superior.
Agregar una biblioteca .NET Standard a la solución, que tiene como destino .NET Standard 2.0.
Eliminar la clase que se agrega a la biblioteca .NET Standard.
Agregue el paquete de NuGet Xamarin.Forms 2,4 (o superior) a la biblioteca .NET Standard.
En los proyectos de plataforma, agregue una referencia a la biblioteca estándar de .NET y quite la referencia al
proyecto PCL que contiene la lógica de interfaz de usuario de Xamarin.Forms.
Copie los archivos desde el proyecto PCL en la biblioteca .NET Standard.
Quitar el proyecto PCL que contiene la lógica de interfaz de usuario de Xamarin.Forms.

Vínculos relacionados
.NET Standard
Opciones de uso compartido de código
Solución de problemas
11/07/2019 • 3 minutes to read • Edit Online

Condiciones de error comunes y cómo resolverlos

Error: "No se puede encontrar una versión de Xamarin.Forms


compatible con..."
Pueden aparecer los errores siguientes en el consola paquetes ventana cuando se actualiza todos los paquetes de
Nuget en una solución de Xamarin.Forms o en un proyecto de aplicación de Xamarin.Forms Android:

Attempting to resolve dependency 'Xamarin.Android.Support.v7.AppCompat (= 23.3.0.0)'.


Attempting to resolve dependency 'Xamarin.Android.Support.v4 (= 23.3.0.0)'.
Looking for updates for 'Xamarin.Android.Support.v7.MediaRouter'...
Updating 'Xamarin.Android.Support.v7.MediaRouter' from version '23.3.0.0' to '23.3.1.0' in project
'Todo.Droid'.
Updating 'Xamarin.Android.Support.v7.MediaRouter 23.3.0.0' to 'Xamarin.Android.Support.v7.MediaRouter 23.3.1.0'
failed.
Unable to find a version of 'Xamarin.Forms' that is compatible with 'Xamarin.Android.Support.v7.MediaRouter
23.3.0.0'.

¿Qué provoca este error?


Visual Studio para Mac (o Visual Studio) puede indicar que las actualizaciones están disponibles para el paquete de
Xamarin.Forms Nuget y todas sus dependencias. En Xamarin Studio, la solución paquetes nodo podría tener este
aspecto (los números de versión podrían ser diferentes):

Este error puede producirse si intenta actualizar todas los paquetes.


Esto es porque con Android, los proyectos se establecen en una versión de compilación o de destino de Android
6.0 (API 23) o a continuación, Xamarin.Forms tiene una dependencia fuerte específico paquetes de soporte de
versiones de Android. Aunque las versiones actualizadas de estos paquetes pueden estar disponibles,
Xamarin.Forms no es necesariamente compatible con ellos.
En este caso debe actualizar sólo el Xamarin.Forms como así se asegurará de que las dependencias permanecen
en las versiones compatibles del paquete. Otros paquetes que ha agregado a su proyecto también se actualizará
individualmente siempre que no hacen que los paquetes de compatibilidad con Android que se va a actualizar.
NOTE
Si usa Xamarin.Forms 2.3.4 o posterior y versión de compilación o de destino del proyecto Android está establecida en
Android 7.0 (API 24) o versiones posteriores, a continuación, las dependencias de disco duras mencionadas anteriormente ya
no se aplican y puede actualizar la compatibilidad paquetes independientemente del paquete de Xamarin.Forms.

Corrección: Quitar todos los paquetes y volver a agregar a Xamarin.Forms


Si el Xamarin.Android.Support paquetes se han actualizado a las versiones incompatibles, la solución más
sencilla consiste en:
1. Elimine manualmente todos los paquetes de Nuget en el proyecto Android, a continuación
2. Volver a agregar el Xamarin.Forms paquete.
Esto descargará automáticamente la correcta versiones de los otros paquetes.
Para ver un vídeo de este proceso, consulte este foros post.
Preguntas más frecuentes de Xamarin.Forms
11/07/2019 • 2 minutes to read • Edit Online

¿Se puede actualizar la plantilla predeterminada de Xamarin.Forms en


un paquete NuGet más reciente?
Esta guía usa la plantilla de biblioteca de Xamarin.Forms .NET Standard como ejemplo, pero también funcionará el
mismo método general para la plantilla de proyecto compartido de Xamarin.Forms.

¿Por qué no funciona el diseñador XAML de Visual Studio para los


archivos XAML de Xamarin.Forms?
Xamarin.Forms no admite actualmente los diseñadores visuales para los archivos XAML.

Error de compilación de Android: error inesperado en la tarea


"LinkAssemblies"
Es posible que vea un mensaje de error The "LinkAssemblies" task failed unexpectedly al compilar un proyecto de
Xamarin.Android que usa formularios. Esto sucede cuando el vinculador está activo (normalmente en un versión
compilación para reducir el tamaño del paquete de aplicación); y se produce debido a los destinos de Android no se
actualizan a la última versión de framework.

"¿Por qué mi proyecto de xamarin.Forms.Maps para Android produce


error compiletodalvik: ERROR NIVEL SUPERIOR INESPERADO?"
Este error puede aparecer en el panel de errores de Visual Studio para Mac o en la ventana de salida de la
compilación de Visual Studio; en los proyectos mediante xamarin.Forms.Maps para Android. Normalmente se
resuelve al aumentar el tamaño del montón de Java para el proyecto de Xamarin.Android.
¿Se puede actualizar la plantilla predeterminada de
Xamarin.Forms en un paquete NuGet más reciente?
11/07/2019 • 2 minutes to read • Edit Online

Esta guía usa la plantilla de biblioteca de Xamarin.Forms .NET Standard como ejemplo, pero también funcionará el
mismo método general para la plantilla de proyecto compartido de Xamarin.Forms. Esta guía está escrita con el
ejemplo de actualización de Xamarin.Forms 1.5.1.6471 a 2.1.0.6529, pero los mismos pasos son posibles
establecer otras versiones como el valor predeterminado en su lugar.
1. Copie la plantilla original .zip desde:

C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\Xamarin\Xamarin\[Xamarin


Version]\T\PT\Cross-Platform\Xamarin.Forms.PCL.zip

2. Descomprima el .zip en una ubicación temporal.


3. Cambie todas las apariciones de la versión anterior del paquete de formularios a la nueva versión que le
gustaría utilizar.
FormsTemplate\FormsTemplate.vstemplate
FormsTemplate.Android\FormsTemplate.Android.vstemplate
FormsTemplate.iOS\FormsTemplate.iOS.vstemplate

Ejemplo: <package id="Xamarin.Forms" version="1.5.1.6471" /> ->


<package id="Xamarin.Forms" version="2.1.0.6529" />

4. Cambie el elemento "name" de los principales archivo de plantilla de varios proyectos (


Xamarin.Forms.PCL.vstemplate ) para que sea único. Por ejemplo:

Aplicación vacía (Xamarin.Forms Portable) - 2.1.0.6529

5. Volver a comprimir la carpeta de plantillas completas. Asegúrese de que coincida con la estructura del
archivo original de la .zip archivo. El Xamarin.Forms.PCL.vstemplate archivo debe estar en la parte superior
de la .zip archivo, no dentro de las carpetas.
6. Cree un subdirectorio "Mobile Apps" en la carpeta de plantillas de Visual Studio por usuario:

%USERPROFILE%\Documents\Visual Studio 2013\Templates\ProjectTemplates\Visual C#\Mobile Apps

7. Copie la nueva carpeta de plantillas de comprimir copia de seguridad en el nuevo directorio "Mobile Apps".
8. Descargue el paquete de NuGet que coincida con la versión del paso 3. Por ejemplo,
http://nuget.org/api/v2/package/Xamarin.Forms/2.1.0.6529 (Vea también
https://stackoverflow.com/questions/8597375/how -to-get-the-url-of-a-nupkg-file ) y cópiela en la
subcarpeta correspondiente de la carpeta de extensiones de Xamarin Visual Studio:
C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\Xamarin\Xamarin\[Xamarin
Version]\Packages
¿Por qué no funciona el diseñador XAML de Visual
Studio para los archivos XAML de Xamarin.Forms?
11/07/2019 • 2 minutes to read • Edit Online

Xamarin.Forms no admite actualmente los diseñadores visuales para los archivos XAML. Por este motivo, al
intentar abrir un archivo XAML de Forms en Visual Studio Diseñador de IU XAML o Diseñador de IU XAML con
codificación, se produce el siguiente mensaje de error:

"No se puede abrir el archivo con el editor seleccionado. Elija otro editor."

Esta limitación se describe en el Introducción sección de la conceptos básicos de XAML de Xamarin.Forms guía:

"No hay todavía un diseñador visual para generar XAML en las aplicaciones de Xamarin.Forms, por lo que
todo XAML debe ser escrito a mano."

Sin embargo, se pueden mostrar la vista previa de XAML de Xamarin.Forms seleccionando el View > Other
Windows > controlador de vista previa de Xamarin.Forms opción de menú.
Error de compilación de Android: tarea
LinkAssemblies el error inesperado
11/07/2019 • 3 minutes to read • Edit Online

Es posible que vea un mensaje de error The "LinkAssemblies" task failed unexpectedly al compilar un proyecto de
Xamarin.Android que usa formularios. Esto sucede cuando el vinculador está activo (normalmente en un versión
compilación para reducir el tamaño del paquete de aplicación); y se produce debido a los destinos de Android no
se actualizan a la última versión de framework. (Obtener más información: Xamarin.Forms para Android
requisitos)
La solución a este problema consiste en asegurarse de que dispone de las versiones de Android SDK más reciente
compatibles y establezca el .NET Framework de destino a la última plataforma instalada. También se
recomienda que establezca el versión Android de destino a la última plataforma instalada y el versión Android
mínima en API 19 o superior. Esto se considera la configuración admitida.

Configuración de Visual Studio para Mac


1. Haga clic con el botón derecho en el proyecto de Android y seleccione opciones en el menú.
2. En el opciones de proyecto cuadro de diálogo, vaya a compilar > General.
3. Establecer el compilar con la versión de Android: (.NET Framework de destino) a la última plataforma
instalada.
4. En el opciones de proyecto cuadro de diálogo, vaya a compilar > aplicación de Android.
5. Establecer el versión Android mínima a nivel de API 19 o superior y el versión Android de destino a la
última plataforma instalada que eligió en (3).

Configuración de Visual Studio


1. Haga clic con el botón derecho en el proyecto de Android y seleccione dimensiones en el menú.
2. En las propiedades del proyecto, vaya a aplicación.
3. Establecer el compilar con la versión de Android: (.NET Framework de destino) a la última plataforma
instalada.
4. En las propiedades del proyecto, vaya a manifiesto de Android.
5. Establecer el versión Android mínima a nivel de API 19 o superior y el versión Android de destino a la
última plataforma instalada que eligió en (3).
Una vez que se ha actualizado esas configuraciones, por favor, limpie y recompile el proyecto para asegurarse de
que se recogen los cambios.
¿Por qué un error mi proyecto de
xamarin.Forms.Maps para Android con ERROR de
nivel superior inesperado COMPILETODALVIK?
11/07/2019 • 2 minutes to read • Edit Online

Este error puede aparecer en el panel de errores de Visual Studio para Mac o en la ventana de salida de la
compilación de Visual Studio; en los proyectos mediante xamarin.Forms.Maps para Android.
Normalmente, esto se resuelve al aumentar el tamaño del montón de Java para el proyecto de Xamarin.Android.
Siga estos pasos para aumentar el tamaño del montón:

Programa para la mejora


1. Haga clic en el proyecto de Android y abrir las opciones del proyecto.
2. Vaya a -> Android opciones avanzadas
3. En el cuadro de texto de tamaño de montón de Java, escriba 1G.
4. Recompile el proyecto.

Visual Studio para Mac


1. Haga clic en el proyecto de Android y abrir las opciones del proyecto.
2. Vaya a compilar -> compilación de Android -> avanzado
3. En el cuadro de texto de tamaño de montón de Java, escriba 1G.
4. Recompile el proyecto.
Crear aplicaciones móviles con Xamarin.Forms libro
11/07/2019 • 10 minutes to read • Edit Online

descargar el ejemplo
El libro Creating Mobile Apps with Xamarin.Forms por Charles Petzold es una guía para
obtener información sobre cómo escribir aplicaciones de Xamarin.Forms. El único requisito
previo es el conocimiento de la C# lenguaje de programación. El libro ofrece una amplia
exploración en la interfaz de usuario de Xamarin.Forms y también cubre la animación, MVVM,
desencadenadores, comportamientos, los diseños personalizados, los representadores
personalizados y mucho más.
El libro se publicó en la primavera de 2016 y no se ha actualizado desde entonces. Hay mucho en el libro que
valioso permanece, pero algunos de los material está obsoleto, y algunos temas ya no son completamente
correcto o completo.

Descargar libro electrónico gratis


Descargue el formato de libro electrónico preferido de Microsoft Virtual Academy:
PDF (56Mb)
ePub (151Mb)
Kindle edition (325Mb)
También puede Descargue capítulos individuales como archivos PDF.

Muestras
Los ejemplos están disponibles en github e incluyen proyectos de iOS, Android y la Plataforma universal de
Windows (UWP ). (Xamarin.Forms ya no es compatible con Windows 10 Mobile, pero las aplicaciones de
Xamarin.Forms se ejecutarán en el escritorio de Windows 10).

Resumen del capítulo


Resumen del capítulo está disponibles en el tabla capítulo muestra a continuación. Estos resúmenes describan el
contenido de cada capítulo e incluyen varios tipos de vínculos:
Vínculos a los reales capítulos del libro (en la parte inferior de la página) y a los artículos relacionados
Vínculos a todos los ejemplos de la xamarin-forms-book-samples repositorio de GitHub
Vínculos a la documentación de API para obtener descripciones más detalladas de las clases, estructuras,
propiedades, enumeraciones y así sucesivamente de Xamarin.Forms
Estos resúmenes también indican cuándo puede ser material en el capítulo un poco anticuado.

Descargue capítulos y resúmenes


CAPÍTULO TEX TO COMPLETO RESUMEN

Capítulo 1. ¿Cómo ajustar Descargar PDF Resumen


Xamarin.Forms?

Capítulo 2. Anatomía de una aplicación Descargar PDF Resumen

Capítulo 3. Más profunda en texto Descargar PDF Resumen

Capítulo 4. Desplazamiento de la pila Descargar PDF Resumen

Capítulo 5. Tratar con tamaños Descargar PDF Resumen

Capítulo 6. Clics de botón Descargar PDF Resumen

Capítulo 7. Frente a XAML. Código Descargar PDF Resumen

Capítulo 8. Código y XAML en armonía Descargar PDF Resumen

Capítulo 9. Llamadas a API específicas Descargar PDF Resumen


de la plataforma

Capítulo 10. Extensiones de marcado Descargar PDF Resumen


XAML

Capítulo 11. La infraestructura Descargar PDF Resumen


enlazable

Capítulo 12. Estilos Descargar PDF Resumen

Capítulo 13. Mapas de bits Descargar PDF Resumen

Capítulo 14. Diseño absoluto Descargar PDF Resumen

Capítulo 15. La interfaz interactiva Descargar PDF Resumen

Capítulo 16. Enlace de datos Descargar PDF Resumen

Capítulo 17. Dominar la cuadrícula Descargar PDF Resumen

Capítulo 18. MVVM Descargar PDF Resumen

Capítulo 19. Vistas de colección Descargar PDF Resumen

Capítulo 20. E/S de archivo y Async Descargar PDF Resumen

Capítulo 21. Transformaciones Descargar PDF Resumen

Capítulo 22. Animación Descargar PDF Resumen

Capítulo 23. Los desencadenadores y Descargar PDF Resumen


comportamientos
CAPÍTULO TEX TO COMPLETO RESUMEN

Capítulo 24. Navegación de páginas Descargar PDF Resumen

Capítulo 25. Variedades de página Descargar PDF Resumen

Capítulo 26. Diseños personalizados Descargar PDF Resumen

Capítulo 27. Representadores Descargar PDF Resumen


personalizados

Capítulo 28. Ubicación y mapas Descargar PDF Resumen

Formas en que el libro está obsoleto


Desde la publicación de Creating Mobile Apps with Xamarin.Forms, se han agregado varias características nuevas
para Xamarin.Forms. Estas nuevas características se describen en los artículos individuales en el Xamarin.Forms
documentación.
Otros cambios han producido parte del contenido del libro para que no esté actualizada:
Las bibliotecas .NET standard 2.0 reemplazaron bibliotecas de clases portables
Por lo general, una aplicación de Xamarin.Forms usa una biblioteca para compartir código entre las diferentes
plataformas. Originalmente, se trataba de una biblioteca de clases Portable (PCL ). Existen muchas referencias a la
PCL en todo el libro y el resumen del capítulo.
Se ha reemplazado la biblioteca de clases Portable con una biblioteca .NET Standard 2.0, como se describe en el
artículo compatibilidad de .NET Standard 2.0 en Xamarin.Forms. Todos los código de ejemplo del libro se ha
actualizado para usar las bibliotecas .NET Standard 2.0.
La mayoría de la información en el libro de la función de la biblioteca de clases Portable sigue siendo el mismo
para una biblioteca de .NET Standard 2.0. Una diferencia es que sólo una PCL tiene un valor numérico "perfil".
Además, existen algunas ventajas a las bibliotecas de .NET Standard 2.0. Por ejemplo, el capítulo 20, E/S de
archivo y Async se describe cómo usar las plataformas subyacentes para realizar E/S de archivos. Esto ya no es
necesario. La biblioteca .NET Standard 2.0 es compatible con la conocida System.IO clases para todas las
plataformas de Xamarin.Forms.
La biblioteca .NET Standard 2.0 también permite usar las aplicaciones de Xamarin.Forms HttpClient acceso a los
archivos a través de Internet en lugar de WebRequest u otras clases.
Se ha reforzado el rol de XAML
Creación de aplicaciones móviles con Xamarin.Forms comienza con la que se describe cómo escribir aplicaciones
de Xamarin.Forms mediante C#. No se introdujo el lenguaje de marcado de aplicaciones Extensible (XAML ) hasta
capítulo 7. Frente a XAML. Código.
XAML tiene ahora un papel más importante en Xamarin.Forms. Las plantillas de solución de Xamarin.Forms
distribuidas con Visual Studio crean archivos de página basados en XAML. Un desarrollador que utiliza
Xamarin.Forms debería familiarizarse con XAML tan pronto como sea posible. El lenguaje de marcado de
aplicaciones eXtensible (XAML ) sección de la documentación de Xamarin.Forms contiene varios artículos acerca
de XAML para que pueda comenzar.
Plataformas compatibles
Xamarin.Forms ya no es compatible con Windows 8.1 y Windows Phone 8.1.
A veces, el libro hace referencia a la en tiempo de ejecución de Windows. Se trata de un término que engloba la
API de Windows que se utiliza en varias versiones de Windows y Windows Phone. Las versiones más recientes de
Xamarin.Forms se restringe a sí mismo a apoyar la plataforma Universal de Windows, que es la API de Windows
10 y Windows 10 Mobile.
Una biblioteca .NET Standard 2.0 no es compatible con cualquier versión de Windows 10 Mobile. Por lo tanto,
una aplicación de Xamarin.Forms mediante una biblioteca .NET Standard no se ejecutará en un dispositivo
Windows 10 Mobile. Aplicaciones de Xamarin.Forms continúan ejecutándose en el escritorio de Windows 10,
versiones 10.0.16299.0 y versiones posteriores.
Xamarin.Forms tiene compatibilidad con la versión preliminar de la Mac, WPF, GTK #, y Tizen plataformas.
Resumen del capítulo
Los resúmenes de capítulo incluyen información sobre los cambios en Xamarin.Forms desde que se escribió el
libro. Estos son a menudo en forma de notas:

NOTE
Notas en cada página indican que diverge Xamarin.Forms desde el material presentado en el libro.

Muestras
En el xamarin-forms-book-samples repositorio de GitHub, el código original de libro rama contiene
ejemplos de programa coherentes con el libro. El maestro rama contiene proyectos que se han actualizado para
quitar la API en desuso y reflejar las API mejoradas. Además, los proyectos de Android de la maestro rama se
han actualizado para Android Material Design a través de AppCompat y generalmente se mostrará el texto negro
sobre un fondo blanco.

Vínculos relacionados
Blog de Microsoft Press
Código de ejemplo del libro
Patrones de aplicación empresarial utilizando el libro
electrónico de Xamarin.Forms
11/07/2019 • 9 minutes to read • Edit Online

Guía de arquitectura para el desarrollo de Xamarin.Forms adaptables, fáciles de mantener y probar las
aplicaciones empresariales

Este libro electrónico proporciona instrucciones sobre cómo implementar el patrón Model-View -ViewModel
(MVVM ), la inserción de dependencias, navegación, validación y la administración de configuración, manteniendo
el acoplamiento flexible. Además, también hay una guía sobre cómo realizar la autenticación y autorización con
IdentityServer, acceso a datos de microservicios en contenedores y las pruebas unitarias.

Prefacio
En este capítulo se explica el propósito y el ámbito de la guía, y a quién está destinada.

Introducción
Los desarrolladores de aplicaciones empresariales enfrentan a varios desafíos que pueden modificar la arquitectura
de la aplicación durante el desarrollo. Por lo tanto, es importante compilar una aplicación para que se pueda
modificar o extender con el tiempo. Diseño de la capacidad de dichos adaptación puede ser difícil, pero
normalmente implica dividir una aplicación en componentes discretos y con acoplamiento flexible que pueden
integrarse fácilmente entre sí en una aplicación.

MVVM
El patrón Modelo-Vista-Modelo de vista (MVVM ) ayuda a separar la lógica de negocios y presentación de una
aplicación de su interfaz de usuario. Mantener una separación clara entre la lógica de aplicación y la interfaz de
usuario ayuda a abordar numerosos problemas de desarrollo y puede hacer que probar una aplicación, mantenerla
y desarrollarla sea más fácil. También puede mejorar enormemente las oportunidades de reutilización de código y
permite a los desarrolladores y diseñadores de interfaz de usuario colaborar con mayor facilidad al desarrollar sus
respectivos elementos de una aplicación.

Inserción de dependencias
Inserción de dependencias permite la separación de tipos concretos desde el código que dependa de estos tipos.
Normalmente usa un contenedor que contiene una lista de registros y las asignaciones entre tipos e interfaces
abstractos y los tipos concretos que implementan o amplían estos tipos.
Contenedores de inserción de dependencia reducen el acoplamiento entre objetos por lo que proporciona una
funcionalidad para crear instancias de clase y administra su duración en función de la configuración del contenedor.
Durante la creación de objetos, el contenedor inserta las dependencias que requiere el objeto en él. Si aún no se
han creado esas dependencias, el contenedor crea y resuelve primero sus dependencias.

Comunicación entre componentes débilmente acoplados


Xamarin.Forms MessagingCenter clase implementa la publicación-patrón, que permite la comunicación basada en
mensajes entre los componentes que no son convenientes para vincular mediante referencias de objeto y el tipo de
suscripción. Este mecanismo permite a los publicadores y suscriptores transmitir sin tener una referencia entre sí,
lo que ayuda a reducir las dependencias entre componentes, mientras que permite que los componentes que se
desarrollan y prueban de forma independiente.

Navegación
Xamarin.Forms incluye compatibilidad con la navegación de página, que normalmente da como resultado de la
interacción del usuario con la interfaz de usuario o de la aplicación, como resultado de los cambios de estado
controlado por la lógica interna. Sin embargo, navegación puede ser difícil de implementar en las aplicaciones que
usan el patrón MVVM.
Este capítulo presenta un NavigationService (clase), que se usa para realizar la navegación de model first de vista
de modelos de vista. Colocar en la vista lógica de navegación clases de modelo significa que la lógica puede
realizarse a través de pruebas automatizadas. Además, el modelo de vista, a continuación, puede implementar la
lógica para controlar el desplazamiento para garantizar que se apliquen determinadas reglas empresariales.

Validación
Cualquier aplicación que acepte entradas de los usuarios debe asegurarse de que la entrada sea válida. Sin
validación, un usuario puede proporcionar datos que hagan que la aplicación produzca un error. La validación
aplica reglas de negocio e impide que un atacante inserte datos malintencionados.
En el contexto de Model-View -ViewModel (MVVM ) de patrón, un modelo de vista o modelo a menudo se
requerirá para realizar la validación de datos y señalar los errores de validación a la vista para que el usuario puede
corregirlos.

Administración de configuraciones
La configuración permite la separación de datos que configuran el comportamiento de una aplicación desde el
código, lo que permite cambiar el comportamiento sin volver a compilar la aplicación. La configuración de la
aplicación se compone de datos que una aplicación crea y administra, y la configuración de usuario es la
configuración personalizable de una aplicación que afecta al comportamiento de la aplicación y no es necesario
volver a ajustarla a menudo.

Microservicios en contenedores
Los Microservicios ofrecen un enfoque al desarrollo de aplicaciones e implementación que se adapte a los
requisitos de agilidad, escalabilidad y confiabilidad de aplicaciones modernas en la nube. Una de las principales
ventajas de los microservicios es que pueden ser escaladas horizontalmente de forma independiente, lo que
significa que se puede escalar un área funcional específica que requiere más procesamiento de energía o ancho de
banda para admitir la demanda, sin escalado innecesariamente áreas de la aplicación que no está experimentando
una mayor demanda.

Autenticación y autorización
Existen muchos enfoques para integrar la autenticación y autorización en una aplicación de Xamarin.Forms que se
comunica con una aplicación web ASP.NET MVC. En este caso, la autenticación y autorización se realizan con un
microservicio en contenedor de identidad que usa 4 IdentityServer. IdentityServer es un marco de código abierto
de OAuth 2.0 y OpenID Connect para ASP.NET Core que se integra con ASP.NET Core Identity para realizar la
autenticación de token de portador.

Acceso a datos remotos


Muchas soluciones modernas basadas en web hacen uso de servicios web, hospedadas por servidores web, para
proporcionar funcionalidad de cliente remoto a aplicaciones. Las operaciones que expone un servicio web
constituyen una API web y aplicaciones de cliente deben ser capaces de usar la API web sin necesidad de saber
cómo se implementan los datos o las operaciones que expone la API.

Pruebas unitarias
Probar los modelos y los modelos de vista de las aplicaciones MVVM es idéntica a las pruebas de otras clases, y se
pueden usar las mismas herramientas y técnicas. Sin embargo, hay algunos patrones típicos de modelo y las clases
de modelo de vista, que pueden beneficiarse de las técnicas de pruebas de unidad específica.

Comentarios
Este proyecto tiene un sitio de la Comunidad, en el que puede publicar preguntas y proporcionar comentarios. El
sitio de la Comunidad se encuentra en GitHub. Como alternativa, se pueden enviar comentarios sobre el libro
electrónico a dotnet-architecture-ebooks-feedback@service.microsoft.com .

Vínculos relacionados
Descargar libro electrónico (PDF de 2Mb)
eShopOnContainers (GitHub) (ejemplo)
Gráficos de SkiaSharp en Xamarin.Forms
11/07/2019 • 5 minutes to read • Edit Online

descargar el ejemplo
Uso SkiaSharp para gráficos 2D en las aplicaciones de Xamarin.Forms
SkiaSharp es un sistema de gráficos 2D para .NET y C# funciona con el motor de gráficos de Skia de código
abierto que se usa habitualmente en productos de Google. Puede usar SkiaSharp en sus aplicaciones de
Xamarin.Forms para dibujar texto, mapas de bits y gráficos vectoriales en 2D. Consulte la plano 2D guía para
obtener información general sobre la biblioteca de SkiaSharp y algunos otros tutoriales.
En esta guía se da por supuesto que está familiarizado con la programación de Xamarin.Forms.

Webinar: SkiaSharp para Xamarin.Forms

Pasos preliminares de SkiaSharp


SkiaSharp para Xamarin.Forms está empaquetado como un paquete de NuGet. Después de crear una solución
de Xamarin.Forms en Visual Studio o Visual Studio para Mac, puede usar el Administrador de paquetes de
NuGet para buscar el SkiaSharp.Views.Forms empaquetar y agregarlo a la solución. Si activa la referencias
sección de cada proyecto después de agregar SkiaSharp, puede ver que varios SkiaSharp bibliotecas se han
agregado a cada uno de los proyectos de la solución.
Si la aplicación de Xamarin.Forms tiene como destino iOS, use la página de propiedades de proyecto para
cambiar el destino de implementación mínimo IOS 8.0.
En cualquier página de C# que utiliza SkiaSharp desea incluir un using la directiva para el SkiaSharp espacio
de nombres, que abarca todas las clases de SkiaSharp, estructuras y enumeraciones que va a usar en los gráficos
la programación. También conviene un using la directiva para el SkiaSharp.Views.Forms espacio de nombres
para las clases específicas de Xamarin.Forms. Esto es una cantidad menor espacio de nombres, con la clase más
importante que es SKCanvasView . Esta clase se deriva de Xamarin.Forms View y hospede su salida gráfica de
SkiaSharp.

IMPORTANT
El SkiaSharp.Views.Forms espacio de nombres también contiene un SKGLView clase que derive de View pero usa
OpenGL para representar gráficos. Para fines de simplicidad, esta guía restringe a sí mismo a SKCanvasView , pero al usar
SKGLView en su lugar, es bastante similar.

Conceptos básicos de dibujo de SkiaSharp


Algunas de las cifras más sencillas de gráficos que puede dibujar con SkiaSharp son círculos, elipses y
rectángulos. Mostrar estas cifras, aprenderá acerca de las coordenadas de SkiaSharp, tamaños y colores. La
presentación de texto y mapas de bits es más compleja, pero estos artículos también presentan estas técnicas.

Trazados y líneas de SkiaSharp


Una ruta de acceso de gráficos es una serie de líneas rectas conectadas y curvas. Se trazan las rutas de acceso,
rellenado, o ambos. En este artículo abarca muchos aspectos del dibujo de líneas, incluidos los extremos de trazo
y uniones y guiones y las líneas de puntos, pero no permite las geometrías de curva.

Transformaciones de SkiaSharp
Las transformaciones permiten que los objetos gráficos uniformemente traducido, escalar, girar, o sesgar.En este
artículo también muestra cómo puede utilizar una matriz de transformación de 3 por 3 estándar para crear
transformaciones no afines y aplicar transformaciones a rutas de acceso.

Trazados y curvas de SkiaSharp


La exploración de las rutas de acceso se continúa con la adición de curvas para los objetos de una ruta de acceso
y aprovechando otras características eficaces de la ruta de acceso. Verá cómo puede especificar una ruta de
acceso completa en una cadena de texto conciso, cómo usar los efectos de la ruta de acceso y cómo profundizar
en los aspectos internos de ruta de acceso.

Mapas de bits de SkiaSharp


Los mapas de bits son matrices rectangulares de bits que corresponden a los píxeles de un dispositivo de
pantalla. Esta serie de artículos muestra cómo cargar, guardar, mostrar, crear, dibuje en, animar y tener acceso a
los bits de SkiaSharp mapas de bits.

Efectos de SkiaSharp
Los efectos son propiedades que cambian la visualización de gráficos, incluidos los degradados lineales y
circulares normal, disposición en mosaico de mapa de bits, blend modos de desenfoque y otros usuarios.

Vínculos relacionados
API de SkiaSharp
SkiaSharpFormsDemos (ejemplo)
SkiaSharp con Xamarin.Forms seminario Web (vídeo)

Potrebbero piacerti anche