When you create a module not only for you and for one special portal, you have to decide how to accomplish the multiple design demands your customers have. You can create a fixed design and deal with a lot of classes, so the user can do all the styling with CSS. But in my opinion, this is often not flexible enough. The most flexible solution is in my eyes working with templates and token replace. With this technique you can give your users the widest possible range of possibilities for arranging all the stuff they need in the design they want.
Think of a RSS module where you can style the display of the news. On one page you want only the headline and date, on another page you need the full text, the date under the text and the headline above… You will never be able to realize all possible combinations with settings like “Show title under text” or “Don’t show headline”
Needed UI elements
In the settings section of your module, you need only a few elements
The user can insert his html-code into the textbox and he can insert the tokens you create for him. It is a good idea to give him some information about these tokens if he clicks the help button :
And if he struggles give him a “Reset to default” – button where he can reset the template to a default display text!
Generating output
The first thing we need is a placeholder control where you later insert our generated output:
<asp:placeholder id="newsPlaceHolder" runat="server"></asp:placeholder>
In the next step we create a new method in my view control called RenderTemplate. This method receives two parameters:
- The template text (string)
- The data object (eg. NewsInfo, POCO object with properties like Title, Link, Description and so on…)
- Return value is of type Control !
private Control RenderTemplate(NewsInfo news, String template)
In RenderTemplate, we need to replace all our tokens with a string representation of ASP.NET-Controls:
template = template.Replace("[TITLE]", "<asp:label id='\"lblHeadline\"' runat='\"server\"'>");
template = template.Replace("[LINK]", news.Link);
template = template.Replace("[TITLELINK]", "<asp:hyperlink id='\"lnkLink\"' runat='\"server\"'>");
template = template.Replace("[DESCRIPTION]", "<asp:literal id='\"ltrDescription\"' runat='\"server\"' mode='\"PassThrough\"'>");
template = template.Replace("[NEWS]", "<asp:literal id='\"ltrNews\"' runat='\"server\"' mode='\"PassThrough\"'>");
template = template.Replace("[PUBDATE]", "<asp:label id='\"lblPubDate\"' runat='\"server\"'>");
template = template.Replace("[SOURCE]", "<asp:label id='\"lblSource\"' runat='\"server\"'>");</asp:label></asp:label></asp:literal></asp:literal></asp:hyperlink></asp:label>
The next step is very important. Now we make a control of our template string !
Control ctrl = ParseControl(template);
The rest is easy now. We need to find every Control inside of ctrl. If it exists, we set the values (if not, the user hasn’t used the appropriate token)
lblHeadline = FindControlRecursive(ctrl, "lblHeadline") as Label;
if (lblHeadline != null)
lblHeadline.Text = (TitleLimit > 0 ? ShortenString(news.Title, TitleLimit) : news.Title);
lnkLink = FindControlRecursive(ctrl, "lnkLink") as HyperLink;
if (lnkLink != null)
{
lnkLink.Text = (TitleLimit > 0 ? ShortenString(news.Title,TitleLimit) : news.Title);
lnkLink.NavigateUrl = news.Link;
if (news.Internal == false)
lnkLink.Target = "_blank";
}
ltrDescription = FindControlRecursive(ctrl, "ltrDescription") as Literal;
if (ltrDescription != null)
ltrDescription.Text = Server.HtmlDecode((DescriptionLimit > 0 ? ShortenString(news.Description,DescriptionLimit) : news.Description));
ltrNews = FindControlRecursive(ctrl, "ltrNews") as Literal;
if (ltrNews != null)
ltrNews.Text = Server.HtmlDecode((NewsLimit > 0 ? ShortenString(news.News,NewsLimit) : news.News));
lblPubDate = FindControlRecursive(ctrl, "lblPubDate") as Label;
if (lblPubDate != null)
lblPubDate.Text = String.Format("{0:" + DateFormat + "}", news.Pubdate);
lblSource = FindControlRecursive(ctrl, "lblSource") as Label;
if (lblSource != null)
{
Uri url = new System.Uri(news.Link);
lblSource.Text = url.Host;
}
return ctrl;
At the end we return our generated control ! (TitleLimit, DescriptionLimit, NewsLimit and DateFormat are other setting values. ShortenString is a helper method to cut of a string if it is longer as limit and add some …)
FindControlRecursive is another helper method which iterates recursive through all objects and tries to find the control with the given controlID:
private Control FindControlRecursive(Control rootControl, string controlID)
{
if (rootControl.ID == controlID)
return rootControl;
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn = FindControlRecursive(controlToSearch, controlID);
if (controlToReturn != null)
return controlToReturn;
}
return null;
}
Last thing we had to do in code behind is to call our RenderTemplate-Method and add our generated control to the placeholder:
string template = "";
if (Settings["Template"] != null)
template = (string)Settings["Template"];
else
template = Controller.GetDefaultTemplate();
NewsInfo news = NewsController.GetNews();
Control ctrl = RenderTemplate(News,template);
newsPlaceHolder.Controls.Add(ctrl);
And that is what it looks like (sorry it’s in german…):