I've been reading up on design patterns for a while now. While i haven't implemented that many myself, i'm familiar enough with them to recognize when a problem comes up for which they provide a solution. Such was the case when we started developing a template system for the website we were working on.
I'm well aware of the fact that page templates typically never work as hoped but i wanted to try to do something to hide the CSS stuff from the programming stuff. At first i thought there would be only 6 or so different page layouts. If that were the case, we were just going to make 6 different ASP.NET user controls and include them on the pages which called for them. Then, on the code behind, we would call methods of the specific template user control to add child controls to each of the "holes" on the template.
The idea seemed reasonable until we quickly realized that the number of templates was quickly growing. Our designer was simply using a four column grid structure and each page would have elements that may stretch across one or more columns and varied in height. Instead we decided to switch to a format where each page would configure a generic template for this specific purpose. This generic control would nest template elements to achieve the desired outcome. This code required a way to be able to effectively work with nested controls.
When i started to think about these child controls, i was reminded of the composite design pattern.This pattern exists specifically to deal with tree like structures. It's built with the idea that there are two types of items in your tree: those that contain other items and those that do not. Each of those two types of classes inherit from a base component class. This base class defines the methods that both the classes that contain other classes (composite classes) and those that do not (leaf classes) must implement. The composite classes can contain collections of the generic component class so it can either contain either composite or leaf classes.
In the case of my template system, i created a class called
TemplatePanelNode to act as the component class. This was an abstract class that ensured that each of the child classes had a height and width property as well as implemented a ToControl() method. The ToControl method must return a
System.Web.UI.Control object. This is because i will append this output to the page and allow the standard page rendering function to actually draw it.
I chose the term
TemplatePanel to refer to the leaf classes. This is where you could actually attach other web controls to be rendered within that "hole" of the template. These objects all had a private "div"
HTMLGenericControl. The public AddControl() method would add controls to the Controls property of this private instance variable. The overridden ToControl() method would simply return this control.
The composite classes were called
TemplatePanelContainers. This has a method called AddRow() which uses a ParamArray to accept any number of TemplatePanelNodes which means you can pass in either TemplatePanel or other TemplatePanelContainers. We add a row at a time to help enforce some of the grid requirements. These elements are added to a private strongly-typed collection of TemplatePanelNodes. It's The overridden ToControl() method creates a
System.Web.UI.WebControls.PlaceHolder to which it appends the values returned by each of the ToControl() methods of the objects in its private collection.
Being able to use the pattern saved me a lot of time and gave me a great place to start. It's nice to build upon the collected knowledge of developers rather than start from scratch all the time. While this type of template system may not be the best long term choice, i can at least be confident the code will work as expected.Posted by Matthew at March 18, 2005 03:59 PM