Convertir un Custom Renderer de Xamarin.Forms a un Custom Handler de .NET MAUI

Las interfaces de usuario de Xamarin.Forms y .NET MAUI utilizan los controles nativos de la plataforma de destino, lo que permite que las aplicaciones conserven la apariencia adecuada para cada plataforma. El uso de Custom Renderers en Xamarin.Forms permite a los desarrolladores personalizar la apariencia y el comportamiento de los controles en cada plataforma.

Por diferentes motivos, entre los que podemos destacar mejoras en el rendimiento, más posibilidades de extensibilidad, en .NET MAUI tenemos el concepto de Custom Handler. Es similar al concepto de Custom Renderer, pero diferente en diferentes puntos.

¡Veamos paso a paso cómo convertir un Renderer a un Handler!. Para centrarnos en los conceptos clave de Renderers y Handlers, vamos a crear una Entry personalizado, un control bien conocido. Por otro lado, vamos a usar Android simplemente porque tanto desde Windows como desde macOS puedes lanzar las demos asociadas.

Custom Renderer

Cada control de Xamarin.Forms tiene un Renderer adjunto para cada plataforma que crea una instancia de un control nativo. El proceso para crear un Renderer es el siguiente:

  • Crea un control personalizado de Xamarin.Forms.
  • Consume el control personalizado de Xamarin.Forms.
  • Crea el Custom Renderer para el control en cada plataforma.

Creación del control Entry personalizado

Se puede crear un control personalizado creando una clase que herede de la clase View:

public class CustomEntry : View
{

}

El control CustomEntry se crea en el proyecto de la librería .NET Standard y es simplemente un control para capturar texto. Para personalizar la apariencia y el comportamiento del control podemos agregar BindableProperties y eventos.

public static readonly BindableProperty TextProperty =
    BindableProperty.Create(nameof(Text), typeof(string), typeof(Entry), string.Empty);


public string Text
{
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value); }
}

Consumir el control personalizado

Se puede hacer referencia al control CustomEntry en XAML en el proyecto de la librería .NET Standard declarando un espacio de nombres y usando el prefijo del espacio de nombres en el elemento de control.


    ...
    
    ...

Creación del Custom Renderer en cada plataforma

El proceso para crear la clase del Custom Renderer es el siguiente:

  1. Crea una clase que herede de ViewRenderer que representa el control nativo.
  2. Sobreescribe el método OnElementChanged que representa el control nativo y la lógica para personalizar el control. Este método se llama cuando se crea el control de Xamarin.Forms correspondiente.
  3. Sobreescribe el método OnElementPropertyChanged que responde a cualquier cambio de una BindableProperty.
  4. Agrega el atributo ExportRenderer a la clase del Custom Renderer para especificar que se usará para representar el control Xamarin.Forms. Este atributo se usa para registrar el representador personalizado con Xamarin.Forms (usando Assembly Scanning).

La clase ViewRenderer expone el método OnElementChanged, al que se llama cuando se crea el control Xamarin.Forms para representar el control nativo correspondiente. Este método toma un parámetro ElementChangedEventArgs que contiene las propiedades OldElement y NewElement. Estas propiedades representan el elemento Xamarin.Forms al que se adjuntó el representador y el elemento Xamarin.Forms al que está adjunto el representador, respectivamente. En la aplicación de ejemplo, la propiedad OldElement será nula y la propiedad NewElement contendrá una referencia al control CustomEntry.

El método OnElementChanged en la clase CustomEntryRenderer es el lugar para realizar la personalización del control nativo. Se puede acceder a una referencia del control nativo que se usa en la plataforma a través de la propiedad Control. Además, se puede obtener una referencia al control Xamarin.Forms que se está representando a través de la propiedad Element.

protected override void OnElementChanged(ElementChangedEventArgs e)
{
    base.OnElementChanged(e);

    if (e.OldElement == null)
    {
        EditText editText = new EditText(Context);

        _defaultTextColors = editText.TextColors;
        _defaultPlaceholderColors = editText.HintTextColors;

        SetNativeControl(editText);
    }

    UpdateText();
    UpdateTextColor();
    UpdatePlaceholder();
    UpdatePlaceholderColor();
    UpdateCharacterSpacing();
    UpdateHorizontalTextAlignment();
    UpdateVerticalTextAlignment();
}

La invalidación OnElementPropertyChanged responde a los cambios de BindableProperties enlazadas al control Xamarin.Forms.

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == CustomEntry.TextProperty.PropertyName)
        UpdateText();
    else if (e.PropertyName == CustomEntry.TextColorProperty.PropertyName)
        UpdateTextColor();
    if (e.PropertyName == CustomEntry.PlaceholderProperty.PropertyName)
        UpdatePlaceholder();
    else if (e.PropertyName == Entry.PlaceholderColorProperty.PropertyName)
        UpdatePlaceholderColor();
    else if (e.PropertyName == Entry.CharacterSpacingProperty.PropertyName)
        UpdateCharacterSpacing();
    else if (e.PropertyName == Entry.HorizontalTextAlignmentProperty.PropertyName)
        UpdateHorizontalTextAlignment();
    else if (e.PropertyName == Entry.VerticalTextAlignmentProperty.PropertyName)
        UpdateVerticalTextAlignment();

    base.OnElementPropertyChanged(sender, e);
}

Nada realmente nuevo hasta ahora, solo una pequeña revisión sobre cómo trabajar con Custom Renderers en Xamarin.Forms, ¿vamos a .NET MAUI?

Pero primero, ¿por qué cambiar de la arqquitectura de Renderers a la de Handlers en .NET MAUI?

Para comprender las motivaciones que impulsan el cambio, incluso conociendo las implicaciones en la cantidad de cambios necesarios, necesitamos saber qué es lo que está mal o es mejorable en los Renderers.

¿Recuerdas el atributo ExportRenderer que usa para registrar el Renderer? Esto le dice a Xamarin.Forms que en el arranque, haciendo uso del escaneo de ensamblados, debe buscar todas las bibliotecas referenciadas y usando este atributo, y si lo encuentra, registra el Renderer. Es fácil de usar, pero… el escaneo del ensamblados es lento y penaliza el inicio.

El método OnElementChanged generalmente causa confusión. ¿Cuándo usar OldElement , y NewElement?, ¿cuándo creo valores por defecto o me suscribo a eventos?. Causar confusión es un problema, pero es un problema aún mayor si no tener una forma fácil de suscribirse/desuscribirse hace que a veces no se anule la suscripción (por ejemplo) y… penaliza el rendimiento.

Todos esos métodos privados para actualizar las propiedades del control nativo son un gran problema. Es posible que debas hacer un pequeño cambio y debido a la falta de acceso (nuevamente, ¡métodos privados aquí y allá!), terminas creando un Custom Renderer más grande de lo necesario, etc.

Resolver estos problemas, y otros menores, es el objetivo fundamental de los Handlers.

Custom Handlers

El proceso para crear la clase del Custom Handler es el siguiente:

  1. Crea una clase que implementa la clase ViewHandler que representa el control nativo.
  2. Sobrecarga el método CreateNativeView que representa el control nativo.
  3. Crea el diccionario Mapper que responda a los cambios de propiedad.
  4. Registra el controlador usando el método AddHandler en la clase Startup.

Creación del control Entry personalizado

Los Handlers utilizan interfaces que derivan de la interfaz IView. Esto evita que el control multiplataforma tenga que hacer referencia a su Handler y que el Handler tenga que hacer referencia al control multiplataforma. El Mapper se encarga de mapear las propiedades multiplataforma a lo nativo en cada plataforma.

De esta forma comenzamos a crear la interfaz que define nuestro control:

public interface ICustomEntry : IView
{
    public string Text { get; }
    public Color TextColor { get; }
    public string Placeholder { get; }
    public Color PlaceholderColor { get; }
    public double CharacterSpacing { get; }
    public TextAlignment HorizontalTextAlignment { get; }
    public TextAlignment VerticalTextAlignment { get; }

    void Completed();
}

Se puede crear un control personalizado heredando de la clase View e implementando la interfaz del control:

public class CustomEntry : View, ICustomEntry
{

}

Crear el Custom Handler cada plataforma

Cree una subclase de la clase ViewHandler que representa el control nativo.

public partial class CustomEntryHandler : ViewHandler
{

}

Parece un cambio trivial, pasamos de heredar de ViewRenderer a ViewHandler, ¡pero es mucho más!.

ViewRenderer en Xamarin.Forms crea un elemento padre, en el caso de Android un ViewGroup, que se usaba para tareas de posicionamiento auxiliares. ViewHandler NO crea ningún elemento padre que ayudea a reducir la jerarquía visual y, por lo tanto, mejora el rendimiento.

Heredando de ViewHandler, tenemos que implementar el método CreateNativeView.

protected override EditText CreateNativeView()
{
    return new EditText(Context);
}

¿Recuerdas que anteriormente revisamos cómo se usó OnElementChanged en Xamarin.Forms?. En este método creamos el control nativo, inicializamos valores predeterminados, nos suscribimos a eventos, etc. Sin embargo, requiere una gran diversidad de conocimientos: qué es OldElement y NewElement, etc.

.NET MAUI simplifica y distribuye todo lo que hicimos anteriormente en el método OnElementChanged en diferentes métodos de una manera más sencilla.

Creamos el control nativo en el método CreateNativeView. Por otro lado, tenemos otros métodos como ConnectHandler y DisconnectHandler.

protected override void ConnectHandler(EditText nativeView)
{
    _defaultTextColors = nativeView.TextColors;
    _defaultPlaceholderColors = nativeView.HintTextColors;

    _watcher.Handler = this;
    nativeView.AddTextChangedListener(_watcher);

    base.ConnectHandler(nativeView);
}

protected override void DisconnectHandler(EditText nativeView)
{
    nativeView.RemoveTextChangedListener(_watcher);
    _watcher.Handler = null;

    base.DisconnectHandler(nativeView);
}

ConnectHandler es el lugar ideal para inicializar, suscribir eventos, etc. y de la misma manera podemos eliminar, cancelar la suscripción de eventos, etc. en DisconnectHandler.

El Mapper

** Mapper ** es un nuevo concepto introducido por los Handlers en .NET MAUI. No es más que un diccionario con las propiedades (y acciones) definidas en la interfaz de nuestro control (recuerda, usamos interfaces en el Handler). Reemplaza todo lo que se hacía en el método OnElementPropertyChanged en Xamarin.Forms.

public static PropertyMapper CustomEntryMapper = new PropertyMapper(ViewHandler.ViewMapper)
{
    [nameof(ICustomEntry.Text)] = MapText,
    [nameof(ICustomEntry.TextColor)] = MapTextColor,
    [nameof(ICustomEntry.Placeholder)] = MapPlaceholder,
    [nameof(ICustomEntry.PlaceholderColor)] = MapPlaceholderColor,
    [nameof(ICustomEntry.CharacterSpacing)] = MapCharacterSpacing,
    [nameof(ICustomEntry.HorizontalLayoutAlignment)] = MapHorizontalLayoutAlignment,
    [nameof(ICustomEntry.VerticalLayoutAlignment)] = MapVerticalLayoutAlignment
};

Mapper mapea propiedades a métodos estáticos.

public static void MapText(CustomEntryHandler handler, ICustomEntry entry)
{
    handler.NativeView?.UpdateText(entry);
}

El Mapper, además de simplificar la gestión de cambios de propiedad (notifica las propiedades al inicializar el control y también, cada vez que cambia una propiedad) nos permite más opciones de extensibilidad.

Por ejemplo:

using Microsoft.Maui; using Microsoft.Maui.Controls;

public partial class App : Application
{
    public App()
    {
        InitializeComponent();

#if __ANDROID__
        CustomEntryMapper[nameof(ICustomEntry.Text)] = (handler, view) =>
        {
            (handler.NativeView as Android.Widget.EditText).Text = view.text + "custom";
        };
#endif
    }
}

Registrar el handler

A diferencia de Xamarin.Forms que usa el atributo ExportRenderer que a su vez hace uso del escaneo de ensamblado, en .NET MAUI el registro del handler es ligeramente diferente.

appBuilder
    .UseMauiApp()
    .ConfigureMauiHandlers(handlers =>
    {
#if __ANDROID__
        handlers.AddHandler(typeof(CustomEntry), typeof(CustomEntryHandler));
#endif
    });

Hacemos uso de AppHostBuilder y el método AddHandler para registrar el Handler. Al final requiere, como en Xamarin.Forms, una línea para indicar que queremos dar de alta el Handler, pero se evita el uso de Assembly Scanning, que es lento y costoso, penalizando el inicio.

.NET MAUI Compatibility: Reutilizar tus Renderers sin cambios!

¿Qué es el paquete de Compatibilidad?

Para hacer transición de Xamarin.Forms a .NET MAUI lo más fluida posible, se agrega el paquete Compatability que agrega la funcionalidad de Xamarin.Forms que permite reutilizar código como Custom Renderers sin necesidad de realizar cambios.

¿Custom Renderers de Xamarin.Forms?

Las interfaces de usuario de Xamarin.Forms se crean mediante controles nativos de la plataforma de destino, lo que permite que las aplicaciones de Xamarin.Forms conserven la apariencia adecuada para cada plataforma. Los Custom Renderers permiten a los desarrolladores utilizar este proceso para personalizar la apariencia y el comportamiento de los controles de Xamarin.Forms en cada plataforma.

Veamos un ejemplo. Vamos a crear un Entry personalizada.

using Xamarin.Forms;

namespace Renderers
{
    public class CustomEntry : Entry
    {

    }
}

La implementación en Android

using Android.Content;
using Renderers;
using Renderers.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(CustomEntry), typeof(CustomEntryRenderer))]
namespace Renderers.Droid.Renderers
{
    public class CustomEntryRenderer : EntryRenderer
    {
        public CustomEntryRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                Control.SetBackgroundColor(global::Android.Graphics.Color.LightGreen);
            }
        }
    }
}

Como podemos ver, solo estamos modificando el color de fondo del control nativo. Algo realmente simple, pero suficiente para el propósito de este documento, para aprender a reutilizar Renderers sin cambiar el código de los mismos.

Reutilizar el Renderer

El Renderer en .NET MAUI usa exactamente el mismo código:

using Android.Content;
using Compatibility;
using Compatibility.Droid.Renderers;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Compatibility;
using Microsoft.Maui.Controls.Compatibility.Platform.Android;

[assembly: ExportRenderer(typeof(CustomEntry), typeof(CustomEntryRenderer))]
namespace Compatibility.Droid.Renderers
{
    public class CustomEntryRenderer : EntryRenderer
    {
        public CustomEntryRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                Control.SetBackgroundColor(global::Android.Graphics.Color.LightGreen);
            }
        }
    }
}

Los únicos cambios son reemplazar algunos espacios de nombres para usar Microsoft.Maui.Controls.Compatibility.

¿Y ya está todo listo?

No exactamente, en lugar de usar Assembly Scanning, .NET MAUI usa la clase Startup para realizar tareas como registrar Handlers o Renderers.

public class Startup : IStartup
{
    public void Configure(IAppHostBuilder appBuilder)
    {
        appBuilder
            .UseMauiApp<App>()
            .ConfigureMauiHandlers(handlers =>
            {
#if __ANDROID__
                handlers.AddCompatibilityRenderer(typeof(CustomEntry), typeof(Droid.Renderers.CustomEntryRenderer));
#endif
            });
    }
}

[XCT] Sombras en Xamarin.Forms

Las sombras consisten en un efecto visual que ayuda al cerebro humano a diferenciar ciertos elementos de la interfaz de usuario. Y esta es una de las razones por las que los diseñadores añaden sombras en sus diseños para aplicaciones móviles.

En este artículo vamos a aprender como añadir sombras, así como personalizar las mismas con diferentes opciones relacionadas con la dirección, tamaño o color de la sombra utilizando el Xamarin Community Toolkit.

Añadiendo Shadows usando el XCT

En el XCT se ha añadido un efecto sumamente útil y sencillo de utilizar que permite añadir sombras a cualquier View que añadimos a la UI.

El efecto ShadowEffect cuenta con una serie de propiedades para permitir personalizar las sombras:

  • Color: Especifica el color usado en la sombra.
  • OffsetX: Este valor nos permite definir el desplazamiento de la sombra. OffsetX especifica la distancia horizontal. Los valores negativos colocan las sombra a la izquierda de el elemento.
  • OffsetY: Este valor nos permite definir el desplazamiento de la sombra. OffsetY especifica la distancia vertical. Los valores negativos colocan las sombra en la parte superior de el elemento.
  • Opacity: Este valor permite especificar la opacidad de la sombra.
  • Radius: Cuando mayor sea este valor, mayor sera la difuminación, por consecuencia la sombra se vuelve más grande y ligera.

Utilizar sombras es sencillo, tras añadir la referencia al paquete NuGet del Xamarin Community Toolkit 1.1 o superior, añadimos el namespace necesario para trabajar con el XCT:

xmlns:xct="http://xamarin.com/schemas/2020/toolkit"

Y utilizando el efecto ShadowEffect con las propiedades que hemos visto previamente:

<Label 
    Text="Label With Shifted Red Shadow"
    xct:ShadowEffect.Color="Red"
    xct:ShadowEffect.OffsetX="10"
    xct:ShadowEffect.OffsetY="10" />

El resultado:

Sencillo, ¿verdad?. Recuerda, puedes usar los comentarios de la entrada para añadir tus dudas o preguntas o bien, si has usado este efecto y quieres compartir con todos tu resultado!.

Más información

Un vistazo al Xamarin Community Toolkit

Hace un tiempo…

Hace ya varios años, y con James Montemagno junto a varios miembros de la comunidad nacía el Xamarin Community Toolkit. La idea era crear una librería con Converters, efectos, controles, etc. donde añadir contenido creado por al comunidad en una librería con respaldo por Microsoft.

Durante un tiempo, se ha mantenido sin grandes movimientos hasta este año…

Vuelve con más y mejor!

Este año, un servidor junto a Gerald Versluis hemos retomado el concepto inicial con la idea de ayudar en el mantenimiento a la hora de revisar PRs, ideas, crear releases, etc. así como ayudar a la comunidad a llegar al Toolkit y…el Xamarin Community Toolkit ha vuelto con más fuerza que nunca!

Xamarin Community Toolkit

¿Qué ofrece el Community Toolkit a día de hoy?

Behaviors

Los Behaviors permiten asociar código que modifica el comportamiento de un elemento visual.

Los Behaviors disponibles son:

  • EmailValidationBehavior: Permite validar si el texto introducido es un email.
  • EventToCommandBehavior: Permite asociar un evento con un comando.
  • NumericValidationBehavior: Valida si el texto introducido es numérico.
  • AnimationBehavior: Permite lanzar animaciones desde código XAML.
  • MaskedBehavior: Permite añadir una máscara para distinguir si los datos que introduce el usuario son apropiados o inapropiados.
  • UriValidationBehavior: Valida si el texto introducido es una Url.
  • MultiValidationBehavior: Uno de los behaviors más versátiles permitiendo hacer múltiples validaciones de forma sencilla.
  • RequiredStringValidationBehavior: Valida si una caja de texto tiene una cadena vacía o no.
  • UserStoppedTypingBehavior: Este behavior espera a que el usuario deje de escribir para ejecutar un comando. El umbral de espera es ajustable, así como también hay una opción para cerrar el teclado después de que el usuario haya dejado de escribir.
  • ImpliedOrderGridBehavior: Valida el orden de filas y columnas de un Grid.
  • MaxLenghtReachedBehavior: Permite detectar si ha alcanzado el límite de caracteres.
  • CharactersValidationBehavior: Permite validar un mínimo o máximo de caracteres y poder reaccionar en ambos casos (cambiar estilos, etc.).

Converters

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.

Sin embargo, cuando no es así, debe realizarse una conversión de tipos. Los Converters se encargan de solucionar estos casos y hacer la conversión de tipos necesario.

El Xamarin Community Toolkit intentará agrupar en una misma librería la mayoría de Converters más utilizados, aquellos que usas en cada proyecto y transfieres de un proyecto a otro.

Los Converters disponibles son:

  • ItemTappedEventArgsConverter
  • ItemSelectedEventArgsConverter
  • ByteArrayToImageSourceConverer
  • MultiConverter
  • DateTimeOffsetConverter

Efectos

Con los efectos se pueden personalizar los controles nativos de cada plataforma y normalmente se usan para pequeños cambios de estilo.

En el Toolkit se cuentan con interesantes efectos:

  • SafeAreaEffect: Es un efecto que se puede agregar a cualquier elemento visual a través de una propiedad adjunta para indicar si ese elemento debe tener en cuenta Safe Area (iOS 11 y superior). Específicamente, ayudará a asegurarse de que el contenido no esté recortado por las esquinas redondeadas del dispositivo. El efecto solo es válido en iOS, lo que significa que en otras plataformas no hace nada.
  • RemoveBorderEffect: Eliminar el borde en Entry. Permite crear cajas de texto totalmente personalizadas.
  • SelectAllTextEffect: Permite seleccionar todo el texto de Entry, etc. al tener el foco.
  • IconTintColorEffect: Permite tintar con un color un icono.
  • TouchEffect: Uno de los efectos que añade más posibilidades. Añade múltiples posibilidades con eventos touch como detectar up/down, long press, etc.

Controles

AvatarView

AvatarView representa de forma visual el nombre de un usuario usando las iniciales y un color de fondo generado. Permite diferentes opciones de personalización tanto con iniciales como con imagen asó como personalizar colores, fuente, etc.

BadgeView

Vista utilizada para notificar a los usuarios notificaciones o el estado de algo. Permite posicionar el elemento en cada esquina (arriba izquierda, arriba derecha, abajo izquierda y abajo derecha) además de poder personalizar tamaños, colores o la animación a usar cuando el badge aparece o desaparece.

CameraView

CameraView permite mostrar una vista previa en vivo de la cámara. ¡Puedes tomar fotos, grabar videos y mucho más!.

DockLayout

Es un Layout que facilita el acoplamiento de contenido en las cuatro direcciones (superior, inferior, izquierda y derecha).

GravatarImageSource

GravatarImageSource permite utilizar fácilmente la imagen de Gravatar de un usuario de Gravatar.com usando nada más que su dirección de correo electrónico.

Expander

El control Expander proporciona un contenedor expandible para alojar cualquier contenido. ¿Te suena este control?. Sí, ha sido movido desde Xamarin.Forms (experimental) al Toolkit.

HexLayout

Un Layout que organiza los elementos en un patrón de panal abejas.

MediaElement

Otro control portado de Xamarin.Forms al Toolkit. Es una vista que permite reproducir video y audio.

RangeSlider

El RangeSlider es un control deslizante con dos Thumbs que permite seleccionar rangos numéricos. Con una infinidad de opciones de personalización tanto de fondo, barra y Thumbs, permite adaptar el control a una enorme variedad de necesidades.

SnackBar

Permite mostrar SnackBar, Toasts, etc.

SideMenuView

SideMenuView es un control que permite mostrar un menú derecho/izquierdo de forma simple y flexible.

Shield

Pueden mostrar cierta información de estado de forma similar a una insignia.

StateLayout

Una colección de propiedades adjuntas que le permiten especificar una o más vistas de estado para cualquiera de los Layouts existentes.

TabView

Un control que permite mostrar un conjunto de pestañas y su contenido respectivo. Con posibilidad de personalizar la apariencia de cada pestaña, gestión de animaciones, personalizar la pestaña seleccionada, etc. Abre muchas posibilidades no posibles hasta ahora a la hora de crear pestañas.

UniformGrid

El UniformGrid es como el Grid, pero con todas las filas y columnas con el mismo tamaño.

¿Qué es lo que viene?

La participación y colaboraciones en el Xamarin Community Toolkit por parte de la comunidad Xamarin es cada vez mayor (Gracias!). Gracias a esto, las novedades y nuevas posibilidades llegan de forma frecuente.

Proximamente tendremos:

Popup

Implementación nativa de Popups en cada plataforma.

SegmentedView

SegmentedControl para Xamarin.Forms:

Más información

GitHub: Xamarin Community Toolkit

[Material] Monkey Conf 2020

El evento

En España tenemos una gran comunidad Xamarin y con la idea de contar con un evento de desarrollo relacionado con Xamarin de gran tamaño, hace unos años nacía la Monkey Conf. Tras un par de ediciones, y este año a pesar de las dificultades, queríamos celebrar la tercera edición de la mejor forma posible.

El pasado 02 de Diciembre, celebramos la Monkey Conf 2020. Con una participación constante vía chats, con diferentes speakers de alrededor del mundo y tras ver que los videos del evento ya suman más de 1800 visualizaciones, no me cabe más que agradecer de entrada a sponsors, speakers y asistentes que han hecho el evento posible.

El material

En esta edición, he podido participar en un par de sesiones.

En la primera de las sesiones hable junto a Gerald Versluis acerca del Xamarin Community Toolkit. Se trata de una librería con un conjunto de controles, efectos, converters, etc. que ofrecen funcionalidad extra a Xamarin.Forms.

Mientras que en la segunda sesión, hable de .NET MAUI. El objetivo fundamental de la sesión era hablar de los handlers («nuevos renderers» en .NET MAUI) además de resolver algunas de las preguntas más frecuentes relacionadas con el tema como, ¿funcionaran mis Custom Renderers actuales en .NET MAUI?, ¿cómo será la migración?.

Sesiones

Las casi 12 horas de duración del streaming con todo el evento está disponible en YouTube:

Sin embargo, en el mismo canal de YouTube de la Monkey Conf podrás encontrar cada sesión en un video independiente para facilitarte acceder a la información que deseas.

Más información

El año 2021 será sumamente importante e interesante para los desarrolladores móviles en .NET. Con la llegada de .NET 6 y de .NET MAUI tendremos una enorme cantidad de novedades y temas de los que hablar…

No es raro entonces que, ya hayamos anunciado la Monkey Conf 2021!.

[Material] DotNet 2020

El evento

El pasado 21 de Octubre se celebrara la DotNet 2020, el mayor evento de tecnologías .NET celebrado en España por Plain Concepts.

Este año, y debido a la situación actual con el Covid, la conferencia ha sido totalmente online con sesiones vía streaming que se podía seguir repartidos en 5 tracks.

Agradecer a todo el mundo que ha hecho posible la conferencia con el cambio de formato incluido manteniendo una agenda tan variada y potente.

El material

Como en años anteriores, he tenido el placer de poder participar en el evento con una charla y poner mi granito de arena.

En esta ocasión, la sesión trata de cubrir las diferentes formas de crear controles para Xamarin.Forms.

Vimos desde como crear Custom Renderers a controles usando plantillas de control pasando por el uso de SkiaSharp. En cada caso, siempre con recomiendaciones, pros y contras y algunos detalles a tener en cuentas.

¿No pudiste asistir al evento?. Una lástima!. La calidad y variedad de sesiones fue alta. Sin embargo, las grabaciones de cada sesión estarán pronto en YouTube.

Más información

[Xamarin.Forms] Introducción a TemplateUI

El orgien

Con el soporte a Shapes y Brushes en Xamarin.Forms quería explorar las posibilidades para poder crear controles personalizados directamente con Xamarin.Forms (sin usar Custom Renderers, SkiaSharp, etc.). Tras crear algunas pruebas, combinar todo con la posibilidad de usar ControlTemplate (plantillas para personalizar la definición del control), etc. nace TemplateUI.

Nace TemplateUI

TemplateUI es una librería que contiene diversos controles, todos ellos, permitiendo el uso de ControlTemplate.

TemplateUI

Las plantillas de control de Xamarin.Forms permiten definir la estructura visual de los controles. Permiten separar la interfaz de usuario del control de la lógica que implementa el control. También se puede insertar contenido adicional en el control con la plantilla en un lugar predefinido. Por ejemplo, se puede acceder a la plantilla que define el control Rate y hacer modificaciones para cambiar la orientación o aplicar un borde. Hablamos de opciones que mediante propiedades no se pueden personalizar.

AvatarView

Es una representación visual de la imagen de usuario que se puede personalizar con texto, imagen, etc.

BadgeView

Control usado para notificar al usuario con notificaciones o cambios de estado.

CarouselView

Permiten navegar entre una colección de vistas.

ChatBubble

Muestra un mensaje con la forma de un bocadillo de conversación de chat.

CircleProgressBar

Es un control que muestra el porcentaje de progreso en una forma circular.

ComparerView

Es un control que permite mostrar dos vistas (puede ser cualquier tipo de contenido) para realizar una comparativa lado a lado.

ComparerView

DataVisualization

Un conjunto de gráficas creadas usando Shapes. Contiene:

  • Line Chart
  • Area Chart
  • Bar Chart

GridSplitter

Control que permite redistribuir el espacio entre las columnas o filas de un Grid.

Marquee

Usa este control al querer llamar la atención del usuario; añade un texto que hara scroll de forma automática y continua por la pantalla.

ProgressBar

Representa una barra de progreso horizontal que se va rellenando en base a un valor de tipo float.

ProgressBar

Rate

Permite al usuario seleccionar un valor de un grupo de símbolos visuales como una estrella.

SegmentedControl

Es un segmento lineal creado con un conjunto de segmentos que permiten al usuario seleccionar una opción.

Shield

Shield es un tipo de badge.

Slider

Es una barra horizontal que puede ser manipulada para seleccionar un valor de tipo doble entre un rango de opciones.

Slider

Tag

Control para crear tags.

ToggleSwitch

Un control que permite al usuario manipular para alternar entre los estados on y off, que están representados por un valor de tipo boolean.

ToggleSwitch

TreeView

Permite crear una lista con jerarquía de modo que se puede expandir o contraer los nodos que contienen otros nodos anidados.

¿Qué es lo próximo?

Como puedes ver, ya hay una gran cantidad de controles y vienen mas en camino!. En próximos artículos conoceremos cómo usar los controles, opciones de personalización con propiedades y usando plantillas de control, etc.

Más información

[Xamarin.Forms] Brushes

Introducción

Con la llegada de Xamarin.Forms 4.8 llega una funcionalidad altamente esperada, la posibilidad de usar Brushes o lo que es lo mismo, gradientes. En este artículo, vamos a conocer las nuevas posibilidades que tenemos usando brushes.

Brushes

Podemos usar un Brush para pintar el interior y el contorno (en algunos casos) de las vistas, Layouts y formas que componen la interfaz de usuario.

Para usar un gradiente en el fondo de cualquier View, podemos usar la propiedad Fill que espera un valor de tipo Brush. Tenemos las siguientes opciones:

  • SolidColorBrush
  • LinearGradientBrush
  • RadialGradientBrush

SolidColorBrush

SolidColorBrush pinta un área con un único color, como por ejemplo rojo o azul. Se trata del Brush más básico. Desde código XAML podemos usar SolidColorBrush de diferentes maneras:

  • Creando un objeto de tipo SolidColorBrush.
  • Usando una cadena que de formato al color que define al Brush (Ejemplo, color hexadecimal).

Veamos ejemplos:

<Grid
     Background="#FF9988"/>

En el ejemplo anterior, gracias a un TypeConverter podemos definir el Brush de forma sencilla, de forma similar a establecer un color de fondo.

<SolidColodBrush x:Key="MyBrush" Color="#FF9988" />


<Grid
     Background="{StaticResource MyBrush}"/>

LinearGradientBrush

LinearGradientBrush pinta un área con un degradado que se define a lo largo de una línea. Esta línea es el eje de degradado. Los colores del degradado y su ubicación en el eje de degradado se especifican con objetos de tipo GradientStop.

NOTA: Por defecto, el eje de degradado va de la esquina superior izquierda a la esquina inferior derecha, es decir, se crea un degradado en diagonal.

Cada GradientStop especifica el Color que se utiliza en el eje hasta cierto Offset. El color se puede establecer usando un nombre de color predefinido o mediante valores mientras que la propiedad Offset especifica la posición de cada GradientStop en el eje de degradado. Un objeto Offset es de tipo double con valores entre 0 y 1. Un Offset con un valor de 0 sitúa el GradientStop al principio del eje de degradado; es decir, cercano a su StartPoint. Por otro lado, un Offset con un valor de 1 sitúa el GradientStop en su EndPoint.

Para cambiar la dirección del degradado usamos las propiedades StartPoint y EndPoint. Podemos crear degradados horizontales o verticales, invertir el sentido de degradado, etc.

Veamos un ejemplo:

<LinearGradientBrush 
     StartPoint="0, 0" EndPoint="1, 0">
     <LinearGradientBrush.GradientStops>
          <GradientStop Color="Red" Offset="0.1" />
          <GradientStop Color="Pink" Offset="1.0" />
     </LinearGradientBrush.GradientStops>
</LinearGradientBrush>

El resultado:

LinearGradientBrush

RadialGradientBrush

Dibuja un degradado radial definido por las propiedades Center, RadiusX y RadiusY. El origen del degradado se establece usando la propiedad Center.

NOTA: Los colores del degradado se inician en el centro de la elipse y terminan en el radio.

Al igual que usando LinearGradientBrush, los colores del degradado radial se definen creando una colección de GradientStops.

Veamos un ejemplo:

<RadialGradientBrush 
     Center="0.5, 0.5">
     <RadialGradientBrush.GradientStops>
          <GradientStop Color="Red" Offset="0.1" />
          <GradientStop Color="Pink" Offset="1.0" />
     </RadialGradientBrush.GradientStops>
</RadialGradientBrush>

El resultado:

RadialGradientBrush

Gradientes en todas partes!

VisualElement cuenta ahora con una nueva propiedad Background esperando un objeto de tipo Brush. ¿Qué quiere decir esto?. Significa que cualquier View en Xamarin.Forms, es decir, en los controles, layouts e incluso páginas podemos usar gradientes ahora.

E incluso en NavigationPage o TabbedPage:

Gradientes en todas partes!

O usando Shapes:

Gradientes en Shapes

NOTA: Puedes notar que al usar Shapes se pueden usar brushes no solo para rellenar la figura, también para definir el contorno.

Usando CSS

Podemos definir brushes en código XAML y C#, pero en Xamarin.Forms también podemos configurar la apariencia de la aplicación usando CSS. Llega también el soporte a brushes usando CSS.

Veamos un ejemplo:

.linearGradientStyleWithCss90deg {
background: linear-gradient(90deg, rgb(255, 0, 0) 0%,rgb(255, 153, 51) 60%);
}

.linearGradientStyleWithCss180deg {
background: linear-gradient(180deg, rgb(255, 0, 0) 0%,rgb(255, 153, 51) 60%);
}

.linearGradientStyleWithCss270deg {
background: linear-gradient(270deg, rgb(255, 0, 0) 0%,rgb(255, 153, 51) 60%);
}

.radialGradientStyleWithCss {
background: radial-gradient(circle, rgb(255, 0, 0) 25%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 75%);
}

.radialGradientStyleWithCssLeft {
background: radial-gradient(circle at left, rgb(255, 0, 0) 25%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 75%);
}

.radialGradientStyleWithCssRight {
background: radial-gradient(circle at right, rgb(255, 0, 0) 25%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 75%);
}

El resultado:

Usando CSS

En las últimas versiones de Xamarin.Forms hemos ido recibiendo algunas novedades relacionadas puramente con las posibilidades a la hora de crear interfaces de usuario personalizadas. Funcionalidad como shapes, poder hacer clip de vistas o brushes permiten un número mayor de posibilidades para personalizar la interfaz de usuario y también a la hora de crear controles personalizados.

¿Qué te parecen estas novedades?. Recuerda, puedes dejar tu duda o pregunta en los comentarios de la entrada.

Más información

[Xamarin.Forms] Recortar vistas (Clip)

La llegada de Shapes a Xamarin.Forms

Con la llegada de Xamarin.Forms 4.7 nos llega la posibilidad de dibujar formas. Sin embargo, ¿sabías que además de poder dibujar una forma podemos cortar cualquier vista con una forma específica?. En este artículo, vamos a aprender cómo cortar vistas con formas.

Clip

La clase VisualElement cuenta ahora con una nueva propiedad llamada Clip de tipo Geometry que define el contorno del contenido.

La clase Geometry (y las clases que derivan de ella) permiten describir la geometría de una forma 2D.

En Xamarin.Forms contamos con geometrías simples como EllipseGeometry, LineGeometry o RectangleGeometry y geometrías más complejas como PathGeometry.

De modo que podemos recortar una imagen para tener una imagen circular de forma sencilla de la siguiente forma:

<Image 
     Source="image.png">
     <Image.Clip>
          <EllipseGeometry 
               RadiusX="100"
               RadiusY="100"
               Center="180,180" />
     </Image.Clip>
</Image>

Ejemplo:

Recortando imágenes!

Se pueden utilizar desde geometrías básicas a complejas usando un Path para definir exactamente la forma deseada.

Pero…funciona con cualquier View!

¿Recuerdas que mencionamos que la propiedad Clip estaba definido en VisualElement?. Esto se traduce en poder recortar cualquier tipo de vista en Xamarin.Forms, no solo imágenes.

Clip Views

De igual forma, recortar dinámicamente cualquier vista y también deshacer el recorte.

Clip Views dinámicamente

Añadir:

var ellipseGeometry = new EllipseGeometry
{
     Center = new Point(75, 75),
     RadiusX = 60,
     RadiusY = 60
};

Image.Clip = ellipseGeometry;

Quitar:

Image.Clip = null;

¿Qué te parece esta funcionalidad?. Recuerda, cualquier duda o comentario es bienvenido en la entrada.

Más información

Microsoft Docs: Xamarin.Forms Shapes

[Xamarin.Forms] Columnas y filas más fácil que nunca en un Grid

Creando columnas y filas en un Grid

XAML es increíble en muchas ocasiones y cumple bien su cometido, pero en ocasiones, puede llegar a ser excesivamente verboso. Un ejemplo de esto lo podemos ver al crear filas y columnas en un Grid:

<Grid>
     <Grid.ColumnDefinitions> 
          <ColumnDefinition Width="100" />
          <ColumnDefinition Width="Auto" />
          <ColumnDefinition Width="Auto" />
          <ColumnDefinition Width="*" />
     </Grid.ColumnDefinitions>
     <Grid.RowDefinitions> 
          <RowDefinition Height="24" />
          <RowDefinition Height= "64" />
          <RowDefinition Height="24" />
          <RowDefinition Height="*" />
          <RowDefinition Height="Auto" />
     </Grid.RowDefinitions>
</Grid>

Esta es la forma clásica de crear filas y columnas hasta la llegada de Xamarin.Forms 4.7…

Simplificando…

Ahora podemos crear filas y columnas de una forma mucho más sencilla:

<Grid ColumnDefinitions="100, Auto, Auto, *" RowDefinitions="24, 64, 24, *, Auto"/>

Fantástico!, ¿no crees?. Esto es posible gracias a un nuevo TypeConverter añadido. Este convertidor analiza el valor que está entrando, que será una cadena y lo convierte en algo que la propiedad entiende permitiéndonos crear filas y columnas de una forma mucho más sencilla.

Más información