Skip to content

Commit 6ee08fc

Browse files
committed
Improve property mapper performance
1 parent f126268 commit 6ee08fc

File tree

8 files changed

+336
-66
lines changed

8 files changed

+336
-66
lines changed

src/Core/src/Handlers/FlyoutView/FlyoutViewHandler.Android.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,6 @@ void UpdateDetail()
118118
void UpdateFlyout()
119119
{
120120
_ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class.");
121-
122-
// Once this issue has been taken care of
123-
// https://github.com/dotnet/maui/issues/8456
124-
// we can remove this code
125-
if (VirtualView.Flyout.Handler?.MauiContext != null &&
126-
VirtualView.Flyout.Handler.MauiContext != MauiContext)
127-
{
128-
VirtualView.Flyout.Handler.DisconnectHandler();
129-
}
130-
131121
_ = VirtualView.Flyout.ToPlatform(MauiContext);
132122

133123
var newFlyoutView = VirtualView.Flyout.ToPlatform();

src/Core/src/Handlers/FlyoutView/FlyoutViewHandler.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,19 @@ namespace Microsoft.Maui.Handlers
1616
{
1717
public partial class FlyoutViewHandler : IFlyoutViewHandler
1818
{
19-
public static IPropertyMapper<IFlyoutView, IFlyoutViewHandler> Mapper = new PropertyMapper<IFlyoutView, IFlyoutViewHandler>(ViewHandler.ViewMapper)
19+
// Like IViewHandler.ContainerView, those properties should be set with priority because other mappers depend on them (like IToolbarElement.Toolbar).
20+
// So we have a separate mapper for them.
21+
private static readonly IPropertyMapper<IFlyoutView, IFlyoutViewHandler> FlyoutLayoutMapper = new PropertyMapper<IFlyoutView, IFlyoutViewHandler>()
2022
{
2123
#if ANDROID || WINDOWS || TIZEN
2224
[nameof(IFlyoutView.Flyout)] = MapFlyout,
2325
[nameof(IFlyoutView.Detail)] = MapDetail,
26+
#endif
27+
};
28+
29+
public static IPropertyMapper<IFlyoutView, IFlyoutViewHandler> Mapper = new PropertyMapper<IFlyoutView, IFlyoutViewHandler>(ViewHandler.ViewMapper, FlyoutLayoutMapper)
30+
{
31+
#if ANDROID || WINDOWS || TIZEN
2432
[nameof(IFlyoutView.IsPresented)] = MapIsPresented,
2533
[nameof(IFlyoutView.FlyoutBehavior)] = MapFlyoutBehavior,
2634
[nameof(IFlyoutView.FlyoutWidth)] = MapFlyoutWidth,
Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33

44
namespace Microsoft.Maui.Handlers
55
{
66
#if ANDROID
7-
class AndroidBatchPropertyMapper<TVirtualView, TViewHandler> : PropertyMapper<TVirtualView, TViewHandler>
8-
where TVirtualView : IElement
9-
where TViewHandler : IElementHandler
7+
class AndroidBatchPropertyMapper
108
{
9+
public const string InitializeBatchedPropertiesKey = "_InitializeBatchedProperties";
10+
1111
// During mass property updates, this list of properties will be skipped
1212
public static HashSet<string> SkipList = new(StringComparer.Ordinal)
1313
{
@@ -27,30 +27,48 @@ class AndroidBatchPropertyMapper<TVirtualView, TViewHandler> : PropertyMapper<TV
2727
nameof(IView.AnchorX),
2828
nameof(IView.AnchorY),
2929
};
30+
}
31+
32+
class AndroidBatchPropertyMapper<TVirtualView, TViewHandler> : PropertyMapper<TVirtualView, TViewHandler>
33+
where TVirtualView : IElement
34+
where TViewHandler : IElementHandler
35+
{
3036

3137
public AndroidBatchPropertyMapper(params IPropertyMapper[] chained) : base(chained) { }
3238

3339
public override IEnumerable<string> GetKeys()
3440
{
35-
foreach (var key in _mapper.Keys)
41+
var skipList = AndroidBatchPropertyMapper.SkipList;
42+
43+
// We want to retain the initial order of the keys to avoid race conditions
44+
// when a property mapping is overridden by a new instance of property mapper.
45+
// As an example, the container view mapper should always run first.
46+
// Siblings mapper should not have keys intersection.
47+
var chainedPropertyMappers = Chained;
48+
if (chainedPropertyMappers is not null)
3649
{
37-
// When reporting the key list for mass updates up the chain, ignore properties in SkipList.
38-
// These will be handled by ViewHandler.SetVirtualView() instead.
39-
if (SkipList.Contains(key))
50+
for (int i = chainedPropertyMappers.Length - 1; i >= 0; i--)
4051
{
41-
continue;
52+
foreach (var key in chainedPropertyMappers[i].GetKeys())
53+
{
54+
yield return key;
55+
}
4256
}
43-
44-
yield return key;
4557
}
4658

47-
if (Chained is not null)
59+
// Enqueue keys from this mapper.
60+
foreach (var mapper in _mapper)
4861
{
49-
foreach (var chain in Chained)
50-
foreach (var key in chain.GetKeys())
51-
yield return key;
62+
var key = mapper.Key;
63+
64+
// When reporting the key list for mass updates up the chain, ignore properties in SkipList.
65+
// These will be handled by ViewHandler.SetVirtualView() instead.
66+
if (!skipList.Contains(key))
67+
{
68+
yield return key;
69+
}
5270
}
5371
}
5472
}
5573
#endif
56-
}
74+
}

src/Core/src/Handlers/View/ViewHandler.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,17 @@ public abstract partial class ViewHandler : ElementHandler, IViewHandler
2626
public static IPropertyMapper<IView, IViewHandler> ViewMapper =
2727
#if ANDROID
2828
// Use a custom mapper for Android which knows how to batch the initial property sets
29-
new AndroidBatchPropertyMapper<IView, IViewHandler>(ElementMapper)
29+
new AndroidBatchPropertyMapper<IView, IViewHandler>(ElementHandler.ElementMapper)
3030
#else
3131
new PropertyMapper<IView, IViewHandler>(ElementHandler.ElementMapper)
3232
#endif
3333
{
34+
// This property is a special one and needs to be set before other properties.
35+
[nameof(IViewHandler.ContainerView)] = MapContainerView,
36+
#if ANDROID
37+
[AndroidBatchPropertyMapper.InitializeBatchedPropertiesKey] = MapInitializeBatchedProperties,
38+
#endif
39+
3440
[nameof(IView.AutomationId)] = MapAutomationId,
3541
[nameof(IView.Clip)] = MapClip,
3642
[nameof(IView.Shadow)] = MapShadow,
@@ -56,7 +62,6 @@ public abstract partial class ViewHandler : ElementHandler, IViewHandler
5662
[nameof(IView.RotationY)] = MapRotationY,
5763
[nameof(IView.AnchorX)] = MapAnchorX,
5864
[nameof(IView.AnchorY)] = MapAnchorY,
59-
[nameof(IViewHandler.ContainerView)] = MapContainerView,
6065
#pragma warning disable CS0618 // Type or member is obsolete
6166
[nameof(IBorder.Border)] = MapBorderView,
6267
#pragma warning restore CS0618 // Type or member is obsolete
@@ -68,10 +73,6 @@ public abstract partial class ViewHandler : ElementHandler, IViewHandler
6873
#if WINDOWS || MACCATALYST
6974
[nameof(IContextFlyoutElement.ContextFlyout)] = MapContextFlyout,
7075
#endif
71-
72-
#if ANDROID
73-
["_InitializeBatchedProperties"] = MapInitializeBatchedProperties
74-
#endif
7576
};
7677

7778
/// <summary>

src/Core/src/Platform/iOS/ContainerViewController.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ public override void LoadView()
6868

6969
void LoadPlatformView(IElement view)
7070
{
71-
currentPlatformView = _pendingLoadedView ?? CreatePlatformView(view);
71+
var platformView = _pendingLoadedView ?? CreatePlatformView(view);
72+
platformView = platformView.Superview as WrapperView ?? platformView;
73+
74+
currentPlatformView = platformView;
7275
_pendingLoadedView = null;
7376

7477
View!.AddSubview(currentPlatformView);

0 commit comments

Comments
 (0)