Avalonia
Avalonia copied to clipboard
Datagrid ClearSort doesn't work if CanUserSortColumns subsequently synchronously set to false
Describe the bug Consider the case where the developer wishes to sometimes let the user sort columns by clicking the header, and at other times disable manual sorting. The developer wants to write code to clear the user-provided sorts for all columns, then disable the user from providing further sorts. In this case, the idiomatic way is to iterate the columns, clearing the sort for each, then set the CanUserSortColumns to false. However, it doesn't work quite right.
To Reproduce Steps to reproduce the behavior:
Front End
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="DataGridTest.MainWindow"
Title="DataGridTest">
<StackPanel>
<Button Content="Clear Sort" Click="OnClearSortClick"></Button>
<DataGrid x:Name="DataGrid" Items="{Binding Persons}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Age" Binding="{Binding Age}" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Window>
Code Behind
using Avalonia.Controls;
using Avalonia.Interactivity;
using System.Collections.ObjectModel;
namespace DataGridTest;
public partial class MainWindow : Window
{
public ObservableCollection<Person> Persons { get; } = new ObservableCollection<Person>
{
new Person { Name = "John Doe", Age = 30 },
new Person { Name = "Jane Doe", Age = 25 },
// ...
};
public MainWindow()
{
DataContext = this;
InitializeComponent();
}
private void OnClearSortClick(object sender, RoutedEventArgs e)
{
foreach (var column in DataGrid.Columns)
{
column.ClearSort();
}
DataGrid.CanUserSortColumns = false;
}
public sealed class Person
{
public string Name { get; set; } = string.Empty;
public int Age { get; set; }
}
}
- Launch the app
- Click a header to set a sort
- Click the clear sort button
Expected behavior Column sorts are cleared and the user cannot provide any futher sorts
Observed behavior Column sorts are not cleared and the user cannot provide any further sorts
Workaround Post a callback to the main thread dispatcher instead of setting CanUserSortColumns synchronously.
foreach (var column in DataGrid.Columns)
{
column.ClearSort();
}
Dispatcher.UIThread.Post(() => DataGrid.CanUserSortColumns = false);
Screenshots run the reproducible example
Desktop (please complete the following information):
- OS: Windows 11
- Version: 0.10.21
Additional context Add any other context about the problem here. DataGridTest.zip
I have another workaround without using the dispatcher. It requires the wrapper I mentioned in this discussion, and with a few changes it works as expected:
public DataGridItemCollectionView<Person> Persons { get; } = new(new List<Person>
{
new() { Name = "John Doe", Age = 30 },
new() { Name = "Jane Doe", Age = 25 },
});
public DataGridCollectionView DataGridView => Persons;
public void OnClearSortClick(object? sender, RoutedEventArgs e)
{
DataGridView.SortDescriptions.Clear();
}
public sealed class Person : PropertyChangedBase
{
public string Name { get; set; } = string.Empty;
public int Age { get; set; }
}
INotifyPropertyChanged
on Person
is only required if you also implement INotifyCollectionChanged
on DataGridItemCollectionView<T>
. This way the sorting can also be modified and cleared from the view model, not code behind.
I have another workaround without using the dispatcher. It requires the wrapper I mentioned in https://github.com/AvaloniaUI/Avalonia/discussions/11907#discussioncomment-6300413, and with a few changes it works as expected:
@VMelnalksnis I am having difficulty replicating your result. Looking at your code, it seems that it only clears the sort order, but does not subsequently and synchronously disable further user-provided sorts. The defect only occurs in the subsequently/synchronously scenario, so based on just clearing the sort orders we should expect no unusual result. After reading your wrapper from the other thread, I am left with the following code-behind:
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Interactivity;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace DataGridTest;
public sealed class DataGridItemCollectionView<T> : IEnumerable<T>
{
private readonly DataGridCollectionView _dataGridCollectionView;
public DataGridItemCollectionView(IEnumerable<T> source)
: this(new DataGridCollectionView(source))
{
}
public DataGridItemCollectionView(DataGridCollectionView dataGridCollectionView)
{
_dataGridCollectionView = dataGridCollectionView;
}
public static implicit operator DataGridCollectionView(DataGridItemCollectionView<T> view) =>
view._dataGridCollectionView;
public IEnumerator<T> GetEnumerator() => _dataGridCollectionView.Cast<T>().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _dataGridCollectionView.GetEnumerator();
}
public sealed class Person : INotifyPropertyChanged
{
private string name = string.Empty;
private int age;
public string Name
{
get => name;
set
{
if (value == name) return;
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
public int Age
{
get => age;
set
{
if (value == age) return;
age = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Age)));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
}
public partial class MainWindow : Window
{
public DataGridItemCollectionView<Person> Persons { get; } = new(new List<Person>
{
new() { Name = "John Doe", Age = 30 },
new() { Name = "Jane Doe", Age = 25 },
});
public DataGridCollectionView DataGridView => Persons;
public void OnClearSortClick(object? sender, RoutedEventArgs e)
{
DataGridView.SortDescriptions.Clear();
}
}
Unfortunately, this doesn't even show the DataGrid. Can you please zip up your working isolation test and attach in reply?
@wsficke I forgot to add DataGrid.CanUserSortColumns = false;
in the above example.
Using this code, after sorting a column and clicking the button, the sorting is reset and cannot be sorted by the user again.
public void OnClearSortClick(object? sender, RoutedEventArgs e)
{
DataGridView.SortDescriptions.Clear();
DataGrid.CanUserSortColumns = false;
}
I'll try to extract all the relevant code a bit later.
@VMelnalksnis best if you can file a minimal sample showing your issue.
Here's examples with DataGridCollectionView
wrapper where this works as expected, both in code-behind and with a view model.