Thursday, December 20, 2007

ASP.NET TreeNode, Parent of the Selected Node, and RenderPreText

I bumped into something interesting today while working on a TreeView control to be used to represent a book's table of contents on a website. The control had 3 levels: the root, the chapter level, and each major section within each chapter, which we call the "a-head level."

If the user is viewing a piece of content from the a-head level, I was asked to NOT expand the chapter level TreeNode, to apply a different background color to the selected node (the a-head leaf TreeNode), and to apply the same background color to the parent chapter level TreeNode. "Should be fine," I said.

Right. In a Windows form, a TreeNode has a property called BackColor that can be set, but in a Web form, this property is not available. I'm sure there's a good reason for this, but it caught me a bit off-guard ... and much swearing ensued when I realized it.

Long story short, check out Danny Chen's great post on customizing TreeNode controls. This was the first case where I really need to mess with how the .NET framework was rendering HTML.

His post goes into much more detail on what you can do, but my needs were fairly simple. I did take advantage of the lesson to actually set a CSS class rather than just change the background color. Here's the custom TreeNode class I created to set the CSS class for the parent node of the selected node (documentation comments removed for brevity):
public class TreeNodeCustom : TreeNode
{
private string _CssClass = "";

public string CssClass
{
get { return _CssClass; }
set { _CssClass = value; }
}

public TreeNodeCustom() { }

public TreeNodeCustom(string cssClass)
{
_CssClass = cssClass;
}

protected override void RenderPreText(HtmlTextWriter writer)
{
foreach (TreeNode tn in this.ChildNodes)
{
if (tn.Selected)
{
writer.WriteFullBeginTag("div class=\"" + CssClass + "\"");
break;
}
}
base.RenderPreText(writer);
}

protected override void RenderPostText(HtmlTextWriter writer)
{
foreach (TreeNode tn in this.ChildNodes)
{
if (tn.Selected)
{
writer.WriteEndTag("div");
break;
}
}
base.RenderPostText(writer);
}
}
And here's how I call it in code when I need it:

TreeNodeCustom chapter = new TreeNodeCustom("customNode");

No comments: