diff --git a/ILSpy/Metadata/BlobHeapTreeNode.cs b/ILSpy/Metadata/BlobHeapTreeNode.cs index aa73c45de..b015a60c8 100644 --- a/ILSpy/Metadata/BlobHeapTreeNode.cs +++ b/ILSpy/Metadata/BlobHeapTreeNode.cs @@ -49,7 +49,7 @@ namespace ILSpy.Metadata { var page = new MetadataTablePageModel { Title = "Blob Heap", - Items = EnsureEntries().Cast().ToList(), + Items = EnsureEntries(), }; MetadataColumnBuilder.Populate(page); return page; diff --git a/ILSpy/Metadata/CoffHeaderTreeNode.cs b/ILSpy/Metadata/CoffHeaderTreeNode.cs index f0ab79c18..c597e6fe2 100644 --- a/ILSpy/Metadata/CoffHeaderTreeNode.cs +++ b/ILSpy/Metadata/CoffHeaderTreeNode.cs @@ -49,7 +49,7 @@ namespace ILSpy.Metadata { var page = new MetadataTablePageModel { Title = "COFF Header", - Items = BuildEntries().Cast().ToList(), + Items = BuildEntries(), }; MetadataColumnBuilder.Populate(page); return page; diff --git a/ILSpy/Metadata/DataDirectoriesTreeNode.cs b/ILSpy/Metadata/DataDirectoriesTreeNode.cs index b70a3377c..9bf15e95a 100644 --- a/ILSpy/Metadata/DataDirectoriesTreeNode.cs +++ b/ILSpy/Metadata/DataDirectoriesTreeNode.cs @@ -50,7 +50,7 @@ namespace ILSpy.Metadata { var page = new MetadataTablePageModel { Title = "Data Directories", - Items = BuildEntries().Cast().ToList(), + Items = BuildEntries(), }; MetadataColumnBuilder.Populate(page); return page; diff --git a/ILSpy/Metadata/DebugDirectoryTreeNode.cs b/ILSpy/Metadata/DebugDirectoryTreeNode.cs index 50d95ff7e..cce0ddd25 100644 --- a/ILSpy/Metadata/DebugDirectoryTreeNode.cs +++ b/ILSpy/Metadata/DebugDirectoryTreeNode.cs @@ -55,7 +55,7 @@ namespace ILSpy.Metadata { var page = new MetadataTablePageModel { Title = "Debug Directory", - Items = BuildEntries().Cast().ToList(), + Items = BuildEntries(), }; MetadataColumnBuilder.Populate(page); return page; diff --git a/ILSpy/Metadata/DosHeaderTreeNode.cs b/ILSpy/Metadata/DosHeaderTreeNode.cs index 8a5ef5a0b..a46d98d0e 100644 --- a/ILSpy/Metadata/DosHeaderTreeNode.cs +++ b/ILSpy/Metadata/DosHeaderTreeNode.cs @@ -48,7 +48,7 @@ namespace ILSpy.Metadata { var page = new MetadataTablePageModel { Title = "DOS Header", - Items = BuildEntries().Cast().ToList(), + Items = BuildEntries(), }; MetadataColumnBuilder.Populate(page); return page; diff --git a/ILSpy/Metadata/GuidHeapTreeNode.cs b/ILSpy/Metadata/GuidHeapTreeNode.cs index 82fb34fd1..1a00525e5 100644 --- a/ILSpy/Metadata/GuidHeapTreeNode.cs +++ b/ILSpy/Metadata/GuidHeapTreeNode.cs @@ -48,7 +48,7 @@ namespace ILSpy.Metadata { var page = new MetadataTablePageModel { Title = "Guid Heap", - Items = EnsureEntries().Cast().ToList(), + Items = EnsureEntries(), }; MetadataColumnBuilder.Populate(page); return page; diff --git a/ILSpy/Metadata/MetadataTableTreeNode.cs b/ILSpy/Metadata/MetadataTableTreeNode.cs index 522026153..e3825b26d 100644 --- a/ILSpy/Metadata/MetadataTableTreeNode.cs +++ b/ILSpy/Metadata/MetadataTableTreeNode.cs @@ -58,6 +58,7 @@ namespace ILSpy.Metadata /// them clickable hyperlinks via . /// public abstract class MetadataTableTreeNode : MetadataTableTreeNode + where TEntry : class { IReadOnlyList? cached; @@ -73,9 +74,16 @@ namespace ILSpy.Metadata public override TabPageModel CreateTab() { + // IReadOnlyList is covariant on T, so a strongly-typed list passes through to + // MetadataTablePageModel.Items (declared as IReadOnlyList) without a + // reflective copy. The runtime type stays IReadOnlyList, which lets + // DataGridCollectionView.GetItemType find the IEnumerable interface and + // resolve property-path sorts correctly — see DataGridSortDescription.Initialize + // + ReflectionHelper.GetNestedPropertyValue, which return null for every row when + // the source's element type is object. var page = new MetadataTablePageModel { Title = $"{Kind} ({RowCount})", - Items = (cached ??= LoadTable()).Cast().ToList(), + Items = cached ??= LoadTable(), }; MetadataColumnBuilder.Populate(page); return page; diff --git a/ILSpy/Metadata/OptionalHeaderTreeNode.cs b/ILSpy/Metadata/OptionalHeaderTreeNode.cs index 0a7c54668..97434db7e 100644 --- a/ILSpy/Metadata/OptionalHeaderTreeNode.cs +++ b/ILSpy/Metadata/OptionalHeaderTreeNode.cs @@ -50,7 +50,7 @@ namespace ILSpy.Metadata { var page = new MetadataTablePageModel { Title = "Optional Header", - Items = BuildEntries().Cast().ToList(), + Items = BuildEntries(), }; MetadataColumnBuilder.Populate(page); return page; diff --git a/ILSpy/Metadata/StringHeapTreeNode.cs b/ILSpy/Metadata/StringHeapTreeNode.cs index 4eb0d0ae4..ae0d7838c 100644 --- a/ILSpy/Metadata/StringHeapTreeNode.cs +++ b/ILSpy/Metadata/StringHeapTreeNode.cs @@ -49,7 +49,7 @@ namespace ILSpy.Metadata { var page = new MetadataTablePageModel { Title = "String Heap", - Items = EnsureEntries().Cast().ToList(), + Items = EnsureEntries(), }; MetadataColumnBuilder.Populate(page); return page; diff --git a/ILSpy/Metadata/UserStringHeapTreeNode.cs b/ILSpy/Metadata/UserStringHeapTreeNode.cs index bffd3f8df..7dd660a43 100644 --- a/ILSpy/Metadata/UserStringHeapTreeNode.cs +++ b/ILSpy/Metadata/UserStringHeapTreeNode.cs @@ -48,7 +48,7 @@ namespace ILSpy.Metadata { var page = new MetadataTablePageModel { Title = "UserString Heap", - Items = EnsureEntries().Cast().ToList(), + Items = EnsureEntries(), }; MetadataColumnBuilder.Populate(page); return page; diff --git a/ILSpy/Views/MetadataTablePage.axaml.cs b/ILSpy/Views/MetadataTablePage.axaml.cs index e5818c193..1ef5ec8d0 100644 --- a/ILSpy/Views/MetadataTablePage.axaml.cs +++ b/ILSpy/Views/MetadataTablePage.axaml.cs @@ -69,13 +69,6 @@ namespace ILSpy.Views grid.AddHandler(PointerPressedEvent, OnGridPointerPressed, global::Avalonia.Interactivity.RoutingStrategies.Bubble, handledEventsToo: true); - // Manually drive the DataGridCollectionView's SortDescriptions when the user - // clicks a column header. ProDataGrid's auto-sort path doesn't reach our - // view (Items is IReadOnlyList wrapped in a DataGridCollectionView, - // and the default sorting adapter's reflection chain doesn't pick up the - // SortMemberPath against an object collection). Toggle ascending → descending - // → unsorted on repeated clicks. - grid.Sorting += OnGridSorting; } } @@ -334,40 +327,6 @@ namespace ILSpy.Views void OnAnyColumnFilterChanged() => itemsView?.Refresh(); - void OnGridSorting(object? sender, DataGridColumnEventArgs e) - { - // Manually drive sort: cycle through ascending → descending → unsorted on repeated - // clicks for the same column. Mark the event handled so ProDataGrid's auto-sort - // path doesn't run after us — without this it would try to add another - // SortDescription against the same path and produce a stuck "double-sorted" state. - if (itemsView is null) - return; - string? path = (e.Column.SortMemberPath ?? e.Column.Tag as string); - if (string.IsNullOrEmpty(path)) - return; - - var existing = itemsView.SortDescriptions - .FirstOrDefault(d => d.PropertyPath == path); - - itemsView.SortDescriptions.Clear(); - foreach (var col in (sender as DataGrid)?.Columns ?? System.Linq.Enumerable.Empty()) - col.SortDirection = null; - - if (existing is null) - { - itemsView.SortDescriptions.Add(DataGridSortDescription.FromPath(path, ListSortDirection.Ascending)); - e.Column.SortDirection = ListSortDirection.Ascending; - } - else if (existing.Direction == ListSortDirection.Ascending) - { - itemsView.SortDescriptions.Add(DataGridSortDescription.FromPath(path, ListSortDirection.Descending)); - e.Column.SortDirection = ListSortDirection.Descending; - } - // Third click clears: SortDescriptions stays empty, SortDirection on column is null. - - e.Handled = true; - } - void ApplySchema() { var grid = this.FindControl("Grid");