简体   繁体   中英

Exclude the Totals Row from sorting a bound DataGridView

I am showing a Totals Row as last row. I want to exclude that row from sorting when the user clicks on the column header. By using sql union I am adding total column to my result. I am using SQL, C# and DataGridView control. I am not able to expose ColumnHeader_Click event. I am only using TableStyle[0].AllowSorting = false . How can I apply that custom sorting on the control?

Thanks

Thanks TaW, your answer helped me. My needs were a little different, I needed the Total to appear at the top and also retain the sort column throughout as my grid is highly interactive with loads of filtering and changes in the data being presented.

My sorting is done via

protected void ReportGridView_Sorting(object sender, GridViewSortEventArgs e)

Here's what I ended up using in my method to populate the GridView:

if (!myDataTable.Columns.Contains("SortLevel"))
{
    myDataTable.Columns.Add("SortLevel", typeof(Int16));
    foreach (DataRow dr in myDataTable.Rows)
    {
        dr["SortLevel"] = 0;
    }
    dt.Rows[0]["SortLevel"] = 1;
}

if ((Session["SortDirection"] != null) && (Session["SortExpression"] != null))
{
    myDataTable.DefaultView.Sort = "SortLevel DESC, " + Session["SortExpression"] + " " + Session["SortDirection"];
}
MyGridView.DataSource = myDataTable;
MyGridView.AllowSorting = true;
MyGridView.DataBind();

Side note: I had to use Sessions to hold the custom sorting instead of ViewState as this wasn't working properly with the dynamically created buttons in my gridview

This solution is based on @TS's suggestion but works directly in the DataSet, not in SQL.

I have tested it in VS2013; I don't know if it will work in .Net 1.1 and would have to revive a very old machine to test that..

I don't know what you mean by

I am not able to expose columnheader_click event.

I have split the solution into a function and the ColumnHeaderMouseClick event; if you really can't use that event you will have to find another way; but one way or the other you need to trigger the sort and decide by which column(s) to sort.

The heart of the solution are the setting of the new column values and the expression by which you identify your 'TotalsRow'. I have used my test table's PK column 'ID' and push the 'ID=1 record' to the bottom. After setting the sort column to 0 on all rows the first row to fit the expression is set to maxInt.

You will have to adapt that to an expression that works for your result set.

I am adding and setting the custom sort column dynamically and remove it after the sort; the DGV is suspending its layout until the whole affair is completed.

DataTable yourTable = .. 

private void dataGridView1_ColumnHeaderMouseClick(object sender, 
                                                  DataGridViewCellMouseEventArgs e)
{
    string col = dataGridView1.Columns[e.ColumnIndex].Name;
    if (col != "") sortDGV (col );
 }

private void sortDGV(string col)
{
    dataGridView1.SuspendLayout();
    yourTable.Columns.Add("sortMe", typeof(Int32));

    yourTable.DefaultView.Sort = col;

    DataRow[] dr = yourTable.Select("ID='1'");
    for (int r = 0; r < yourTable.Rows.Count; r++) yourTable.Rows[r]["sortMe"] = 0;
    dr[0]["sortMe"] = int.MaxValue;

    yourTable.DefaultView.Sort = "sortMe," + col;

    yourTable.Columns.Remove("sortMe");
    dataGridView1.ResumeLayout();
}

Although 8 years old, I came across this desire myself but had a different solution: I wanted the last Row to be an "Editing Row" which would always remain at the bottom regardless of sorting. So, create a DataGridViewRow separate from the regular data grid, add it after filling the DGV. Before sorting, remove the Row from the DGV, then add it again after sorting. I used Clicking on the RowHeaderText to trigger the sort (manual sort using a private class comparer for the sorting, and the DGV_Sort event to indicate sorting has completed. (You have to keep track of when you enter this event as I found it enters before and after sorting - so I used a form global boolean to keep tack of that.

I keep track of any editing on my 'my Removeable Row' separately, but you can always just Clone the row before removing if you don't want to do that.

Note that I have AllowUserToAddRows = true when I start, but after I programmatically fill the grid, I switch it to false, to prevent the addition of more 'editable' rows beneath my 1 row edit at a time desire. I have a button to add the row to the DGV when finished editing, at which time I just create a new myRemovableRow and add that row to the DGV.

Public partial class Form1 : Form
{
     public DataGridViewRow myRemoveableRow;
     public bool bDoingSort = false;
     .
     .
     private DataGridViewRow CreateNewBlankRow()
     { // create your a new row with whatever default values for DGV

       DataGridViewRow newRow = (DataGridViewRow) DGV.Rows[0].Clone();

       // or however you want to create your row and fill in default values

       return newRow;
     }

     private void FillDGV()
     {
      // Do whatever to fill your DataGridView  (called DGV here)

         myRemoveableRow = CreateNewBlankRow();
         DGV.Rows.Add(myRemoveableRow);
     }

     private void DGV_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
     {
         bDoingSort = true;
         DGV.Rows.Remove(myRemoveableRow);
         SortOrder dir = SortOrder.Ascending; // or whatever logic you use
         .
         DGV.Sort(new RowComparer(dir, e.ColumnIndex));
      }

      private void DGV_Sorted(object sender, EventArgs e)  
      {  // 'Sorted'  Event from the DataGridView events
         if (bDoingSort) 
         {  
               DGV.Rows.Add(myRemoveableRow);  
               bDoingSort = false;         //reset sorting boolean
         }
         else  // we haven't set the sorting up yet
         {
             return;
         }
     }

     // setup manual sorter (I use separate Classes for int, string, double etc)
     private class RowComparer : System.Collections.IComparer
     {   
         private static int sortOrderModifier = 1;  
         private readonly int Column;
         
         public RowComparer(SortOrder sortorder, int iColumn)
         {
            if (sortOrder == SortOrder.Descending) 
                sortOrderModifier = -1;
            else
                sortOrderModifier = 1;
            this.Column = iColumn;
         }
         public int Compare (Object objA, Object objB)
         {
             DataGridViewRow row1 = (DataGridViewRow)objA;
             DataGridViewRow row2 = (DataGridViewRow)objB;
         // do your sort compare (for eg.)
              return sortOrderModifier * (row1.Cells[Column].Value).CompareTo(row2.Cells[Column].Value); 
          }
      }
  }

I find this works well - I not only sort but filter (out) entries on the fly. (VS 2022)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM