0
votes

I have a GridView which I dynamically add rows to through the button_OnClick event, this adds a new row with the Product name & Product ID from a DropDownList and also contains a column with an empty text box for user input.

My problem is that when I test it and enter data in the text box, then add another Product, the post back causes my data to be lost (the correct number of rows are still there with product names / ids).

                            <asp:GridView runat="server" ID="grdSelectedProducts" BorderWidth="1px" CellPadding="3" CellSpacing="2" AutoGenerateColumns="False" OnRowDataBound="grdSelectedProducts_OnRowDataBound" ShowHeaderWhenEmpty="True" DataKeyNames="ProductId"
                                OnRowCommand="grdSelectedProducts_RowCommand" OnRowDeleted="grdSelectedProducts_RowDeleted" OnRowDeleting="grdSelectedProducts_RowDeleting" EmptyDataText="Please select a Product and click 'Add'" EnableViewState="True">
                                <Columns>
                                    <asp:BoundField DataField="Product" HeaderText="Product" ReadOnly="False"/>
                                    <asp:TemplateField HeaderText="Description">
                                        <ItemTemplate>
                                                 <asp:TextBox runat="server" ID="txtDescriptionEntry" Text="" style="width:98% !important" EnableViewState="True"></asp:TextBox>
                                        </ItemTemplate>
                                    </asp:TemplateField>
                                    <asp:TemplateField>
                                        <ItemTemplate>
                                            <asp:LinkButton runat="server" ID="linkDelete" runat="server" CommandName="Delete" CommandArgument="<%# Container.DataItemIndex %>">Remove</asp:LinkButton>
                                        </ItemTemplate>
                                    </asp:TemplateField>
                                    <asp:BoundField DataField="ProductId" HeaderText="ProductId" ReadOnly="False" Visible="False" />
                                </Columns>
                            </asp:GridView>

How can I avoid postback scrapping the data on each txtDescriptionEntry that is created? There could be between 0 and an infinite number of these text boxes, so I won't have exact names at any one time.

Edit, as per a comment below I'm including the code for how I add rows to the grid: private DataTable ProductDataTable { get {return ViewState["ProductDataTable"] as DataTable ?? new DataTable(); } set { ViewState["ProductDataTable"] = value; } }

    private DataTable CreateDataTable(bool isAddingValue, string selectedProduct, string selectedId)
    {
        // if isAddingValue is FALSE then it isn't from a button click to add a Product, it is just
        // a call to create the datatable

        DataTable dataTable = ProductDataTable;
        if (!dataTable.Columns.Contains("Product"))
        {
            dataTable.Columns.Add("Product");
            dataTable.Columns.Add("Description"); // This column is free format text that the user enters.
            dataTable.Columns.Add("ProductId");
        }

        if (isAddingValue)
        {
            // Get the data from ViewState
            //dataTable = ProductDataTable;
            DataRow dataRow;
            dataRow = dataTable.NewRow();
            dataRow["Product"] = selectedProduct;
            dataRow["ProductId"] = selectedId;
            dataTable.Rows.Add(dataRow);
        }
        else
        {
            grdSelectedProducts.DataSource = null;
            grdSelectedProducts.DataSource = ProductDataTable;
            grdSelectedProducts.DataBind();

        }
        // Save the data back to ViewState
        ProductDataTable = dataTable;

        return dataTable;
    }

    protected void btnAddProduct_OnClick(object sender, EventArgs e)
    {
        string selectedProduct = ddlProduct.SelectedItem.Text;
        string selectedId = ddlProduct.SelectedValue;

        DataTable dataTable = CreateDataTable(true, selectedProduct, selectedId);

        grdSelectedProducts.DataSource = dataTable;
        grdSelectedProducts.DataBind();

    }

    protected void grdSelectedProducts_RowCommand(object sender, GridViewCommandEventArgs e)
    {
        if (e.CommandName == "Delete")
        {
            if (!string.IsNullOrEmpty(e.CommandArgument.ToString()))
            {
                int rowIndex = Convert.ToInt32(e.CommandArgument);
                DataTable table = CreateDataTable(false, string.Empty, string.Empty);
                table.Rows.RemoveAt(rowIndex);
                grdSelectedProducts.DataSource = table;
                grdSelectedProducts.DataBind();
            }
        }
    }

And in the Page_Load event, if it isn't a PostBack there is also binding of an empty list

        grdSelectedProducts.DataSource = new List<Products>();
        grdSelectedProducts.DataBind();
1
How you add data in GridView - Adil
@Adil I've updated the post. - MattR
Dont you get records in GridView when btnAddProduct do the postback? Do you bind GridView anywhere else also? - Adil
There is a bind on the Page_load event which binds an empty list to the grid, and there is binding when removing a row, but nothing other than that. I've updated the main text again. - MattR
Could you please add the page_load as well to the code. And how are you storing the text data. Do you push it to the database and pull it back? - Alok

1 Answers

0
votes

I figured this out, not the nicest solution (so I probably wouldn't recommend it) but it got me out of a jam.

I used the OnRowDataBound event so for each row that was being bound to the table I would use the RowIndex to get the appropriate row in the DataTable, I would then assign the text box (also obtained via the index) the value from the DT. Example below.

    protected void grdSelectedproducts_OnRowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.RowIndex > -1)
        {
            TextBox txtDescription = e.Row.FindControl("txtDescriptionEntry") as TextBox;

            if (txtDescription != null)
            {
                DataTable dt = ProductDataTable;
                DataRow row = dt.Rows[e.Row.RowIndex] as DataRow;
                txtDescription.Text = row[1].ToString();
            }
        }
    }