August 23, 2006

Using FontInfo in a Custom Server Control

In many circumstances it's appropriate to assign a FontInfo structure as part of a custom server control. This easily allow programmers to adjust the look at feel of certain areas of text but can be a little tricky to implement because FontInfo doesn't have a public constructor. One way to accomplish this is to create a CSS Style object and use it's Font property to do the dirty work. This technique would look something like this:
private Style _headerFontStyle;

[Bindable(true),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
NotifyParentProperty(true),
Category("Appearance"),
Description("The font for the header."),
Localizable(true)]
public FontInfo HeaderFont
{
get
{
if (_headerFontStyle == null)
{
_headerFontStyle = new Style();
}
return _headerFontStyle.Font;
}
set
{
_headerFontStyle.Font.CopyFrom(value);
}
}

So is that all there is to it? Well, not exactly. Unfortunately, FontInfo is not serializable, nor does it implement IStateManager making it difficult for us to track it's state if any changes are programmatically made to the structure.

The good news is that in the code above we're using a Style object to get at the underlying FontInfo. Since Style implements IStateManager we can override the following methods to make sure that the FontInfo property is always kept in ViewState across post backs.

protected override object SaveViewState()
{
object[] state = new object[2];
state[0] = base.SaveViewState();
state[1] = ((IStateManager)_headerFontStyle).SaveViewState();
return state;
}

protected override void LoadViewState(object savedState)
{
object[] state = (object[])savedState;
base.LoadViewState(state[0]);
((IStateManager)_headerFontStyle).LoadViewState(state[1]);
}

protected override void TrackViewState()
{
base.TrackViewState();
if (_headerFontStyle != null)
{
((IStateManager)_headerFontStyle).TrackViewState();
}
}

July 29, 2006

Approximating Master Pages in PHP

Most sites on the Internet have common design elements that do not change from page to page. Usually, only content or minor navigational cues vary. To handle the unchanging aspects, developers have often relied on server-side includes - dividing regions like headers, side navigations and footers into separate files.

But now with the advent of ASP.NET 2.0 Master Pages offer a more complete templating alternative. With Master Pages an entire template common to the site is stored in one file with a .master extension. Developers add a ContentPlaceHolder control inside a Master Page to indicate where they would like their page specific content to appear. Then one or more Web Forms may be set up to automatically dress themselves with the shared visual components defined in the Master Page.

So is it possible to make PHP emulate this behavior? To some degree, yes, with output buffering and one server-side include file that represents the "Master Page." Lets say we are aiming to create our HTML by combining this template file and a content file (we'll call these master.php and index.php, respectfully). Our simplified example will aim to achieve the following end result:



The template that will act as our "Master Page" will need to contain all aspects of the design labeled "Template Specific." Since no such control like the ContentPlaceHolder exists, we will use PHP variables to indicate where we would like our page specific content to appear.

master.php
<html>
<head>
<title><?php echo $pagetitle; ?></title>
</head>
<body style="margin-top:20px;margin-left:20px;margin-right:20px;">
<table width="100%" border="0" cellpadding="10" cellspacing="0"border="0">
<tr bgcolor="#33FFFF">
<td colspan="5"><h2>Template Specific Header</h2></td>
</tr>
<tr bgcolor="#EEEEEE">
<td nowrap><a href=#">Navigation Link 1</a></td>
<td nowrap><a href="#">Navigation Link 2</a></td>
<td nowrap><a href="#">Navigation Link 3</a></td>
<td nowrap><a href="#">Navigation Link 4</a></td>
<td width="100%">&nbsp;</td>
</tr>
</table>
<br />
<table width="100%" cellpadding="10" cellspacing="0" border="0">
<tr>
<td width="30%" valign="top" bgcolor="#EEEEEE"><strong>Template Specific
Navigation</strong><br /><br />
<a href="#">Link 1</a><br />
<a href="#">Link 2</a><br />
<a href="#">Link 3</a><br />
</td>
<td width="70%" valign="top"><?php
echo $pagemaincontent;
?></td>
</tr>
</table>
<br />
<table width="100%" cellspacing="0" cellpadding="10" border="0">
<tr>
<td colspan="2" bgcolor="#33FFFF">Template Specific Footer</td>
</tr>
</table>
</body>
</html>

Now any pages that we would like to have adhere to the template only need to define the page specific variables and include our master.php file.

index.php
<?php
//Buffer larger content areas like the main page content
ob_start();
?>
<em>Page Specific Content Text</em><br />
Lorem ipsum dolor sit amet, consectetuer adipiscing
elit, sed nonummy nibh euismod tincidunt ut laoreet
dolore magna aliat volutpat. Ut wisi enim ad minim
veniam, quis nostrud exercita ullamcorper
suscipit lobortis nisl ut aliquip ex consequat.
<br /><br />
Duis autem vel eum iriure dolor in hendrerit in
vulputate velit molestie consequat, vel illum
dolore eu feugiat nulla facilisis ats eros et
accumsan et iusto odio dignissim qui blandit
prasent up zzril delenit augue duis dolore te
feugait nulla facilisi. Lorem euismod tincidunt
erat volutpat.
<?php
//Assign all Page Specific variables
$pagemaincontent = ob_get_contents();
ob_end_clean();
$pagetitle = "Page Specific Title Text";
//Apply the template
include("master.php");
?>

Even though it's not perfectly aligned with ASP.NET's Master Page feature set, the technique described above allows us to consolidate what might otherwise be numerous server-side include files.

July 21, 2006

Control Designer Gotcha

This week I've been programming a custom web server control. It required some specific behavior while being manipulated in the design view of Visual Studio 2005. To achieve this I decided to extend a ContainerControlDesigner to suit my needs. In order to test my new control as I worked on it, I added my efforts to a web project.

However, despite importing the ContainerControlDesigner's namespace (System.Web.UI.Design), the class definition could not be found and as a result it wouldn't compile. I discovered that the classes in the System.Web.UI.Design namespace belong to the System.Design.dll assembly which is not referenced by default in a Visual Studio 2005 web project. I had mistakenly assumed this class was already a part of the referenced System.Web.dll. After adding a reference to System.Design.dll (see image below) my custom control designer compiled flawlessly.