Freigeben über


Steuerelemente mit Handlern anpassen

Beispiel durchsuchen. Durchsuchen Sie das Beispiel

Handler können angepasst werden, um das Erscheinungsbild und Verhalten eines plattformübergreifenden Steuerelements über die Anpassungsmöglichkeiten der API des Steuerelements hinaus zu erweitern. Diese Anpassung, die die systemeigenen Ansichten für das plattformübergreifende Steuerelement ändert, wird durch Ändern des Mappers für einen Handler mit einer der folgenden Methoden erreicht:

  • PrependToMapping, wodurch der Mapper für einen Handler geändert wird, bevor die .NET MAUI-Steuerelementzuordnungen angewendet wurden.
  • ModifyMapping, das eine bestehende Zuordnung modifiziert.
  • AppendToMapping, der den Mapper für einen Handler ändert, nachdem die .NET MAUI-Steuerelementzuordnungen angewendet wurden.

Jede dieser Methoden weist eine identische Signatur auf, die zwei Argumente erfordert:

  • Ein string-basierter Schlüssel. Beim Ändern einer der von .NET MAUI bereitgestellten Zuordnungen muss der von .NET MAUI verwendete Schlüssel angegeben werden. Die von .NET MAUI-Steuerelementzuordnungen verwendeten Schlüsselwerte basieren auf Schnittstellen- und Eigenschaftsnamen, zum Beispiel nameof(IEntry.IsPassword). Sie finden die Schnittstellen, die jedes plattformübergreifende Steuerelement im dotnet/maui repo abstrahieren. Dies ist das Schlüsselformat, das verwendet werden soll, wenn die Anpassung des Handlers bei jeder Änderung einer Eigenschaft ausgeführt werden soll. Andernfalls kann der Schlüssel ein beliebiger Wert sein, der nicht dem Namen einer Eigenschaft entspricht, die von einem Typ verfügbar gemacht wird. Kann beispielsweise MyCustomization als Schlüssel angegeben werden, wobei jede systemeigene Ansichtsänderung als Anpassung ausgeführt wird. Dies hat jedoch zur Folge, dass die Anpassung des Handlers nur ausgeführt wird, wenn der Mapper für den Handler zuerst geändert wird.
  • Ein Action Wert, der die Methode darstellt, die die Handleranpassung durchführt. Das Action gibt zwei Argumente an:
    • Ein handler Argument, das eine Instanz des angepassten Handlers bereitstellt.
    • Ein view Argument, das eine Instanz des plattformübergreifenden Steuerelements bereitstellt, das der Handler implementiert.

Von Bedeutung

Anpassungen von Handlern sind global und gelten nicht für eine bestimmte UI-Steuerelementinstanz. Die Anpassung des Handlers ist überall in Ihrer App zulässig. Sobald ein Handler angepasst wurde, wirkt er sich auf alle Steuerelemente dieses Typs überall in Ihrer App aus.

Jede Handlerklasse macht die systemeigene Ansicht für das plattformübergreifende Steuerelement über die PlatformView-Eigenschaft verfügbar. Auf diese Eigenschaft kann zugegriffen werden, um systemeigene Ansichtseigenschaften festzulegen, systemeigene Ansichtsmethoden aufzurufen und systemeigene Ansichtsereignisse zu abonnieren. Darüber hinaus wird das plattformübergreifende Steuerelement, das vom Handler implementiert wird, über seine VirtualView-Eigenschaft verfügbar gemacht.

Handler können pro Plattform mithilfe der bedingten Kompilierung an Multizielcode basierend auf der Plattform angepasst werden. Alternativ können Sie Teilklassen verwenden, um Ihren Code in plattformspezifische Ordner und Dateien zu organisieren. Weitere Informationen zur bedingten Kompilierung finden Sie unter "Bedingte Kompilierung".

Anpassen eines Steuerelements

Die .NET MAUI-Ansicht Entry ist ein einzeiliges Texteingabesteuerelement, das die IEntry Schnittstelle implementiert. Die EntryHandler ordnet die Entry Ansicht den folgenden nativen Ansichten für jede Plattform zu:

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

Die folgenden Diagramme zeigen, wie die Entry Ansicht ihren nativen Ansichten über folgendes EntryHandlerzugeordnet wird:

Architektur des Eintragshandlers.

Architektur des Eintragshandlers.

Die Entry Eigenschaftenzuordnung in der EntryHandler Klasse ordnet die plattformübergreifenden Steuerelementeigenschaften an die API der nativen Ansicht zu. Dadurch wird sichergestellt, dass beim Festlegen einer Eigenschaft für eine Entry die zugrunde liegende native Ansicht nach Bedarf aktualisiert wird.

Der Eigenschaftsmapper kann geändert werden, um ihn auf jeder Plattform anzupassen Entry.

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 diesem Beispiel erfolgt die Entry Anpassung in einer Seitenklasse. Daher werden alle Entry Steuerelemente unter Android, iOS und Windows angepasst, sobald eine Instanz der CustomizeEntryPage erstellt wird. Die Anpassung erfolgt durch den Zugriff auf die Handlereigenschaft PlatformView, die Zugriff auf die systemeigene Ansicht bietet, die dem plattformübergreifenden Steuerelement auf jeder Plattform zugeordnet ist. Nativer Code passt dann den Handler an, indem er den gesamten Text in der Entry auswählt, wenn er den Fokus bekommt.

Weitere Informationen zu Mappern finden Sie unter Mappers.

Anpassen einer bestimmten Steuerungsinstanz

Handler sind global und das Anpassen eines Handlers für ein Steuerelement führt dazu, dass alle Steuerelemente desselben Typs in Ihrer App angepasst werden. Handler für bestimmte Steuerelementinstanzen können jedoch durch Unterklassen des Steuerelements angepasst werden, und dann durch Ändern des Handlers für den Basissteuerelementtyp nur, wenn das Steuerelement vom unterklassigen Typ ist. Um beispielsweise ein bestimmtes Entry Steuerelement auf einer Seite anzupassen, das mehrere Entry Steuerelemente enthält, sollten Sie zuerst das Entry Steuerelement unterklassen:

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

Anschließend können Sie die EntryHandler-Eigenschaftszuordnung anpassen, um die gewünschte Änderung nur für MyEntry-Instanzen auszuführen.

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

Wenn die Handleranpassung in Ihrer App Klasse ausgeführt wird, werden alle MyEntry Instanzen in der App gemäß der Änderung des Handlers angepasst.

Anpassen eines Steuerelements mithilfe des Lebenszyklus eines Handlers

Alle handlerbasierten .NET MAUI-Steuerelemente unterstützen HandlerChanging und HandlerChanged Ereignisse. Das HandlerChanged Ereignis wird ausgelöst, wenn die systemeigene Ansicht, die das plattformübergreifende Steuerelement implementiert, verfügbar und initialisiert ist. Das HandlerChanging Ereignis wird ausgelöst, wenn der Handler des Steuerelements aus dem plattformübergreifenden Steuerelement entfernt werden soll. Weitere Informationen zu Handlerlebenszyklusereignissen finden Sie unter Handler-Lebenszyklus.

Der Handlerlebenszyklus kann verwendet werden, um Handleranpassungen durchzuführen. Um beispielsweise systemeigene Ansichtsereignisse zu abonnieren und abzubestellen, müssen Sie Ereignishandler für die Ereignisse HandlerChanged und HandlerChanging auf dem plattformübergreifenden Steuerelement registrieren, das angepasst wird.

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

Handler können pro Plattform mithilfe der bedingten Kompilierung oder mithilfe von partiellen Klassen angepasst werden, um Ihren Code in plattformspezifische Ordner und Dateien zu organisieren. Jeder Ansatz wird nacheinander besprochen, indem ein Entry angepasst wird, sodass sein gesamter Text ausgewählt wird, wenn er den Fokus erhält.

Bedingte Kompilierung

Die Code-Behind-Datei, die die Ereignishandler für die HandlerChanged- und HandlerChanging-Ereignisse enthält, wird im folgenden Beispiel gezeigt, das die bedingte Kompilierung verwendet.

#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
}
#if ANDROID
using Microsoft.Maui.Platform;
#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 MauiAppCompatEditText).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
}

Das HandlerChanged Ereignis wird ausgelöst, nachdem die systemeigene Ansicht, die das plattformübergreifende Steuerelement implementiert, erstellt und initialisiert wurde. Daher ist der Ereignishandler der Ort, an dem native Ereignisabonnements durchgeführt werden sollten. Dies erfordert die Umwandlung der PlatformView-Eigenschaft des Handlers in den Typ oder Basistyp der nativen Ansicht, damit auf native Ereignisse zugegriffen werden kann. In diesem Beispiel abonniert das OnEntryHandlerChanged-Ereignis unter iOS, Mac Catalyst und Windows die nativen Ansichtsereignisse, die ausgelöst werden, wenn die nativen Ansichten den Entry-Fokus erhalten.

Die Ereignishandler OnEditingDidBegin und OnGotFocus greifen auf die native Ansicht für Entry auf ihren jeweiligen Plattformen zu und wählen den gesamten Text aus, der sich in Entry befindet.

Das HandlerChanging Ereignis wird ausgelöst, bevor der vorhandene Handler aus dem plattformübergreifenden Steuerelement entfernt wird und bevor der neue Handler für das plattformübergreifende Steuerelement erstellt wird. Daher ist der Ereignishandler der Ort, wo systemeigene Ereignisabonnements entfernt werden sollten, und andere Bereinigungen sollten durchgeführt werden. Das HandlerChangingEventArgs Objekt, das dieses Ereignis begleitet, hat OldHandler und NewHandler Eigenschaften, die auf die alten bzw. neuen Handler festgelegt werden. In diesem Beispiel entfernt das OnEntryHandlerChanging Ereignis das Abonnement für die systemeigenen Ansichtsereignisse unter iOS, Mac Catalyst und Windows.

Partielle Klassen

Anstatt die bedingte Kompilierung zu verwenden, ist es auch möglich, Teilklassen zum Organisieren des Steuerelementanpassungscodes in plattformspezifische Ordner und Dateien zu verwenden. Bei diesem Ansatz wird Ihr Anpassungscode in eine plattformübergreifende partielle Klasse und eine plattformspezifische partielle Klasse unterteilt:

  • Die plattformübergreifende partielle Klasse definiert üblicherweise Mitglieder, implementiert sie jedoch nicht, und wird für alle Plattformen erstellt. Diese Klasse sollte nicht in einem der untergeordneten Ordner " Platforms " Ihres Projekts platziert werden, da dies zu einer plattformspezifischen Klasse werden würde.
  • Die plattformspezifische Partielle Klasse implementiert in der Regel die elemente, die in der plattformübergreifenden Partielle Klasse definiert sind, und wird für eine einzelne Plattform erstellt. Diese Klasse sollte im untergeordneten Ordner des Ordners "Plattformen " für Ihre ausgewählte Plattform platziert werden.

Das folgende Beispiel zeigt eine plattformübergreifende Partielle Klasse:

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 diesem Beispiel rufen die beiden Ereignishandler partielle Methoden namens ChangedHandler und ChangingHandler, deren Signaturen in der plattformübergreifenden partiellen Klasse definiert sind. Die partiellen Methodenimplementierungen werden dann in den plattformspezifischen partiellen Klassen definiert, die in den richtigen untergeordneten Ordnern "Platforms" platziert werden sollten, um sicherzustellen, dass das Buildsystem nur versucht, nativen Code beim Erstellen für die spezifische Plattform zu erstellen. Der folgende Code zeigt zum Beispiel die Klasse CustomizeEntryPartialMethodsPage im Ordner Platforms>Windows des Projekts:

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

Der Vorteil dieses Ansatzes besteht darin, dass die bedingte Kompilierung nicht erforderlich ist und dass die partiellen Methoden nicht auf jeder Plattform implementiert werden müssen. Wenn eine Implementierung nicht auf einer Plattform bereitgestellt wird, werden die Methode und alle Aufrufe der Methode zur Kompilierungszeit entfernt. Informationen zu partiellen Methoden finden Sie unter Partielle Methoden.

Informationen zur Organisation des Ordners "Plattformen" in einem .NET MAUI-Projekt finden Sie unter Partielle Klassen und Methoden. Informationen zum Konfigurieren von Multitargeting, sodass Sie Plattformcode nicht in Unterordner des Ordners Plattformen platzieren müssen, finden Sie unter Konfigurieren von Multitargeting.