Condividi tramite


Personalizzare i controlli con i gestori

Sfogliare l'esempio. Esplorare l'esempio

I gestori possono essere personalizzati per aumentare l'aspetto e il comportamento di un controllo multipiattaforma oltre la personalizzazione possibile tramite l'API del controllo. Questa personalizzazione, che modifica le visualizzazioni native per il controllo multipiattaforma, viene ottenuta modificando il mapper per un gestore con uno dei metodi seguenti:

  • PrependToMapping, che modifica il mapper per un gestore prima dell'applicazione dei mapping dei controlli MAUI .NET.
  • ModifyMapping, che modifica un mapping esistente.
  • AppendToMapping, che modifica il mapper per un gestore dopo l'applicazione dei mapping dei controlli MAUI .NET.

Ognuno di questi metodi ha una firma identica che richiede due argomenti:

  • Chiave stringbasata su . Quando si modifica uno dei mapping forniti da .NET MAUI, è necessario specificare la chiave usata da .NET MAUI. I valori chiave usati dai mapping dei controlli MAUI .NET sono basati su nomi di interfaccia e proprietà, ad esempio nameof(IEntry.IsPassword). Le interfacce e le relative proprietà, che astraggono ogni controllo multipiattaforma sono disponibili qui. Questo è il formato di chiave che deve essere usato se si vuole che la personalizzazione del gestore venga eseguita ogni volta che una proprietà cambia. In caso contrario, la chiave può essere un valore arbitrario che non deve corrispondere al nome di una proprietà esposta da un tipo. Ad esempio, MyCustomization può essere specificato come chiave, con qualsiasi modifica della visualizzazione nativa eseguita come personalizzazione. Tuttavia, una conseguenza di questo formato di chiave è che la personalizzazione del gestore verrà eseguita solo quando il mapper per il gestore viene modificato per la prima volta.
  • Oggetto Action che rappresenta il metodo che esegue la personalizzazione del gestore. Specifica Action due argomenti:
    • Argomento handler che fornisce un'istanza del gestore da personalizzare.
    • Argomento view che fornisce un'istanza del controllo multipiattaforma implementato dal gestore.

Importante

Le personalizzazioni del gestore sono globali e non hanno come ambito un'istanza di controllo specifica. La personalizzazione del gestore può essere eseguita ovunque nell'app. Dopo aver personalizzato un gestore, influisce su tutti i controlli di quel tipo, ovunque nell'app.

Ogni classe del gestore espone la visualizzazione nativa per il controllo multipiattaforma tramite la relativa PlatformView proprietà. È possibile accedere a questa proprietà per impostare le proprietà della visualizzazione nativa, richiamare i metodi di visualizzazione nativa e sottoscrivere eventi di visualizzazione nativa. Inoltre, il controllo multipiattaforma implementato dal gestore viene esposto tramite la relativa VirtualView proprietà.

I gestori possono essere personalizzati per ogni piattaforma usando la compilazione condizionale, al codice multi-destinazione basato sulla piattaforma. In alternativa, è possibile usare classi parziali per organizzare il codice in cartelle e file specifici della piattaforma. Per altre informazioni sulla compilazione condizionale, vedere Compilazione condizionale.

Personalizzare un controllo

La visualizzazione MAUI Entry .NET è un controllo input di testo a riga singola che implementa l'interfaccia IEntry . La EntryHandler vista viene mappata Entry alle visualizzazioni native seguenti per ogni piattaforma:

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

I diagrammi seguenti illustrano come viene eseguito il mapping della Entry vista alle visualizzazioni native tramite EntryHandler:

Architettura del gestore di voci.

Il Entry mapper di proprietà, nella classe , esegue il EntryHandler mapping delle proprietà del controllo multipiattaforma all'API di visualizzazione nativa. In questo modo, quando una proprietà è impostata su , Entryla visualizzazione nativa sottostante viene aggiornata in base alle esigenze.

Il mapper di proprietà può essere modificato per personalizzare Entry in ogni piattaforma:

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
        });
    }
}

In questo esempio la Entry personalizzazione viene eseguita in una classe di pagina. Pertanto, tutti i Entry controlli in Android, iOS e Windows verranno personalizzati dopo la creazione di un'istanza CustomizeEntryPage di . La personalizzazione viene eseguita accedendo alla proprietà gestori PlatformView , che consente di accedere alla visualizzazione nativa mappata al controllo multipiattaforma in ogni piattaforma. Il codice nativo personalizza quindi il gestore selezionando tutto il testo in Entry quando ottiene lo stato attivo.

Per altre informazioni sui mapper, vedere Mapper.

Personalizzare un'istanza del controllo specifica

I gestori sono globali e la personalizzazione di un gestore per un controllo comporterà la personalizzazione di tutti i controlli dello stesso tipo nell'app. Tuttavia, i gestori per istanze di controllo specifiche possono essere personalizzati sottoclassando il controllo e quindi modificando il gestore per il tipo di controllo di base solo quando il controllo è del tipo sottoclassato. Ad esempio, per personalizzare un controllo specifico Entry in una pagina contenente più Entry controlli, è necessario prima sottoclassare il Entry controllo:

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

È quindi possibile personalizzare , EntryHandlertramite il relativo mapper di proprietà, per eseguire la modifica desiderata solo per MyEntry le istanze:

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
    }
});

Se la personalizzazione del gestore viene eseguita nella App classe, tutte MyEntry le istanze dell'app verranno personalizzate in base alla modifica del gestore.

Personalizzare un controllo usando il ciclo di vita del gestore

Tutti i controlli MAUI basati su gestori supportano HandlerChanging e HandlerChanged eventi. L'evento HandlerChanged viene generato quando la visualizzazione nativa che implementa il controllo multipiattaforma è disponibile e inizializzata. L'evento HandlerChanging viene generato quando il gestore del controllo sta per essere rimosso dal controllo multipiattaforma. Per altre informazioni sugli eventi del ciclo di vita del gestore, vedere Ciclo di vita del gestore.

Il ciclo di vita del gestore può essere usato per eseguire la personalizzazione del gestore. Ad esempio, per sottoscrivere e annullare la sottoscrizione, gli eventi di visualizzazione nativa è necessario registrare i gestori eventi per gli HandlerChanged eventi e HandlerChanging nel controllo multipiattaforma da personalizzare:

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

I gestori possono essere personalizzati per ogni piattaforma usando la compilazione condizionale o usando classi parziali per organizzare il codice in cartelle e file specifici della piattaforma. Ogni approccio verrà discusso a sua volta, personalizzando un oggetto Entry in modo che tutto il testo venga selezionato quando ottiene lo stato attivo.

Compilazione condizionale

Il file code-behind contenente i gestori eventi per gli HandlerChanged eventi e HandlerChanging viene illustrato nell'esempio seguente, che usa la compilazione condizionale:

#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
}

L'evento HandlerChanged viene generato dopo la visualizzazione nativa che implementa il controllo multipiattaforma è stato creato e inizializzato. Di conseguenza, il gestore eventi è la posizione in cui devono essere eseguite le sottoscrizioni di eventi native. A tale scopo, è necessario eseguire il cast della PlatformView proprietà del gestore al tipo o al tipo di base della visualizzazione nativa in modo che sia possibile accedere agli eventi nativi. In questo esempio, in iOS, Mac Catalyst e Windows, l'evento OnEntryHandlerChanged sottoscrive gli eventi di visualizzazione nativa generati quando le visualizzazioni native che implementano lo Entry stato attivo.

I OnEditingDidBegin gestori eventi e OnGotFocus accedono alla visualizzazione nativa per l'oggetto Entry nelle rispettive piattaforme e selezionano tutto il Entrytesto presente in .

L'evento HandlerChanging viene generato prima che il gestore esistente venga rimosso dal controllo multipiattaforma e prima che venga creato il nuovo gestore per il controllo multipiattaforma. Di conseguenza, il gestore eventi è la posizione in cui devono essere rimosse le sottoscrizioni di eventi native e deve essere eseguita un'altra pulizia. L'oggetto HandlerChangingEventArgs che accompagna questo evento ha OldHandler proprietà e NewHandler , che verranno impostate rispettivamente sui gestori precedenti e nuovi. In questo esempio, l'evento OnEntryHandlerChanging rimuove la sottoscrizione agli eventi di visualizzazione nativa in iOS, Mac Catalyst e Windows.

Classi parziali

Invece di usare la compilazione condizionale, è anche possibile usare classi parziali per organizzare il codice di personalizzazione del controllo in cartelle e file specifici della piattaforma. Con questo approccio, il codice di personalizzazione è separato in una classe parziale multipiattaforma e in una classe parziale specifica della piattaforma:

  • La classe parziale multipiattaforma definisce in genere i membri, ma non li implementa e viene creato per tutte le piattaforme. Questa classe non deve essere inserita in nessuna delle cartelle figlio Platforms del progetto, perché in questo modo sarebbe una classe specifica della piattaforma.
  • La classe parziale specifica della piattaforma implementa in genere i membri definiti nella classe parziale multipiattaforma e viene compilato per una singola piattaforma. Questa classe deve essere inserita nella cartella figlio della cartella Piattaforme per la piattaforma scelta.

L'esempio seguente mostra una classe parziale multipiattaforma:

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);
}

In questo esempio, i due gestori eventi chiamano metodi parziali denominati ChangedHandler e ChangingHandler, le cui firme sono definite nella classe parziale multipiattaforma. Le implementazioni parziali del metodo vengono quindi definite nelle classi parziali specifiche della piattaforma, che devono essere inserite nelle cartelle figlio Piattaforme corrette per garantire che il sistema di compilazione tenti di compilare codice nativo solo durante la compilazione per la piattaforma specifica. Ad esempio, il codice seguente mostra la CustomizeEntryPartialMethodsPage classe nella cartella Platforms>Windows del progetto:

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();
        }
    }
}

Il vantaggio di questo approccio è che la compilazione condizionale non è necessaria e che i metodi parziali non devono essere implementati in ogni piattaforma. Se un'implementazione non viene fornita in una piattaforma, il metodo e tutte le chiamate al metodo vengono rimosse in fase di compilazione. Per informazioni sui metodi parziali, vedere Metodi parziali.

Per informazioni sull'organizzazione della cartella Platforms in un progetto MAUI .NET, vedere Classi e metodi parziali. Per informazioni su come configurare multitargeting in modo che non sia necessario inserire il codice della piattaforma in sottocartelle della cartella Piattaforme , vedere Configurare multitargeting.