I'm trying to sort data in datagrid, but when I click on the header of the column which has binding with the converter, nothing happens. I use MVVM pattern. Example is attached below. In the example, the grid shows column (Type) which displays type of person and therefore I use converter (class TypeValueConverter). When I use this converter, the grid doesn't sort column Type.
<Window x:Class="GridSort.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:GridSort"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False">
<DataGrid.Resources>
<my:TypeValueConverter x:Key="typeConverter" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding FirstName}" Header="FirstName" />
<DataGridTextColumn Binding="{Binding Surname}" Header="Surname" />
<DataGridTextColumn Binding="{Binding Converter={StaticResource ResourceKey=typeConverter}}" Header="Type" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
public class ViewModel
{
private ICollection<Person> people;
public ICollection<Person> People
{
get
{
if (this.people == null)
{
this.people = new List<Person>();
this.people.Add(new Student() { FirstName = "Charles", Surname = "Simons" });
this.people.Add(new Student() { FirstName = "Jake", Surname = "Baron" });
this.people.Add(new Teacher() { FirstName = "John", Surname = "Jackson" });
this.people.Add(new Student() { FirstName = "Patricia", Surname = "Phillips" });
this.people.Add(new Student() { FirstName = "Martin", Surname = "Weber" });
}
return this.people;
}
}
}
public class TypeValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
{
return DependencyProperty.UnsetValue;
}
return value.GetType().Name;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public abstract class Person
{
public string FirstName
{
get;
set;
}
public string Surname
{
get;
set;
}
}
public class Student : Person
{
}
public class Teacher : Person
{
}
I resolved this issue myself :)
I created new behavior for grid with attached property UseBindingToSort. When I set this property to true then event Sorting on grid is subscribed. After grid fires event sorting I use custom comparer with IValueConverter which is defined in binding. Solution is below:
Change on view
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" my:GridSortingBehavior.UseBindingToSort="True">
New behavior with attached property:
public static class GridSortingBehavior
{
public static readonly DependencyProperty UseBindingToSortProperty = DependencyProperty.RegisterAttached("UseBindingToSort", typeof(bool), typeof(GridSortingBehavior), new PropertyMetadata(new PropertyChangedCallback(GridSortPropertyChanged)));
public static void SetUseBindingToSort(DependencyObject element, bool value)
{
element.SetValue(UseBindingToSortProperty, value);
}
private static void GridSortPropertyChanged(DependencyObject elem, DependencyPropertyChangedEventArgs e)
{
DataGrid grid = elem as DataGrid;
if (grid != null){
if ((bool)e.NewValue)
{
grid.Sorting += new DataGridSortingEventHandler(grid_Sorting);
}
else
{
grid.Sorting -= new DataGridSortingEventHandler(grid_Sorting);
}
}
}
static void grid_Sorting(object sender, DataGridSortingEventArgs e)
{
DataGridTextColumn clm = e.Column as DataGridTextColumn;
if (clm != null)
{
DataGrid grid = ((DataGrid)sender);
IValueConverter converter = null;
if (clm.Binding != null)
{
Binding binding = clm.Binding as Binding;
if (binding.Converter != null)
{
converter = binding.Converter;
}
}
if (converter != null)
{
e.Handled = true;
ListSortDirection direction = (clm.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending;
clm.SortDirection = direction;
ListCollectionView lcv = (ListCollectionView)CollectionViewSource.GetDefaultView(grid.ItemsSource);
lcv.CustomSort = new ComparerWithComparer(converter, direction);
}
}
}
And finally my custom comparer:
class ComparerWithComparer : IComparer
{
private System.Windows.Data.IValueConverter converter;
private System.ComponentModel.ListSortDirection direction;
public ComparerWithComparer(System.Windows.Data.IValueConverter converter, System.ComponentModel.ListSortDirection direction)
{
this.converter = converter;
this.direction = direction;
}
public int Compare(object x, object y)
{
object transx = this.converter.Convert(x, typeof(string), null, System.Threading.Thread.CurrentThread.CurrentCulture);
object transy = this.converter.Convert(y, typeof(string), null, System.Threading.Thread.CurrentThread.CurrentCulture);
if (direction== System.ComponentModel.ListSortDirection.Ascending){
return Comparer.Default.Compare(transx, transy);
}
else
{
return Comparer.Default.Compare(transx, transy) * (-1);
}
}
}
And that's all.
I used Juptin's solution. But in my case I use ConverterParameter. So I add it to his code:
class ComparerWithComparer : IComparer
{
private IValueConverter converter;
private ListSortDirection direction;
private object parameter;
public ComparerWithComparer(IValueConverter converter, ListSortDirection direction, object parameter)
{
this.converter = converter;
this.direction = direction;
this.parameter = parameter;
}
public int Compare(object x, object y)
{
object transx = converter.Convert(x, typeof(string), parameter, System.Threading.Thread.CurrentThread.CurrentCulture);
object transy = converter.Convert(y, typeof(string), parameter, System.Threading.Thread.CurrentThread.CurrentCulture);
if (direction == ListSortDirection.Ascending)
{
return Comparer.Default.Compare(transx, transy);
}
return Comparer.Default.Compare(transx, transy) * (-1);
}
}
static void grid_Sorting(object sender, DataGridSortingEventArgs e)
{
DataGridTextColumn column = e.Column as DataGridTextColumn;
if (column != null)
{
DataGrid grid = ((DataGrid)sender);
IValueConverter converter = null;
object parameter = null;
if (column.Binding != null)
{
Binding binding = column.Binding as Binding;
if (binding.Converter != null)
{
converter = binding.Converter;
}
parameter = binding.ConverterParameter;
}
if (converter != null)
{
e.Handled = true;
ListSortDirection direction = (column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending;
column.SortDirection = direction;
var listCollectionView = (ListCollectionView)CollectionViewSource.GetDefaultView(grid.ItemsSource);
listCollectionView.CustomSort = new ComparerWithComparer(converter, direction,parameter);
}
}
}
You are using MVVM. If you can't add a property Type
to your Person
class
public abstract class Person
{
public string FirstName { get; set; }
public string Surname { get; set; }
public string Type
{
get { return this.GetType().Name; }
}
}
you should create a PersonViewModel
public class PersonViewModel
{
public Person Person { get; private set; }
public PersonViewModel(Person person)
{
this.Person = person;
}
public string Type
{
get { return this.Person.GetType().Name; }
}
}
adapt your People collection
private ICollection<PersonViewModel> people;
public ICollection<PersonViewModel> People
{
get
{
if (this.people == null)
{
this.people = new List<PersonViewModel>();
this.people.Add(new PersonViewModel(new Student { FirstName = "Charles", Surname = "Simons" }));
this.people.Add(new PersonViewModel(new Student { FirstName = "Jake", Surname = "Baron" }));
this.people.Add(new PersonViewModel(new Teacher { FirstName = "John", Surname = "Jackson" }));
this.people.Add(new PersonViewModel(new Student { FirstName = "Patricia", Surname = "Phillips" }));
this.people.Add(new PersonViewModel(new Student { FirstName = "Martin", Surname = "Weber" }));
}
return this.people;
}
}
and use this XAML
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Person.FirstName}" Header="FirstName" />
<DataGridTextColumn Binding="{Binding Person.Surname}" Header="Surname" />
<DataGridTextColumn Binding="{Binding Type}" Header="Type" />
</DataGrid.Columns>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With