Skip navigation

Here’s the code, read on for the explanation.

So hopefully my last series on enabling MultiSelect checkboxes with attached properties reinforced the idea that the WPF team has been trying to bring home: subclassing a control should be used in extreme circumstances, because most enhancements can be done using Styles, Templates, and as I just showed Attached Properties. Here’s yet another trick that we’re using in a custom project.

For some reason the ATC team didn’t provide an IsSortable property on the GridView…all though they did show how simple it is to add sorting to a gridview using code behind. Well you know me, I like simple. So I created an Attached Property that uses the Plover Remora Pattern to add sorting to a ListView (with a Gridview as its View) without any code-behind (other than the code that’s in the attached property). The final XAML to use this functionality is as simple as this:

<ListView
  Util:WPFUtils.IsGridSortable="True"
  Width="Auto"
  Height="Auto"
  ItemsSource="{Binding MyItems}"
  SelectionMode="Multiple"
  Margin="5">
    <ListView.View>
      <GridView>
         <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
         <GridViewColumn Header="Department" DisplayMemberBinding="{Binding Department}"/>

         <GridViewColumn Header="Extension" DisplayMemberBinding="{Binding Extension}"/>
      </GridView>
    </ListView.View>
</ListView>

Basically all you have to do to make your GridView sortable is set the attached property IsGridSortable to true and it handles the rest. Since we already know how the Remora property works (an attached property adds functionality to the Control it’s attached to during the PropertyChangedCallback) We’ll cut straight to the code for our PropertyChangedCallback. (Full code will be available at the end of this posting).

private static void OnRegisterSortableGrid(DependencyObject sender,
  DependencyPropertyChangedEventArgs args)
{
  ListView grid = sender as ListView;
  if (grid != null)
  {
    RegisterSortableGridView(grid, args);
  }
}

private static void RegisterSortableGridView(ListView grid,
  DependencyPropertyChangedEventArgs args)
{
  if (args.NewValue is Boolean && (Boolean)args.NewValue)
  {
    grid.AddHandler(GridViewColumnHeader.ClickEvent, GridViewColumnHeaderClickHandler);
  }
  else
  {
    grid.RemoveHandler(GridViewColumnHeader.ClickEvent, GridViewColumnHeaderClickHandler);
  }
}

The callback basically checks if the property has been set on a listview (I am of course adding a check for if it’s set on a PowerGrid) and if so calls the RegisterSortableGridView function. This function adds a custom handler for the GridViewColumnHeader.Click Event or removes it based on the value assigned to the property by the user. The handler works pretty much the same way as the ATC sample with a few differences.

if (header != null)
{
  ListSortDirection sortDirection;
  GridViewColumnHeader tmpHeader = GetLastSorted(lv);
  if (tmpHeader != null)
    tmpHeader.Column.HeaderTemplate = null;
  if (header != tmpHeader)
    sortDirection = ListSortDirection.Ascending;
  else
  {
    ListSortDirection tmpDirection = GetLastSortDirection(lv);
    if (tmpDirection == ListSortDirection.Ascending)
      sortDirection = ListSortDirection.Descending;
    else
     
sortDirection = ListSortDirection.Ascending;
}

You’ll see I highlighted calls to two functions GetLastSorted(lv); and GetLastSortDirection(lv). These are Getters for Read-only Attached Dependency Properties LastSorted and LastSortDirection, respectively (and the SDK said they aren’t useful), that are set on the ListView during a sort operation…if this is the first sort operation of course they’ll be null. The other cool feature…

switch (sortDirection)
{
  case ListSortDirection.Ascending: resourceTemplateName = "HeaderTemplateSortAsc"; break;
  case ListSortDirection.Descending: resourceTemplateName = "HeaderTemplateSortDesc"; break;
}
DataTemplate tmpTemplate = lv.TryFindResource(resourceTemplateName) as DataTemplate;
if (tmpTemplate != null)
{
  header.Column.HeaderTemplate = tmpTemplate;
}

If the user has declared HeaderTemplateSortAsc and/or HeaderTemplateSortDesc in their resource tree, it will be set on the GridViewColumnHeader. Today’s upload only includes the cs file for the WPFUtils class. I’ll pull it out of our main app and into it’s own project later.

Again, this shows that WPF Controls can be customized without subclassing them. And without codebehind for the UI of the code cluttering your business logic code. Next time, I want to talk about MVC with WPF.

 

Advertisements

8 Comments

  1. Cool stuff!  Providing a ListView sorting service via one attached property is very elegant.
     
    Have you toyed with the idea of adding multi-column sorting into your service (sort by Name, then Date, etc.)?
     
    Thanks,
    Josh Smith

  2. Can you upload the source code again?  Your file must have been deleted by fileden.Thanks,Mario

  3. I\’d have to re-construct the code. Give me a few days to hook that up.

  4. Great article!Becouse I\’m still a WPF newbie and I can\’t get the source from the above link can you maybe upload it again.ThxZoltan

  5. Hi, great article!I\’ve noticed one problem problem and one thing that could be nice.Here it is:)If you resize the coumns, so that it\’s possible to press the \’last column without a header the program crash\’es. That\’s the problem.If you rename the column header it won\’t sort the column. Would be nice if the columnheader could be renamed though.I can\’t figure out how to fix theese problem…//Thomas

  6. It appears that both the code files have been removed again. Could you please repost them?Thanks

  7. Looks like the WPFUtils.cs file is no longer available. Is there any chance of providing it again? I think it’d still be useful in WPF 4.0.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: