Skip to content

Commit 4bfd8d7

Browse files
albyrock87MartyIX
authored andcommitted
Skip useless handler mappings calls while connecting (#27259)
* Skip useless initial handler mappings calls * Apply suggestions from code review, fixes check on Maximum* * Windows: automation properties are extremely costly * Fix broken UI tests * Workaround Windows broken tests --------- Co-authored-by: MartyIX <203266+MartyIX@users.noreply.github.com>
1 parent 80f75d2 commit 4bfd8d7

14 files changed

+398
-92
lines changed

src/Controls/src/Core/Element/Element.Windows.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ public partial class Element
99
{
1010
public static void MapAutomationPropertiesIsInAccessibleTree(IElementHandler handler, Element element)
1111
{
12+
if (handler.IsConnectingHandler() && element.GetValue(AutomationProperties.IsInAccessibleTreeProperty) is null)
13+
return;
14+
1215
Platform.AccessibilityExtensions.SetAutomationPropertiesAccessibilityView(
1316
handler.PlatformView as Microsoft.UI.Xaml.FrameworkElement, element);
1417
}

src/Controls/src/Core/Label/Label.Mapper.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public partial class Label
2121

2222
// these are really a single property
2323
LabelHandler.Mapper.ReplaceMapping<Label, ILabelHandler>(nameof(Text), MapText);
24-
LabelHandler.Mapper.ReplaceMapping<Label, ILabelHandler>(nameof(FormattedText), MapText);
24+
LabelHandler.Mapper.ReplaceMapping<Label, ILabelHandler>(nameof(FormattedText), MapFormattedText);
2525

2626
LabelHandler.Mapper.ReplaceMapping<Label, ILabelHandler>(nameof(LineBreakMode), MapLineBreakMode);
2727
LabelHandler.Mapper.ReplaceMapping<Label, ILabelHandler>(nameof(MaxLines), MapMaxLines);
@@ -54,8 +54,17 @@ public static void MapTextType(ILabelHandler handler, Label label) =>
5454
MapTextOrFormattedText(handler, label);
5555
static void MapTextTransform(ILabelHandler handler, Label label) =>
5656
MapTextOrFormattedText(handler, label);
57+
static void MapFormattedText(ILabelHandler handler, Label label)
58+
{
59+
if (label.IsConnectingHandler()) return;
60+
61+
MapText(handler, label);
62+
}
63+
5764
static void MapTextOrFormattedText(ILabelHandler handler, Label label)
5865
{
66+
if (label.IsConnectingHandler()) return;
67+
5968
if (label.HasFormattedTextSpans)
6069
handler.UpdateValue(nameof(FormattedText));
6170
else
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace Microsoft.Maui
5+
{
6+
internal static class InternalElementExtensions
7+
{
8+
/// <summary>
9+
/// The handler is connecting for the first time to the element and mapping all properties.
10+
/// </summary>
11+
/// <param name="element"></param>
12+
/// <returns></returns>
13+
internal static bool IsMappingProperties(this IElement element) =>
14+
(element.Handler as IElementHandlerStateExhibitor)?.State.HasFlag(ElementHandlerState.MappingProperties) ?? false;
15+
16+
/// <summary>
17+
/// Indicates whether the handler is connecting for the first time to the element and mapping all properties.
18+
/// </summary>
19+
/// <param name="element"></param>
20+
/// <returns></returns>
21+
internal static bool IsConnectingHandler(this IElement element) =>
22+
(element.Handler as IElementHandlerStateExhibitor)?.State.HasFlag(ElementHandlerState.Connecting) ?? false;
23+
24+
/// <summary>
25+
/// Indicates whether the connected handler is now connecting to a new element and updating properties.
26+
/// </summary>
27+
/// <param name="element"></param>
28+
/// <returns></returns>
29+
internal static bool IsReconnectingHandler(this IElement element) =>
30+
(element.Handler as IElementHandlerStateExhibitor)?.State.HasFlag(ElementHandlerState.Reconnecting) ?? false;
31+
}
32+
}

src/Core/src/Handlers/Border/BorderHandler.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using PlatformView = System.Object;
1212
#endif
1313

14+
using System.Runtime.CompilerServices;
1415
using Microsoft.Maui.Graphics;
1516

1617
namespace Microsoft.Maui.Handlers
@@ -86,13 +87,29 @@ public static void MapBackground(IBorderHandler handler, IBorderView border)
8687
((PlatformView?)handler.PlatformView)?.UpdateBackground(border);
8788
}
8889

90+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
91+
private static bool ShouldSkipStrokeMappings(IBorderHandler handler) {
92+
#if __IOS__ || MACCATALYST || ANDROID
93+
// During the initial connection, the `MapBackground` takes care of updating the stroke properties
94+
// so we can skip the stroke mappings to avoid repetitive and useless updates.
95+
return handler.IsConnectingHandler();
96+
#else
97+
return false;
98+
#endif
99+
}
100+
89101
/// <summary>
90102
/// Maps the abstract <see cref="IBorderStroke.Shape"/> property to the platform-specific implementations.
91103
/// </summary>
92104
/// <param name="handler">The associated handler.</param>
93105
/// <param name="border">The associated <see cref="IBorderView"/> instance.</param>
94106
public static void MapStrokeShape(IBorderHandler handler, IBorderView border)
95107
{
108+
if (ShouldSkipStrokeMappings(handler))
109+
{
110+
return;
111+
}
112+
96113
((PlatformView?)handler.PlatformView)?.UpdateStrokeShape(border);
97114
MapBackground(handler, border);
98115
}
@@ -104,6 +121,11 @@ public static void MapStrokeShape(IBorderHandler handler, IBorderView border)
104121
/// <param name="border">The associated <see cref="IBorderView"/> instance.</param>
105122
public static void MapStroke(IBorderHandler handler, IBorderView border)
106123
{
124+
if (ShouldSkipStrokeMappings(handler))
125+
{
126+
return;
127+
}
128+
107129
((PlatformView?)handler.PlatformView)?.UpdateStroke(border);
108130
MapBackground(handler, border);
109131
}
@@ -115,6 +137,11 @@ public static void MapStroke(IBorderHandler handler, IBorderView border)
115137
/// <param name="border">The associated <see cref="IBorderView"/> instance.</param>
116138
public static void MapStrokeThickness(IBorderHandler handler, IBorderView border)
117139
{
140+
if (ShouldSkipStrokeMappings(handler))
141+
{
142+
return;
143+
}
144+
118145
((PlatformView?)handler.PlatformView)?.UpdateStrokeThickness(border);
119146
MapBackground(handler, border);
120147
}
@@ -126,6 +153,11 @@ public static void MapStrokeThickness(IBorderHandler handler, IBorderView border
126153
/// <param name="border">The associated <see cref="IBorderView"/> instance.</param>
127154
public static void MapStrokeLineCap(IBorderHandler handler, IBorderView border)
128155
{
156+
if (ShouldSkipStrokeMappings(handler))
157+
{
158+
return;
159+
}
160+
129161
((PlatformView?)handler.PlatformView)?.UpdateStrokeLineCap(border);
130162
}
131163

@@ -136,6 +168,11 @@ public static void MapStrokeLineCap(IBorderHandler handler, IBorderView border)
136168
/// <param name="border">The associated <see cref="IBorderView"/> instance.</param>
137169
public static void MapStrokeLineJoin(IBorderHandler handler, IBorderView border)
138170
{
171+
if (ShouldSkipStrokeMappings(handler))
172+
{
173+
return;
174+
}
175+
139176
((PlatformView?)handler.PlatformView)?.UpdateStrokeLineJoin(border);
140177
}
141178

@@ -146,6 +183,11 @@ public static void MapStrokeLineJoin(IBorderHandler handler, IBorderView border)
146183
/// <param name="border">The associated <see cref="IBorderView"/> instance.</param>
147184
public static void MapStrokeDashPattern(IBorderHandler handler, IBorderView border)
148185
{
186+
if (ShouldSkipStrokeMappings(handler))
187+
{
188+
return;
189+
}
190+
149191
((PlatformView?)handler.PlatformView)?.UpdateStrokeDashPattern(border);
150192
}
151193

@@ -156,6 +198,11 @@ public static void MapStrokeDashPattern(IBorderHandler handler, IBorderView bord
156198
/// <param name="border">The associated <see cref="IBorderView"/> instance.</param>
157199
public static void MapStrokeDashOffset(IBorderHandler handler, IBorderView border)
158200
{
201+
if (ShouldSkipStrokeMappings(handler))
202+
{
203+
return;
204+
}
205+
159206
((PlatformView?)handler.PlatformView)?.UpdateStrokeDashOffset(border);
160207
}
161208

@@ -166,6 +213,11 @@ public static void MapStrokeDashOffset(IBorderHandler handler, IBorderView borde
166213
/// <param name="border">The associated <see cref="IBorderView"/> instance.</param>
167214
public static void MapStrokeMiterLimit(IBorderHandler handler, IBorderView border)
168215
{
216+
if (ShouldSkipStrokeMappings(handler))
217+
{
218+
return;
219+
}
220+
169221
((PlatformView?)handler.PlatformView)?.UpdateStrokeMiterLimit(border);
170222
}
171223

src/Core/src/Handlers/Element/ElementHandler.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Microsoft.Maui.Handlers
44
{
5-
public abstract partial class ElementHandler : IElementHandler
5+
public abstract partial class ElementHandler : IElementHandler, IElementHandlerStateExhibitor
66
{
77
public static IPropertyMapper<IElement, IElementHandler> ElementMapper = new PropertyMapper<IElement, IElementHandler>()
88
{
@@ -15,6 +15,9 @@ public abstract partial class ElementHandler : IElementHandler
1515
internal readonly IPropertyMapper _defaultMapper;
1616
internal readonly CommandMapper? _commandMapper;
1717
internal IPropertyMapper _mapper;
18+
ElementHandlerState _handlerState;
19+
20+
ElementHandlerState IElementHandlerStateExhibitor.State => _handlerState;
1821

1922
protected ElementHandler(IPropertyMapper mapper, CommandMapper? commandMapper = null)
2023
{
@@ -40,24 +43,38 @@ public virtual void SetVirtualView(IElement view)
4043
_ = view ?? throw new ArgumentNullException(nameof(view));
4144

4245
if (VirtualView == view)
46+
{
4347
return;
48+
}
4449

4550
var oldVirtualView = VirtualView;
4651

4752
bool setupPlatformView = oldVirtualView == null;
4853

4954
VirtualView = view;
50-
PlatformView ??= CreatePlatformElement();
55+
if (PlatformView is null)
56+
{
57+
_handlerState = ElementHandlerState.Connecting;
58+
PlatformView = CreatePlatformElement();
59+
}
60+
else
61+
{
62+
_handlerState = ElementHandlerState.Reconnecting;
63+
}
5164

5265
if (VirtualView.Handler != this)
66+
{
5367
VirtualView.Handler = this;
68+
}
5469

5570
// We set the previous virtual view to null after setting it on the incoming virtual view.
5671
// This makes it easier for the incoming virtual view to have influence
5772
// on how the exchange of handlers happens.
5873
// We will just set the handler to null ourselves as a last resort cleanup
5974
if (oldVirtualView?.Handler != null)
75+
{
6076
oldVirtualView.Handler = null;
77+
}
6178

6279
if (setupPlatformView)
6380
{
@@ -77,6 +94,8 @@ public virtual void SetVirtualView(IElement view)
7794
}
7895

7996
_mapper.UpdateProperties(this, VirtualView);
97+
98+
_handlerState = ElementHandlerState.Connected;
8099
}
81100

82101
public virtual void UpdateValue(string property)
@@ -129,6 +148,8 @@ void IElementHandler.DisconnectHandler()
129148
PlatformView = null;
130149
DisconnectHandler(oldPlatformView);
131150
}
151+
152+
_handlerState = ElementHandlerState.Disconnected;
132153
}
133154
}
134155
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
3+
namespace Microsoft.Maui
4+
{
5+
/// <summary>
6+
/// Exposes the state of an element handler.
7+
/// </summary>
8+
[Flags]
9+
internal enum ElementHandlerState : byte
10+
{
11+
/// <summary>
12+
/// The handler is not connected to an element.
13+
/// </summary>
14+
Disconnected = 0x0,
15+
/// <summary>
16+
/// The handler is mapping all properties to the element.
17+
/// </summary>
18+
MappingProperties = 0x1,
19+
/// <summary>
20+
/// The handler is connecting for the first time to the element and mapping all properties.
21+
/// </summary>
22+
Connecting = MappingProperties | 0x2,
23+
/// <summary>
24+
/// The connected handler is now connecting to a new element and updating properties.
25+
/// </summary>
26+
Reconnecting = MappingProperties | 0x4,
27+
/// <summary>
28+
/// The handler is connected to an element.
29+
/// </summary>
30+
Connected = 0x8
31+
}
32+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace Microsoft.Maui
2+
{
3+
/// <summary>
4+
/// Exposes the state of an element handler.
5+
/// </summary>
6+
/// <remarks>
7+
/// To be migrated to a public API.
8+
/// </remarks>
9+
internal interface IElementHandlerStateExhibitor
10+
{
11+
/// <summary>
12+
/// Gets the state of the element handler.
13+
/// </summary>
14+
ElementHandlerState State { get; }
15+
}
16+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace Microsoft.Maui
5+
{
6+
internal static class InternalElementHandlerExtensions
7+
{
8+
/// <summary>
9+
/// The handler is connecting for the first time to the element and mapping all properties.
10+
/// </summary>
11+
/// <param name="handler"></param>
12+
/// <returns></returns>
13+
internal static bool IsMappingProperties(this IElementHandler handler) =>
14+
(handler as IElementHandlerStateExhibitor)?.State.HasFlag(ElementHandlerState.MappingProperties) ?? false;
15+
16+
/// <summary>
17+
/// Indicates whether the handler is connecting for the first time to the element and mapping all properties.
18+
/// </summary>
19+
/// <param name="handler"></param>
20+
/// <returns></returns>
21+
internal static bool IsConnectingHandler(this IElementHandler handler) =>
22+
(handler as IElementHandlerStateExhibitor)?.State.HasFlag(ElementHandlerState.Connecting) ?? false;
23+
24+
/// <summary>
25+
/// Indicates whether the connected handler is now connecting to a new element and updating properties.
26+
/// </summary>
27+
/// <param name="handler"></param>
28+
/// <returns></returns>
29+
internal static bool IsReconnectingHandler(this IElementHandler handler) =>
30+
(handler as IElementHandlerStateExhibitor)?.State.HasFlag(ElementHandlerState.Reconnecting) ?? false;
31+
}
32+
}

0 commit comments

Comments
 (0)