简体   繁体   中英

UserControl's RenderControl is asking for a form tag in (C# .NET)

I asked how to render a UserControl's HTML and got the code working for a dynamically generated UserControl.

Now I'm trying to use LoadControl to load a previously generated Control and spit out its HTML, but it's giving me this:

Control of type 'TextBox' must be placed inside a form tag with runat=server.

I'm not actually adding the control to the page, I'm simply trying to grab its HTML. Any ideas?

Here's some code I'm playing with:

TextWriter myTextWriter = new StringWriter();
HtmlTextWriter myWriter = new HtmlTextWriter(myTextWriter);

UserControl myControl = (UserControl)LoadControl("newUserControl.ascx");
myControl.RenderControl(myWriter);

return myTextWriter.ToString();

Alternatively you could disable the ServerForm/Event-validation on the page that is rendering the control to a string.

The following example illustrates how to do this.

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string rawHtml = RenderUserControlToString();
    }

    private string RenderUserControlToString()
    {
        UserControl myControl = (UserControl)LoadControl("WebUserControl1.ascx");

        using (TextWriter myTextWriter = new StringWriter())
        using (HtmlTextWriter myWriter = new HtmlTextWriter(myTextWriter))
        {
            myControl.RenderControl(myWriter);

            return myTextWriter.ToString();
        }
    }

    public override void VerifyRenderingInServerForm(Control control)
    { /* Do nothing */ }

    public override bool EnableEventValidation
    {
        get { return false; }
        set { /* Do nothing */}
    }
}

You can add the control into page, render html and then remove the control from page.

Or try this:

Page tmpPage = new TempPage(); // temporary page
Control tmpCtl = tmpPage.LoadControl( "~/UDynamicLogin.ascx" );
//the Form is null that's throws an exception
tmpPage.Form.Controls.Add( tmpCtl );

StringBuilder html = new StringBuilder();
using ( System.IO.StringWriter swr = new System.IO.StringWriter( html ) ) {
    using ( HtmlTextWriter writer = new HtmlTextWriter( swr ) ) {
        tmpPage.RenderControl( writer );
    }
}

This is a dirty solution I used for the moment (get it working then get it right, right?).

I had already created a new class that inherits the UserControl class and from which all other "UserControls" I created were derived. I called it formPartial (nod to Rails), and this is going inside the public string renderMyHTML() method:

TextWriter myTextWriter = new StringWriter();
HtmlTextWriter myWriter = new HtmlTextWriter(myTextWriter);

UserControl myDuplicate = new UserControl();
TextBox blankTextBox;

foreach (Control tmpControl in this.Controls)
{
    switch (tmpControl.GetType().ToString())
    {
        case "System.Web.UI.LiteralControl":
            blankLiteral = new LiteralControl();
            blankLiteral.Text = ((LiteralControl)tmpControl).Text;
            myDuplicate.Controls.Add(blankLiteral);
            break;
        case "System.Web.UI.WebControls.TextBox":
            blankTextBox = new TextBox();
            blankTextBox.ID = ((TextBox)tmpControl).ID;
            blankTextBox.Text = ((TextBox)tmpControl).Text;
            myDuplicate.Controls.Add(blankTextBox);
            break;

            // ...other types of controls (ddls, checkboxes, etc.)

    }
}

myDuplicate.RenderControl(myWriter);
return myTextWriter.ToString();

Drawbacks off the top of my head:

  1. You need a case statement with every possible control (or controls you expect).
  2. You need to transfer all the important attributes from the existing control (textbox, etc) to the new blank control.
  3. Doesn't take full advantage of Controls' RenderControl method.

It'd be easy to mess up 1 or 2. Hopefully, though, this helps someone else come up with a more elegant solution.

You can either add a form to your user control, or use a regular html input box

 <input type="text" />

Edit: If you are trying to do something AJAXy, maybe you want something like this http://aspadvice.com/blogs/ssmith/archive/2007/10/19/Render-User-Control-as-String-Template.aspx

    public static string RenderView<D>(string path, D dataToBind)
    {
        Page pageHolder = new Page();
        UserControl viewControl = (UserControl) pageHolder.LoadControl(path);
        if(viewControl is IRenderable<D>)
        {
            if (dataToBind != null)
            {
                ((IRenderable<D>) viewControl).PopulateData(dataToBind);
            }
        }
        pageHolder.Controls.Add(viewControl);
        StringWriter output = new StringWriter();
        HttpContext.Current.Server.Execute(pageHolder, output, false);

        return output.ToString();
    }

You can remove the data binding part if not needed.

I was having the same problem using similar code to @TcKs and haven't been able to make any of these examples work for me. I got it working by using the LoadControl method of a UserControl as such:

UserControl uc = new UserControl();
Control c = uc.LoadControl("newUserControl.ascx");
c.RenderControl(myWriter);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM