How to make an ASP.NET user control behave like a custom WebControl – level 200

If you’ve used User Controls for any amount of time, you’ve lamented that there is no containing control for attributes.  With any other custom controls, you have WebControl as the base class, and that allows you to use a <span/> by default or change the TagKey to another tag, and when you declaratively set an attribute like [style=”padding:10px;”], the attribute automatically gets added to the tag.


Not so with User Controls, but they aren’t designed to work that way.  They are meant to be pagelets: a portion of a page.  A User Control can contain anything and doesn’t have to be encapsulated in any control, so there is no tag to add attributes to.


I have made a hybrid User-Custom web control.  It inherits from UserControl, so I have the nice declarative layout, but I needed functionality of a WebControl as well.  I added a WebControl as a member of my user control, and on PreRender, I wrap all my user control contents in this WebControl.  Then I bestow all the attributes of the user control on the WebControl and add the WebControl as the only 1st-level child of the user control.  The end result is that I have a user control that wraps itself in a <span/> or whatever tag I want, and I can set attributes on the Page, and they will get rendered to the browser.  This is especially useful when I need to add a style property or “class”.


Here’s my code-behind.  Feel free to copy/steal my code:



   14     public class Magic : System.Web.UI.UserControl


   15     {


   16         private WebControl _frame;


   17 


   18         public WebControl Frame


   19         {


   20             get {


   21                 this.EnsureChildControls();


   22                 return this._frame;


   23             }


   24         }


   25 


   26         protected override void CreateChildControls()


   27         {


   28             base.CreateChildControls ();


   29 


   30             this._frame = new WebControl(HtmlTextWriterTag.Div);


   31         }


   32 


   33         protected override void OnPreRender(EventArgs e)


   34         {


   35             base.OnPreRender (e);


   36 


   37             Control[] ctrls = new Control[this.Controls.Count];


   38             this.Controls.CopyTo(ctrls, 0);


   39 


   40             foreach(Control ctrl in ctrls)


   41             {


   42                 this.Frame.Controls.Add(ctrl);


   43             }


   44 


   45             foreach(string attribute in this.Attributes.Keys)


   46             {


   47                 this.Frame.Attributes[attribute] = this.Attributes[attribute];


   48             }


   49 


   50             this.Controls.Add(this.Frame);


   51         }


   52 


   53 


   54         private void Page_Load(object sender, System.EventArgs e)


   55         {


   56             // Put user code to initialize the page here


   57         }


   58 


   59         #region Web Form Designer generated code


   60         override protected void OnInit(EventArgs e)


   61         {


   62             //


   63             // CODEGEN: This call is required by the ASP.NET Web Form Designer.


   64             //


   65             InitializeComponent();


   66             base.OnInit(e);


   67         }


   68 


   69         /// <summary>


   70         ///        Required method for Designer support – do not modify


   71         ///        the contents of this method with the code editor.


   72         /// </summary>


   73         private void InitializeComponent()


   74         {


   75             this.Load += new System.EventHandler(this.Page_Load);


   76         }


   77         #endregion


   78     }


Here is my user control markup:



<%@ Control Language=”c#” AutoEventWireup=”false” Codebehind=”Magic.ascx.cs” Inherits=”MagicUserControl.Magic” TargetSchema=”http://schemas.microsoft.com/intellisense/ie5″%>
This is a tag.
<asp:Label Runat=”server”>Yo, buddy</asp:Label>


And here is my Page markup (where my User Control is used):


<uc1:Magic id=”Magic1″ style=”margin: 10px;” myAttrib=”Palermo” Frame-CssClass=”myClass” runat=”server”></uc1:Magic>


See how easy it is now to add any sort of attribute and have it render?  I created this control out of necessity because I wanted the simplicity of user controls with the power and flexibility of a WebControl-derived control.