Pages

Friday 9 September 2011

Dynamically add/remove user controls using asp.net

Introduction

I often find myself in a situation where I have a user input form that I want to reuse, but I don't know how many times I'll be reusing it. For example, let's say you want to let users on your site upload images, but you don't know how many images they'll want to upload. Giving the user an "Add" button can let them dynamically add as many upload boxes to the page as they want.
That's fine if you have a single control that you want to add dynamically, but what if you have several controls in a complex layout? In that case, the easiest way to handle this is by wrapping the controls you want to add into a single user control, and then dynamically adding the user control to your page. I'll show you how to do that, how to remove user controls, and also how to access the values of the controls within the user control.

Using the code

First, let's create a user control and populate it with several controls for our "template". The user control content looks like this:


<%@ Control Language="C#" AutoEventWireup="true" CodeFile="WebUserControl.ascx.cs"
    Inherits="WebUserControl" %>
<style type="text/css">
    .pnlCSS
    {
        font-weight: bold;
        cursor: pointer;
        padding: 2px;
        border: solid 1px #c0c0c0;
    }
</style>
<table width="100%">
    <tr>
        <td>
            <asp:Panel ID="pnlH1" runat="server" Width="100%" Height="350px" BorderColor="LightBlue"
                BorderWidth="1px">
                <table width="100%">
                    <tr>
                        <td colspan="6">
                            <div style="background-image: url('Images/green_bg.gif'); height: 30px; vertical-align: middle">
                                <div style="float: left; color: White; padding: 5px 5px 0 0">
                                    <div style="float: left; color: White; padding: 0px 0px 0 0; background: Gray; width: 20px;
                                        height: 20px; vertical-align: middle;">
                                        &nbsp; 1</div>
                                    What kind of quote do you want?
                                </div>
                                <div style="float: right; color: White; padding: 5px 5px 0 0">
                                    <asp:Label ID="Label4" runat="server" Text="Right Headding" />
                                </div>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            <asp:Label ID="Label1" runat="server" Text="Package" />
                        </td>
                        <td>
                            <asp:TextBox ID="txtuser" runat="server" />
                        </td>
                        <td>
                            <asp:Label ID="Label2" runat="server" Text="Package Weight" />
                        </td>
                        <td>
                            <asp:DropDownList ID="ddl1" runat="server" Width="135px">
                                <asp:ListItem Text="Dropdown 1" />
                                <asp:ListItem Text="Dropdown 2" />
                                <asp:ListItem Text="Dropdown 3" />
                            </asp:DropDownList>
                        </td>
                        <td>
                        </td>
                        <td>
                        </td>
                    </tr>
                </table>
            </asp:Panel>
        </td>
    </tr>
    <%--<tr>
        <td>
            Dropdown Example:
        </td>
        <td>
            <asp:DropDownList ID="ddl1" runat="server">
                <asp:ListItem Text="Dropdown 1" />
                <asp:ListItem Text="Dropdown 2" />
                <asp:ListItem Text="Dropdown 3" />
            </asp:DropDownList>
        </td>
    </tr>--%>
</table>
<asp:Button ID="btnRemove" runat="server" Text="Remove" OnClick="btnRemove_Click" />
<hr />

Now, add an event handler to the code-behind on the user control so we can tell when the Delete button was clicked:


 public event EventHandler RemoveUserControl;
    protected void btnRemove_Click(object sender, EventArgs e)
    {
        if (RemoveUserControl != null)
        {
            RemoveUserControl(sender, e);
        }
    }

Now that we have the controls we want to display, we'll add the user control to another page dynamically.

There are three important things we have to do in the page load. First, we need to determine what control fired the postback. We need to make sure the "Add" button was clicked.
This needs to be done in the page load event, instead of in the onclick event for the "Add" button, because of the way the ASP.NET page lifecycle works. If we try to add any controls in the onclick event for the Add button (instead of the page load), those values will be blown away when the page posts back.

If the "Add" button was clicked, the next thing to do is increment the count of the user controls to add to the page, so we know how many controls to display. I store this value in a Literal control across postbacks.

We also need to attach an event handler to take care of the 'Delete' button on the user control when it gets clicked. This will remove the control from the Panel and decrement the number of controls on the page.

Finally, once all the setup is taken care of, we can dynamically load all the requested user controls into a PlaceHolder control.

<%@ Register Src="~/WebUserControl.ascx" TagPrefix="uc" TagName="WebUserControl" %>
 <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
            <div class="demo">
                <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
                <asp:Panel ID="Panel1" runat="server">
                </asp:Panel>
                <asp:Button ID="btnAdd" runat="server" Text="Add" OnClick="btnAdd_Click1" />
            </div>
            <div class="demo">
                <asp:Literal ID="ltlValues" runat="server" />
                <asp:Button ID="btnDisplayValues" runat="server" Text="Display Values"
                    onclick="btnDisplayValues_Click1" />
            </div>
        </ContentTemplate>
    </asp:UpdatePanel>
       <asp:Literal ID="ltlCount" runat="server" Text="0" Visible="false" />
    <asp:Literal ID="ltlRemoved" runat="server" Visible="false" />



    protected void Page_Load(object sender, System.EventArgs e)
    {
        AddAndRemoveDynamicControls();
    }
    private void AddAndRemoveDynamicControls()
    {
        try
        {
            Control c = GetPostBackControl(Page);
            if ((c != null))
            {
                if (c.ID == "btnAdd")
                {
                    //int val2 = Convert.ToInt16();
                    ltlCount.Text = Convert.ToString(Convert.ToInt16(ltlCount.Text) + 1);
                }
            }
            PlaceHolder1.Controls.Clear();
            int ControlID = 0;
            for (int i = 0; i <= (Convert.ToInt16(ltlCount.Text) - 1); i++)
            {
                WebUserControl DynamicUserControl = (WebUserControl)LoadControl("WebUserControl.ascx");
                while (InDeletedList("uc" + ControlID) == true)
                {
                    ControlID += 1;
                }
                DynamicUserControl.ID = "uc" + ControlID;
                DynamicUserControl.RemoveUserControl += this.HandleRemoveUserControl;

                PlaceHolder1.Controls.Add(DynamicUserControl);
                ControlID += 1;
            }
        }
        catch
        {
            throw;
        }
    }
    private bool InDeletedList(string ControlID)
    {
        try
        {
            string split = ltlRemoved.Text;
            string[] DeletedList = split.Split('|');
            for (int i = 0; i <= DeletedList.GetLength(0) - 1; i++)
            {
                if (ControlID.ToLower() == DeletedList[i].ToLower())
                {
                    return true;
                }
            }
            return false;
        }
        catch
        {
            throw;
        }
    }
    void HandleRemoveUserControl(object sender, EventArgs e)
    {
        try
        {
            Button btn = (Button)sender;
            PlaceHolder1.Controls.Remove(btn.Parent);
            ltlRemoved.Text += btn.ID + "|";
            ltlCount.Text = Convert.ToString(Convert.ToInt16(ltlCount.Text) - 1);
        }
        catch
        {
            throw;
        }
    }

    protected void btnAdd_Click(object sender, System.EventArgs e)
    {
        //Handled in page load
    }
    public Control GetPostBackControl(Page page)
    {
        try
        {
            Control control = null;
            string ctrlname = page.Request.Params.Get("__EVENTTARGET");
            if ((ctrlname != null) & ctrlname != string.Empty)
            {
                control = page.FindControl(ctrlname);
            }
            else
            {
                foreach (string ctl in page.Request.Form)
                {
                    Control c = page.FindControl(ctl);
                    if (c is System.Web.UI.WebControls.Button)
                    {
                        control = c;
                        break;
                    }
                }
            }
            return control;
        }
        catch
        {
            throw;
        }
    }
    protected void btnAdd_Click1(object sender, EventArgs e)
    {
    }

There's one more issue here. How do we get at the values stored in the user controls? It's actually fairly straightforward. We loop through all the controls in the PlaceHolder, then look at the name of the control to determine if this is one of the user controls we added to the PlaceHolder.
If it is, we can use FindControl to access the values stored in textboxes, dropdowns, checkboxes, or anything else we added to the user control. Here, I'm getting those values and then writing them out to the screen:

protected void btnDisplayValues_Click1(object sender, EventArgs e)
    {
        try
        {
            ltlValues.Text = "";
            foreach (Control c in PlaceHolder1.Controls)
            {
                if (c.GetType().Name.ToLower() == "webusercontrol_ascx")
                {
                    UserControl uc = (UserControl)c;
                    DropDownList ddl1 = (DropDownList)uc.FindControl("ddl1");
                    TextBox tbx1 = (TextBox)uc.FindControl("txtuser");
                    System.Text.StringBuilder sb = new System.Text.StringBuilder();
                    sb.Append("Textbox value: " + tbx1.Text + "<br />");
                    sb.Append("Dropdown value: " + ddl1.SelectedValue + "<br />");
                    sb.Append("<hr />");
                    ltlValues.Text += sb.ToString();
                }
            }
        }
        catch
        {
            throw;
        }
    }

No comments:

Post a Comment