diff --git a/src/UniGetUI.Avalonia/Models/PackageCollections.cs b/src/UniGetUI.Avalonia/Models/PackageCollections.cs
index b2220f5d61..f7449739bc 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;
@@ -489,19 +490,34 @@ 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);
+ }
+
+ // 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 string GetSortKey(PackageWrapper w) => CurrentSorter switch
+ 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,
};
+
+ // 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) => Compare(a, b, sorter));
}
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);