Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fadc040
[net11.0] Trimmable element handlers
simonrozsival Mar 11, 2026
b86d97f
Document parameterless ctor requirement for [ElementHandler]
simonrozsival Mar 11, 2026
83ebdd1
Fix IL2068 trim annotation on TryGetRegisteredHandlerType out parameter
simonrozsival Mar 17, 2026
d1de62c
Remove calls to obsolete AddMauiControlsHandlers in device tests
simonrozsival Mar 18, 2026
d526b49
Fix handler resolution failures for TabbedPage and Shell
simonrozsival Mar 31, 2026
70b9228
Fix ILC error: use conditional DI registration for HybridWebViewHandler
simonrozsival Mar 31, 2026
f7b8911
Address trimmable handler review feedback
simonrozsival May 2, 2026
fd758d4
Use conditional attributes for Material3 handlers
simonrozsival May 2, 2026
2671f11
Avoid rooting Material3 handlers from attributes
simonrozsival May 2, 2026
4311c80
Keep ElementHandlerAttribute internal
simonrozsival May 2, 2026
035c9db
Avoid unconditional Material3 handler references
simonrozsival May 2, 2026
0e70ded
Use control-specific nested handler attributes
simonrozsival May 2, 2026
8775157
Resolve HybridWebView handler with conditional attribute
simonrozsival May 2, 2026
a8b9267
Move conditional handler attributes into controls
simonrozsival May 2, 2026
9ab31f6
Add diagnostic output to HandlersHaveAllExpectedContructors test
simonrozsival May 12, 2026
b298933
Address handler resolution review feedback
simonrozsival May 21, 2026
36f3926
Add handler attribute cache regression test
simonrozsival May 21, 2026
3d42479
Add handler attribute coverage
simonrozsival May 21, 2026
b8972bb
Avoid rooting HybridWebViewHandler from attributes
Copilot May 27, 2026
a2cd361
Merge net11.0 into handler attributes branch
Copilot Jun 11, 2026
7536ccd
Use instance-based Controls mapper remapping
simonrozsival Jun 15, 2026
c110c98
Fix cell handler platform guards
simonrozsival Jun 15, 2026
86ee494
Decouple command dependencies from mapper remaps
simonrozsival Jun 15, 2026
ea04ec8
Trigger Controls remaps from handler attributes
simonrozsival Jun 15, 2026
78ed698
Revert "Trigger Controls remaps from handler attributes"
simonrozsival Jun 15, 2026
22a5599
Simplify Controls mapper remap hook
simonrozsival Jun 15, 2026
d669775
Add DI remap regression coverage
simonrozsival Jun 15, 2026
5b238ef
Reduce remap method formatting churn
simonrozsival Jun 15, 2026
a80e926
Keep command dependency setup in mapper partials
simonrozsival Jun 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions docs/design/HandlerResolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,20 @@ Handler Resolution

# Introduction

Handlers are the platform components used to render a cross platform `View` on the screen. Every platform registers a handler against a .NET Maui type.
Handlers are the platform components used to render a cross-platform `View` on the screen. Each view type is associated with a handler that knows how to create and manage the corresponding platform-native control.

## Declaring a Handler with `[ElementHandler]`

Most built-in .NET MAUI views declare their handler using the `[ElementHandler]` attribute directly on the view class:

```csharp
[ElementHandler(typeof(ButtonHandler))]
public partial class Button : View, IButton { ... }
```

This is the primary mechanism for associating views with handlers. It is trimmer-safe and AOT-friendly because the handler type is statically referenced.

The attribute is declared with `Inherited = false`, so each view type must explicitly declare it. However, `MauiHandlersFactory` walks the type's base class hierarchy (`Type.BaseType`) when looking for the attribute, so a base class attribute acts as a fallback for derived types that don't declare their own.

## Registering a Handler in Code

Expand All @@ -14,6 +27,31 @@ builder.ConfigureMauiHandlers(handlers =>
}
```

DI registration should only be used to override an existing `[ElementHandler]` declaration or when the element type is an interface (e.g., `IScrollView`). DI-registered handlers take priority over `[ElementHandler]` attributes when the registered type is assignable from the requested element type.

## Resolution Order

Both `MauiHandlersFactory.GetHandler(Type)` and `MauiHandlersFactory.GetHandlerType(Type)` follow the same resolution order:

1. **Exact DI registration** — checks if a handler was registered for this exact type via `AddHandler`
2. **Assignable DI registration** — uses `RegisteredHandlerServiceTypeSet` to find the best matching concrete or interface registration (e.g., a handler registered for `Button` matches a derived `FancyButton`, and a handler registered for `IScrollView` matches a `ScrollView` instance)
3. **`[ElementHandler]` attribute** — walks the type's base class hierarchy looking for the attribute
4. **`IContentView` fallback** — returns `ContentViewHandler` for any `IContentView` implementation
5. **`GetHandlerType` returns `null`** / **`GetHandler` throws `HandlerNotFoundException`** — if none of the above matched

### Handler Instantiation

How a handler instance is created depends on how it was resolved:

- **DI-registered handlers** (steps 1 & 2): Instantiated through `MauiFactory.GetService()`, which uses `Activator.CreateInstance` on the registered `ImplementationType`, or invokes the `ImplementationFactory` delegate if one was provided.
- **`[ElementHandler]` attribute** (step 3): Instantiated directly via `Activator.CreateInstance` — no DI involvement.
- **Fallback in `ElementExtensions.ToHandler()`**: When `Activator.CreateInstance` fails with a `MissingMethodException` (e.g., the handler requires constructor parameters), `ActivatorUtilities.CreateInstance` is used instead, which supports constructor injection from the DI container.

> **Note:** Handlers registered via `[ElementHandler]` must have a public parameterless constructor.
> They are instantiated with `Activator.CreateInstance()`, not through the DI container.
> The `ActivatorUtilities.CreateInstance()` fallback only applies to DI-registered handlers
> resolved through `ElementExtensions.ToHandler()`.

## Types used in the resolution of Handlers to Views

### `MauiFactory`
Expand All @@ -34,8 +72,7 @@ public class MauiHandlersFactory : MauiFactory, IMauiHandlersFactory
- `MauiFactory` has support for `ctor` resolution but we currently have it disabled in all cases.
- Handlers will currently attempt to instantiate through [Extensions.DependencyInjection.ActivatorUtilities.CreateInstance](https://github.com/dotnet/maui/blob/cc53f0979baf5d6bb8a5d6bf84b64f3cf591c56f/src/Core/src/Platform/ElementExtensions.cs#L34 ) if a default constructor hasn't been created. So the ctor resolution feature of `MauiFactory` probably doesn't have any currently useful purpose.
- `MauiFactory` currently doesn't support Scoped Services which is the main reason why we switched to `Ms.Ext.DI` for our main implementation. .NET MAUI Blazor requires Scoped Services and we've started using Scoped Services as well for multi-window.
- `MauiFactory` retrieves all base types from the requested type and all implemented interfaces. It first iterates over base types and then if nothing is found it loops through the interfaces. The interface behavior currently leads to some odd behavior because everything implements `IView`. This means that if a handler isn't registered then `MauiFactory` just returns a random handler because technically every single handler is registered against a cross platform view that implements`IView`. https://github.com/dotnet/maui/issues/1298
- We should probably remove the interface matching part of `MauiFactory`
- `MauiFactory` retrieves the handler type registered for the requested type. Interface-based registration matching is now handled by `RegisteredHandlerServiceTypeSet`, which finds the most specific matching interface to avoid ambiguity (the old behavior of matching any `IView`-implementing interface has been fixed — see https://github.com/dotnet/maui/issues/1298).

### IMauiHandlersFactory

Expand All @@ -54,7 +91,6 @@ public interface IMauiHandlersFactory : IMauiFactory
Type? GetHandlerType(Type iview);
IElementHandler? GetHandler(Type type);
IElementHandler? GetHandler<T>() where T : IElement;
IMauiHandlersCollection GetCollection();
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ public void Initialize(IServiceProvider services)
}
}

#pragma warning disable CS0618 // Obsolete
BordelessEntryServiceBuilder.HandlersCollection ??= services.GetRequiredService<IMauiHandlersFactory>().GetCollection();
#pragma warning restore CS0618

if (BordelessEntryServiceBuilder.PendingHandlers.Count > 0)
{
Expand Down
23 changes: 22 additions & 1 deletion src/Controls/src/Core/ActivityIndicator/ActivityIndicator.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#nullable disable
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;

namespace Microsoft.Maui.Controls
{
Expand All @@ -13,8 +15,27 @@ namespace Microsoft.Maui.Controls
/// This control gives a visual clue to the user that something is happening, without information about its progress.
/// </remarks>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
#if ANDROID
[ActivityIndicatorHandler]
#else
[ElementHandler(typeof(ActivityIndicatorHandler))]
#endif
public partial class ActivityIndicator : View, IColorElement, IElementConfiguration<ActivityIndicator>, IActivityIndicator
{
#if ANDROID
internal sealed class ActivityIndicatorHandlerAttribute : ElementHandlerAttribute
{
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public override Type GetHandlerType()
{
if (RuntimeFeature.IsMaterial3Enabled)
return typeof(ActivityIndicatorHandler2);

return typeof(ActivityIndicatorHandler);
}
}
#endif

/// <summary>Bindable property for <see cref="IsRunning"/>.</summary>
public static readonly BindableProperty IsRunningProperty = BindableProperty.Create(nameof(IsRunning), typeof(bool), typeof(ActivityIndicator), default(bool));

Expand Down Expand Up @@ -63,4 +84,4 @@ private protected override string GetDebuggerDisplay()
return $"{base.GetDebuggerDisplay()}, {debugText}";
}
}
}
}
9 changes: 8 additions & 1 deletion src/Controls/src/Core/Application/Application.Mapper.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
#nullable disable
using System;
using System.Threading;
using Microsoft.Maui.Controls.Compatibility;

namespace Microsoft.Maui.Controls
{
public partial class Application
{
internal static new void RemapForControls()
static int s_remappedForControls;
internal override void RemapForControls()
{
if (Interlocked.CompareExchange(ref s_remappedForControls, 1, 0) != 0)
return;

base.RemapForControls();

// Adjust the mappings to preserve Controls.Application legacy behaviors
#if ANDROID
// There is also a mapper on Window for this property since this property is relevant at the window level for
Expand Down
1 change: 1 addition & 0 deletions src/Controls/src/Core/Application/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace Microsoft.Maui.Controls
/// <summary>
/// Represents the main application class that provides lifecycle management, resources, and theming.
/// </summary>
[ElementHandler(typeof(ApplicationHandler))]
public partial class Application : Element, IResourcesProvider, IApplicationController, IElementConfiguration<Application>, IVisualTreeElement, IApplication
{
readonly WeakEventManager _weakEventManager = new WeakEventManager();
Expand Down
2 changes: 2 additions & 0 deletions src/Controls/src/Core/Border/Border.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Layouts;
using Microsoft.Maui.Handlers;

namespace Microsoft.Maui.Controls
{
Expand All @@ -17,6 +18,7 @@ namespace Microsoft.Maui.Controls
/// background, shape, padding, and more to create visually rich containers.
/// </remarks>
[ContentProperty(nameof(Content))]
[ElementHandler(typeof(BorderHandler))]
public class Border : View, IContentView, IBorderView, IPaddingElement, ISafeAreaElement, ISafeAreaView2
{
float[]? _strokeDashPattern;
Expand Down
2 changes: 2 additions & 0 deletions src/Controls/src/Core/BoxView/BoxView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
using System;
using System.Runtime.CompilerServices;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls.Handlers;

namespace Microsoft.Maui.Controls
{
/// <summary>
/// A <see cref="View" /> used to draw a solid colored rectangle.
/// </summary>
[ElementHandler(typeof(BoxViewHandler))]
public partial class BoxView : View, IColorElement, ICornerElement, IElementConfiguration<BoxView>, IShapeView, IShape
{
WeakBrushChangedProxy _fillProxy = null;
Expand Down
11 changes: 8 additions & 3 deletions src/Controls/src/Core/Button/Button.Mapper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using Microsoft.Maui.Controls.Compatibility;
using Microsoft.Maui.Controls.Platform;
Expand All @@ -11,9 +11,14 @@ namespace Microsoft.Maui.Controls
public partial class Button
{
// IButton does not include the ContentType property, so we map it here to handle Image Positioning

internal new static void RemapForControls()
static int s_remappedForControls;
internal override void RemapForControls()
{
if (Interlocked.CompareExchange(ref s_remappedForControls, 1, 0) != 0)
return;

base.RemapForControls();

ButtonHandler.Mapper.ReplaceMapping<Button, IButtonHandler>(nameof(ContentLayout), MapContentLayout);
#if IOS
ButtonHandler.Mapper.ReplaceMapping<Button, IButtonHandler>(nameof(Padding), MapPadding);
Expand Down
2 changes: 2 additions & 0 deletions src/Controls/src/Core/Button/Button.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Handlers;

using Microsoft.Maui.Graphics;

Expand All @@ -15,6 +16,7 @@ namespace Microsoft.Maui.Controls
/// A button <see cref="View" /> that reacts to touch events.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
[ElementHandler(typeof(ButtonHandler))]
public partial class Button : View, IFontElement, ITextElement, IBorderElement, IButtonController, IElementConfiguration<Button>, IPaddingElement, IImageController, IViewController, IButtonElement, ICommandElement, IImageElement, IButton, ITextButton, IImageButton
{
const double DefaultSpacing = 10;
Expand Down
5 changes: 5 additions & 0 deletions src/Controls/src/Core/Cells/Cell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ namespace Microsoft.Maui.Controls
{
// Don't add IElementConfiguration<Cell> because it kills performance on UWP structures that use Cells
/// <summary>Provides base class and capabilities for all Microsoft.Maui.Controls cells. Cells are elements meant to be added to <see cref="Microsoft.Maui.Controls.ListView"/> or <see cref="Microsoft.Maui.Controls.TableView"/>.</summary>
#if WINDOWS || ANDROID || IOS || MACCATALYST
#pragma warning disable CS0618 // Type or member is obsolete
[ElementHandler(typeof(Handlers.Compatibility.CellRenderer))]
#pragma warning restore CS0618 // Type or member is obsolete
#endif
public abstract class Cell : Element, ICellController, IFlowDirectionController, IPropertyPropagationController, IVisualController, IWindowController, IVisualTreeElement
{
/// <summary>The default height of cells.</summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Controls/src/Core/Cells/EntryCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ namespace Microsoft.Maui.Controls
{
/// <summary>A <see cref="Microsoft.Maui.Controls.Cell"/> with a label and a single line text entry field.</summary>
[Obsolete("The controls which use EntryCell (ListView and TableView) are obsolete. Please use CollectionView instead.")]
#if WINDOWS || ANDROID || IOS || MACCATALYST
#pragma warning disable CS0618 // Type or member is obsolete
[ElementHandler(typeof(Handlers.Compatibility.EntryCellRenderer))]
#pragma warning restore CS0618 // Type or member is obsolete
#endif
public class EntryCell : Cell, ITextAlignmentElement, IEntryCellController, ITextAlignment
{
/// <summary>Bindable property for <see cref="Text"/>.</summary>
Expand Down
3 changes: 3 additions & 0 deletions src/Controls/src/Core/Cells/ImageCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ namespace Microsoft.Maui.Controls
{
/// <summary>A <see cref="Microsoft.Maui.Controls.TextCell"/> that has an image.</summary>
[Obsolete("The controls which use ImageCell (ListView and TableView) are obsolete. Please use CollectionView instead.")]
#if WINDOWS || ANDROID || IOS || MACCATALYST
[ElementHandler(typeof(Handlers.Compatibility.ImageCellRenderer))]
#endif
public class ImageCell : TextCell
{
/// <summary>Bindable property for <see cref="ImageSource"/>.</summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Controls/src/Core/Cells/SwitchCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ namespace Microsoft.Maui.Controls
{
/// <summary>A <see cref="Microsoft.Maui.Controls.Cell"/> with a label and an on/off switch.</summary>
[Obsolete("The controls which use SwitchCell (ListView and TableView) are obsolete. Please use CollectionView instead.")]
#if WINDOWS || ANDROID || IOS || MACCATALYST
#pragma warning disable CS0618 // Type or member is obsolete
[ElementHandler(typeof(Handlers.Compatibility.SwitchCellRenderer))]
#pragma warning restore CS0618 // Type or member is obsolete
#endif
public class SwitchCell : Cell
{
/// <summary>Bindable property for <see cref="On"/>.</summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Controls/src/Core/Cells/TextCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ namespace Microsoft.Maui.Controls
{
/// <summary>A <see cref="Microsoft.Maui.Controls.Cell"/> with primary <see cref="Microsoft.Maui.Controls.TextCell.Text"/> and <see cref="Microsoft.Maui.Controls.TextCell.Detail"/> text.</summary>
[Obsolete("The controls which use TextCell (ListView and TableView) are obsolete. Please use CollectionView instead.")]
#if WINDOWS || ANDROID || IOS || MACCATALYST
#pragma warning disable CS0618 // Type or member is obsolete
[ElementHandler(typeof(Handlers.Compatibility.TextCellRenderer))]
#pragma warning restore CS0618 // Type or member is obsolete
#endif
public class TextCell : Cell, ICommandElement
{
/// <summary>Bindable property for <see cref="Command"/>.</summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Controls/src/Core/Cells/ViewCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ namespace Microsoft.Maui.Controls
/// <summary>A <see cref="Microsoft.Maui.Controls.Cell"/> containing a developer-defined <see cref="Microsoft.Maui.Controls.View"/>.</summary>
[Obsolete("The controls which use ViewCell (ListView and TableView) are obsolete. Please use CollectionView instead.")]
[ContentProperty("View")]
#if WINDOWS || IOS || MACCATALYST || ANDROID
#pragma warning disable CS0618 // Type or member is obsolete
[ElementHandler(typeof(Handlers.Compatibility.ViewCellRenderer))]
#pragma warning restore CS0618 // Type or member is obsolete
#endif
public class ViewCell : Cell
{
View _view;
Expand Down
11 changes: 7 additions & 4 deletions src/Controls/src/Core/CheckBox/CheckBox.Mapper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Handlers;
Expand All @@ -14,12 +14,15 @@ static CheckBox()
// Register dependency: Command depends on CommandParameter for CanExecute evaluation
// See https://github.com/dotnet/maui/issues/31939
CommandProperty.DependsOn(CommandParameterProperty);
RemapForControls();
}

private new static void RemapForControls()
static int s_remappedForControls;
internal override void RemapForControls()
{
VisualElement.RemapForControls();
if (Interlocked.CompareExchange(ref s_remappedForControls, 1, 0) != 0)
return;

base.RemapForControls();

CheckBoxHandler.Mapper.ReplaceMapping<ICheckBox, ICheckBoxHandler>(nameof(Color), MapColor);
}
Expand Down
1 change: 1 addition & 0 deletions src/Controls/src/Core/CheckBox/CheckBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace Microsoft.Maui.Controls
/// Use the <see cref="IsChecked"/> property to determine or set the state.
/// </remarks>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
[ElementHandler(typeof(CheckBoxHandler))]
public partial class CheckBox : View, IElementConfiguration<CheckBox>, IBorderElement, IColorElement, ICheckBox, ICommandElement
{
readonly Lazy<PlatformConfigurationRegistry<CheckBox>> _platformConfigurationRegistry;
Expand Down
10 changes: 8 additions & 2 deletions src/Controls/src/Core/ContentPage/ContentPage.Mapper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Handlers;
Expand All @@ -9,8 +9,14 @@ namespace Microsoft.Maui.Controls
{
public partial class ContentPage
{
internal new static void RemapForControls()
static int s_remappedForControls;
internal override void RemapForControls()
{
if (Interlocked.CompareExchange(ref s_remappedForControls, 1, 0) != 0)
return;

base.RemapForControls();

PageHandler.Mapper.ReplaceMapping<ContentPage, IPageHandler>(nameof(ContentPage.HideSoftInputOnTapped), MapHideSoftInputOnTapped);
#if IOS
PageHandler.Mapper.ReplaceMapping<ContentPage, IPageHandler>(PlatformConfiguration.iOSSpecific.Page.PrefersHomeIndicatorAutoHiddenProperty.PropertyName, MapPrefersHomeIndicatorAutoHidden);
Expand Down
2 changes: 2 additions & 0 deletions src/Controls/src/Core/ContentView/ContentView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Diagnostics;
using Microsoft.Maui;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Layouts;

namespace Microsoft.Maui.Controls
Expand All @@ -15,6 +16,7 @@ namespace Microsoft.Maui.Controls
/// </remarks>
[ContentProperty("Content")]
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
[ElementHandler(typeof(ContentViewHandler))]
public partial class ContentView : TemplatedView, IContentView, ISafeAreaView2, ISafeAreaElement
{
/// <summary>Bindable property for <see cref="Content"/>.</summary>
Expand Down
9 changes: 8 additions & 1 deletion src/Controls/src/Core/DatePicker/DatePicker.Mapper.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
#nullable disable
using System;
using System.Threading;
using Microsoft.Maui.Controls.Compatibility;

namespace Microsoft.Maui.Controls
{
public partial class DatePicker
{
internal static new void RemapForControls()
static int s_remappedForControls;
internal override void RemapForControls()
{
if (Interlocked.CompareExchange(ref s_remappedForControls, 1, 0) != 0)
return;

base.RemapForControls();

// Adjust the mappings to preserve Controls.DatePicker legacy behaviors
#if IOS
DatePickerHandler.Mapper.ReplaceMapping<DatePicker, IDatePickerHandler>(PlatformConfiguration.iOSSpecific.DatePicker.UpdateModeProperty.PropertyName, MapUpdateMode);
Expand Down
Loading
Loading