Monika Dyrda

WPF Dev Girl

February 2010 - Posts

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 :-)

More Posts