Skip to content

Commit bbc8bfe

Browse files
oysteinkrogClaude (Initial Force WPF Bot)claude
authored
fix(AdornerLayer): remove dead _layoutDirty infrastructure (CS0414) (#7)
Follow-up to f703b12. The previous PR removed the unsafe _layoutDirty gate from OnLayoutUpdated but kept the field and its surrounding plumbing (Subscribe/Unsubscribe/OnAdornedElementLayoutUpdated + _subscribedElements HashSet) to keep that diff minimal and reversible. CI builds with TreatWarningsAsErrors=true, so the now-unused field trips CS0414 ("The field 'AdornerLayer._layoutDirty' is assigned but its value is never used") and blocks the if.81 publish. This is the planned cleanup. Removes: - _layoutDirty field (read-eliminated by the prior commit) - _subscribedElements HashSet - OnAdornedElementLayoutUpdated handler - SubscribeToElementLayout / UnsubscribeFromElementLayout helpers - All call sites that armed _layoutDirty (Add/Remove/Update/SetAdornerZOrder) - Per-element LayoutUpdated subscription from Add/Remove Net behavior unchanged from f703b12 - the gate was already removed, so these write-only assignments and unread subscriptions had no observable effect. Co-authored-by: Claude (Initial Force WPF Bot) <wpf-bot@initialforce.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent f703b12 commit bbc8bfe

1 file changed

Lines changed: 1 addition & 72 deletions

File tree

  • src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/AdornerLayer.cs

Lines changed: 1 addition & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,6 @@ public void Remove(Adorner adorner)
232232
RemoveAdornerInfo(_zOrderMap, adorner, adornerInfo.ZOrder);
233233
_children.Remove(adorner);
234234
RemoveLogicalChild(adorner);
235-
236-
// If no more adorners remain for this element, unsubscribe from its LayoutUpdated
237-
// to break the AdornerLayer/UIElement retention cycle.
238-
if (ElementMap[adorner.AdornedElement] == null)
239-
{
240-
UnsubscribeFromElementLayout(adorner.AdornedElement);
241-
}
242-
_layoutDirty = true;
243235
}
244236

245237
/// <summary>
@@ -261,7 +253,6 @@ public void Update()
261253
}
262254
}
263255

264-
_layoutDirty = true;
265256
UpdateAdorner(null);
266257
}
267258

@@ -285,7 +276,6 @@ public void Update(UIElement element)
285276
InvalidateAdorner((AdornerInfo)adornerInfos[i++]);
286277
}
287278

288-
_layoutDirty = true;
289279
UpdateAdorner(element);
290280
}
291281

@@ -612,15 +602,10 @@ internal void Add(Adorner adorner, int zOrder)
612602

613603
AddAdornerInfo(ElementMap, adornerInfo, adorner.AdornedElement);
614604

615-
// Subscribe to the adorned element's LayoutUpdated so we can arm _layoutDirty
616-
// only when something actually changes, rather than on every layer-level fire.
617-
SubscribeToElementLayout(adorner.AdornedElement);
618-
619605
AddAdornerToVisualTree(adornerInfo, zOrder);
620606

621607
AddLogicalChild(adorner);
622608

623-
_layoutDirty = true;
624609
UpdateAdorner(adorner.AdornedElement);
625610
}
626611

@@ -639,18 +624,6 @@ internal static void InvalidateAdorner(AdornerInfo adornerInfo)
639624
adornerInfo.SimpleTransform = default;
640625
}
641626

642-
// TODO: regression tests for OnLayoutUpdated fast path (no DRT harness available in fork):
643-
// 1. EmptyAdornerLayer_OnLayoutUpdated_DoesNotCallUpdateAdorner
644-
// Create an AdornerLayer, call OnLayoutUpdated — verify UpdateAdorner was NOT called
645-
// (mock or subclass override) and _layoutDirty ends up false.
646-
// 2. AdornerLayer_AddAdornerAfterIdle_TriggersUpdateAdorner
647-
// Create empty layer, fire OnLayoutUpdated (empty fast-path, _layoutDirty→false),
648-
// Add() an adorner, fire OnLayoutUpdated again — verify UpdateAdorner IS called.
649-
// 3. AdornerLayer_AddRemoveDuringLayoutUpdated_NoStaleDirtyFlag
650-
// Simulate Add() inside a LayoutUpdated handler that fires concurrently with the
651-
// layer's own handler; confirm that by the time the next pass fires, the adorner
652-
// is walked (ElementMap.Count > 0 path) and _layoutDirty is not stranded false.
653-
654627
/// <summary>
655628
/// OnLayoutUpdated event handler
656629
/// </summary>
@@ -670,7 +643,7 @@ internal void OnLayoutUpdated(object sender, EventArgs args)
670643
return;
671644
}
672645

673-
// Non-empty layer: always run UpdateAdorner. A cached _layoutDirty flag
646+
// Non-empty layer: always run UpdateAdorner. A cached dirty flag
674647
// cannot comprehensively observe every coordinate-space change that
675648
// requires re-walking adorners (ancestor RenderTransform changes,
676649
// ContentPresenter re-templating that swaps the subtree between an
@@ -682,40 +655,6 @@ internal void OnLayoutUpdated(object sender, EventArgs args)
682655
UpdateAdorner(null);
683656
}
684657

685-
/// <summary>
686-
/// LayoutUpdated handler subscribed per adorned element.
687-
/// Arms the layer-level dirty bit so the next OnLayoutUpdated fires UpdateAdorner.
688-
/// </summary>
689-
private void OnAdornedElementLayoutUpdated(object sender, EventArgs e)
690-
{
691-
_layoutDirty = true;
692-
}
693-
694-
/// <summary>
695-
/// Subscribe to LayoutUpdated on the given element exactly once (tracked via
696-
/// _subscribedElements). Called when the first adorner is registered for an element.
697-
/// </summary>
698-
private void SubscribeToElementLayout(UIElement element)
699-
{
700-
_subscribedElements ??= new HashSet<UIElement>();
701-
if (_subscribedElements.Add(element))
702-
{
703-
element.LayoutUpdated += OnAdornedElementLayoutUpdated;
704-
}
705-
}
706-
707-
/// <summary>
708-
/// Unsubscribe from LayoutUpdated on the given element.
709-
/// Called when the last adorner for an element is removed.
710-
/// </summary>
711-
private void UnsubscribeFromElementLayout(UIElement element)
712-
{
713-
if (_subscribedElements != null && _subscribedElements.Remove(element))
714-
{
715-
element.LayoutUpdated -= OnAdornedElementLayoutUpdated;
716-
}
717-
}
718-
719658
/// <summary>
720659
/// Set the zOrder on the given adorner.
721660
/// </summary>
@@ -739,7 +678,6 @@ internal void SetAdornerZOrder(Adorner adorner, int zOrder)
739678
adornerInfo.ZOrder = zOrder;
740679
AddAdornerToVisualTree(adornerInfo, zOrder);
741680
InvalidateAdorner(adornerInfo);
742-
_layoutDirty = true;
743681
UpdateAdorner(adorner.AdornedElement);
744682
}
745683

@@ -1264,15 +1202,6 @@ private GeneralTransform GetProposedTransform(Adorner adorner, GeneralTransform
12641202
// re-enter this same AdornerLayer cannot clobber an outer pass's snapshot.
12651203
private object[] _zOrderValuesSnapshotBuffer;
12661204

1267-
// Dirty-bit gate for OnLayoutUpdated. Set on adorner add/remove and on any
1268-
// per-element LayoutUpdated event; cleared at the top of UpdateAdorner so a
1269-
// re-entrant fire during the walk re-arms for the next pass.
1270-
// Starts true so the very first layout pass is never skipped.
1271-
private bool _layoutDirty = true;
1272-
// Set of elements for which we have a LayoutUpdated subscription.
1273-
// Maintained to ensure subscribe/unsubscribe are balanced.
1274-
private HashSet<UIElement> _subscribedElements;
1275-
12761205
#endregion Private Fields
12771206
}
12781207
}

0 commit comments

Comments
 (0)