views:

426

answers:

3

I am trying to render a composite control to a string and write it to the page as such:

Dim sb As New StringBuilder
Dim sw As New StringWriter(sb)
Dim hw As New HtmlTextWriter(sw)
Dim CustomCompositeControl as New MyCustomCompositeControl
CustomCompositeControl.RenderControl(hw)
HttpContext.Current.Response.Write(sb.ToString)

Unfortunately it is merely rendering a empty span tag.

Is it even possible to render CompositeControls into strings as you would, say, a DataGrid?

Without getting into the literal internals of my CompositeControl, is there something I should be making sure of inside the CompositeControl before it can be rendered via RenderControl?

NOTE: I am not overriding the RenderControl in my CompositeControl. Do I need to do that?

UPDATE Ok. I sorta figured this out. I have to make sure my CompositeControl's EnsureChildControls method is called before the RenderControl method runs. I'm just not so sure about the best spot to call it internally.

+1  A: 

Yes this is possible to do, but is it possible without understanding the controls internals and its dependancies on various events within the asp.net lifecycle? I don't think that is possible.

A DataGrid probally depends on being inside a form for example and my guess is it would error out indicating there is no form.

JoshBerke
I'm able to render a bound datagrid straight to the response page without any problems in the same location I'm trying to render this CompositeControl.
Matias Nino
I'm tried to render a DataGridView in a simillar manner and ran into issues it's a year since I did this though so I'm not exactly sure.
JoshBerke
+2  A: 

If you have the access to simply call the Render() method, just do that, it will give you the result you're looking for. Otherwise, yes, you will have to override RenderControl()... depending on when you're calling it, you may be able to get away with just calling the Render() method insider RenderControl().

Also, you should note that the top level html tag of your composite control will always be a span, unless you specify it to be something else, like a div:

/// <summary>
/// Render as div instead of the default span.
/// </summary>
/// <value></value>
/// <returns>HtmlTextWriterTag.Div</returns>
protected override HtmlTextWriterTag TagKey
{
   get
   {
      return HtmlTextWriterTag.Div;
   }
}
womp
+1 for showing me how to render it as a div, ta.
Generic Error
A: 

Take a look at this MSDN page discussing CompositeControls, in particular the section titled "The Rendering Engine of Composite Controls"

The request processing code (in the Page class) calls EnsureChildControls immediately before firing the PreRender event to the page and each child control.

During a normal ASP.NET request, the CreateChildControls() method that you override in a CompositeControl is not called until it reaches a late stage of the request life cycle, right before PreRender. If you're trying to render a CompositeControl outside of the normal ASP.NET page life cycle, then the child controls will never be added because no child control creation methods are ever called. The best way to guarantee that EnsureChildControls() is called when manually rendering the control is to override RenderControl(), like so:

public override void RenderControl(HtmlTextWriter writer)
{
    // We must explicitly call EnsureChildControls() in cases where we are rendering 
    // a CompositeControl outside of the normal ASP.NET page lifecycle
    EnsureChildControls();
    base.RenderControl(writer);
}

It seems odd to me that the CompositeControl class itself doesn't already override the RenderControl() method in a similar fashion. If you use Reflector to examine the implementation details of CompositeControl, you'll see that all of the methods overridden in the class are done so solely to call EnsureChildControls(); even Render() will call the EnsureChildControls() method if the control is in design mode. From Reflector:

protected internal override void Render(HtmlTextWriter writer)
{
    if (base.DesignMode)
    {
        this.EnsureChildControls();
    }
    base.Render(writer);
}
David Mills