One common practice in web site design is the use of visual containers. Containers can take the form of rounded corner containers, shadowed containers and many other variations. There are also many techniques for simplifying the implementation of containers. There are many JavaScript based solutions for rounding corners or for expressing a shadow, however the JavaScript solutions are never as visually effective as containers based on a layered Photoshop design. The Photoshop slice tool is used to cut the needed elements (as small as possible) to be used within a layered DIV container approach, along with cascading style sheet (CSS) styling. The Photoshop slice tool is used to cut the top header row left and right corners and then a 1 pixel wide element to be repeated between the left and right corners to form a complete header row. The slice tool is then used to cut the same elements for the bottom footer row, and lastly the slice tool is used to cut the variable height content section left and right edges and 1 pixel wide inner content background.
Using the Photoshop slice tool cannot be avoided, however the ASP.NET implementation can be greatly simplified for repeated container implementations. The focus of this article is to show how a custom server control can be written and used to programmatically generate variable width and height containers such that the DIV layering complexity is completely abstracted away within the server control.
To illustrate, the following sample web page contains one such custom server control:
1: <body>
2: <form id="form1" runat="server">
3: <div>
4: <socrates:InjectContainer ID="test" runat="server">
5: <ContentTemplate>
6: <br />
7: <br />
8: <asp:Button ID="btnStart" runat="server" Text="Start"
9: OnClick="btnStart_Click" />
10: <br />
11: <asp:Label ID="lblStatus" runat="server" />
12: <br />
13: </ContentTemplate>
14: </socrates:InjectContainer>
15: </div>
16: </form>
17: </body>
The ContentTemplate contains a combination of HTML and ASP.NET server controls; in this case a very simple button and status line to display Hello World when the button is depressed, as rendered below:
The InjectContainer control will enclose the content within the ContentTemplate template with the various DIV layers necessary to achieve an effective cross browser container implementation. The Container size is NOT limited in any respects, fully controlled by the cascading style sheet. In the above example the following HTML is generated and injected into the web page.
1: <div id="test">
2: <div class="Header">
3: <div class="HeaderLeft">
4: <div class="HeaderRight">
5: <div class="HeaderContent">
6: </div>
7: </div>
8: </div>
9: </div>
10: <div class="Body">
11: <div class="BodyLeft">
12: <div class="BodyRight">
13: <div class="BodyContent">
14: <br />
15: <br />
16: <input type="submit"
17: name="test$ctl08$btnStart"
18: value="Start"
19: id="test_ctl08_btnStart" />
20: <br />
21: <span id="test_ctl08_lblStatus"></span>
22: <br />
23: </div>
24: </div>
25: </div>
26: </div>
27: <div class="Footer">
28: <div class="FooterLeft">
29: <div class="FooterRight">
30: <div class="FooterContent">
31: </div>
32: </div>
33: </div>
34: </div>
35: </div>
36: </div>
As you can see, most of the generated HTML are the layers of DIV sections; necessary to achieve the visually effective container. Most of the heavy lifting is done by the InjectContainer custom server control, as well as the CSS style sheet, shown below:
1: <style type="text/css">
2:
3: body
4: {
5: background-color: Beige;
6: margin-top: 100px;
7: margin-left: 100px;
8: }
9:
10: #test
11: {
12: width: 310px;
13: }
14:
15: #test .Header
16: {
17: }
18:
19: #test .HeaderLeft
20: {
21: padding-left: 2px;
22: height: 3px;
23: background-image: url(/Images/Containers/White-Block/HeaderLeftEdge.png);
24: background-repeat: no-repeat;
25: background-position: top left;
26: }
27:
28: #test .HeaderContent
29: {
30: height: 3px;
31: background-image: url(/Images/Containers/White-Block/HeaderContent.png);
32: background-repeat: repeat-x;
33: background-position: top left;
34: }
35:
36: #test .HeaderRight
37: {
38: padding-right: 8px;
39: height: 3px;
40: background-image: url(/Images/Containers/White-Block/HeaderRightEdge.png);
41: background-repeat: no-repeat;
42: background-position: top right;
43: }
44:
45: #test .Body
46: {
47: }
48:
49: #test .BodyLeft
50: {
51: padding-left: 2px;
52: height: 200px;
53: background-image: url(/Images/Containers/White-Block/BodyLeftEdge.png);
54: background-repeat: repeat-y;
55: background-position: top left;
56: }
57:
58: #test .BodyContent
59: {
60: height: 200px;
61: background-image: url(/Images/Containers/White-Block/BodyContent.png);
62: background-repeat: repeat;
63: background-position: top left;
64: text-align: center;
65: }
66:
67: #test .BodyRight
68: {
69: padding-right: 8px;
70: height: 200px;
71: background-image: url(/Images/Containers/White-Block/BodyRightEdge.png);
72: background-repeat: repeat-y;
73: background-position: top right;
74: }
75:
76: #test .Footer
77: {
78: }
79:
80: #test .FooterLeft
81: {
82: padding-left: 2px;
83: height: 9px;
84: background-image: url(/Images/Containers/White-Block/FooterLeftEdge.png);
85: background-repeat: no-repeat;
86: background-position: top left;
87: }
88:
89: #test .FooterContent
90: {
91: height: 9px;
92: background-image: url(/Images/Containers/White-Block/FooterContent.png);
93: background-repeat: repeat-x;
94: background-position: top left;
95: }
96:
97: #test .FooterRight
98: {
99: padding-right: 8px;
100: height: 9px;
101: background-image: url(/Images/Containers/White-Block/FooterRightEdge.png);
102: background-repeat: no-repeat;
103: background-position: top right;
104: }
105:
106: </style>
The InjectContainer control can also be used in a nested manner to generate a different container within a container, provided of course the ID= attribute is different for each InjectContainer instance so each container can be uniquely styled within its companion cascading style sheet.
The C# source code for the InjectContainer custom server control is as follows:
1: using System;
2: using System.ComponentModel;
3: using System.Text;
4: using System.IO;
5: using System.Web;
6: using System.Web.UI;
7: using System.Web.UI.WebControls;
8: using System.Web.UI.HtmlControls;
9: using System.Drawing;
10: using System.Resources;
11:
12: namespace Socrates.CustomControls.Containers
13: {
14: public class MyTemplateContainer : Control, INamingContainer
15: {
16: /// <summary>
17: /// MyTemplateContainer.
18: /// </summary>
19: public MyTemplateContainer()
20: {
21: }
22: }
23:
24: [
25: ToolboxData("<{0}:InjectContainer runat=server></{0}:InjectContainer>"),
26: ParseChildren(true),
27: PersistChildren(true)
28: ]
29: public class InjectContainer : CompositeControl
30: {
31: private ITemplate m_ContentTemplate = null;
32:
33: private Control RenderHeader()
34: {
35: return RenderBlock("Header", null);
36: }
37:
38: private Control RenderBody(MyTemplateContainer i)
39: {
40: return RenderBlock("Body", i);
41: }
42:
43: private Control RenderFooter()
44: {
45: return RenderBlock("Footer", null);
46: }
47:
48: private Control RenderBlock(string BlockName,
49: MyTemplateContainer i)
50: {
51: HtmlGenericControl divContainer = new HtmlGenericControl("div");
52: divContainer.Attributes.Add("class", BlockName);
53: HtmlGenericControl divLeft = new HtmlGenericControl("div");
54: divLeft.Attributes.Add("class", BlockName + "Left");
55: HtmlGenericControl divContent = new HtmlGenericControl("div");
56: divContent.Attributes.Add("class", BlockName + "Content");
57: HtmlGenericControl divRight = new HtmlGenericControl("div");
58: divRight.Attributes.Add("class", BlockName + "Right");
59:
60: if (i != null)
61: {
62: divContent.Controls.Add(i);
63: }
64:
65: divRight.Controls.Add(divContent);
66: divLeft.Controls.Add(divRight);
67: divContainer.Controls.Add(divLeft);
68:
69: return divContainer;
70: }
71:
72: protected override HtmlTextWriterTag TagKey
73: {
74: get
75: {
76: return HtmlTextWriterTag.Div;
77: }
78: }
79:
80: protected override void CreateChildControls()
81: {
82: Controls.Clear();
83:
84: if (ContainerClass.Length > 0)
85: {
86: if (base.Attributes["class"] == null)
87: {
88: base.Attributes.Add("class", ContainerClass);
89: }
90: }
91:
92: base.Controls.Add(RenderHeader());
93:
94: if (ContentTemplate != null)
95: {
96: MyTemplateContainer i = new MyTemplateContainer();
97: ContentTemplate.InstantiateIn(i);
98: base.Controls.Add(RenderBody(i));
99: }
100: else
101: {
102: base.Controls.Add(RenderBody(null));
103: }
104:
105: base.Controls.Add(RenderFooter());
106: }
107:
108: [
109: PersistenceMode(PersistenceMode.InnerProperty),
110: TemplateContainer(typeof(MyTemplateContainer))
111: ]
112: public ITemplate ContentTemplate
113: {
114: get
115: {
116: return m_ContentTemplate;
117: }
118: set
119: {
120: m_ContentTemplate = value;
121: }
122: }
123:
124: public string ContainerClass
125: {
126: get
127: {
128: return (this.ViewState["_ContainerClass"] == null) ? "" : (string)this.ViewState["_ContainerClass"];
129: }
130: set
131: {
132: this.ViewState["_ContainerClass"] = value;
133: }
134: }
135: }
136: }
If you have any questions, please feel free to send me an email.