使用處理程式自定義控制件

Browse sample. 流覽範例

您可以自定義處理程式,以增強跨平臺控件的外觀和行為,而不需要透過控件的 API 進行自定義。 此自定義可修改跨平臺控制件的原生檢視,方法是使用下列其中一種方法修改處理程式的對應程式:

  • PrependToMapping,它會在套用 .NET MAUI 控制件對應之前修改處理程式的對應程式。
  • ModifyMapping,修改現有的對應。
  • AppendToMapping,它會在套用 .NET MAUI 控制對應之後修改處理程式的對應程式。

每個方法都有一個相同的簽章,需要兩個自變數:

  • string型索引鍵。 修改 .NET MAUI 所提供的其中一個對應時,必須指定 .NET MAUI 所使用的索引鍵。 .NET MAUI 控制件對應的索引鍵值是以介面和屬性名稱為基礎,例如 nameof(IEntry.IsPassword)。 您可以在這裡找到每個跨平臺控件的介面及其屬性,以抽象化每個跨平臺控制件。 這是當您想要每次屬性變更時執行處理程式自定義時,應該使用的索引鍵格式。 否則,索引鍵可以是不需要對應至類型所公開屬性名稱的任意值。 例如, MyCustomization 可以指定為索引鍵,並執行任何原生檢視修改做為自定義。 不過,此索引鍵格式的結果是,只有在第一次修改處理程式的對應程式時,您的處理程式自定義才會執行。
  • Action,表示執行處理程式自定義的方法。 Action指定兩個自變數:
    • 提供 handler 所自定義處理程序實例的自變數。
    • view 變數,提供處理程序實作之跨平臺控件的實例。

重要

處理程式自定義是全域的,而且不會限定在特定控件實例的範圍內。 允許在應用程式中的任何位置進行處理程式自定義。 自定義處理程式之後,它會影響該應用程式中所有該類型的控件。

每個處理程式類別都會透過其 PlatformView 屬性公開跨平臺控制件的原生檢視。 您可以存取這個屬性來設定原生檢視屬性、叫用原生檢視方法,以及訂閱原生檢視事件。 此外,處理程式所實作的跨平臺控件會透過其 VirtualView 屬性公開。

您可以使用條件式編譯,根據平臺自定義每個平台的處理程式,以多重目標程序代碼。 或者,您可以使用部分類別將程式代碼組織成平臺特定的資料夾和檔案。 如需條件式編譯的詳細資訊,請參閱 條件式編譯

自定義控制件

.NET MAUI Entry 檢視是實作 IEntry 介面的單行文字輸入控件。 會將 EntryHandler 檢視對應 Entry 至每個平臺的下列原生檢視:

  • iOS/Mac CatalystUITextField
  • AndroidAppCompatEditText
  • WindowsTextBox

下圖顯示檢視 Entry 如何透過 EntryHandler對應至其原生檢視:

Entry handler architecture.

類別 Entry 中的 EntryHandler 屬性對應程式會將跨平臺控件屬性對應至原生檢視 API。 這可確保在 上 Entry設定屬性時,基礎原生檢視會視需要更新。

屬性對應程式可以修改為在每個平臺上自訂 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
        });
    }
}

在此範例中 Entry ,自定義會在頁面類別中發生。 因此,建立 實例CustomizeEntryPage之後,將會自定義Android、iOS和Windows上的所有Entry控件。 自定義是藉由存取 handlers PlatformView 屬性來執行,其可讓您存取對應至每個平臺上跨平臺控件的原生檢視。 原生程式代碼接著會在取得焦點時選取 中的所有 Entry 文字,以自定義處理程式。

如需對應程式的詳細資訊,請參閱 對應器

自定義特定控件實例

處理程式是全域的,而自定義控件的處理程式會導致應用程式中自定義相同類型的所有控件。 不過,特定控件實例的處理程式可以透過子類別化控件來自定義,然後只有在控件是子類別化類型時,才修改基底控件類型的處理程式。 例如,若要在包含多個Entry控件的頁面上自定義特定Entry控件,您應該先將控件子類別Entry化:

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

然後,您可以透過其屬性對應程式自定義 EntryHandler,只 MyEntry 對實例執行所需的修改:

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

如果您的類別中 App 執行處理程式自定義,應用程式中的任何 MyEntry 實例都會根據處理程式修改來自定義。

使用處理程式生命週期自定義控制件

所有處理程式型 .NET MAUI 控制件都支援 HandlerChangingHandlerChanged 事件。 當 HandlerChanged 實作跨平臺控件的原生檢視可供使用並初始化時,就會引發 事件。 HandlerChanging當控件的處理程式即將從跨平臺控件中移除時,就會引發 事件。 如需處理程式生命週期事件的詳細資訊,請參閱 處理程式生命週期

處理程式生命週期可用來執行處理程式自定義。 例如,若要訂閱和取消訂閱原生檢視事件,您必須為 HandlerChanged 要自定義之跨平臺控制件上的和 HandlerChanging 事件註冊事件處理程式:

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

您可以使用條件式編譯,或使用部分類別將程式代碼組織成平臺特定的資料夾和檔案,來自定義每個平臺的處理程式。 每個方法都會藉由自定義 Entry 來討論,以便在取得焦點時選取其所有文字。

條件式編譯

下列範例顯示包含 和 HandlerChanging 事件的事件處理程式HandlerChanged的程式代碼後置檔案,其使用條件式編譯:

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

HandlerChanged 已建立和初始化實作跨平臺控件的原生檢視之後,就會引發 事件。 因此,其事件處理程式是應該執行原生事件訂閱的位置。 這需要將 PlatformView 處理程式的 屬性轉換成原生檢視的類型或基底類型,以便存取原生事件。 在此範例中,在iOS、Mac Catalyst和 Windows 上 OnEntryHandlerChanged ,事件會訂閱原生檢視事件,這些事件會在實 Entry 作取得焦點的原生檢視時引發。

OnEditingDidBeginOnGotFocus 事件處理程式會在其各自的平臺上存取 的原生檢視Entry,並選取 中的所有Entry文字。

HandlerChanging 從跨平臺控件中移除現有處理程式之前,以及建立跨平臺控件的新處理程式之前,就會引發 事件。 因此,其事件處理程式是應該移除原生事件訂閱的位置,而且應該執行其他清除。 伴隨 HandlerChangingEventArgs 此事件的物件具有 OldHandlerNewHandler 屬性,這將會分別設定為舊處理程式和新處理程式。 在此範例中 OnEntryHandlerChanging ,事件會移除 iOS、Mac Catalyst 和 Windows 上原生檢視事件的訂用帳戶。

部分類別

與其使用條件式編譯,也可以使用部分類別將控件自定義程式代碼組織成平臺特定的資料夾和檔案。 使用這種方法,您的自定義程式代碼會分成跨平臺部分類別和平臺特定的部分類別。 下列範例顯示跨平臺部分類別:

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

重要

跨平臺部分類別不應放在您專案的任何 [平臺 ] 子資料夾中。

在此範例中,這兩個事件處理程式會呼叫名為 ChangedHandlerChangingHandler的部分方法,其簽章定義於跨平臺部分類別中。 接著,部分方法實作會定義在平臺特定部分類別中,而這個部分類別應該放在正確的 [平臺 ] 子資料夾中,以確保建置系統只會在建置特定平臺時嘗試建置機器碼。 例如,下列程式代碼會在專案的 [平臺 Windows]>資料夾中顯示 CustomizeEntryPartialMethodsPage 類別:

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

此方法的優點是不需要條件式編譯,而且部分方法不需要在每個平臺上實作。 如果未在平臺上提供實作,則在編譯階段會移除方法和所有對 方法的呼叫。 如需部分方法的相關信息,請參閱 部分方法

如需 .NET MAUI 專案中 Platform 資料夾組織的相關信息,請參閱部分類別和方法 如需如何設定多重目標,讓您不必將平臺程序代碼放入 [平臺] 資料夾的子資料夾中的資訊,請參閱設定多目標