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