Looking for a good way of sorting a ListView
For sorting data in my application I needed to call a method on a presenter which will calls a service to get data ordered by this property. Without that sorting view only would be easy but I need paging so things got complicated :-)
First of all I wanted to add a name of property to sort by to each column. I simply added one property to an original GridViewColumn:
public class SortableGridViewColumn : GridViewColumn { public string SortPropertyName { get { return (string)GetValue(SortPropertyNameProperty); } set { SetValue(SortPropertyNameProperty, value); } } public static readonly DependencyProperty SortPropertyNameProperty = DependencyProperty.Register("SortPropertyName", typeof(string), typeof(SortableGridViewColumn), new UIPropertyMetadata("")); }
Than I tried sorting in code in a control and call a method from its data context. To make it less wrong I used and interface implemented by a presenter:
public interface ISortingProvider { void SortByProperty(string propertyName); }
I simply used GridViewColumnHeader.Click:
<ListView Name="listView1" GridViewColumnHeader.Click="OnColumnHeaderClicked" > <ListView.View> <GridView> <local:SortableGridViewColumn Header="First Name"
SortPropertyName="FirstName"/> <local:SortableGridViewColumn Header="Last Name"
SortPropertyName="LastName"/> </GridView> </ListView.View> </ListView>
In code behind a method from presenter is called:
private void OnColumnHeaderClicked(object sender, RoutedEventArgs e) { var headerClicked = e.OriginalSource as GridViewColumnHeader; if (headerClicked != null) { var sortableGridViewColumn = (headerClicked.Column)
as SortableGridViewColumn; Sort(sortableGridViewColumn); } } private void Sort(SortableGridViewColumn sortableGridViewColumn) { if (listView1.DataContext is ISortingProvider && sortableGridViewColumn != null && !String.IsNullOrEmpty(sortableGridViewColumn.SortPropertyName)) { ((ISortingProvider)listView1.DataContext) .SortByProperty(sortableGridViewColumn.SortPropertyName); } }
This is a very simple way of achieving my goal but I don’t like it much since the view is too much aware of the presenter here. So I’ve decided to try another way – create a template which will call the presenter method using Caliburn actions.
Template – on clicking on header a method is called with a parameter – column header:
<GridView.ColumnHeaderContainerStyle> <Style TargetType="GridViewColumnHeader"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type GridViewColumnHeader}"> <Border cal:Message.Attach="[Event MouseLeftButtonDown]
= [Action Sort($source.TemplatedParent)]"> <ContentPresenter Margin="2,2,2,2" /> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </GridView.ColumnHeaderContainerStyle>
Now my presenter implements a method for sorting which uses column header:
public void Sort(GridViewColumnHeader gridViewColumnHeader) { var sortableGridViewColumn = gridViewColumnHeader.Column as ISortableGridViewColumn; if (sortableGridViewColumn != null) { MessageBox.Show(sortableGridViewColumn.SortPropertyName); } }
The only problem now is that my presenter is aware of GridViewColumnHeader which I don’t like much but I can’t find a way of creating a GridViewColumnHeader template which will be able to set a SortProperty name from SortableGridViewColumn as parameter. If I did that I will update this post :-)