UserControl的RenderControl方法需要在(C# .NET)中添加一个form标签。

13

我问了如何呈现UserControl的HTML,并为动态生成的UserControl使代码工作。

现在我正在尝试使用LoadControl来加载先前生成的控件并输出其HTML,但它给了我这个:

类型为'TextBox'的控件必须放置在带有runat=server的表单标记内。

我实际上并没有将控件添加到页面中,我只是试图获取它的HTML。 有什么想法吗?

这是我正在尝试的一些代码:

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

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

return myTextWriter.ToString();

1
当我从UserControl的控件中删除runat="server"时,这种情况就不会发生。 - Jon Smock
5个回答

16

或者,您可以在将控件呈现为字符串的页面上禁用ServerForm/事件验证。

下面的示例说明了如何执行此操作。

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 */}
    }
}

很遗憾你必须覆盖整个页面,但这对我有效。不错。 - Jon Smock
是的,这并不漂亮,但很容易。 我想你可以引入一个布尔类变量,并使用它来切换“禁用”开关。这样,您可以在呈现控件时暂时将其关闭,然后再为页面的其余部分打开它。如果需要,尝试一下。 - Tom Jelen

1
你可以将控件添加到页面中,渲染 HTML,然后从页面中删除该控件。
或者尝试这个:
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 );
    }
}

我实际上尝试将其添加到页面中,但让我尝试一下你正在做的事情。 - Jon Smock
它说我在那个上下文中无法访问tmpForm。(TempPage 应该改为 Page,对吗?) - Jon Smock
糟糕,应该是 tmpPage。 - Jon Smock

1

这是我目前使用的一个不太优雅的解决方案(先让它工作,再让它正确,对吧?)。

我已经创建了一个继承自UserControl类的新类,并从所有其他我创建的“用户控件”派生。我称之为formPartial(向Rails致敬),并且这将放在公共字符串renderMyHTML()方法中:

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();

我脑海中的缺点如下:

  1. 您需要一个带有每个可能控件(或您期望的控件)的Case语句。
  2. 您需要将所有重要属性从现有控件(文本框等)传输到新的空白控件。
  3. 无法充分利用Controls的RenderControl方法。

很容易搞砸1或2。不过,希望这能帮助其他人想出更优雅的解决方案。


0

您可以在用户控件中添加表单,或使用常规的HTML输入框。

 <input type="text" />

编辑:如果你想要做一些 AJAX 相关的事情,也许你需要像这样的东西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();
    }

如果不需要,您可以删除数据绑定部分。


我考虑添加第二个UserControl,每个控件上都减去runat="server"的相同视图,但这绝对不是DRY的,并且它会对下拉框等事物施加很多限制。 - Jon Smock
问题在于,如果没有表单,您的下拉菜单将无法与服务器进行通信,最好的方法是使用<form runat="server">包装用户控件的内容。 - Bob
我在整个页面周围放置了一个 <form runat="server">,但是这个控件好像没有看到。你还建议使用吗? - Jon Smock
我已经成功地将该控件添加到页面中,但是当我尝试RenderControl()控件时,它会出现问题。 我抓取文本的方式有问题吗? - Jon Smock
将用户控件用form标签包装会产生错误。(一个页面只能有一个服务器端Form标签。) - Jon Smock

-1

我使用类似于@TcKs的代码遇到了同样的问题,并且无法使这些示例中的任何一个正常工作。我通过使用UserControlLoadControl方法来解决了这个问题,如下所示:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接