Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Adding Rendering with SetRenderMethodDelegate() - aeh, sort of


:P
On this page:

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>...

 

 


The Voices of Reason


 

Steve from Pleasant Hill
June 24, 2006

# re: Adding Rendering with SetRenderMethodDelegate() - aeh, sort of

It would seem that unless one wanted to replace stock rendering with their own they would not call base.Render(), and if they wanted to add to the functionality, call base.Render() in an overridden control and then call your own code.

That is a pain for every damn control, so having a SetRenderMethodDelegate method to slap an assignment into is a wonderful idea -- and I agree, it should fire for every control that has it.

Jason Haley
June 24, 2006

# Interesting Finds: June 24, 2006 AM edition


Rick Strahl
June 24, 2006

# re: Adding Rendering with SetRenderMethodDelegate() - aeh, sort of

Steve,

SetRenderMethodDelegate() has some odd behaviors. It looks like it fires on all Containers and when it does it completely replaces the output of rendering that would normally occur. So if I hook a Panel control, and SetRenderMethod(), and override it to return a plain string I only see the plain string, not the Panel.

After working with this some more (see next post) I think Microsoft devised this method for a very specific cases, which is overriding container rendering in the Page Parse Tree when script expressions are embedded into the container. When there are script expressions, ASP.NET creates a custom render method for the control and uses it instead of standard Controls parsing.

I guess in this case the 'internal use' designation on the SetRenderMethodDelegate is a warning that should be taken seriously <g>...

paja
June 25, 2006

# control adapters

I'm not sure, if this is the case, but you can create your own render methods even for stock controls without overriding them. MS itself released CSS friendly control adapters, which demonstrate this technique. http://www.asp.net/cssadapters/ You specify rendering methods in web.config and can force different methods for different browsers, for example. Maybe this is the place, where SetRenderMethodDelegate looks for basic controls.

Rick Strahl
June 25, 2006

# re: Adding Rendering with SetRenderMethodDelegate() - aeh, sort of

Paja, I looked into that but the problem is that you need to create specific adapaters and attach them to something. This is not an option for generic code. They definitely have their place, but it's not an ad hoc feature.

I commented on this in another referenced above...


Rick Strahl's Web Log
October 15, 2006

# Understanding how &lt;% %&gt; expressions render and why Controls.Add() doesn't work - Rick Strahl's Web Log

I've been talking about the issue of not being able to call Controls.Add() on a control if the naming container contains <% %> markup expressions. Today I did a little more digging to see exactly what the problem is and here are some thoughts.

Mira Fahmy
May 01, 2008

# re: Adding Rendering with SetRenderMethodDelegate() - aeh, sort of

I have a problem similar to the topic addressed in this topic and i need ur help in it:
- I am using an HTTP Module to control the appearance of a web page (To Change some
Controls on the web Page)
- I can access the controls on the web page as i can hookup up into the PreRenderComplete event of the Web Page.
- I want to use the SetRenderMethodControl on each control i want to change , the problem is that the render method of the control does not have base.Render, therefore the SetRenderMethodDelegate is not fired , do u have a solution to this problem, by the way i can't access the render method of the control
Thanks for your help

Jmiles
September 09, 2008

# re: Adding Rendering with SetRenderMethodDelegate() - aeh, sort of

Is this something i could use to get a linkbutton to put "javascript:__dopostback" into an onclick attribute instead of href?

(sorry for the ignorance, just trying to get a grasp here....)

Rick Strahl
September 09, 2008

# re: Adding Rendering with SetRenderMethodDelegate() - aeh, sort of

@Jmiles - to get the __dopostback you can use Page.ClientScript.GetPostbackEventReference() and embed the result.

Bobby
May 29, 2009

# re: Adding Rendering with SetRenderMethodDelegate() - aeh, sort of

What you can do, for those controls that don't call base.Render(), place the control inside a panel and then set the delegate for the panel instead.

Inside the delegate render the control in the following way:

protected void renderDelegate(HtmlTextWriter writer, Control container)
{
StringWriter tw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(tw);
myControl.RenderControl(hw);//your control here
string html = tw.ToString();
writer.Write(html);
}

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024