diff --git a/examples/Demo/Shared/Pages/DataGrid/Examples/DataGridRankSort.razor b/examples/Demo/Shared/Pages/DataGrid/Examples/DataGridRankSort.razor
new file mode 100644
index 0000000000..341e43f3fa
--- /dev/null
+++ b/examples/Demo/Shared/Pages/DataGrid/Examples/DataGridRankSort.razor
@@ -0,0 +1,43 @@
+
+
+
+
+ @context.Group
+
+
+
+
+Keep numbers always sorted ascending inside the group when sorting by group
+
+
+
+
+ @context.Group
+
+
+
+
+
+
+@code {
+ GridSort groupRank = GridSort
+ .ByAscending(x => x.Group)
+ .ThenAscending(x => x.Number);
+
+ GridSort groupRankNumberAlwaysAscending = GridSort
+ .ByAscending(x => x.Group)
+ .ThenAlwaysAscending(x => x.Number);
+
+ private static readonly IQueryable _gridData = new GridRow[] {
+ new(2, "B"),
+ new(1, "A"),
+ new(4, "B"),
+ new(3, "A")
+ }.AsQueryable();
+
+ public class GridRow(int number, string group)
+ {
+ public int Number { get; } = number;
+ public string Group { get; } = group;
+ }
+}
diff --git a/src/Core/Components/DataGrid/Columns/GridSort.cs b/src/Core/Components/DataGrid/Columns/GridSort.cs
index beb0a14558..456888ef70 100644
--- a/src/Core/Components/DataGrid/Columns/GridSort.cs
+++ b/src/Core/Components/DataGrid/Columns/GridSort.cs
@@ -59,6 +59,18 @@ public static GridSort ByDescending(Expression>
=> new((queryable, asc) => asc ? queryable.OrderByDescending(expression) : queryable.OrderBy(expression),
(expression, false));
+ ///
+ /// Produces a instance that sorts according to the specified
+ /// using the specified , descending.
+ ///
+ /// The type of the expression's value.
+ /// An expression defining how a set of instances are to be sorted.
+ /// Defines how a items in a set of instances are to be compared.
+ /// A instance representing the specified sorting rule.
+ public static GridSort ByDescending(Expression> expression, IComparer comparer)
+ => new((queryable, asc) => asc ? queryable.OrderByDescending(expression, comparer) : queryable.OrderBy(expression, comparer),
+ (expression, false));
+
///
/// Updates a instance by appending a further sorting rule.
///
@@ -67,13 +79,25 @@ public static GridSort ByDescending(Expression>
/// A instance representing the specified sorting rule.
public GridSort ThenAscending(Expression> expression)
{
- _then ??= [];
- _thenExpressions ??= [];
- _then.Add((queryable, asc) => asc ? queryable.ThenBy(expression) : queryable.ThenByDescending(expression));
- _thenExpressions.Add((expression, true));
- _cachedPropertyListAscending = null;
- _cachedPropertyListDescending = null;
- return this;
+ return AddThenExpression(
+ (queryable, asc) => asc ? queryable.ThenBy(expression) : queryable.ThenByDescending(expression),
+ (expression, true)
+ );
+ }
+
+ ///
+ /// Updates a instance by appending a further sorting rule.
+ ///
+ /// The type of the expression's value.
+ /// An expression defining how a set of instances are to be sorted.
+ /// Defines how a items in a set of instances are to be compared.
+ /// A instance representing the specified sorting rule.
+ public GridSort ThenAscending(Expression> expression, IComparer comparer)
+ {
+ return AddThenExpression(
+ (queryable, asc) => asc ? queryable.ThenBy(expression, comparer) : queryable.ThenByDescending(expression, comparer),
+ (expression, true)
+ );
}
///
@@ -83,13 +107,92 @@ public GridSort ThenAscending(Expression> expre
/// An expression defining how a set of instances are to be sorted.
/// A instance representing the specified sorting rule.
public GridSort ThenDescending(Expression> expression)
+ {
+ return AddThenExpression(
+ (queryable, asc) => asc ? queryable.ThenByDescending(expression) : queryable.ThenBy(expression),
+ (expression, false));
+ }
+
+ ///
+ /// Updates a instance by appending a further sorting rule.
+ ///
+ /// The type of the expression's value.
+ /// An expression defining how a set of instances are to be sorted.
+ /// Defines how a items in a set of instances are to be compared.
+ /// A instance representing the specified sorting rule.
+ public GridSort ThenDescending(Expression> expression, IComparer comparer)
+ {
+ return AddThenExpression(
+ (queryable, asc) => asc ? queryable.ThenByDescending(expression, comparer) : queryable.ThenBy(expression, comparer),
+ (expression, false)
+ );
+ }
+
+ ///
+ /// Updates a instance by appending a further sorting rule.
+ ///
+ /// The type of the expression's value.
+ /// An expression defining how a set of instances are to be sorted.
+ /// A instance representing the specified sorting rule.
+ public GridSort ThenAlwaysAscending(Expression> expression)
+ {
+ return AddThenExpression(
+ (queryable, _) => queryable.ThenBy(expression),
+ (expression, true));
+ }
+
+ ///
+ /// Updates a instance by appending a further sorting rule.
+ ///
+ /// The type of the expression's value.
+ /// An expression defining how a set of instances are to be sorted.
+ /// Defines how a items in a set of instances are to be compared.
+ /// A instance representing the specified sorting rule.
+ public GridSort ThenAlwaysAscending(Expression> expression, IComparer comparer)
+ {
+ return AddThenExpression(
+ (queryable, _) => queryable.ThenBy(expression, comparer),
+ (expression, true)
+ );
+ }
+
+ ///
+ /// Updates a instance by appending a further sorting rule.
+ ///
+ /// The type of the expression's value.
+ /// An expression defining how a set of instances are to be sorted.
+ /// A instance representing the specified sorting rule.
+ public GridSort ThenAlwaysDescending(Expression> expression)
+ {
+ return AddThenExpression(
+ (queryable, _) => queryable.ThenByDescending(expression),
+ (expression, false));
+ }
+
+ ///
+ /// Updates a instance by appending a further sorting rule.
+ ///
+ /// The type of the expression's value.
+ /// An expression defining how a set of instances are to be sorted.
+ /// Defines how a items in a set of instances are to be compared.
+ /// A instance representing the specified sorting rule.
+ public GridSort ThenAlwaysDescending(Expression> expression, IComparer comparer)
+ {
+ return AddThenExpression(
+ (queryable, _) => queryable.ThenByDescending(expression, comparer),
+ (expression, false)
+ );
+ }
+
+ private GridSort AddThenExpression(Func, bool, IOrderedQueryable> thenSortExpression, (LambdaExpression, bool) thenExpression)
{
_then ??= [];
_thenExpressions ??= [];
- _then.Add((queryable, asc) => asc ? queryable.ThenByDescending(expression) : queryable.ThenBy(expression));
- _thenExpressions.Add((expression, false));
+ _then.Add(thenSortExpression);
+ _thenExpressions.Add(thenExpression);
_cachedPropertyListAscending = null;
_cachedPropertyListDescending = null;
+
return this;
}
diff --git a/tests/Core/DataGrid/GridSortTests.cs b/tests/Core/DataGrid/GridSortTests.cs
new file mode 100644
index 0000000000..e71d7f0a99
--- /dev/null
+++ b/tests/Core/DataGrid/GridSortTests.cs
@@ -0,0 +1,101 @@
+using FluentAssertions;
+using Xunit;
+
+namespace Microsoft.FluentUI.AspNetCore.Components.Tests.DataGrid;
+
+public class GridSortTests : TestBase
+{
+ private static readonly GridRow[] _gridData = [
+ new(2, "B"),
+ new(1, "A"),
+ new(4, "B"),
+ new(3, "A")
+ ];
+
+#pragma warning disable CA1861 // Avoid constant arrays as arguments
+
+ [Theory]
+ [InlineData(true, new int[] { 1, 2, 3, 4 })]
+ [InlineData(false, new int[] { 4, 3, 2, 1 })]
+ public void GridSortTests_SortBy_Number(bool ascending, IList expected)
+ {
+ var sort = GridSort.ByAscending(x => x.Number);
+ var ordered = sort.Apply(_gridData.AsQueryable(), ascending);
+
+ ordered.Select(x => x.Number)
+ .SequenceEqual(expected)
+ .Should().BeTrue();
+ }
+
+ [Theory]
+ [InlineData(true, new int[] { 1, 3, 2, 4 })]
+ [InlineData(false, new int[] { 4, 2, 3, 1 })]
+ public void GridSortTests_SortBy_GroupThenNumberAscending(bool ascending, IList expected)
+ {
+ var sort = GridSort
+ .ByAscending(x => x.Group)
+ .ThenAscending(x => x.Number);
+
+ var ordered = sort.Apply(_gridData.AsQueryable(), ascending);
+
+ ordered.Select(x => x.Number)
+ .SequenceEqual(expected)
+ .Should().BeTrue();
+ }
+
+ [Theory]
+ [InlineData(true, new int[] { 3, 1, 4, 2 })]
+ [InlineData(false, new int[] { 2, 4, 1, 3 })]
+ public void GridSortTests_SortBy_GroupThenNumberDescending(bool ascending, IList expected)
+ {
+ var sort = GridSort
+ .ByAscending(x => x.Group)
+ .ThenDescending(x => x.Number);
+
+ var ordered = sort.Apply(_gridData.AsQueryable(), ascending);
+
+ ordered.Select(x => x.Number)
+ .SequenceEqual(expected)
+ .Should().BeTrue();
+ }
+
+ [Theory]
+ [InlineData(true, new int[] { 1, 3, 2, 4 })]
+ [InlineData(false, new int[] { 2, 4, 1, 3 })]
+ public void GridSortTests_SortBy_GroupThenNumberAlwaysAscending(bool ascending, IList expected)
+ {
+ var sort = GridSort
+ .ByAscending(x => x.Group)
+ .ThenAlwaysAscending(x => x.Number);
+
+ var ordered = sort.Apply(_gridData.AsQueryable(), ascending);
+
+ ordered.Select(x => x.Number)
+ .SequenceEqual(expected)
+ .Should().BeTrue();
+ }
+
+ [Theory]
+ [InlineData(true, new int[] { 3, 1, 4, 2 })]
+ [InlineData(false, new int[] { 4, 2, 3, 1 })]
+ public void GridSortTests_SortBy_GroupThenNumberAlwaysDescending(bool ascending, IList expected)
+ {
+ var sort = GridSort
+ .ByAscending(x => x.Group)
+ .ThenAlwaysDescending(x => x.Number);
+
+ var ordered = sort.Apply(_gridData.AsQueryable(), ascending);
+
+ ordered.Select(x => x.Number)
+ .SequenceEqual(expected)
+ .Should().BeTrue();
+ }
+
+#pragma warning restore CA1861 // Avoid constant arrays as arguments
+
+ public class GridRow(int number, string group)
+ {
+ public int Number { get; } = number;
+ public string Group { get; } = group;
+ }
+}