AvaloniaUI / Avalonia.Controls.TreeDataGrid

A combined TreeView/DataGrid for Avalonia.
MIT License
233 stars 48 forks source link

TreeSelectioNode (and probably SelectionNodeBase) breaks when we try to Move a selection #297

Open heuristicAL opened 4 weeks ago

heuristicAL commented 4 weeks ago

I'm currently using DynamicData to populate, filter and sort the datasource for my TreeDataGrid. This all works wonderfully except when I try to change a field in the Grid which triggers re-sorting of that row. When the sort happens, it seems that the TreeSelectionNode triggers OnSourceChanged with a Moved action with this value (only the index changes):

{
  "Action": "Move",
  "NewItems": [
    <RowModel> // Same as OldItems
  ],
  "NewStartingIndex": 0, // The sort is moving it to the top, as expected
  "OldItems": [
    <RowModel> // Same as NewItems
  ],
  "OldStartingIndex": 10
}

The Moved action throws a NotSupportedException. From reading the OnSourceCollectionChanged method, it seems to me that this Move action could potentially be easily implemented by using almost the same implementation as the one for Replaced? Willing to contribute to this if needed unless someone already has an idea of how to do this.

For reference, the best way to reproduce this is to create a DynamicData SourceCache and then sort it on a value that you then change. Here is a reproduction of what I'm doing but in ie:

using System.Collections.ObjectModel;
using System.Reactive.Linq;

using Avalonia.Controls;

using DynamicData;
using DynamicData.Binding;

using ReactiveUI;
using ReactiveUI.Fody.Helpers;

public class Person(int Id, string Name, bool PinToTop) : ReactiveObject {
    public int Id { get; init; } = Id;
    public string Name { get; init; } = Name;

    [Reactive] public bool PinToTop { get; init; } = PinToTop;
}

public class Program {
    public static void Main() {
        SourceCache<Person, int> test = new(x => x.Id);

        test.Connect()
            .AutoRefresh(model => model.PinToTop) // Re-evaluate the sort when this property is updated
            .Sort(SortExpressionComparer<Person>.Descending(x => x.PinToTop).ThenByDescending(t => t.Name),
                SortOptimisations.ComparesImmutableValuesOnly, 25)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Bind(out ReadOnlyObservableCollection<Person> people);

        var dataSource = new FlatTreeDataGridSource<Person>(people);

        // Then use datasource from the view
    }
}

Here are my package references:

<PackageReference Include="Avalonia.Controls.TreeDataGrid" Version="11.0.6" />
<PackageReference Include="DynamicData" Version="8.4.1" />
<PackageReference Include="ReactiveUI" Version="20.1.1" />
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />

Environment: TreeDataGrid Version: 11.0.2 Avalonia Version: 11.0.6 Operating System: Windows 10