Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 24 additions & 13 deletions src/Avalonia.Controls.TreeDataGrid/FlatTreeDataGridSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,29 +135,40 @@ void ITreeDataGridSource.DragDropRows(
}
}

bool ITreeDataGridSource.SortBy(IColumn? column, ListSortDirection direction)
public bool SortBy(IColumn? column, ListSortDirection direction)
{
if (column is IColumn<TModel> typedColumn)
if (column is IColumn<TModel> typedColumn &&
Columns.Contains(typedColumn))
{
if (!Columns.Contains(typedColumn))
return true;

var comparer = typedColumn.GetComparison(direction);
if (comparer is null)
return false;

Sort(comparer);
foreach (var c in Columns)
c.SortDirection = c == column ? direction : null;

if (comparer is not null)
{
_comparer = new FuncComparer<TModel>(comparer);
_rows?.Sort(_comparer);
Sorted?.Invoke();
foreach (var c in Columns)
c.SortDirection = c == column ? direction : null;
}
return true;
}

return false;
}

public void Sort(Comparison<TModel>? comparison)
{
_comparer = comparison is not null ? new FuncComparer<TModel>(comparison!) : null;
_rows?.Sort(_comparer);
Sorted?.Invoke();
}

public void Unsort()
{
Sort(null);

foreach (var c in Columns)
c.SortDirection = null;
}

IEnumerable<object> ITreeDataGridSource.GetModelChildren(object model)
{
return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ public void Sort(Comparison<TModel>? comparison)
{
_comparison = comparison;
_rows?.Sort(_comparison);
Sorted?.Invoke();
}

public void Unsort()
{
Sort(null);

foreach (var c in Columns)
c.SortDirection = null;
}

IEnumerable<object>? ITreeDataGridSource.GetModelChildren(object model)
Expand All @@ -208,7 +217,6 @@ public bool SortBy(IColumn? column, ListSortDirection direction)
columnBase.GetComparison(direction) is Comparison<TModel> comparison)
{
Sort(comparison);
Sorted?.Invoke();
foreach (var c in Columns)
c.SortDirection = c == column ? direction : null;
return true;
Expand Down
13 changes: 13 additions & 0 deletions src/Avalonia.Controls.TreeDataGrid/ITreeDataGridSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ void DragDropRows(
/// <param name="direction">The sort direction.</param>
/// <returns>True if the sort could be performed; otherwise false.</returns>
bool SortBy(IColumn column, ListSortDirection direction);

/// <summary>
/// Removes any active sort and restores the source to its unsorted state.
/// </summary>
void Unsort();
}

/// <summary>
Expand All @@ -87,5 +92,13 @@ public interface ITreeDataGridSource<TModel> : ITreeDataGridSource
/// Gets or sets the items in the data source.
/// </summary>
new IEnumerable<TModel> Items { get; set; }

/// <summary>
/// Sorts the data source using the specified comparison.
/// </summary>
/// <param name="comparison">
/// A <see cref="Comparison{TModel}"/> delegate that defines the item order.
/// </param>
void Sort(Comparison<TModel>? comparison);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System;
using System.Xml.Linq;
using Avalonia.Controls.Models.TreeDataGrid;
using Avalonia.Controls.Selection;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.VisualTree;

namespace Avalonia.Controls.Primitives
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,73 @@ public void Should_Preserve_Horizontal_ScrollBar_When_Rows_Removed()
Assert.Equal(new(200, 0), headerScroll.Extent);
}

[AvaloniaFact]
public void SortBy_Sorts_Display_And_Sets_ColumnIndicator()
{
var (target, items) = CreateTarget();

// Apply sort by ID descending
Assert.False(target.Source!.IsSorted);
var ok = target.Source!.SortBy(target.Columns![0], ListSortDirection.Descending);
Assert.True(ok);
Assert.True(target.Source.IsSorted);
Assert.Equal(ListSortDirection.Descending, target.Columns![0].SortDirection);

// Verify rows are presented in descending order
var rows = target.Source!.Rows;
var sorted = items.OrderByDescending(x => x.Id).ToList();

for (var i = 0; i < sorted.Count && i < rows.Count; ++i)
{
var row = rows[i];
Assert.Equal(sorted[i].Id, (row.Model as Model)?.Id);
}
}

[AvaloniaFact]
public void Sort_Using_Comparison_Sorts_Display()
{
var (target, items) = CreateTarget();

var source = (FlatTreeDataGridSource<Model>)target.Source!;
Assert.False(source.IsSorted);

source.Sort((x, y) => y.Id - x.Id);

Assert.True(source.IsSorted);

var sorted = items.OrderByDescending(x => x.Id).ToList();

for (var i = 0; i < sorted.Count && i < source.Rows.Count; ++i)
{
var row = source.Rows[i];
Assert.Equal(sorted[i].Id, (row.Model as Model)?.Id);
}
}

[AvaloniaFact]
public void Unsort_Restores_Original_Order()
{
var (target, items) = CreateTarget();

var source = (FlatTreeDataGridSource<Model>)target.Source!;

// Apply and then clear sort
var ok = source.SortBy(source.Columns[0], ListSortDirection.Descending);
Assert.True(ok);
Assert.True(source.IsSorted);

source.Unsort();
Assert.False(source.IsSorted);

// Verify original insertion order restored
for (var i = 0; i < items.Count && i < source.Rows.Count; ++i)
{
var row = (IRow<Model>)source.Rows[i];
Assert.Equal(items[i].Id, ((Model)row.Model).Id);
}
}

private static void AssertRowIndexes(TreeDataGrid target, int firstRowIndex, int rowCount)
{
var presenter = target.RowsPresenter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,67 @@ public void Should_Recycle_Focused_Cell_When_Row_Collapsed()
Assert.Equal(-1, cell.RowIndex);
}

[AvaloniaFact]
public void SortBy_Sorts_Roots_And_Sets_Indicator()
{
var (target, source) = CreateTarget();

Assert.False(source.IsSorted);

var ok = source.SortBy(source.Columns[0], ListSortDirection.Descending);
Assert.True(ok);
Assert.True(source.IsSorted);
Assert.Equal(ListSortDirection.Descending, source.Columns[0].SortDirection);

// Verify top-level rows are sorted by Id descending
var sorted = source.Items.OrderByDescending(x => x.Id).ToList();
for (var i = 0; i < sorted.Count && i < source.Rows.Count; ++i)
{
var row = source.Rows[i];
Assert.Equal(sorted[i].Id, (row.Model as Model)?.Id);
}
}

[AvaloniaFact]
public void Sort_Using_Comparison_Sorts_Display()
{
var (target, source) = CreateTarget();

Assert.False(source.IsSorted);

source.Sort((x, y) => y.Id - x.Id);

Assert.True(source.IsSorted);
var sorted = source.Items.OrderByDescending(x => x.Id).ToList();

for (var i = 0; i < sorted.Count && i < source.Rows.Count; ++i)
{
var row = source.Rows[i];
Assert.Equal(sorted[i].Id, (row.Model as Model)?.Id);
}
}

[AvaloniaFact]
public void Unsort_Restores_Original_Order()
{
var (target, source) = CreateTarget();
var original = source.Items.ToList();

var ok = source.SortBy(source.Columns[0], ListSortDirection.Descending);
Assert.True(ok);
Assert.True(source.IsSorted);

source.Unsort();
Assert.False(source.IsSorted);

// Verify top-level rows have original sort order
for (var i = 0; i < original.Count && i < source.Rows.Count; ++i)
{
var row = source.Rows[i];
Assert.Equal(original[i].Id, (row.Model as Model)?.Id);
}
}

private static (TreeDataGrid, HierarchicalTreeDataGridSource<Model>) CreateTarget(
IEnumerable<IColumn<Model>>? columns = null,
bool runLayout = true)
Expand All @@ -503,14 +564,14 @@ private static (TreeDataGrid, HierarchicalTreeDataGridSource<Model>) CreateTarge
},
};

columns ??= new IColumn<Model>[]
{
columns ??=
[
new HierarchicalExpanderColumn<Model>(
new TextColumn<Model, int>("ID", x => x.Id),
x => x.Children,
x => true),
new TextColumn<Model, string?>("Title", x => x.Title),
};
];

var source = new HierarchicalTreeDataGridSource<Model>(items);
source.Columns.AddRange(columns);
Expand Down
Loading