Ecyrb
Ecyrb

Reputation: 2080

Dynamically added controls missing during GridView RowUpdating

I have a GridView with a TemplateField column that I put PlaceHolder controls in. During the DataBound event for the GridView I dynamically add a few CheckBoxes to the PlaceHolder. That works fine and displays as expected.

My problem is that during the RowUpdating event the PlaceHolder contains no controls; my CheckBoxes are missing. I also noticed that they're missing during the RowEditing event.

I want to be able to get the values of the CheckBoxes during the RowUpdating event so I can save them to the database.

Here's some example code. I've trimmed out a lot to reduce size, but if you want to see specifics just ask and I'll be happy to add more.

HTML:

<asp:GridView ID="gridView" runat="server" AutoGenerateColumns="False" 
    ondatabound="gridView_DataBound" onrowupdating="gridView_RowUpdating" 
    onrowediting="gridView_RowEditing" DataKeyNames="ID">
    <Columns>
        <asp:TemplateField HeaderText="Countries">
            <ItemTemplate>
                <asp:PlaceHolder ID="countriesPlaceHolder" runat="server"></asp:PlaceHolder>
            </ItemTemplate>
            <EditItemTemplate>
                <asp:PlaceHolder ID="countriesPlaceHolder" runat="server"></asp:PlaceHolder>
            </EditItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField ShowHeader="False">
            <ItemTemplate>
                <asp:LinkButton ID="editButton" runat="server" CommandName="Edit" Text="Edit"></asp:LinkButton>
            </ItemTemplate>
            <EditItemTemplate>
                <asp:LinkButton ID="updateButton" runat="server" CommandName="Update" Text="Update"></asp:LinkButton>
            </EditItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Code behind:

// This method works fine, no obvious problems here.
protected void gridView_DataBound(object sender, EventArgs e)
{
    // Loop through the Holidays that are bound to the GridView
    var holidays = (IEnumerable<Holiday>)gridView.DataSource;
    for (int i = 0; i < holidays.Count(); i++)
    {
        // Get the row the Holiday is bound to
        GridViewRow row = gridView.Rows[i];
        // Get the PlaceHolder control
        var placeHolder = (PlaceHolder)row.FindControl("countriesPlaceHolder");
        // Create a CheckBox for each country and add it to the PlaceHolder
        foreach (Country country in this.Countries)
        {
            bool isChecked = holidays.ElementAt(i).Countries.Any(item => item.ID == country.ID);
            var countryCheckBox = new CheckBox
            {
                Checked = isChecked,
                ID = country.Abbreviation + "CheckBox",
                Text = country.Abbreviation
            };
            placeHolder.Controls.Add(countryCheckBox);
        }
    }
}

protected void gridView_RowEditing(object sender, GridViewEditEventArgs e)
{
    // EXAMPLE: I'm expecting checkBoxControls to contain my CheckBoxes, but it's empty.
    var checkBoxControls = gridView.Rows[e.NewEditIndex].FindControl("countriesPlaceHolder").Controls;

    gridView.EditIndex = e.NewEditIndex;
    BindData();
}

protected void gridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    // EXAMPLE: I'm expecting checkBoxControls to contain my CheckBoxes, but it's empty.
    var checkBoxControls = ((PlaceHolder)gridView.Rows[e.RowIndex].FindControl("countriesPlaceHolder")).Controls;

    // This is where I'd grab the values from the controls, create an entity, and save the entity to the database.

    gridView.EditIndex = -1;
    BindData();
}

This is the article that I followed for my data binding approach: http://www.aarongoldenthal.com/post/2009/04/19/Manually-Databinding-a-GridView.aspx

Upvotes: 1

Views: 6930

Answers (1)

Renae
Renae

Reputation: 442

You need to call your BindData() method on page load.

"Dynamic controls or columns need to be recreated on every page load, because of the way that controls work. Dynamic controls do not get retained so you have to reload them on every page postback; however, viewstate will be retained for these controls."

See Cells in gridview lose controls on RowUpdating event

Also in the article you linked, there is an ItemTemplate and an EditItemTemplace because they have different displays, i.e. read only and editable. Yours are the same so I think you could simplify your design:

<asp:GridView ID="gridView" runat="server" AutoGenerateColumns="False" DataKeyNames="ID" ondatabound="gridView_DataBound">
<Columns>
    <asp:TemplateField HeaderText="Countries">
        <ItemTemplate>
            <asp:PlaceHolder ID="countriesPlaceHolder" runat="server"></asp:PlaceHolder>
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField ShowHeader="False">
        <ItemTemplate>
            <asp:LinkButton ID="editButton" runat="server" Text="Edit" onclick="editButton_Click" ></asp:LinkButton>
            <asp:LinkButton ID="updateButton" runat="server" Text="Update" onclick="updateButton_Click" ></asp:LinkButton>
        </ItemTemplate>
    </asp:TemplateField>
</Columns>
</asp:GridView>

Code Behind:

    protected void gridView_DataBound(object sender, EventArgs e)
    {
        // Loop through the Holidays that are bound to the GridView
        var holidays = (IEnumerable<Holiday>)gridView.DataSource;
        for (int i = 0; i < holidays.Count(); i++)
        {
            // Get the row the Holiday is bound to
            GridViewRow row = gridView.Rows[i];
            // Get the PlaceHolder control
            var placeHolder = (PlaceHolder) row.FindControl("countriesPlaceHolder");
            var countryCheckBox = new CheckBox
                                      {
                                          Checked = true,
                                          ID = "auCheckBox",
                                          Text = "Aus",
                                          Enabled = false
                                      };
            placeHolder.Controls.Add(countryCheckBox);

            var editButton = (LinkButton)row.FindControl("editButton");
            editButton.CommandArgument = i.ToString();
            var updateButton = (LinkButton)row.FindControl("updateButton");
            updateButton.CommandArgument = i.ToString();
            updateButton.Visible = false;
        }
    }

    protected void editButton_Click(object sender, EventArgs e)
    {
        LinkButton editButton = (LinkButton) sender;
        int index = Convert.ToInt32(editButton.CommandArgument);

        GridViewRow row = gridView.Rows[index];
        // Get the PlaceHolder control
        LinkButton updateButton = (LinkButton)row.FindControl("updateButton");
        updateButton.Visible = true;
        editButton.Visible = false;

        CheckBox checkbox = (CheckBox)row.FindControl("auCheckBox");
        if (checkbox != null)
        {
            checkbox.Enabled = true;
            // Get value and update
        }
    }

    protected void updateButton_Click(object sender, EventArgs e)
    {
        LinkButton updateButton = (LinkButton)sender;
        int index = Convert.ToInt32(updateButton.CommandArgument);

        GridViewRow row = gridView.Rows[index];
        // Get the PlaceHolder control
        LinkButton editButton = (LinkButton)row.FindControl("updateButton");
        editButton.Visible = true;
        updateButton.Visible = false;

        CheckBox checkbox = (CheckBox)row.FindControl("auCheckBox");
        if (checkbox != null)
        {
            // Get value and update

            checkbox.Enabled = false;
        }
    }

If you want to be it enabled from the get go, just remove the enabled checks and you can delete your edit button.

Hope that helps.

Upvotes: 1

Related Questions