- Solving the Problem No One Seems to Care About
I subtitled this post with the tongue-in-cheek phrase "Solving the Problem No One Seems to Care About" because I recently discovered what I would consider to be a flaw in the design of ASP.NET user controls, but I seem to be the only one who cares.
The flaw is that there is no HTML emitted for an ASP.NET user control. Don't be mislead by my previous statement though. Certainly the contents of the user control are emitted, but if your user control contains nothing, there will be no HTML emitted by it. Here is some example ASP.NET markup for a page:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register src="UserControlA.ascx" tagname="UserControlA" tagprefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<uc1:UserControlA ID="UserControlA1" runat="server" />
</form>
</body>
</html>
Here is the ASP.NET markup for the user control, UserControlA:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="UserControlA.ascx.cs" Inherits="UserControlA" %>
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="UserControlA.ascx.cs" Inherits="UserControlA" %>
Notice that there is no markup for the user control. And here is the emitted HTML for the page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>
Untitled Page
</title></head>
<body>
<form name="form1" method="post" action="Default.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="ID__VIEWSTATE" value="/wEPDwUJMjM2Mjk4NzA0ZGT6f3M26FuBr6agyMspUhhwhPM2jw==" />
</div>
</form>
</body>
</html>
Notice that there is no sign of the user control in the HTML. You can delete the addition of the user control in the page markup and the HTML will be identical. What is the significance of this and why do I care? I care because I want to use CSS to position my user control on the page. Since the point of a user control is to create a reusable component, and I most likely will be reusing the user control on different pages, I most likely will want the user control to be placed in different positions on different pages. If I want to use CSS to control the positioning, how do I do this? One thought would be to create a div immediately inside the user control and specify a CSS selector mapped to a style that will position the user control, like this:
<div class="MyUCPositioningClass">
...
</div>
The flaw with this approach is that it means the position would be the same on every page I add the user control to, and that is no good.
The next approach developers seem to want to take is wrapping the page markup for the user control in a div, like this:
<form id="form1" runat="server">
<div class="MyUCPositioningClass">
<uc1:UserControlA ID="UserControlA1" runat="server" />
</div>
</form>
This basically works. It allows you to specify the CSS class outside of the user control contents, which means you can use CSS to control the positioning and it be specific to the page. However, it creates a coordination effort between the div and the user control if you need to make the user control invisible. In my case, I had several user controls and depending on the user's action, some controls would be invisible while others were visible, and vice versa. While I can do this, it is a hassle.
What would be best is if the user control itself emitted HTML of its own...a div representing the user control. I could then set the user control's class or CssClass to be whatever style I want on each page and therefore each page would have the ability to control the user control's positioning. But alas, the user control won't do this.
Matthew MacDonald, author of Pro ASP.NET 3.5 in C# 2008, did give me this work-around, which seems very usable. I thought I would share it with you. I will start with the same basic code as above, except I will create a div immediately inside of the user control, like this:
<div id="<%# ID %>">
<h1><%# ID %></h1>
</div>
This allows me to have a div inside the user control, but whose id is set based on the user control's ID. I have also added a header (h1) containing the user control's ID to make it easy to identify the user control on the page. For this to work, you need to add a call to DataBind() in your Page_Load() method for the user control like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
DataBind();
}
}
The next step is to create a CSS file. I have created a CSS file named StyleSheet.css and added it to my page's aspx file:
...
<head runat="server">
<title>Untitled Page</title>
<link href="StyleSheet.css" rel="stylesheet" type="text/css" />
</head>
...
In my page's markup, I add the user controls. As an example, I will add two instances to the same page, each with a different ID thereby allowing me to create a different style for each in the CSS file:
<body>
<form id="form1" runat="server">
<uc1:UserControlA ID="UserControlA1" runat="server" />
<uc1:UserControlA ID="UserControlA2" runat="server" />
</form>
</body>
Notice the ID for the first instance of the user control is UserControlA1 and the second ID is UserControlA2. Now all I need to do is add separate selectors and styles for each in my CSS, and I have positioning (and any other CSS control I want) controlled at the page (or even instance) level using CSS. Here is my CSS:
#UserControlA1
{
background-color: Aqua;
position: relative;
left: 50px;
top: 75px;
height: 200px;
width: 350px;
}
#UserControlA2
{
background-color: Yellow;
position: relative;
left: 300px;
top: 175px;
height: 150px;
width: 200px;
}
That's it. To recap, here are the steps enumerated for your convenience:
- Add a div immediately inside the user control and specify its id to be equal to "<%# ID %>". This will get replaced with the user control's ID when we call DataBind() in the user control's Page_Load() method.
- Add a call to DataBind() in your user control's Page_Load method.
- Add the markup to your page for each user control giving them a unique ID.
- Add a style in the CSS for each user control ID.
Here is a screen shot demonstrating the placement of the two user controls:

And here is the project you can download to play with.