I stumbled onto something I didn’t know before in ASP.NET control rendering which is bound to prove useful in some scenarios. The Control class has a SetRenderMethodDelegate method that allows specifying a method that is called after a control renders.
So imagine for a minute you have a real happy control like this one:
public class CustomControl : Control
{
protected override void Render(HtmlTextWriter writer)
{
writer.Write("<div style='font-weight:bold'>Hello Happy World @ " + DateTime.Now.ToString() + "</div>");
base.Render(writer);
}
}
Now let’s assume we’re not happy at all with the state of this affair and we want to be a little more realistic about the world view on a specific page and we want to add some output to the original to set it straight. We can write code like this:
protected void Page_Load(object sender, EventArgs e)
{
this.MyCustomControl.SetRenderMethodDelegate(this.RenderCustom);
}
protected void RenderCustom(System.Web.UI.HtmlTextWriter writer,
System.Web.UI.Control Container)
{
writer.Write("<b>Now way! Screw the world</b><br/>");
}
This essentially allows you to hook into the control’s rendering process and add additional output to the control.
I was thinking about this in light of the HTML injection code I talked about a few days ago in light of the problems related to the Control.Add() behavior if <% %> tags are embedded in that page or container. My workaround solution for this problem was to inject a JavaScript function and a call to this function to embed the HTML into the page.
The RenderMethod delegate code might actually work to inject the HTML at render time which would be nicer than having to use the explicit JavaScript embedded into the page. But it would be more tricky.
There are also a few caveats with this approach:
- If any custom control doesn’t call base.Render() in its Render() method the delegate is not fired.
- The placement of base.Render() inside of the Render() method implementation determines when the delegate fires.
So if the custom method is implemented like this:
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
writer.Write("<div style='font-weight:bold'>Hello Happy World @ " + DateTime.Now.ToString() + "</div>");
}
then the message would preceed the rendering of the control.
Aargh. I took a quick look at some of the stock controls and I notice here that if I try to override the rendering of a TextBox or Button nothing happens. This means that these controls don’t call base.Render() at all most likely. Bummer… that kind of takes this solution out of the loop.
It looks like the delegate DOES fire on any container controls. It works on Panel and the DataGrid.
I was just thinking the same thing for some of my custom controls – I don’t think I’ve been a good citizen in most of them by explicitly calling base.Render() although I will probably try to do so from now on just to make sure that the delegate has a chance to fire.
This may still be useful in some situations when dealing with my own controls. Knowing that I can control the rendering externally through say an extender control has potential. It’s a shame that this isn’t wired more directly into the framework though – it sure seems that the delegate should ALWAYS fire.
I ran into this as I'm writing up an article about Page Compilation and looking at the Page Parse Tree generation. ASP.NET internally uses SetRenderMethodDelegate to hook up custom rendering for all containers by generating page level methods that hold the parse tree for the container. This would be why it works with Containers and not with simple controls <g>...
Other Posts you might also like