Browse Source

Forward column-filter changes via a single page event

The page now subscribes to its own ColumnFilters' PropertyChanged via
CollectionChanged and re-raises ColumnFilterChanged. The view listens
to that single event instead of chasing per-filter listeners on every
schema swap, which was leaving the auto-refresh path silent on the
running app even though the predicate itself was correct.

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
42d1dc6c88
  1. 35
      ILSpy.Tests/Metadata/MetadataFilterTests.cs
  2. 31
      ILSpy/ViewModels/MetadataTablePageModel.cs
  3. 31
      ILSpy/Views/MetadataTablePage.axaml.cs

35
ILSpy.Tests/Metadata/MetadataFilterTests.cs

@ -89,6 +89,41 @@ public class MetadataFilterTests @@ -89,6 +89,41 @@ public class MetadataFilterTests
}).Should().BeFalse();
}
[Test]
public void Setting_ColumnFilter_Text_Raises_ColumnFilterChanged_On_The_Page()
{
// The view subscribes to the page's ColumnFilterChanged event to drive its
// DataGridCollectionView.Refresh — without this forwarding the filter row would be
// invisible to the grid and typing would have no effect on visible rows.
var page = new MetadataTablePageModel();
var filter = new ColumnFilter("Name");
page.ColumnFilters.Add(filter);
var fired = 0;
page.ColumnFilterChanged += () => fired++;
filter.Text = "System";
fired.Should().Be(1);
}
[Test]
public void Removing_A_ColumnFilter_Stops_Forwarding_Its_Text_Changes()
{
// When Populate clears the collection on a schema swap, the previously-attached
// filters must stop driving refreshes — otherwise stale headers from a former tab
// would keep firing into the new view's CollectionView.
var page = new MetadataTablePageModel();
var filter = new ColumnFilter("Name");
page.ColumnFilters.Add(filter);
page.ColumnFilters.Remove(filter);
var fired = 0;
page.ColumnFilterChanged += () => fired++;
filter.Text = "System";
fired.Should().Be(0);
}
[Test]
public void MatchesFilters_Returns_False_When_The_Filtered_Column_Does_Not_Exist_On_The_Row()
{

31
ILSpy/ViewModels/MetadataTablePageModel.cs

@ -20,6 +20,8 @@ using System; @@ -20,6 +20,8 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;
using Avalonia.Controls;
@ -59,13 +61,34 @@ namespace ILSpy.ViewModels @@ -59,13 +61,34 @@ namespace ILSpy.ViewModels
public ObservableCollection<ColumnFilter> ColumnFilters { get; } = new();
/// <summary>
/// Raised whenever any column's filter text changes. The view subscribes to this so
/// it can refresh the underlying <c>DataGridCollectionView</c> without each column
/// having to know how to find the live view instance.
/// Raised whenever any column's filter text changes. The view subscribes here to
/// refresh its <c>DataGridCollectionView</c>; tests use it as the canonical filter
/// signal too. The page wires this internally via <c>ColumnFilters.CollectionChanged</c>
/// + each filter's <see cref="INotifyPropertyChanged"/> so callers don't have to chase
/// individual filter instances.
/// </summary>
public event Action? ColumnFilterChanged;
internal void RaiseColumnFilterChanged() => ColumnFilterChanged?.Invoke();
public MetadataTablePageModel()
{
ColumnFilters.CollectionChanged += OnColumnFiltersCollectionChanged;
}
void OnColumnFiltersCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems is not null)
foreach (ColumnFilter f in e.OldItems)
f.PropertyChanged -= OnColumnFilterTextChanged;
if (e.NewItems is not null)
foreach (ColumnFilter f in e.NewItems)
f.PropertyChanged += OnColumnFilterTextChanged;
}
void OnColumnFilterTextChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ColumnFilter.Text))
ColumnFilterChanged?.Invoke();
}
/// <summary>
/// Raised when the user clicks a hyperlink-styled token cell. The host (the dock

31
ILSpy/Views/MetadataTablePage.axaml.cs

@ -130,13 +130,13 @@ namespace ILSpy.Views @@ -130,13 +130,13 @@ namespace ILSpy.Views
if (boundModel != null)
{
boundModel.PropertyChanged -= OnModelPropertyChanged;
DetachColumnFilterListeners(boundModel);
boundModel.ColumnFilterChanged -= OnAnyColumnFilterChanged;
}
boundModel = DataContext as MetadataTablePageModel;
if (boundModel != null)
{
boundModel.PropertyChanged += OnModelPropertyChanged;
AttachColumnFilterListeners(boundModel);
boundModel.ColumnFilterChanged += OnAnyColumnFilterChanged;
}
ApplySchema();
ApplyScrollTarget();
@ -146,37 +146,12 @@ namespace ILSpy.Views @@ -146,37 +146,12 @@ namespace ILSpy.Views
{
if (e.PropertyName is nameof(MetadataTablePageModel.Columns)
or nameof(MetadataTablePageModel.Items))
{
if (sender is MetadataTablePageModel m)
{
DetachColumnFilterListeners(m);
AttachColumnFilterListeners(m);
}
ApplySchema();
}
else if (e.PropertyName == nameof(MetadataTablePageModel.ScrollToRow))
{
ApplyScrollTarget();
}
}
void OnColumnFilterChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ColumnFilter.Text))
itemsView?.Refresh();
}
void AttachColumnFilterListeners(MetadataTablePageModel model)
{
foreach (var f in model.ColumnFilters)
f.PropertyChanged += OnColumnFilterChanged;
}
void DetachColumnFilterListeners(MetadataTablePageModel model)
{
foreach (var f in model.ColumnFilters)
f.PropertyChanged -= OnColumnFilterChanged;
}
void OnAnyColumnFilterChanged() => itemsView?.Refresh();
void ApplySchema()
{

Loading…
Cancel
Save