Freigeben über


Anpassen von Steuerelementen mit Handlern

Beispiel durchsuchen.Durchsuchen Sie das Beispiel

Handler können angepasst werden, um Darstellung und Verhalten eines plattformübergreifenden Steuerelements über die Anpassung hinaus zu erweitern, die über die API des Steuerelements möglich ist. Diese Anpassung, die die nativen Ansichten für das plattformübergreifende Steuerelement ändert, wird durch Ändern von Mapper für einen Handler mit einer der folgenden Methoden erreicht:

  • PrependToMapping, wodurch der Mapper für einen Handler geändert wird, bevor die Zuordnungen der .NET MAUI-Steuerelemente angewendet wurden.
  • ModifyMapping, wodurch eine vorhandene Zuordnung geändert wird.
  • AppendToMapping, wodurch der Mapper für einen Handler geändert wird, nachdem die Zuordnungen der .NET MAUI-Steuerelemente angewendet wurden.

Jede dieser Methoden weist eine identische Signatur auf, die zwei Argumente benötigt:

  • Ein string-basierter Schlüssel. Wenn eine der von .NET MAUI bereitgestellten Zuordnungen geändert wird, muss der von .NET MAUI verwendete Schlüssel angegeben werden. Die von Zuordnungen von .NET MAUI-Steuerelementen verwendeten Schlüsselwerte basieren auf Schnittstellen- und Eigenschaftennamen, zum Beispiel nameof(IEntry.IsPassword). Die Schnittstellen und ihre Eigenschaften, die jedes plattformübergreifende Steuerelement abstrahieren, finden Sie hier. Dies ist das Schlüsselformat, das verwendet werden soll, wenn die Handleranpassung bei jeder Änderung einer Eigenschaft ausgeführt werden soll. Andernfalls kann der Schlüssel ein beliebiger Wert sein, der nicht dem Namen einer von einem Typ verfügbar gemachten Eigenschaft entspricht. MyCustomization kann beispielsweise als Schlüssel angegeben werden, wobei jede Änderung der nativen Ansicht als Anpassung ausgeführt wird. Dies hat jedoch zur Folge, dass die Handleranpassung nur ausgeführt wird, wenn der Mapper für den Handler zuerst geändert wird.
  • Eine Action, die die Methode darstellt, die die Handler-Anpassung durchführt. Über die Action werden zwei Argumente angegeben:
    • 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.

Wichtig

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

Jeder Handler macht die native Ansicht für das plattformübergreifende Steuerelement über seine PlatformView-Eigenschaft verfügbar. Auf diese Eigenschaft kann zugegriffen werden, um Eigenschaften nativer Ansichten festzulegen, native Ansichtsmethoden aufzurufen und native 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 angepasst werden, indem bedingte Kompilierung verwendet wird, um Code basierend auf der Plattform mehrfach auszurichten. Alternativ können Sie partielle Klassen verwenden, um den Code in plattformspezifische Ordner und Dateien zu organisieren. Weitere Informationen über die bedingte Kompilierung finden Sie unter Bedingte Kompilierung.

Anpassen von Steuerelementen

Die .NET MAUI-Entry-Ansicht ist ein Steuerelement zur einzeiligen Texteingabe, 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

Die folgenden Diagramme zeigen, wie die Entry-Ansicht über die EntryHandler ihren nativen Ansichten zugeordnet wird:

Architektur des Eintragshandlers.

Der Entry-Eigenschaftsmapper in der EntryHandler-Klasse ordnet die plattformübergreifenden Steuerelementeigenschaften der nativen Ansichts-API 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 modifiziert werden, um Entry auf jeder Plattform individuell anzupassen:

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 wurde. Die Anpassung erfolgt durch den Zugriff auf die PlatformView-Eigenschaft des Handlers, die Zugriff auf die native Ansicht bietet, die dem plattformübergreifenden Steuerelement auf jeder Plattform zugeordnet ist. Anschließend wird der Handler durch nativen Code angepasst, indem der gesamte Text in der Entry ausgewählt wird, wenn er den Fokus erhält.

Weitere Informationen zu Mappern finden Sie unter Mapper.

Anpassen einer bestimmten Steuerelementinstanz

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

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

Anschließend können Sie den EntryHandler über den Eigenschaftsmapper 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 Handler-Anpassung in der App-Klasse ausgeführt wird, werden alle MyEntry-Instanzen in der App gemäß der Handler-Änderung angepasst.

Anpassen eines Steuerelements mithilfe des Handler-Lebenszyklus

Alle handlerbasierten .NET MAUI-Steuerelemente unterstützen HandlerChanging-und HandlerChanged-Ereignisse. Das HandlerChanged-Ereignis wird ausgelöst, wenn die native 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 Handler-Lebenszyklusereignissen finden Sie unter Handler-Lebenszyklus.

Der Handler-Lebenszyklus kann verwendet werden, um Anpassungen des Handlers durchzuführen. Um beispielsweise native Ansichtsereignisse zu abonnieren und abbestellen zu können, müssen Sie Ereignis-Handler für die HandlerChanged- und HandlerChanging-Ereignisse für das plattformübergreifende Steuerelement registrieren, das angepasst wird:

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

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

Bedingte Kompilierung

Die CodeBehind-Datei, die die Ereignis-Handler für die HandlerChanged- und HandlerChanging-Ereignisse enthält, wird im folgenden Beispiel, das die bedingte Kompilierung verwendet, gezeigt:

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

Das HandlerChanged-Ereignis wird ausgelöst, nachdem die native Ansicht, die das plattformübergreifende Steuerelement implementiert, erstellt und initialisiert wurde. Daher befindet sich der Ereignis-Handler dort, wo native Ereignisabonnements ausgeführt werden sollen. Dies erfordert das Umwandeln der PlatformView-Eigenschaft des Handlers in den Typ oder den Basistyp der nativen Ansicht, sodass auf native Ereignisse zugegriffen werden kann. In diesem Beispiel abonniert das OnEntryHandlerChanged-Ereignis unter iOS, Mac Catalyst und Windows native Ansichtsereignisse, die ausgelöst werden, wenn die nativen Ansichten, die den Entry implementieren, den Fokus erhalten.

Die Ereignis-Handler OnEditingDidBegin und OnGotFocus greifen auf die native Ansicht für den Entry auf den jeweiligen Plattformen zu und wählen den gesamten Text im Entry aus.

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 befindet sich der Ereignis-Handler dort, wo native Ereignisabonnements entfernt werden und andere Bereinigungen ausgeführt werden sollen. Das HandlerChangingEventArgs-Objekt, das dieses Ereignis begleitet, hat die Eigenschaften OldHandler und NewHandler, die auf die alten bzw. neuen Handler festgelegt werden. In diesem Beispiel entfernt das OnEntryHandlerChanging-Ereignis das Abonnement für die nativen Ansichtsereignisse unter iOS, Mac Catalyst und Windows.

Teilklassen

Anstatt der bedingten Kompilierung können Sie auch partielle Klassen verwenden, um den Code zur Steuerelementanpassung in plattformspezifische Ordner und Dateien zu organisieren. Bei diesem Ansatz wird Ihr Anpassungscode in eine plattformübergreifende und eine plattformspezifische partielle Klasse unterteilt:

  • Die plattformübergreifende partielle Klasse wird für alle Plattformen erstellt und definiert in der Regel Member, ohne diese jedoch zu implementieren. Diese Klasse sollte nicht in einem der untergeordneten Ordner von Platforms Ihres Projekts platziert werden, da sie so zu einer plattformspezifischen Klasse werden würde.
  • Die plattformspezifische partielle Klasse implementiert in der Regel die Member, die in der plattformübergreifenden partiellen Klasse definiert sind, und wird für eine einzelne Plattform erstellt. Diese Klasse sollte im untergeordneten Ordner des Ordners Platforms der Plattform Ihrer Wahl 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 Ereignis-Handler partielle Methoden namens ChangedHandler und ChangingHandler auf, deren Signaturen in der plattformübergreifenden partiellen Klasse definiert sind. Die Implementierungen der partiellen Methoden werden dann in den plattformspezifischen partiellen Klassen definiert, die in den richtigen untergeordneten Ordnern Plattformen platziert werden sollten, um sicherzustellen, dass das Buildsystem nur dann versucht, nativen Code zu erstellen, wenn dies für die spezifische Plattform erfolgt. Der folgende Code zeigt beispielsweise die Klasse CustomizeEntryPartialMethodsPage im Ordner Plattformen>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 benötigt wird und dass die partiellen Methoden nicht auf jeder Plattform implementiert werden müssen. Wenn eine Implementierung auf einer Plattform nicht bereitgestellt wird, werden die Methode sowie alle Aufrufe an die Methode zur Kompilierzeit 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 der Festlegung von Zielversionen, sodass Sie Plattform-Code nicht in Unterordner des Ordners Plattformen platzieren müssen, finden Sie unter Konfigurieren der Festlegung von Zielversionen.