One of the most common controls used in .Net is the Web User Control, which is in fact a website piece to be reused in different pages.
You normally place them in the page while developing it just dropping it into the page and then using them as part of it, but, what if you want more than one of them in the same page and you don’t know how many of them you are going to use?
For doing that you can always set 10 or more of them in the page and then show as many as you need or, you can dynamically load as many of them as you need this way:
First, I’m going to place the control in a namespace, this is not necessary, but it helps to have things clearer and avoid mistakes. You have to add it just before the partial class of the control begins.
namespace MyNameSpace.UserControls { public partial class webcontrols_CustomBox : System.Web.UI.UserControl { ... } }
Now that we have set the namespace at its back code, we have to change the call to the class at the design file (the one with the HTML) using the new namespace:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CustomBox.ascx.cs" Inherits="MyNameSpace.UserControls.webcontrols_CustomBox" %>
Finally, to load a Web User Control in a page, despite we are loading it dynamically, we need to register it at the top of the design page. If you want to go fast just drag it to the page using the solution explorer and delete it again, the register line will keep there.
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeFile="DynWUC.aspx.cs" Inherits="DynWUC" %> <%@ Register src="./webcontrols/CustomBox.ascx" tagname="CustomBox" tagprefix="uc1" %>
And now, we can load it declaring a Control object and doing a Cast if we need to access one of its public properties/methods, or just declaring that type of control object using the namespace and casting the LoadControl():
//The first way, step to step: Control DynCustBox = LoadControl("~/webcontrols/CustomBox.ascx"); ((MyNameSpace.UserControls.webcontrols_CustomBox)DynCustBox).Show(); pnlWhereTheControlGoes.Controls.Add(DynCustBox); //The second way, better if you need to access different properties: MyNameSpace.UserControls.webcontrols_CustomBox DynCustBox = (MyNameSpace.UserControls.webcontrols_CustomBox)LoadControl("~/webcontrols/CustomBox.ascx"); DynCustBox.Title = "Title"; DynCustBox.Text = "Some text goes here, lalala."; DynCustBox.BGColor = "#FF0"; DynCustBox.Show(); pnlWhereTheControlGoes.Controls.Add(DynCustBox);
As you see, we just load the control, add some values to its properties (if we have declared some on it) and place it in a panel we have for this purpose. Do not forget to add it to the page!!!
The Web User Control disappears on postback!!
Yes it does. As it is not part of the page and .Net Web Forms technology does not save it in the viewstate. Yes, you may argue that it should be saved on the Viewstate and I agree, but it seems that it would give some problems on the viewstate validation since it cannot know if that object had to be there or not.
What can you do to show it after PostBack? Easy. Load it again and put it back on the page.
The Web User Control does not raise its events!!
Again the same problem as before. Since the controls disappear on PostBack, they cannot raise their events because they do not exist…
To raise the dynamic control events (and this works for Web User Controls as well as for any dynamic Control) you have to load them, again, in the Page_Load and give them the same ID they had to avoid .Net giving them a different ID since .Net gives IDs automatically.
Here’s an approximate example:
protected void Page_Load(object sender, EventArgs e) { if(ViewState["identificator_to_know_which_controls_to_show"] != null) { int myOldID = (int)ViewState["identificator_to_know_which_controls_to_show"]; showDynBoxes(myOldID); } } protected void Page_PreRender(object sender, EventArgs e) { int myNewId = iveDoneSomeStuffBetweenLoadAndPreRenderAndIHaveANewID(); showDynBoxes(MyNewId); ViewState.Add("identificator_to_know_which_controls_to_show", myNewId); } private void showDynBoxes(int ID){ List<SqlParameter> pl = new List<SqlParameter>(); pl.Add(new SqlParameter("ID", ID)); DataReader dr = dao.getDataReader("some_stored", pl); while(dr.Read()){ MyNameSpace.webcontrols.webcontrols_CustomBox DynCustBox = (MyNameSpace.webcontrols.webcontrols_CustomBox)LoadControl("~/webcontrols/CustomBox.ascx"); DynCustBox.ID = dr["ID"]; DynCustBox.Text = dr["text"]; pnlWUC.Controls.Add(DynCustBox); } }
To understand the example, imagine your users can show different boxes at their sidebar (like the ones you can set in a blog sidebar) and you get those boxes depending on the user when you are in the profile page. Well, you will get those boxes from the DB so that let you know how many of them you have to show, their properties and something to use as an ID to identify each one of them.
This is a bit annoying but once you get used to it you will find this dynamic control trick very useful… despite you will only use it when strictly necessary. In the case you don’t need to wait until PreRender to show them, then you just need to load them in the Page_Load once and that will do. Or, in the case they have no events to raise, you do not need to worry about this.