Home > Back-end >  How to select the correct rows in a gridview after sort
How to select the correct rows in a gridview after sort

Time:11-30

I have been working on this gridview project for a few days and have been able to make some progress. Where I am having difficulty is after sorting the gridview which works fine, I want to take the selected rows from my gridview and place those selected rows into a repeater control. I have been looking here for people with the same issue and have tried using viewstate but I continue to only get the default sort order into the repeater control. Example row 0 is selected with the name john smith after sort I have jane doe at row 0 but when selecting row 0 and jane doe I get john smith in the repeater control. I know that this has to be something simple but I have tried many things with not luck. This is my sort code which works.

protected void GridView1_Sorting1(object sender, GridViewSortEventArgs e)
{
  DataTable dtrslt = (DataTable)ViewState["dirState"];
 
  if (dtrslt.Rows.Count > 0)
  {
    
    if (Convert.ToString(ViewState["sortdr"]) == "Asc")
    {
      dtrslt.DefaultView.Sort = e.SortExpression   " Desc";
      ViewState["sortdr"] = "Desc";
    }
    else
    {
      dtrslt.DefaultView.Sort = e.SortExpression   " Asc";
      ViewState["sortdr"] = "Asc";
    }
    ViewState["sortexp"] = e.SortExpression; //resort and store.
    ViewState["dirState"] = dtrslt;
    GridView1.DataSource = dtrslt;
    GridView1.DataBind();
  }
}

Now I have a button that will copy the data from the first datatable to a new datatable which only contains the selected rows. I am sure that I am having the computer do more work than it should but I was trying everything.

This code below is copying the data into the new datatable and binding to the repeater control. Works fine if I don't sort the gridview. Please let me know your thoughts or if I need to provide additional information.

protected void CopyToDataTable(object sender, EventArgs e)
{
  //Create a new DataTable.
  DataTable dtNotary = new DataTable();
  DataTable dtrslt = (DataTable)ViewState["dirState"];
  
  dtNotary = dtrslt.Clone();

  DataRow dr = dtrslt.Rows[0];
  
  //Loop through the GridView and copy rows.
  foreach (GridViewRow gRow in GridView1.Rows)
  {
    CheckBox ckBox = (CheckBox)gRow.FindControl("notaryselect");
    if (ckBox.Checked)
    {
      dr = dtrslt.Rows[gRow.RowIndex];
      dtNotary.ImportRow(dr);
    }

  }
 
  RepeaterNotary.DataSource = dtNotary
  RepeaterNotary.DataBind();

}

CodePudding user response:

Ok, keep in mind that you using the "default" view.

that means the table (and hence row index INTO that table will not match) after you sort.

So, when you use a "data view", it does NOT effect nor change the order of hte base table.

So, this:

        DataTable rstData = new DataTable();
        rstData = General.MyRst("SELECT ID, HotelName FROM tblHotelsA");

        for (int i = 0; i < rstData.Rows.Count; i  )
        {
            Debug.Print($"{rstData.Rows[i]["ID"].ToString()} H= {rstData.Rows[i]["HotelName"]}");
        }

        // now sort table by id REVERSE order!
        rstData.DefaultView.Sort = "ID DESC";

        for (int i = 0; i < rstData.Rows.Count; i  )
        {
            Debug.Print($"{rstData.Rows[i]["ID"].ToString()} H= {rstData.Rows[i]["HotelName"]}");
        }

        Debug.Print("---- sorted output");

        foreach(DataRowView OneRow in rstData.DefaultView)
        {
            Debug.Print($"{OneRow["ID"].ToString()} H= {OneRow["HotelName"]}");
        }

Output:

    1 H = My Cool
    2 H = Ramada Lodge
    3 H = Sandman Inn
    4 H = Super 8
    5 H = Inns of Banff
    6 H = Swiss Village
    7 H = Mary's Motel
    16 H = Batman's Cave
    1 H = My Cool
    2 H = Ramada Lodge
    3 H = Sandman Inn
    4 H = Super 8
    5 H = Inns of Banff
    6 H = Swiss Village
    7 H = Mary's Motel
    16 H = Batman's Cave
    ---- sorted output
    16 H = Batman's Cave
    7 H = Mary's Motel
    6 H = Swiss Village
    5 H = Inns of Banff
    4 H = Super 8
    3 H = Sandman Inn
    2 H = Ramada Lodge
    1 H = My Cool

NOTE VERY close - after I sorted the "data view", the table order DOES not change when I reference the rows!!!!

Only the 3rd run shows the data sorted. And you could do say this:

        Debug.Print("---- sorted output example 2");
        DataTable dt2 = rstData.DefaultView.ToTable();

        for (int i = 0; i < dt2.Rows.Count; i  )
        {
            Debug.Print($"{dt2.Rows[i]["ID"].ToString()} H= {dt2.Rows[i]["HotelName"]}");

        }

So, in above, I used the "totable()" to get a real sorted view!

Now, lets check the results of that table in the grid view.

this will show this even better:

        DataTable rstData = new DataTable();
        rstData = General.MyRst("SELECT ID, HotelName FROM tblHotelsA");
        // now sort table by id REVERSE order!

        rstData.DefaultView.Sort = "ID DESC";

        GridView1.DataSource = rstData;
        GridView1.DataBind();

        foreach (GridViewRow gRow in GridView1.Rows)
        {
            Debug.Print($"Grid row {gRow.RowIndex} First GV ID = {gRow.Cells[0].Text} First Table row ID = {rstData.Rows[gRow.RowIndex]["ID"]}");
        }

Output:

        Grid row 0 First GV ID = 16 First Table row ID = 1
        Grid row 1 First GV ID = 7 First Table row ID = 2
        Grid row 2 First GV ID = 6 First Table row ID = 3
        Grid row 3 First GV ID = 5 First Table row ID = 4
        Grid row 4 First GV ID = 4 First Table row ID = 5
        Grid row 5 First GV ID = 3 First Table row ID = 6
        Grid row 6 First GV ID = 2 First Table row ID = 7
        Grid row 7 First GV ID = 1 First Table row ID = 16

So, once again, you can see the "default" view is used by the GV, but the base table is still not sorted.

I tend to NOT use the built in "default" data view, since you can wind up with the above confusing. thus, a lot of my code feeds the "data view" to the table, and not the "default view". I do this "only" to reduce confusing.

eg this code is common:

        Dim rstGridData As DataTable
        rstGridData = MyrstP(cmdSQL)

        Dim dView As DataView = rstGridData.DefaultView
        dView.RowFilter = strWhere
        dView.Sort = "QuoteDate DESC, ID DESC"
        Me.ListView1.DataSource = rstBlank

Note HOW I send the data view to the gridview/lv in above.

Ok, I (probably spent too much time on above - my sorry!!).

So, next up:

I would actually re-pull the data based on the PK settings. both GV, and LV have what is called "data keys", and it is a great collection of the PK row "id", and even better is NEVER exposed to the client side browser (good for security - people can't mess with your pk's in the markup!!!).

And also, keep in mind for most cases, re-pulling the data will cost LESS then persisting the data table in viewstate!!!

So, I would say suggest using this:

        string MyIdList = "";
        foreach (GridViewRow gRow in GridView1.Rows)
        {
            CheckBox ckBox = (CheckBox)gRow.FindControl("chkActive");
            if (ckBox.Checked)
            {
                if (MyIdList != "")
                    MyIdList  = ",";

                MyIdList  = GridView1.DataKeys[gRow.RowIndex]["ID"].ToString();
            }

        }

        DataView MyView = rstData.DefaultView;
        MyView.RowFilter = $"ID IN({MyIdList})";

        Repeater1.DataSource = MyView;
        Repeater1.DataBind();

In other words, operate by PK database ID (assuming you have one).

So, in the gv define, you need to set the datakeys "ID" (column name) of the PK value.

eg:

        <asp:GridView ID="GridView1" runat="server" CssClass="table"
            DataKeyNames="ID" AutoGenerateColumns="false">
            <Columns>
                <asp:BoundField DataField="FirstName" HeaderText="FirstName" />

etc.

So, datakeys is nice, since then based on GV row, you can always get/have use of the database PK row id, we don't care about order, and even better is the PK row idea does not have to be exposed nor included in the markup.

  • Related