Home > Mobile >  How to get the values of the DataSource row that was used to compose another asp:GridView field?
How to get the values of the DataSource row that was used to compose another asp:GridView field?

Time:11-28

The asp:GridView displays the order lines. It contains only two BoundFields: ID (not visible) and html_text_info (visible). The gvOrderLines.DataSource is explicitly assigned by the DataTable that was obtained by calling the SQL stored procedure. Before that,the html_text_info is added to the DataTable, and it is filled by the formatted HTML text where other columns are the source for the information. See the following method (simplified):

private void LoadGridViewOrderLines()
{
    // Get the data table with the order-line data for the order.
    DataTable dt = DbUtil.GetAppTable(
        "usp_order_lines @order_code=@order_code",
        new Hashtable { { "order_code", OrderCode } },
        commandTimeoutSeconds: 180);

    // Add the field html_text_info, and format the info into it.
    dt.Columns.Add("html_text_info", typeof(string));
    StringBuilder sb = new StringBuilder();
    
    foreach (DataRow row in dt.Rows)
    {
        sb.Clear();

        // Get the values from the row.
        string product_code = row.Field<string>("product_code");
        string product_name = row.Field<string>("product_name");
        double quantity = ... get the quantity and convert to double...;
        string unit =  row.Field<string>("unit");
        double price = ... get the price and convert to double...;

        // Format it as an HTML string.
        sb.Append($"{product_code}<br />");
        sb.Append($"<b>{product_name}</b><br />");
        sb.Append($"Quantity: <b>{quantity:f2} {unit}</b><br />");
        sb.Append($"Price: <b>{price:f3}</b><br />");

        // Set the formatted value to the field. 
        row["html_text_info"] = sb.ToString();
    }

    gvOrderLines.DataSource = dt;
    gvOrderLines.DataBind();
}

Now, I want to edit/update the order item. So, I need to access the row from the DataTable that is used as the DataSource. I already have the handler that gets correctly the ID into the property UpdateID (because it was named as DataKeyNames="ID" for the grid view; see below). How can I get the source values that form the composed field?

protected void gvOrderLines_RowEditing(object sender, System.Web.UI.WebControls.GridViewEditEventArgs e)
{
    UpdateID = (int)gvOrderLines.DataKeys[e.NewEditIndex].Value;

    // How to get the source values that form the composed field? 
    tbCode.Text = ???;
    tbName.Text = ???;
    tbQuantity.Text = ???;
    tbPrice.Text = ???;

    gvOrderLines.EditIndex = -1;   // cancel
    gvOrderLines.DataBind();
}

Is the DataRow for current e.NewEditIndex easily accessible? Or do I have to search on my own in the DataTable (that is the gvOrderLines.DataSource)?

CodePudding user response:

The "data row" source ONLY persists during the data binding operations.

So for example, you have "looping" code after you pull the data to on the fly create those extra columns. I often do that, but I still in most cases would suggest you do that "one row" operation in the gv, and not against the table.

This suggestion is not a "must" do, but it can help. Since then you are free to have other buttons, filters or whatever and JUST toss/pass/send/throw to the gv the data source. (so, each time you pull/create/have/use/filter etc. the data source, you don't have to modify it with that extra column.

Thus this:

   protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
    {

        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            DataRowView gData = e.Row.DataItem as DataRowView;  // the data source row!!!!
            Label Info = (Label)e.Row.FindControl("lblInfo");

            StringBuilder sb = new StringBuilder();
            sb.Clear();

            // Get the values from the row.
            string product_code = gData["product_code"].ToString();

            string product_name = gData["product_name"].ToString();
            double quantity = ... get the quantity and convert to double...;
            string unit = gData["unit"].ToString();
            double price = ... get the price and convert to double...;

            // Format it as an HTML string.
            sb.Append($"{product_code}<br />");
            sb.Append($"<b>{product_name}</b><br />");
            sb.Append($"Quantity: <b>{quantity:f2} {unit}</b><br />");
            sb.Append($"Price: <b>{price:f3}</b><br />");

            // Set the formatted value to the field. 
            Info.Text = sb.ToString();

        }
    }

however, as I stated/noted, the so-called data source (dataitem) that seems to show all over the place in most GV events, is ONLY persisted during the binding process. Once binding is done, then that data item goes out of scope.

Next up, a button click to edit/get the one row.

I (now) in most cases do NOT bother with the gv event model WHEN most of the code and editing is custom code (code you the developer is writing and wanting control of what the heck is going to occur!!!).

So, just drop in a plane jane button into the grid row, and use a plane jane standard button click event.

Say, like this:

                <asp:BoundField DataField="HotelName" HeaderText="Hotel Name"    />
                <asp:BoundField DataField="Description" HeaderText="Description" ItemStyle-Width="270" />
                <asp:TemplateField HeaderText="Edit">
                    <ItemTemplate>
                        <asp:Button ID="cmdEdit" runat="server" Text="Edit" 
                            CssClass="btn" OnClick="cmdEdit_Click" />
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>

So, note how I do (did not) bother with using the built in gv events.

So, the button click event - a plane jane one looks like this:

    protected void cmdEdit_Click(object sender, EventArgs e)
    {
        Button btn = sender as Button;
        GridViewRow gRow = btn.NamingContainer as GridViewRow;
        int PKID = (int)GridView1.DataKeys[gRow.RowIndex]["ID"];
        Debug.Print("row index click = "   gRow.RowIndex);
        Debug.Print("database PK id = "   PKID);

        string strSQL = "SELECT * FROM tblHotelsA WHERE ID = "   PKID;
        DataRow rstData = MyRst(strSQL).Rows[0];

.. etc. etc.

From that row click, we get/use/have the database PK id, and thus have to re-pull the data. Note how we NEVER exposed/used/have the database PK id in the gv markup - (for reasons of security). (and I see you ALSO using datakeys - good!!).

So, your edit button click (now a plane jane click event) becomes somthing like this:

    protected void cmdEdit_Click(object sender, EventArgs e)
    {
        GridViewRow gRow = btn.NamingContainer as GridViewRow;
        int PKID = (int)GridView1.DataKeys[gRow.RowIndex]["ID"];

        // How to get the source values that form the composed field? 

        // answer: we re-pull the database row based on pk

        int UpdateID = (int)gvOrderLines.DataKeys[e.NewEditIndex].Value;
        string strSQL = "SELECT * FROM MyTable WHERE ID = "   UpdateID;

        DataRow OneRow = MyRst(strSQL).Rows[0];

        // any display value in gv - you use find control ,or cells[] collection
        TextBox txtHotelName = gRow.FindControl("txtHotel") as TextBox;

Just remember that dataitem" is available ONLY during binding, and after the databinding to the gv is done, then it (the dataitem) goes out of scope.

If the value(s) you need are not in the gv "display", then you need to re-pull that one row for such values.

templated controls in gv - use find control built in, use cells[] collection.

Now, to be fair, I don't much use the built in "edit" function, and I just drop in a simple edit button, and either hide the gv, show the hidden edit "div" I have. Or better yet, pop that div like this:

enter image description here

  • Related