Personalización de controles con controladores

Examinar ejemplo. Examinar el ejemplo

Los controladores se pueden personalizar para aumentar la apariencia y el comportamiento de un control multiplataforma más allá de la personalización que es posible a través de la API del control. Esta personalización, que modifica las vistas nativas para el control multiplataforma, se logra modificando el asignador de un controlador con uno de los métodos siguientes:

  • PrependToMapping, que modifica el asignador de un controlador antes de aplicar las asignaciones de controles MAUI de .NET.
  • ModifyMapping, que modifica una asignación existente.
  • AppendToMapping, que modifica el asignador de un controlador después de aplicar las asignaciones de controles MAUI de .NET.

Cada uno de estos métodos tiene una firma idéntica que requiere dos argumentos:

  • Una stringclave basada en . Al modificar una de las asignaciones proporcionadas por .NET MAUI, se debe especificar la clave usada por .NET MAUI. Los valores de clave utilizados por las asignaciones de controles MAUI de .NET se basan en nombres de interfaz y propiedad, por ejemplo nameof(IEntry.IsPassword). Las interfaces y sus propiedades, que abstraen cada control multiplataforma se pueden encontrar aquí. De lo contrario, esta clave puede ser un valor arbitrario que no tiene que corresponder al nombre de una propiedad expuesta por un tipo. Por ejemplo, MyCustomization se puede especificar como clave, con cualquier modificación de vista nativa que se realice como personalización.
  • que Action representa el método que realiza la personalización del controlador. Action especifica dos argumentos:
    • Argumento handler que proporciona una instancia del controlador que se va a personalizar.
    • Argumento view que proporciona una instancia del control multiplataforma que implementa el controlador.

Importante

Las personalizaciones del controlador son globales y no tienen como ámbito una instancia de control específica. La personalización del controlador puede producirse en cualquier lugar de la aplicación. Una vez que se personaliza un controlador, afecta a todos los controles de ese tipo, en todas partes de la aplicación.

Cada clase de controlador expone la vista nativa para el control multiplataforma a través de su PlatformView propiedad . Se puede tener acceso a esta propiedad para establecer propiedades de vista nativas, invocar métodos de vista nativa y suscribirse a eventos de vista nativos. Además, el control multiplataforma implementado por el controlador se expone a través de su VirtualView propiedad .

Los controladores se pueden personalizar por plataforma mediante la compilación condicional, para el código de varios destinos basado en la plataforma. Como alternativa, puede usar clases parciales para organizar el código en carpetas y archivos específicos de la plataforma. Para obtener más información sobre la compilación condicional, vea Compilación condicional.

Personalizar un control

La vista MAUI Entry de .NET es un control de entrada de texto de una sola línea, que implementa la IEntry interfaz . asigna EntryHandler la Entry vista a las siguientes vistas nativas para cada plataforma:

  • Catalyst de iOS/Mac: UITextField
  • Android: AppCompatEditText
  • Windows: TextBox

En los diagramas siguientes se muestra cómo se asigna la Entry vista a sus vistas nativas a través de EntryHandler:

Arquitectura del controlador de entrada.

El Entry asignador de propiedades, en la EntryHandler clase , asigna las propiedades de control multiplataforma a la API de vista nativa. Esto garantiza que, cuando se establece una propiedad en , Entryla vista nativa subyacente se actualiza según sea necesario.

El asignador de propiedades se puede modificar para personalizar Entry en cada plataforma:

namespace CustomizeHandlersDemo.Views;

public partial class CustomizeEntryPage : ContentPage
{
    public CustomizeEntryPage()
    {
        InitializeComponent();
        ModifyEntry();
    }

    void ModifyEntry()
    {
        Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping("MyCustomization", (handler, view) =>
        {
#if ANDROID
            handler.PlatformView.SetSelectAllOnFocus(true);
#elif IOS || MACCATALYST
            handler.PlatformView.EditingDidBegin += (s, e) =>
            {
                handler.PlatformView.PerformSelector(new ObjCRuntime.Selector("selectAll"), null, 0.0f);
            };
#elif WINDOWS
            handler.PlatformView.GotFocus += (s, e) =>
            {
                handler.PlatformView.SelectAll();
            };
#endif
        });
    }
}

En este ejemplo, la Entry personalización se produce en una clase de página. Por lo tanto, todos los Entry controles de Android, iOS y Windows se personalizarán una vez creada una instancia de CustomizeEntryPage . La personalización se realiza mediante el acceso a la propiedad handlers PlatformView , que proporciona acceso a la vista nativa que se asigna al control multiplataforma en cada plataforma. A continuación, el código nativo personaliza el controlador seleccionando todo el texto de Entry cuando obtiene el foco.

Para obtener más información sobre los asignadores, vea Asignadores.

Personalización de una instancia de control específica

Los controladores son globales y la personalización de un controlador para un control hará que todos los controles del mismo tipo se personalicen en la aplicación. Sin embargo, los controladores de instancias de control específicas se pueden personalizar mediante la subclases del control y, a continuación, modificando el controlador para el tipo de control base solo cuando el control es del tipo con subclases. Por ejemplo, para personalizar un control específico Entry en una página que contiene varios Entry controles, primero debe subclase el Entry control:

namespace CustomizeHandlersDemo.Controls
{
    internal class MyEntry : Entry
    {
    }
}

A continuación, puede personalizar , EntryHandlera través de su asignador de propiedades, para realizar la modificación deseada solo en MyEntry instancias:

Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping("MyCustomization", (handler, view) =>
{
    if (view is MyEntry)
    {
#if ANDROID
        handler.PlatformView.SetSelectAllOnFocus(true);
#elif IOS || MACCATALYST
        handler.PlatformView.EditingDidBegin += (s, e) =>
        {
            handler.PlatformView.PerformSelector(new ObjCRuntime.Selector("selectAll"), null, 0.0f);
        };
#elif WINDOWS
        handler.PlatformView.GotFocus += (s, e) =>
        {
            handler.PlatformView.SelectAll();
        };
#endif
    }
});

Si la personalización del controlador se realiza en la App clase , las MyEntry instancias de la aplicación se personalizarán según la modificación del controlador.

Personalización de un control mediante el ciclo de vida del controlador

Todos los controles MAUI de .NET basados en controladores admiten HandlerChanging eventos y HandlerChanged . El HandlerChanged evento se genera cuando la vista nativa que implementa el control multiplataforma está disponible e inicializada. El HandlerChanging evento se genera cuando el controlador del control está a punto de quitarse del control multiplataforma. Para obtener más información sobre los eventos del ciclo de vida del controlador, consulte Ciclo de vida del controlador.

El ciclo de vida del controlador se puede usar para realizar la personalización del controlador. Por ejemplo, para suscribirse a eventos de vista nativa y cancelar su suscripción, debe registrar controladores de eventos para los HandlerChanged eventos y HandlerChanging en el control multiplataforma que se está personalizando:

<Entry HandlerChanged="OnEntryHandlerChanged"
       HandlerChanging="OnEntryHandlerChanging" />

Los controladores se pueden personalizar por plataforma mediante la compilación condicional o mediante clases parciales para organizar el código en carpetas y archivos específicos de la plataforma. Cada enfoque se analizará a su vez, mediante la personalización de para Entry que todo su texto se seleccione cuando obtiene el foco.

Compilación condicional

El archivo de código subyacente que contiene los controladores de eventos para los HandlerChanged eventos y HandlerChanging se muestra en el ejemplo siguiente, que usa la compilación condicional:

#if ANDROID
using AndroidX.AppCompat.Widget;
#elif IOS || MACCATALYST
using UIKit;
#elif WINDOWS
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml;
#endif

namespace CustomizeHandlersDemo.Views;

public partial class CustomizeEntryHandlerLifecyclePage : ContentPage
{
    public CustomizeEntryHandlerLifecyclePage()
    {
        InitializeComponent();
    }

    void OnEntryHandlerChanged(object sender, EventArgs e)
    {
        Entry entry = sender as Entry;
#if ANDROID
        (entry.Handler.PlatformView as AppCompatEditText).SetSelectAllOnFocus(true);
#elif IOS || MACCATALYST
        (entry.Handler.PlatformView as UITextField).EditingDidBegin += OnEditingDidBegin;
#elif WINDOWS
        (entry.Handler.PlatformView as TextBox).GotFocus += OnGotFocus;
#endif
    }

    void OnEntryHandlerChanging(object sender, HandlerChangingEventArgs e)
    {
        if (e.OldHandler != null)
        {
#if IOS || MACCATALYST
            (e.OldHandler.PlatformView as UITextField).EditingDidBegin -= OnEditingDidBegin;
#elif WINDOWS
            (e.OldHandler.PlatformView as TextBox).GotFocus -= OnGotFocus;
#endif
        }
    }

#if IOS || MACCATALYST                   
    void OnEditingDidBegin(object sender, EventArgs e)
    {
        var nativeView = sender as UITextField;
        nativeView.PerformSelector(new ObjCRuntime.Selector("selectAll"), null, 0.0f);
    }
#elif WINDOWS
    void OnGotFocus(object sender, RoutedEventArgs e)
    {
        var nativeView = sender as TextBox;
        nativeView.SelectAll();
    }
#endif
}

El HandlerChanged evento se genera después de que se haya creado e inicializado la vista nativa que implementa el control multiplataforma. Por lo tanto, su controlador de eventos es donde se deben realizar suscripciones de eventos nativos. Esto requiere convertir la PlatformView propiedad del controlador al tipo, o tipo base, de la vista nativa para que se pueda tener acceso a los eventos nativos. En este ejemplo, en iOS, Mac Catalyst y Windows, el OnHandlerChanged evento se suscribe a eventos de vista nativa que se generan cuando las vistas nativas que implementan el Entry enfoque de ganancia.

Los OnEditingDidBegin controladores de eventos y OnGotFocus acceden a la vista nativa de Entry en sus respectivas plataformas y seleccionan todo el texto que se encuentra en .Entry

El HandlerChanging evento se genera antes de quitar el controlador existente del control multiplataforma y antes de crear el nuevo controlador para el control multiplataforma. Por lo tanto, su controlador de eventos es donde se deben quitar las suscripciones de eventos nativos y se debe realizar otra limpieza. El HandlerChangingEventArgs objeto que acompaña a este evento tiene OldHandler propiedades y NewHandler , que se establecerán en los controladores antiguos y nuevos, respectivamente. En este ejemplo, el OnHandlerChanging evento quita la suscripción a los eventos de vista nativa en iOS, Mac Catalyst y Windows.

Clases parciales

En lugar de usar la compilación condicional, también es posible usar clases parciales para organizar el código de personalización del control en carpetas y archivos específicos de la plataforma. Con este enfoque, el código de personalización se divide en una clase parcial multiplataforma y una clase parcial específica de la plataforma. En el ejemplo siguiente se muestra la clase parcial multiplataforma:

namespace CustomizeHandlersDemo.Views;

public partial class CustomizeEntryPartialMethodsPage : ContentPage
{
    public CustomizeEntryPartialMethodsPage()
    {
        InitializeComponent();
    }

    partial void ChangedHandler(object sender, EventArgs e);
    partial void ChangingHandler(object sender, HandlerChangingEventArgs e);

    void OnEntryHandlerChanged(object sender, EventArgs e) => ChangedHandler(sender, e);
    void OnEntryHandlerChanging(object sender, HandlerChangingEventArgs e) => ChangingHandler(sender, e);
}

Importante

La clase parcial multiplataforma no debe colocarse en ninguna de las carpetas secundarias Plataformas del proyecto.

En este ejemplo, los dos controladores de eventos llaman a métodos parciales denominados ChangedHandler y ChangingHandler, cuyas firmas se definen en la clase parcial multiplataforma. A continuación, las implementaciones de método parcial se definen en las clases parciales específicas de la plataforma, que deben colocarse en las carpetas secundarias Plataformas correctas para asegurarse de que el sistema de compilación solo intenta compilar código nativo al compilar para la plataforma específica. Por ejemplo, el código siguiente muestra la CustomizeEntryPartialMethodsPage clase en la carpeta Platforms>Windows del proyecto:

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace CustomizeHandlersDemo.Views
{
    public partial class CustomizeEntryPartialMethodsPage : ContentPage
    {
        partial void ChangedHandler(object sender, EventArgs e)
        {
            Entry entry = sender as Entry;
            (entry.Handler.PlatformView as TextBox).GotFocus += OnGotFocus;
        }

        partial void ChangingHandler(object sender, HandlerChangingEventArgs e)
        {
            if (e.OldHandler != null)
            {
                (e.OldHandler.PlatformView as TextBox).GotFocus -= OnGotFocus;
            }
        }

        void OnGotFocus(object sender, RoutedEventArgs e)
        {
            var nativeView = sender as TextBox;
            nativeView.SelectAll();
        }
    }
}

La ventaja de este enfoque es que la compilación condicional no es necesaria y que los métodos parciales no tienen que implementarse en cada plataforma. Si no se proporciona una implementación en una plataforma, el método y todas las llamadas al método se quitan en tiempo de compilación. Para obtener información sobre los métodos parciales, vea Métodos parciales.

Para obtener información sobre la organización de la carpeta Platforms en un proyecto MAUI de .NET, vea Clases y métodos parciales. Para obtener información sobre cómo configurar varios destinos para que no tenga que colocar código de plataforma en subcarpetas de la carpeta Plataformas , consulte Configuración de varios destinos.