From d3275fef2962b1e31b07ebdd2c51fa6f11e09a16 Mon Sep 17 00:00:00 2001 From: GabrielDuf Date: Thu, 16 Jul 2026 13:07:48 -0400 Subject: [PATCH 1/2] Fix installed-packages column sorting under NativeAOT --- .../Models/PackageCollections.cs | 11 +++++++++- .../AbstractPackagesPage.axaml.cs | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/UniGetUI.Avalonia/Models/PackageCollections.cs b/src/UniGetUI.Avalonia/Models/PackageCollections.cs index b2220f5d61..0062d60c79 100644 --- a/src/UniGetUI.Avalonia/Models/PackageCollections.cs +++ b/src/UniGetUI.Avalonia/Models/PackageCollections.cs @@ -1,3 +1,4 @@ +using System.Collections; using System.Collections.ObjectModel; using System.ComponentModel; using System.Net.Http; @@ -494,7 +495,9 @@ public IEnumerable ApplyToList(IEnumerable items ? items.OrderBy(GetSortKey, StringComparer.OrdinalIgnoreCase) : items.OrderByDescending(GetSortKey, StringComparer.OrdinalIgnoreCase); - private string GetSortKey(PackageWrapper w) => CurrentSorter switch + private string GetSortKey(PackageWrapper w) => GetSortKey(w, CurrentSorter); + + private static string GetSortKey(PackageWrapper w, Sorter sorter) => sorter switch { Sorter.Checked => w.IsChecked ? "0" : "1", Sorter.Name => w.Package.Name, @@ -504,4 +507,10 @@ public IEnumerable ApplyToList(IEnumerable items Sorter.Source => w.Package.Source.AsString_DisplayName, _ => w.Package.Name, }; + + // Comparer for the DataGrid's native column sorting. Reflection-free (unlike SortMemberPath), + // so it keeps working under full-trim NativeAOT release builds — see issue #5103. + public static IComparer GetColumnComparer(Sorter sorter) => + Comparer.Create((a, b) => + string.Compare(GetSortKey(a, sorter), GetSortKey(b, sorter), StringComparison.OrdinalIgnoreCase)); } diff --git a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs index 538d0e7737..a55aced453 100644 --- a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs @@ -84,6 +84,26 @@ or nameof(PackagesPageViewModel.SortAscending)) // Double-click a list row → show details PackageList.DoubleTapped += (_, _) => _ = ShowDetailsForPackage(SelectedItem); + // Native DataGrid column sorting. The default SortMemberPath path resolves the property by + // reflection, which full-trim NativeAOT release builds strip away — so CanUserSort reports + // false and header clicks are silently dropped (issue #5103). A strongly-typed + // CustomSortComparer plus an explicit CanUserSort keeps Avalonia's built-in sort AOT-safe. + foreach (var col in PackageList.Columns) + { + ObservablePackageCollection.Sorter? sorter = (col.Tag as string) switch + { + "Name" => ObservablePackageCollection.Sorter.Name, + "Id" => ObservablePackageCollection.Sorter.Id, + "Version" => ObservablePackageCollection.Sorter.Version, + "NewVersion" => ObservablePackageCollection.Sorter.NewVersion, + "Source" => ObservablePackageCollection.Sorter.Source, + _ => null, + }; + if (sorter is null) continue; + col.CanUserSort = true; + col.CustomSortComparer = ObservablePackageCollection.GetColumnComparer(sorter.Value); + } + // Keyboard shortcuts on the package list. Handled on the tunnel route (and even when // already handled) because the DataGrid swallows Enter on the bubble route otherwise. PackageList.AddHandler(KeyDownEvent, PackageList_KeyDown, RoutingStrategies.Tunnel, handledEventsToo: true); From 9228b0c992247ec593fb6a3a5b839ff0a96d83b7 Mon Sep 17 00:00:00 2001 From: GabrielDuf Date: Thu, 16 Jul 2026 13:21:39 -0400 Subject: [PATCH 2/2] Sort Version/New version columns semantically --- .../Models/PackageCollections.cs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/UniGetUI.Avalonia/Models/PackageCollections.cs b/src/UniGetUI.Avalonia/Models/PackageCollections.cs index 0062d60c79..f7449739bc 100644 --- a/src/UniGetUI.Avalonia/Models/PackageCollections.cs +++ b/src/UniGetUI.Avalonia/Models/PackageCollections.cs @@ -490,20 +490,28 @@ public void ClearSelection() public void SetSortDirection(bool ascending) => _ascending = ascending; /// Returns in the current sort order. - public IEnumerable ApplyToList(IEnumerable items) => - _ascending - ? items.OrderBy(GetSortKey, StringComparer.OrdinalIgnoreCase) - : items.OrderByDescending(GetSortKey, StringComparer.OrdinalIgnoreCase); + public IEnumerable ApplyToList(IEnumerable items) + { + var comparer = Comparer.Create((a, b) => Compare(a, b, CurrentSorter)); + return _ascending ? items.OrderBy(w => w, comparer) : items.OrderByDescending(w => w, comparer); + } - private string GetSortKey(PackageWrapper w) => GetSortKey(w, CurrentSorter); + // Shared by the "Order by" menu (ApplyToList) and the DataGrid's native column sorting + // (GetColumnComparer) so both order identically. Versions compare semantically via + // CoreTools.Version; a string key can't be used there because that struct has no ToString + // override, so its key would be a constant type name that never sorts. + private static int Compare(PackageWrapper a, PackageWrapper b, Sorter sorter) => sorter switch + { + Sorter.Version => a.Package.NormalizedVersion.CompareTo(b.Package.NormalizedVersion), + Sorter.NewVersion => a.Package.NormalizedNewVersion.CompareTo(b.Package.NormalizedNewVersion), + _ => string.Compare(GetSortKey(a, sorter), GetSortKey(b, sorter), StringComparison.OrdinalIgnoreCase), + }; private static string GetSortKey(PackageWrapper w, Sorter sorter) => sorter switch { Sorter.Checked => w.IsChecked ? "0" : "1", Sorter.Name => w.Package.Name, Sorter.Id => w.Package.Id, - Sorter.Version => w.Package.NormalizedVersion.ToString() ?? string.Empty, - Sorter.NewVersion => w.Package.NormalizedNewVersion.ToString() ?? string.Empty, Sorter.Source => w.Package.Source.AsString_DisplayName, _ => w.Package.Name, }; @@ -511,6 +519,5 @@ public IEnumerable ApplyToList(IEnumerable items // Comparer for the DataGrid's native column sorting. Reflection-free (unlike SortMemberPath), // so it keeps working under full-trim NativeAOT release builds — see issue #5103. public static IComparer GetColumnComparer(Sorter sorter) => - Comparer.Create((a, b) => - string.Compare(GetSortKey(a, sorter), GetSortKey(b, sorter), StringComparison.OrdinalIgnoreCase)); + Comparer.Create((a, b) => Compare(a, b, sorter)); }