peter
peter

Reputation: 2113

Fix the first row in a DataGrid

I am trying to keep one row in a DataGrid always on top while still being able to sort all other rows by columns (int and string columns).

The structure of my data provides a little help: The first column is named "Id", and the row that I am trying to keep on top always has the lowest Id of all Ids. This row contains aggregated values.

A typical DataGrid might look like this:

ID | Name | Result1 | Result2
5  | avg  |  2      | 5
6  | opt1 |  1      | 3
7  | opt2 |  3      | 7

There may be n columns and the number of columns will change in runtime. The DataGrid is bound to a ListCollectionView and I've also implemented a custom sorter based on trilson86's answer:

    <DataGrid result:CustomSortBehaviour.AllowCustomSort="True" 
              IsReadOnly="True" 
              ItemsSource="{Binding ResultDataView}">
    </DataGrid>

So far, using trilson86's soution, I managed to keep the first line on top when sorting. This is the handler in my CustomSortBehavior-class that prepares useful data chunks (for example the minimum-Id in the current DataGrid) for a custom sorter:

    private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e)
    {
        var dataGrid = sender as DataGrid;
        if (dataGrid == null || !GetAllowCustomSort(dataGrid)) return;
        var listColView = dataGrid.ItemsSource as ListCollectionView;
        var min = listColView.Cast<DataRowView>().Min(x => x.Row[0]);
        var sorter = new MyComparer();
        e.Handled = true;
        var direction = (e.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending;
        e.Column.SortDirection = sorter.SortDirection = direction;
        sorter.IdOfFirstRow = Convert.ToInt32(min);
        listColView.CustomSort = sorter;
    }

The custom sorter itself:

    public int Compare(object x, object y)
    {
        var rowView1 = x as DataRowView;
        var rowView2 = y as DataRowView;
        var row1 = rowView1.Row;
        var row2 = rowView2.Row;
        var row1Id = Convert.ToInt32(row1[0]);
        var row2Id = Convert.ToInt32(row2[0]);

        if (row1IdValue == IdOfFirstRow)
            return -1;

        if (row2IdValue == IdOfFirstRow)
            return 1;

        if (SortDirection == ListSortDirection.Ascending) {
            return row2Id.CompareTo(row1Id); 
        }
        else
        {
            return row1Id.CompareTo(row2Id);
        }
    }

This is only half of a solution.. hardcoded as is, I can only sort by Id. Because columns will be added at runtime, I cannot, at design time, define all columns and attach a sorter according to the columns value type (int or string).

How can I sort by all other columns while keeping the restriction that the row with the minimum Id stays on top?

Upvotes: 0

Views: 1322

Answers (1)

Bilal Bashir
Bilal Bashir

Reputation: 1493

Why not use the DisplayIndex property to get the column you are sorting.

private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e)
{
    DataGrid dataGrid = sender as DataGrid;
    if (dataGrid == null || !GetAllowCustomSort(dataGrid)) return;
    ListSortDirection direction = (e.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending;
    ListCollectionView lcv = (ListCollectionView)CollectionViewSource.GetDefaultView(dataGrid.ItemSource);
    int min = lcv.Cast<DataRowView>().Min(x => x.Row[0]);

    lcv.CustomSort = new CustomComparer(direction, e.Column.DisplayIndex, min); //DisplayIndex gets you your column
    e.Handled = true;
}

and then this comparer should do what you are looking for, not sort min ID row while sorting ints and strings in column.

public class CustomComparer : IComparer
{
     ListSortDirection _direction;
     int colNum;
     int _IdOfFirstRow;
     public CustomComparer(ListSortDirection direction, int colNum, int IdOfFirstRow)
     {
          _direction = _direction;
          _colNum = colNum;
          _IdOfFirstRow = IdOfFirstRow;
     }
     public int Compare(object x, object y)
     {
          DataRowView rowView1 = x as DataRowView;
          DataRowView rowView2 = y as DataRowView;
          int valX, valY;
          if (x == y)
             return 0;

          //Don't sort min Id
          var row1Id = Convert.ToInt32(rowView1[0]);
          var row2Id = Convert.ToInt32(rowView2[0]);

          if (row1Id == _IdOfFirstRow) 
              return -1;
          else if (row2Id == _IdOfFirstRow)
              return 1;

          string strX = rowView1[_colNum] as string;
          string strY = rowView2[_colNum] as string;
          bool ret1 = int.TryParse(strX, valX);
          bool ret2 = int.TryParse(strY, valY);

          if (ret1 == false && ret2 == false) //is a string
          {

               if (_direction == ListSortDirection.Ascending)
               {
                    return strX.CompareTo(strY);
               }
               else
               {
                    return strY.CompareTo(strX);
               }
          }
          else
          {
               if (_direction == ListSortDirection.Ascending)
               {
                    return valX.CompareTo(valY);
               }
               else
               {
                    return valY.CompareTo(valX);
               }
          }
     }
}

Upvotes: 1

Related Questions