Skip to content

Commit dd7ce54

Browse files
fix(navigationview): consume reuse entries + explicit Where (CR feedback)
Address PR #454 review: - Slow-path rebuild now consumes the reuse map entry (Dictionary.Remove) when a container is reused, so duplicate sibling keys (duplicate Tags, or duplicate Content when no Tag is set) fall through to a freshly created container instead of adding the same WinUI item to the collection twice. - Make the snapshot loops' filtering explicit with .Where()/.OfType(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 21a1a4a commit dd7ce54

2 files changed

Lines changed: 15 additions & 14 deletions

File tree

src/Reactor/Core/Reconciler.Update.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1873,15 +1873,13 @@ private void ReconcileNavMenuItems(
18731873
// existing containers by Tag so matches can be reused, then rebuild the
18741874
// collection in the new order. Reused containers retain their IsExpanded.
18751875
var reusable = new global::System.Collections.Generic.Dictionary<string, WinUI.NavigationViewItem>();
1876-
foreach (var item in live)
1877-
if (item is WinUI.NavigationViewItem nvi && nvi.Tag is string tag)
1878-
reusable[tag] = nvi;
1876+
foreach (var nvi in live.OfType<WinUI.NavigationViewItem>().Where(x => x.Tag is string))
1877+
reusable[(string)nvi.Tag] = nvi;
18791878

18801879
var oldByTag = new global::System.Collections.Generic.Dictionary<string, NavigationViewItemData>();
18811880
if (oldData is not null)
1882-
foreach (var d in oldData)
1883-
if (!d.IsHeader)
1884-
oldByTag[d.Tag ?? d.Content] = d;
1881+
foreach (var d in oldData.Where(d => !d.IsHeader))
1882+
oldByTag[d.Tag ?? d.Content] = d;
18851883

18861884
live.Clear();
18871885
foreach (var data in newData)
@@ -1892,8 +1890,11 @@ private void ReconcileNavMenuItems(
18921890
continue;
18931891
}
18941892

1893+
// Consume the reuse entry so duplicate sibling keys (duplicate Tags,
1894+
// or duplicate Content when no Tag is set) fall through to a fresh
1895+
// container rather than adding the same WinUI item to live twice.
18951896
var key = data.Tag ?? data.Content;
1896-
if (reusable.TryGetValue(key, out var nvi))
1897+
if (reusable.Remove(key, out var nvi))
18971898
UpdateNavItemInPlace(nvi, oldByTag.GetValueOrDefault(key), data);
18981899
else
18991900
nvi = CreateNavItem(data);

src/Reactor/Core/V1Protocol/Descriptor/Descriptors/NavigationViewDescriptor.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,13 @@ private static void ReconcileMenuItems(
175175
}
176176

177177
var reusable = new global::System.Collections.Generic.Dictionary<string, WinUI.NavigationViewItem>();
178-
foreach (var item in live)
179-
if (item is WinUI.NavigationViewItem nvi && nvi.Tag is string tag)
180-
reusable[tag] = nvi;
178+
foreach (var nvi in live.OfType<WinUI.NavigationViewItem>().Where(x => x.Tag is string))
179+
reusable[(string)nvi.Tag] = nvi;
181180

182181
var oldByTag = new global::System.Collections.Generic.Dictionary<string, NavigationViewItemData>();
183182
if (oldData is not null)
184-
foreach (var d in oldData)
185-
if (!d.IsHeader)
186-
oldByTag[d.Tag ?? d.Content] = d;
183+
foreach (var d in oldData.Where(d => !d.IsHeader))
184+
oldByTag[d.Tag ?? d.Content] = d;
187185

188186
live.Clear();
189187
foreach (var data in newData)
@@ -194,8 +192,10 @@ private static void ReconcileMenuItems(
194192
continue;
195193
}
196194

195+
// Consume the reuse entry so duplicate sibling keys fall through to a
196+
// fresh container rather than adding the same WinUI item to live twice.
197197
var key = data.Tag ?? data.Content;
198-
if (reusable.TryGetValue(key, out var nvi))
198+
if (reusable.Remove(key, out var nvi))
199199
UpdateNavItemInPlace(nvi, oldByTag.GetValueOrDefault(key), data);
200200
else
201201
nvi = CreateNavItem(data);

0 commit comments

Comments
 (0)