Flexible ASP.NET Web Part UI Pattern
A pattern for building a templated UI for Web parts

Introduction
Typically, building a Web part for ASP.NET means that you are following the MSDN samples and building your UI elements in code and adding them to the Web part controls collection. This means that to make any changes, you are forced to recompile and redeploy. There are some solutions to this problem already such as the Smart Part, which makes use of user controls, but this article shows a different approach. It separates the HTML into a property of the Web part that end users can manipulate, the Web part code then binds any data sources to the controls that the end user defined.
I found this pattern particularly useful when I was asked to create a webpart that was the front end to a search engine, the Web part would POST the details the user entered to an intranet search site. The catch was that the webpart was used in a number of different parts of the site and in each instance, it had to present the user with a different view. What I really needed was a templating system, the resulting code is what I present below.
Using the Code
The code I present below is fairly simple, the power is in the call to Page.ParseControl
.
The property TemplateHTML
is decorated with attributes to make it editable via the Web part's tool part, which gives the end users the ability to change the look and feel.
In the example below, I've defined the Web part to have a textbox
and an image button. In the CreateChildControls
method, I add the template to the controls collection after the Page.ParseControl
method. Next I use the FindControl
method to hook up the control to any code that needs to manipulate it, this might include data binding to any dropdowns, etc.
using System;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.WebControls;
namespace Template
{
public class Template : System.Web.UI.WebControls.WebParts.WebPart
{
//string with UI code to be added to the page
protected string templateHTML = string.Empty;
//Control IDs, the template must use these ID's
private string searchButtonID = "searchImgBtn";
private string allTextID = "allTxt";
//controls that will be found from the template
protected TextBox allTxt = null;
protected ImageButton searchBtn = null;
public Template()
{
}
protected override void CreateChildControls()
{
if (templateHTML != string.Empty)
{
try
{
//create a control that has been parsed by ASP.NET
Control template = Page.ParseControl(templateHTML);
//add it to the page
this.Controls.Add(template);
//search for the known controls
allTxt = template.FindControl(allTextID) as TextBox;
searchBtn = template.FindControl(searchButtonID) as ImageButton;
//hook up any events
if (searchBtn != null)
{
searchBtn.Click += new ImageClickEventHandler(btnSearch_Click);
}
}
catch (Exception err)
{
this.Controls.Add(
new LiteralControl(string.Format
("Error applying template: {0}", err.Message))
);
}
}
else
{
this.Controls.Add(new LiteralControl
("Please enter a template in the toolpart settings"));
}
base.CreateChildControls();
}
void btnSearch_Click(object sender, ImageClickEventArgs e)
{
//if the template included the textbox, show its results
if (allTxt != null)
{
this.Controls.Add(new LiteralControl("Searched On: " + allTxt.Text));
}
}
[System.Web.UI.WebControls.WebParts.WebBrowsable(true),
System.Web.UI.WebControls.WebParts.WebDisplayName("Layout Template"),
System.Web.UI.WebControls.WebParts.WebDescription
("Template that is used for layout"),
System.Web.UI.WebControls.WebParts.Personalizable(
System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
System.ComponentModel.Category("Template Settings"),
System.ComponentModel.DefaultValue("")
]
public string TemplateHTML
{
get{return templateHTML;}
set{templateHTML = value;}
}
}
}
A template for the example might look like...
<table>
<tr>
<td>Search On:</td>
<td><asp:TextBox ID="allTxt" runat="server" />
</tr>
<tr>
<td colspan="2"><asp:ImageButton runat="server"
ID="searchImgBtn" ImageUrl="Search.gif"/>
</tr>
</table>
or:
<div>
<p>Search On:<asp:TextBox ID="allTxt" runat="server" /></p>
<p><asp:ImageButton runat="server" ID="searchImgBtn" ImageUrl="Search.gif"/></p>
</div>
Points of Interest
This isn't really a framework to reuse code, but more of a pattern that can prove helpful if you work in an environment where users are constantly asking for minor UI changes.
There is still a large coupling of concerns in this pattern, the control IDs in the template must match what the code behind is looking for; in that respect the UI and code are very tightly coupled.
History
- 15th October, 2008 - Initial version