Welcome to MSDN Blogs Sign in | Join | Help

Vincent Sibal's Blog

WPF Programming Topics
WPF DataGrid: Tri-state Sorting sample

Here is a short sample on how to create a tri-state sorting experience with the WPF DataGrid.  In the current design, when clicking on a column header it will toggle sorting of the column starting from ascending to descending.  Unfortunately you cannot get back to the original state without adding your own custom logic for it.  To get back to the original state you have to set the column.SortDirection back to null and clear out the SortDescriptions that were added when you click on the column header.  In this sample I decided to add one more toggle state so when you click on the column header a third time it goes back to the original state.  I’m adding this functionality to the DataGrid.Sorting event and stop the default sort if the toggle state is going back to the original state.  Here is the code:       

private void DataGrid_Standard_Sorting(object sender, DataGridSortingEventArgs e)

{

  DataGrid dataGrid = sender as DataGrid;

  string sortPropertyName = Helpers.GetSortMemberPath(e.Column);

  if (!string.IsNullOrEmpty(sortPropertyName))

  {

    // sorting is cleared when the previous state is Descending

    if (e.Column.SortDirection.HasValue && e.Column.SortDirection.Value == ListSortDirection.Descending)

    {
      int
index = Helpers.FindSortDescription(dataGrid.Items.SortDescriptions, sortPropertyName);

      if (index != -1)

      {
          e.Column.SortDirection = null;

 

          // remove the sort description

          dataGrid.Items.SortDescriptions.RemoveAt(index);

          dataGrid.Items.Refresh();

 

          if ((Keyboard.Modifiers & ModifierKeys.Shift) != ModifierKeys.Shift)

          {
             // clear any other sort descriptions for the multisorting case

             dataGrid.Items.SortDescriptions.Clear();

             dataGrid.Items.Refresh();
          }

 

          // stop the default sort

          e.Handled = true;

      }
    }
  }

}

 

I also decided to add some extra functionality for displaying the sort order of the column when doing multi-column sorting.  By default when you do multi-column sorting you really have no idea which column is sorted in which order as it only shows that direction triangle.  I just add text to the header but you can always get creative and add all that WPF eye candy.  Here is a screenshot:

sortingOrder

The full sample is here.

Other DataGrid Samples:

ScrollViewer with ToolTip

Custom sorting, column selection, single-click editing

Styling rows and columns based on header conditions

Clipboard Copy

Posted: Friday, August 29, 2008 8:59 AM by vinsibal
Attachment(s): DataGrid_V1_TriStateSortingSample.zip

Comments

Vincent Sibal's Blog said:

Overview The DataGrid uses a set of DataGridColumns to describe how to display its data just like a GridView

# August 29, 2008 12:32 PM

Vincent Sibal's Blog said:

If you haven’t already, you can download the binaries and source for the DataGrid here . DataGridComboBoxColumn

# August 29, 2008 12:35 PM

_gdc said:

Hello,

I need to use the DataGrid with a class that implements IEnumerable<>,

I don't want to subclass ObservableCollection<>,

but I have problems doing this.

Using your example, I change your class:

public class People : ObservableCollection<Person>

with this new (where I use _peopleList to keep the data):

public class People : IEnumerable<Person>

{

 private List<Person> _peopleList;

 private Random random = new Random();

 public People()

 {

   CreateGenericPersonData(1);

 }

 public People(int multiplier)

 {

   CreateGenericPersonData(multiplier);

 }

 private void CreateGenericPersonData(int multiplier)

 {

   _peopleList = new List<Person>();

   if (multiplier > 0)

   {

     for (int i = 0; i < multiplier; i++)

     {

       _peopleList.Add(new Person("George", "Washington", GenerateRandomDate()));

       _peopleList.Add(new Person("John", "Adams", GenerateRandomDate()));

       _peopleList.Add(new Person("Thomas", "Jefferson", GenerateRandomDate()));

       //...

       _peopleList.Add(new Person("George", "W.", "Bush", GenerateRandomDate()));

     }

   }

 }

 private DateTime GenerateRandomDate()

 {

   int randInt = random.Next();

   return new DateTime(Convert.ToInt64(randInt));

 }

 #region IEnumerable<Person> Members

 public IEnumerator<Person> GetEnumerator()

 {

   return _peopleList.GetEnumerator();

 }

 #endregion

 #region IEnumerable Members

 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()

 {

   return _peopleList.GetEnumerator();

 }

 #endregion

}

If I try this, I receive the exception:

Cannot create instance of 'DataGridBasicSample_Demo' defined in assembly  ...

Where am I wrong?

Thank you

# September 2, 2008 5:59 AM

vinsibal said:

_gdc,

In my example I am getting the view from the ItemsSource and expect a ListCollectionView.  Since you have changed it to IEnumerable<T> it is no longer a ListCollectionView.  In the constructor of my sample, try removing the lines related to getting the CollectionViewSource.  It should work after that.

-Vince

# September 2, 2008 9:56 AM

Karl said:

Started playing with the Datagrid in a WPF app runing on a desktop.

I have notice that when I drag the scroll button on some machines, there is a pretty chunky lag. The content redraw updates in a unpleasant, laggy way.

There were only 500 items in the grid...

# September 8, 2008 7:51 AM

vinsibal said:

Karl,

Would you mind posting the specs for the machine that you find that it is lagging.

Thanks,

Vince

# September 8, 2008 8:28 AM

Karl said:

Opps never mind. Scrolling works fine.

The issue is that if I set my ItemSource to an custom

ObservableCollection<T>

T must define a public string property named "Id" or the binding will throw a bad path and silently swallow the exception on all the rows.

Which absolutely kills the performance of scroll....

You get this in the output window of the debugger.

System.Windows.Data Error: 39 :

BindingExpression path error: 'Id' property not found on 'object' ''RowProxy' (HashCode=11295760)'.

BindingExpression:Path=Id; DataItem='RowProxy' (HashCode=11295760);

target element is 'DataGridRow' (Name='');

target property is 'Header' (type 'Object')

# September 8, 2008 8:29 AM

RobertT said:

Hi Vincent!

Will you be so kind and help me with a following scenario? =)

Imagine I have a DataGrid bound with ObservableCollection. I want to prevent user from editing existing rows and enable only to add new rows and edit'em right in a place or delete new or existing rows. How can I achieve such a scenario?

Best regards,

RobertT

# September 16, 2008 3:36 AM

vinsibal said:

RobertT,

One possible solution is to listen to the DataGrid.BeginningEdit event and cancel the BeginEdit if the row they are trying to edit is an existing row.  Deleting is a separate operation so it will work as normal with this solution.

# September 16, 2008 9:07 AM

Vincent Sibal's Blog said:

As you might have heard, .NET Framework 3.5 SP1 and Visual Studio 2008 SP1 are out today! There are a

# September 16, 2008 11:14 AM

Vincent Sibal's Blog said:

In the v1 release (and CTP) of the WPF DataGrid there will be support for Clipboard.Copy but no support

# September 19, 2008 11:07 AM

Priya said:

Hi, can v populate DataGrid dynamically. If yes, can u post a sample on the same using DataGridTextColumn() and DataGridRow() (similar to how we add in DataTable) Or is there other way of doing it cos am stuck with it.

Thanks in Advance.

# December 4, 2008 7:31 AM

vinsibal said:

Priya,

DataGrid can be populated dynamically, but not isn the same way as the DataTable.  The DataGrid is bound to an ItemsSource so as items are added and removed the DataGrid will update as necessary.  Could you expand on your scenario a bit and hopefully I can provide further assistance.

# December 18, 2008 2:51 PM

Bharti said:

Hello,

 I am using WPF toolokit's data grid, and inthat grid I have a column for checkbox which I add as below:

<toolkit:DataGridTemplateColumn >

                       <toolkit:DataGridTemplateColumn.CellTemplate>

                           <DataTemplate>

                               <CheckBox Click="OnUsedClicked"  

                                       IsChecked="{Binding Path=Used}"

                                        />

                           </DataTemplate>

                       </toolkit:DataGridTemplateColumn.CellTemplate>

                   </toolkit:DataGridTemplateColumn>

I would like to sort on all columns of my grid, so to the grid defination I added

CanUserSortColumns="True"  to my grid.

On doing this the rest of the columns get sorted but not the checkbox column.By checkbox column sorting I mean that all checked boxes(rows) to be together and all unchecked boxes(rows) to be together. I do understand there might be multiple checked so what basis needs to be used, I have a time column , I can use that along with it.

i.e sorting on Checkbox + Time

this would lead to all say unchecked together , based on time as the second parameter.

I am not sure if this is possible, if yes how?

Regards

Barti

I would like that

# November 12, 2009 1:37 AM

Thomas said:

I'm curious how to shrink the column back to its original size after the sort description is removed.  For example, click the Cake column 3 times.  When the header goes back to "Cake", the column doesn't shrink.

Thanks for a super helpful blogpost.

# January 14, 2010 9:56 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

  
Enter Code Here: Required

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker