I want to bind a sort command to a DataGridColumn.Header the functions works if it's bind to a button, but I want to implement it anyway in the columns header of the datagrid. I use MVVMCross as pattern so I should use a IMVXCommand to fire the function.
This is my XAML code in WPF if the "SortColumn"-button gets clicked nothing happens.
<DataGridTemplateColumn Width="300" >
<DataGridTemplateColumn.Header>
<Button Content="SortColumn" Command="{Binding IMvxSortCommand}"/>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Names}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Thanks.
Read several discussions on Stackoverflow and tried different approaches.
Normally, sorting in the UI is a simple sorting of the data presentation layer and not the original data. For this reason, the sorting must take place in the view (and not in the view model or model).
You can move the sorting algorithm to a custom IComparer<T>
implementation or use the property based ICollectionView.SortDescriptions
feature.
Even if you don't use MVVM, implementing the sort algorithm in a dedicated IComparer<T>
implementation provides a very clean solution that doesn't pollute your data models with data view (UI) related sorting logic.
If you use MVVM this separation becomes mandatory.
This solution is also cleaner in terms of the UI click area of the column headers as it will preserve its default click behavior.
Because adding a Button
as content of the column header won't disable the default click behavior which will cause weird behavior if the user clicks on the padded area around the Button
!
NameComparer.cs
class NameComparer : IComparer<string>
{
public int Compare(string firstName, secondName)
{
// TODO::Implement comparison rule:
// return -1 when firstName < secondName
// 0 when firstName == secondName
// 1 when firstName > secondName
}
}
MainWindow.xaml.cs
partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnColumnSortingUsingIComparer(object sender, DataGridSortingEventArgs e)
{
var dataGrid = (DataGrid)sender;
bool isNameColumn = e.Column.Header is string columnName
&& columnName.Equals("Name", StringComparison.OrdinalIgnoreCase);
if (!isNameColumn)
{
// Wrong column --> do not handle sorting of this column.
// Continue and let the DataGrid use the default comparison for sorting.
return;
}
// Cancel default sorting for this column
e.Handled = true;
ICollectionView dataGridDataView = CollectionViewSource.GetDefaultView(dataGrid.ItemsSource);
if (dataGridDataView is ListCollectionView listCollectionView)
{
IComparer columnSortComparer = new NameComparer();
listCollectionView.CustomSort = columnSortComparer;
}
}
private void OnColumnSortingUsingSortDescription(object sender, DataGridSortingEventArgs e)
{
var dataGrid = (DataGrid)sender;
var sortDirection = e.Column.SortDirection;
string propertyName = nameof(MyDatModel.SomeProperty);
var columnSortDecsription = new SortDescription(propertyName, sortDirection);
dataGrid.Items.SortDescriptions.Clear();
dataGrid.Items.SortDescriptions.Add(columnSortDecsription);
}
}
MainWindow.xaml
<Window>
<DataGrid Sorting="OnColumnSortingUsingSortDescription">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding Name}" />
</DataGrid.Columns>
</DataGrid>
<DataGrid Sorting="OnColumnSortingUsingIComparer">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding Name}" />
</DataGrid.Columns>
</DataGrid>
</Window>
If for whatever reason you insist on adding a Button
to attach an ICommand
then you should define a DataTemplate
for the DataGridColumn.HeaderTemplate
property. This way you can continue to use the DataGridColumn.Header
property independently:
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding Name}">
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<!--
The Command binding assumes that the command 'MySortCommand'
is defined in the DataContext of the DataGrid
-->
<Button Content="{Binding}"
Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.MySortCommand}" />
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>