donsonde
donsonde

Reputation: 87

C#- Maintain State of Checkbox and textbox in gridview on page changing

I have a gridview having a textbox and a checkbox.when the checkbox is checked,a value will be populated into the textbox which a user may alter the value.

Here is the code for the gridview

<asp:GridView ID="gvPayment" runat ="server" AutoGenerateColumns="False" CellPadding="4" ForeColor="#333333" GridLines="Vertical" Width="100%" ShowFooter="true" CssClass="jumbSize1" OnRowDataBound="gvPayment_RowDataBound" AllowPaging="true" PageSize="5" OnPageIndexChanging="gvPayment_PageIndexChanging" >
<Columns>
    <asp:TemplateField HeaderStyle-Width="5%">
        <ItemTemplate>
            <asp:CheckBox ID="chkSelect" runat="server" CssClass="checkbox" OnCheckedChanged="chkSelect_CheckedChanged" AutoPostBack="true" />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="TRANSACTION DATE" HeaderStyle-Width="15%">
        <ItemTemplate>
            <asp:Label ID="SalesDate" runat="server" Text='<%#Eval("Date", "{0:dd/MM/yyyy}") %>' />
        </ItemTemplate>
        <FooterTemplate>
            <asp:Button ID="Submit" Text="Submit" runat="server" OnClick="Submit_Click" CssClass="btn btn-primary" />
        </FooterTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Sales Code" HeaderStyle-Width="15%">
        <ItemTemplate>
            <asp:Label ID="SalesCode" runat="server" Text='<%#Eval("SALES_CODE") %>' />
        </ItemTemplate>
         <FooterTemplate>
        <div style="padding: 0 0 5px 0;">
            <asp:Label Text="Page Totals" runat="server" align="right" />
        </div>
         <div>
            <asp:Label Text="Grand Totals" runat="server" align="right" />
        </div>
    </FooterTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="ACTUAL SALE" HeaderStyle-Width="15%">
        <ItemTemplate>
            <asp:Label ID="ActualSales" runat="server" Text='<%#Eval("ACTUAL", "{0:N2}") %>' />
        </ItemTemplate>
         <FooterTemplate>
        <div style="padding: 0 0 5px 0;">
            <asp:Label ID="PageActual" runat="server" align="right" />
        </div>
         <div>
            <asp:Label ID="GrandActual" runat="server" align="right" />
        </div>
    </FooterTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="ADVANCE" HeaderStyle-Width="15%">
        <ItemTemplate>
            <asp:Label ID="AdvPay" runat="server" Text='<%#Eval("ADVANCE", "{0:N2}") %>' />
        </ItemTemplate>
         <FooterTemplate>
        <div style="padding: 0 0 5px 0;">
            <asp:Label ID="PageAdvance" runat="server" align="left" />
        </div>
         <div>
            <asp:Label ID="GrandAdvance" runat="server" align="left" />
        </div>
    </FooterTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="AMOUNT DUE" HeaderStyle-Width="15%">
        <ItemTemplate>
            <asp:Label ID="AmtDue" runat="server" Text='<%#Eval("DUE", "{0:N2}") %>' />
        </ItemTemplate>
         <FooterTemplate>
        <div style="padding: 0 0 5px 0;">
            <asp:Label ID="PageDue" runat="server" align="left" />
        </div>
         <div>
            <asp:Label ID="GrandDue" runat="server" align="left" />
        </div>
    </FooterTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="AMOUNT PAID" HeaderStyle-Width="15%">
        <ItemTemplate>
            <asp:TextBox ID="AmtPaid" runat="server" CssClass="form-control CapLock" />
        </ItemTemplate>
    </asp:TemplateField>
</Columns>
<AlternatingRowStyle BackColor="#4870BE" ForeColor="#FFFFFF" />
<FooterStyle BackColor="#76543c" ForeColor="#FFFFFF" Font-Bold="true" />
<HeaderStyle BackColor="#76543c" ForeColor="#FFFFFF" Font-Bold="true" />
<PagerSettings Mode="NextPrevious" NextPageText="Next &amp;gt;&amp;gt;" PreviousPageText="Prev &amp;lt;&amp;lt;" />
<PagerStyle BackColor="#76543c" ForeColor="#FFFFFF" HorizontalAlign="Center" />
<RowStyle BackColor="#EFF3FB" /></asp:Gridview>

Here is the code to bind gridview to data source and get the grand totals in viewstate

private void GetCustomer()
{
    dt = new DataTable();
    try
    {
        using (con = new SqlConnection(conString))
        {
            using (cmd = con.CreateCommand())
            {
                string query = @"dbo.sp_get_sales";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.CommandText = query;
                cmd.Parameters.AddWithValue("@ID", CustInfo.SelectedValue);
                con.Open();

                da = new SqlDataAdapter();
                da.SelectCommand = cmd;
                da.Fill(dt);

                //CALCULATE THE TOTAL AMOUNTS AND HOLD THE VALUE IN A "VIEWSTATE"
                ViewState["TotalActual"] = null;
                if (ViewState["TotalActual"] == null)
                {
                    Decimal dActual = 0;
                    for (int i = 0; i <= dt.Rows.Count - 1; i++)
                    {
                        dActual += dt.Rows[i].Field<Decimal>("ACTUAL");
                    }
                    ViewState["TotalActual"] = dActual.ToString("N2");
                }

                ViewState["TotalAdvance"] = null;
                if (ViewState["TotalAdvance"] == null)
                {
                    Decimal dAdvance = 0;
                    for (int i = 0; i <= dt.Rows.Count - 1; i++)
                    {
                        dAdvance += dt.Rows[i].Field<Decimal>("ADVANCE");
                    }
                    ViewState["TotalAdvance"] = dAdvance.ToString("N2");
                }

                ViewState["TotalDue"] = null;
                if (ViewState["TotalDue"] == null)
                {
                    Decimal dDue = 0;
                    for (int i = 0; i <= dt.Rows.Count - 1; i++)
                    {
                        dDue += dt.Rows[i].Field<Decimal>("DUE");
                    }
                    ViewState["TotalDue"] = dDue.ToString("N2");
                }
            }
            //BIND QUERY RESULT WITH THE GRIDVIEW
            gvPayment.DataSource = dt;
            gvPayment.DataBind();
        }
    }
    catch (Exception ex)
    {
        ErrorMessage.Text = "An error occured: " + ex.Message;
    }
    finally
    {
        con.Close();
        con.Dispose();
    }

    foreach (GridViewRow row in gvPayment.Rows)
    {
        if (row.RowType == DataControlRowType.DataRow)
        {
            TextBox AmtPaid = (TextBox)row.FindControl("AmtPaid");
            AmtPaid.Attributes.Add("readonly", "readonly");
        }
    }
}

Here is the code to enable and populate textbox on check changed event

protected void chkSelect_CheckedChanged(object sender, EventArgs e)
{
    GridViewRow row = ((GridViewRow)((CheckBox)sender).NamingContainer);
    int index = row.RowIndex;
    CheckBox chkSelect = (CheckBox)gvPayment.Rows[index].FindControl("chkSelect");
    TextBox AmtPaid = (TextBox)gvPayment.Rows[index].FindControl("AmtPaid");
    Label AmtDue = (Label)gvPayment.Rows[index].FindControl("AmtDue");

    if (chkSelect.Checked && chkSelect != null)
    {
        AmtPaid.Text = AmtDue.Text.Replace(",", "");
        AmtPaid.Attributes.Remove("readonly");
    }
    else
    {
        AmtPaid.Text = string.Empty;
        AmtPaid.Attributes.Add("readonly", "readonly");
    }
}

The textbox get populated with the desired value when the checkbox is selected but both controls lost their values on pageindexchanging event.

I want the checkbox to maintain its state and textbox to maintain whatever value user entered when the pageindexchanging event is called.

Here is the code to move to next page

protected void gvPayment_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
    gvPayment.PageIndex = e.NewPageIndex;
    GetCustomer();
}

Note: I have gone through all suggested similar questions with no luck or success.

Upvotes: 1

Views: 1609

Answers (1)

Tim Schmelter
Tim Schmelter

Reputation: 460078

You have to persist them, for example by storing a Disctionary<int, bool> in the ViewState where the key is the RowIndex and the value is the bool(CheckBox is Checked?):

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        ViewState["EnabledAmtPaidTextBoxes"] = new Dictionary<int, bool>();
        GetCustomer();
    }
}

I would encapsulate this logic in a method, you need to call it from RowDataBound and CheckedChanged. You don't need that final loop in GetCustomer anymore:

protected void gvPayment_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        EnabledAmtPaidTextBox(e.Row);
    }
}

protected void chkSelect_CheckedChanged(object sender, EventArgs e)
{
    CheckBox chkSelect = (CheckBox)sender;
    GridViewRow row = (GridViewRow)chkSelect.NamingContainer;
    EnabledAmtPaidTextBox(row, chkSelect.Checked);
}

private void EnabledAmtPaidTextBox(GridViewRow row, bool? newCheckedOrApplyOld = null)
{
    TextBox AmtPaid = (TextBox)row.FindControl("AmtPaid");
    Label AmtDue = (Label)row.FindControl("AmtDue");
    CheckBox chkSelect = (CheckBox)row.FindControl("chkSelect");

    Dictionary<int, bool> enabledAmtPaidTextBoxes = (Dictionary<int, bool>)ViewState["EnabledAmtPaidTextBoxes"];
    if (!enabledAmtPaidTextBoxes.ContainsKey(row.RowIndex))
        enabledAmtPaidTextBoxes[row.RowIndex] = false;

    if (newCheckedOrApplyOld.HasValue)
        enabledAmtPaidTextBoxes[row.RowIndex] = newCheckedOrApplyOld.Value;
    else 
        chkSelect.Checked = enabledAmtPaidTextBoxes[row.RowIndex];

    if (enabledAmtPaidTextBoxes[row.RowIndex])
    {
        AmtPaid.Text = AmtDue.Text.Replace(",", "");
        AmtPaid.Attributes.Remove("readonly");
    }
    else
    {
        AmtPaid.Text = string.Empty;
        AmtPaid.Attributes.Add("readonly", "readonly");
    }
}

Also, if you use the using-statement you don't need con.Close and con.Dispose in finally because that is done by the using-statement already.

Upvotes: 1

Related Questions