Browse Source

CompareView "Show Identical" now actually filters rows

The ShowIdentical toggle was wired model-side
(CompareTabPageModel.OnShowIdenticalChanged → RootEntry.EnsureChildrenFiltered)
and view-side (Rebind on PropertyChanged), and the cascade was
correctly setting IsHidden via ComparisonEntryTreeNode.Filter. But the
HierarchicalModel's ChildrenSelector and SetRoots passed node.Children
through unfiltered — so IsHidden flags had no effect on what the
DataGrid actually rendered. Toggling the checkbox looked like a no-op.

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
a576d20cfb
  1. 52
      ILSpy.Tests/Compare/CompareViewRenderTests.cs
  2. 14
      ILSpy/Views/CompareView.axaml.cs

52
ILSpy.Tests/Compare/CompareViewRenderTests.cs

@ -106,4 +106,56 @@ public class CompareViewRenderTests
() => grid.GetVisualDescendants().OfType<DataGridRow>().Any(), () => grid.GetVisualDescendants().OfType<DataGridRow>().Any(),
description: "the diff grid must render at least one row once the merged tree is bound"); description: "the diff grid must render at least one row once the merged tree is bound");
} }
[AvaloniaTest]
public async Task CompareView_ShowIdentical_Toggle_Changes_Visible_Row_Set()
{
// Comparing an assembly with itself yields only identical rows. With ShowIdentical=false
// (default) every row should be hidden; flipping ShowIdentical=true must surface them.
// Regression: the bug was that the HierarchicalModel's ChildrenSelector returned
// node.Children unfiltered, so IsHidden flags set by ApplyFilterToChild had no effect
// on what the DataGrid actually rendered.
var window = AppComposition.Current.GetExport<MainWindow>();
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
var assembly = vm.AssemblyTreeModel.AssemblyList!.GetAssemblies()
.First(a => a.IsLoadedAsValidAssembly);
// Construct the compare model directly with the same assembly on both sides so every
// entry collapses to DiffKind.None. The View renders via the standard OpenNewTab path.
var page = new CompareTabPageModel(assembly, assembly);
vm.DockWorkspace.OpenNewTab(page);
var view = await window.WaitForComponent<CompareView>();
var grid = await view.WaitForComponent<DataGrid>();
await Waiters.WaitForAsync(() => grid.HierarchicalModel != null);
global::Avalonia.Threading.Dispatcher.UIThread.RunJobs();
// Baseline: ShowIdentical=false. All rows are identical-only, so the visible row count
// must be zero (every entry filters to Hidden).
page.ShowIdentical.Should().BeFalse("baseline state");
var rowsBeforeToggle = grid.GetVisualDescendants().OfType<DataGridRow>().Count();
rowsBeforeToggle.Should().Be(0,
"with ShowIdentical=false and only-identical content, no rows must be visible");
// Flip ShowIdentical=true and re-evaluate.
page.ShowIdentical = true;
global::Avalonia.Threading.Dispatcher.UIThread.RunJobs();
await Waiters.WaitForAsync(
() => grid.GetVisualDescendants().OfType<DataGridRow>().Any(),
description: "flipping ShowIdentical=true must surface previously-hidden identical rows");
var rowsAfterToggle = grid.GetVisualDescendants().OfType<DataGridRow>().Count();
rowsAfterToggle.Should().BeGreaterThan(0,
"with ShowIdentical=true, identical rows must surface in the grid");
// Flip back and verify it returns to the hidden state.
page.ShowIdentical = false;
global::Avalonia.Threading.Dispatcher.UIThread.RunJobs();
await Waiters.WaitForAsync(
() => grid.GetVisualDescendants().OfType<DataGridRow>().Count() == 0,
description: "flipping ShowIdentical=false again must re-hide all identical rows");
}
} }

14
ILSpy/Views/CompareView.axaml.cs

@ -18,6 +18,7 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.DataGridHierarchical; using Avalonia.Controls.DataGridHierarchical;
@ -80,15 +81,24 @@ namespace ILSpy.Compare
ilspy.EnsureChildrenFiltered(); ilspy.EnsureChildrenFiltered();
else else
node.EnsureLazyChildren(); node.EnsureLazyChildren();
return node.Children; return FilterHidden(node.Children);
}, },
IsLeafSelector = node => !node.ShowExpander, IsLeafSelector = node => !node.ShowExpander,
VirtualizeChildren = false, VirtualizeChildren = false,
IsExpandedPropertyPath = nameof(SharpTreeNode.IsExpanded), IsExpandedPropertyPath = nameof(SharpTreeNode.IsExpanded),
}; };
var model = new HierarchicalModel<SharpTreeNode>(options); var model = new HierarchicalModel<SharpTreeNode>(options);
model.SetRoots(root.Children); model.SetRoots(FilterHidden(root.Children));
DiffGrid.HierarchicalModel = model; DiffGrid.HierarchicalModel = model;
} }
// ShowIdentical=false hides identical rows by setting IsHidden=true via
// ComparisonEntryTreeNode.Filter, but the DataGrid's HierarchicalModel only sees
// what ChildrenSelector / SetRoots feed it — IsHidden alone has no effect on the
// rendered row set. Filter the children here so the cascade-set IsHidden flags
// actually reach the grid. Same pattern AssemblyListPane.FilterChildren uses for
// the assembly tree's ShowApiLevel cascade.
static System.Collections.Generic.IEnumerable<SharpTreeNode> FilterHidden(System.Collections.Generic.IEnumerable<SharpTreeNode> children)
=> children.Where(c => c is not TreeNodes.ILSpyTreeNode it || !it.IsHidden).ToList();
} }
} }

Loading…
Cancel
Save