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

1 Comment

  • If you change the signature of your sort method to:

    public void Sort(ISortableGridViewColumn column)

    Caliburn should be able to perform the interface cast and you shouldn't need a dependency on the control from within your presenter.

    You might even think about renaming the interface to be less control focused. Perhaps something like ISupportSorting or ISpecifySorting, etc

Comments have been disabled for this content.