Thursday, December 26, 2013

Sorting Evolution (3) - Sorting a WPF ListView/GridView by clicking on the header - Sort in ViewModel

Third Generation


After reading Messenger and View Services in MVVM by Laurent Bugnion, I thought about moving sorting again to the ViewModel. The View will still handle the visual elements, while the ViewModel has again the responsibility for sorting. So sorting can also be triggered by the business logic, if needed.


The ViewModel is nearly the same as in the First Generation, but the RelayCommand is not needed.

private ObservableCollection<ResultData> _thirdResultData;
private CollectionViewSource _thirdResultDataView;
private string _sortColumn;
private ListSortDirection _sortDirection;
 
public ObservableCollection<ResultData> ThirdResultData
{
  get
  {
    return _thirdResultData;
  }
  set
  {
    _thirdResultData = value;
    _thirdResultDataView = new CollectionViewSource();
    _thirdResultDataView.Source = _thirdResultData;
  }
}

public ListCollectionView ThirdResultDataView
{
  get
  {
    return (ListCollectionView)_thirdResultDataView.View;
  }
}

public void Sort(string column)
{
  if (_sortColumn == column)
  {
    // Toggle sorting direction
    _sortDirection = _sortDirection == ListSortDirection.Ascending ?
                                       ListSortDirection.Descending :
                                       ListSortDirection.Ascending;
  }
  else
  {
    _sortColumn = column;
    _sortDirection = ListSortDirection.Ascending;
  }

  _thirdResultDataView.SortDescriptions.Clear();
  _thirdResultDataView.SortDescriptions.Add(
                            new SortDescription(_sortColumn, _sortDirection));
}

The XAML part of the View is the same as in the Second Generation.

<UserControl.Resources>
  <DataTemplate x:Key="ArrowUp">
    <DockPanel>
      <TextBlock HorizontalAlignment="Center"
                 Text="{Binding}" />
      <Path VerticalAlignment="Center"
            Fill="Black"
            Data="M 5,5 15,5 10,0 5,5" />
    </DockPanel>
  </DataTemplate>
  <DataTemplate x:Key="ArrowDown">
    <DockPanel>
      <TextBlock HorizontalAlignment="Center"
                 Text="{Binding}" />
      <Path VerticalAlignment="Center"
            Fill="Black"
            Data="M 5,0 10,5 15,0 5,0" />
    </DockPanel>
  </DataTemplate>
</UserControl.Resources>
 
<Grid>
  <ListView ItemsSource="{Binding ThirdResultDataView}"
            GridViewColumnHeader.Click="ThirdResultDataViewClick">
    <ListView.View>
      <GridView>
        <GridViewColumn DisplayMemberBinding="{Binding ResultNumber}">
          <GridViewColumnHeader Content="Number" />
        </GridViewColumn>
        <GridViewColumn DisplayMemberBinding="{Binding ResultOutput}">
          <GridViewColumnHeader Content="Output" />
        </GridViewColumn>
      </GridView>
    </ListView.View>
  </ListView>
</Grid>

The code-behind of the View is nearly the same as in the Second Generation, but the call of the sorting method in the ViewModel is added.

private ListSortDirection _sortDirection;
private GridViewColumnHeader _sortColumn;

private void ThirdResultDataViewClick(object sender, RoutedEventArgs e)
{
  GridViewColumnHeader column = e.OriginalSource as GridViewColumnHeader;
  if (column == null)
  {
    return;
  }

  if (_sortColumn == column)
  {
    // Toggle sorting direction
    _sortDirection = _sortDirection == ListSortDirection.Ascending ?
                                       ListSortDirection.Descending :
                                       ListSortDirection.Ascending;
  }
  else
  {
    // Remove arrow from previously sorted header
    if (_sortColumn != null)
    {
      _sortColumn.Column.HeaderTemplate = null;
      _sortColumn.Column.Width = _sortColumn.ActualWidth - 20;
    }

    _sortColumn = column;
    _sortDirection = ListSortDirection.Ascending;
    column.Column.Width = column.ActualWidth + 20;
  }
 
  if (_sortDirection == ListSortDirection.Ascending)
  {
    column.Column.HeaderTemplate = Resources["ArrowUp"as DataTemplate;
  }
  else
  {
    column.Column.HeaderTemplate = Resources["ArrowDown"as DataTemplate;
  }

  string header = string.Empty;

  // if binding is used and property name doesn't match header content
  Binding b = _sortColumn.Column.DisplayMemberBinding as Binding;
  if (b != null)
  {
    header = b.Path.Path;
  }

  var viewModel = DataContext as SortingViewModel;
  viewModel.Sort(header);
}

The source code can be downloaded from http://code.msdn.microsoft.com/Sorting-a-WPF-ListView-by-ce9cf6d7

Further Posts

  1. Sorting a WPF ListView/GridView by clicking on the header
  2. Sort Direction Indicators 
  3. Sort in ViewModel 
  4. Sort Direction Indicators with Adorners
  5. Reusability
  6. More Reusability
  7. Attached Property
  8. Behaviors (Expression Blend)


No comments:

Post a Comment