C# 在 ASMX Web 服务中使用 Server.Execute() 呈现 UserControl 的问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/976524/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:41:11  来源:igfitidea点击:

Issues rendering UserControl using Server.Execute() in an ASMX web service

c#asp.netuser-controlsasmx

提问by Jeff Leonard

Can anyone explain to why Server.Execute() is requiring my rendered UserControls to contain <form>tags (or alternately, what I am doing wrong that is making Server.Execute() require form tags in my UserControls)?

任何人都可以解释为什么 Server.Execute() 要求我呈现的 UserControls 包含<form>标签(或者,我做错了什么使 Server.Execute() 在我的 UserControls 中需要表单标签)?

I have created an ASMX service to dynamically load UserControls via JQuery+JSON as follows:

我创建了一个 ASMX 服务来通过 JQuery+JSON 动态加载 UserControls,如下所示:

ControlService.asmx

ControlService.asmx

<%@ WebService Language="C#" CodeBehind="ControlService.asmx.cs" Class="ManagementConcepts.WebServices.ControlService" %>

ControlService.cs

控制服务.cs

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class ControlService : System.Web.Services.WebService
{
    private string GetControl(String controlName, String ClassId)
    {
        Page page = new Page();
        UserControl ctl = (UserControl)page.LoadControl(controlName);

        page.Controls.Add(ctl);
        StringWriter writer = new StringWriter();
        HttpContext.Current.Server.Execute(page, writer, false);
        return writer.ToString();
    }
    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
    public string GetSimpleControl(string ClassId)
    {
        return GetControl("SimpleControl.ascx", ClassId);
    }
}

I load the control into a page via the following bit of JQuery which replaces a with the id ContentPlaceholder with the HTML returned from the service:

我通过以下 JQuery 将控件加载到页面中,该 JQuery 将带有 ID ContentPlaceholder 的 a 替换为从服务返回的 HTML:

JQueryControlLoadExample.aspx

JQueryControlLoadExample.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="JQueryControlLoadExample.aspx.cs" Inherits="ControlService_Prototype._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>ControlService Prototype</title>
</head>
<body>
    <form id="theForm" runat="server" action="JQueryControlLoadExample.aspx">
        <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" >
            <Scripts>
                <asp:ScriptReference NotifyScriptLoaded="true" Path="~/Scripts/jquery-1.3.2.js" />
            </Scripts>
        </asp:ScriptManager>
        <div>
        <asp:HiddenField runat="server" ID="hdncourse"/>
        <asp:HiddenField runat="server" ID="hdnTargetContent" Value="GetSimpleControl"/>
        <div runat="server" id="ContentPlaceholder" class="loading"></div>
        </div>
        <script type="text/javascript">
            $(document).ready(function() {
                var servicemethod = document.getElementById("hdnTargetContent").value;
                $.ajax({
                type: "POST",
                    url: "ControlService.asmx/" + servicemethod,
                    data: "{'ClassId':'"+document.getElementById("hdncourse").value+"'}",
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    success: function(msg) {
                        $('#ContentPlaceholder').html(msg.d);
                    }
                });
            });
        </script>
    </form>
</body>
</html>

This works with one huge caveat. If I don't define a form inside the .ascx control's markup then HttpContext.Current.Server.Execute() throws an HttpException with the following message:

这适用于一个巨大的警告。如果我没有在 .ascx 控件的标记中定义表单,则 HttpContext.Current.Server.Execute() 会抛出一个带有以下消息的 HttpException:

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

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

SimpleControl.ascx

简单控件.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SimpleControl.ascx.cs" Inherits="ControlService_Prototype.UserControls.SimpleControl" %>
    <asp:HiddenField runat="server" ID="hdnspecialoffer"/>

When I added a form tag to the ascx control to work around this, the form would render, but the renderer rewrites the form tag in the control so that it POSTs back to the ASMX service instead of the form defined in the aspx page.

当我向 ascx 控件添加一个表单标签来解决这个问题时,表单会呈现,但呈现器会重写控件中的表单标签,以便它 POST 回 ASMX 服务,而不是在 aspx 页面中定义的表单。

I googled around and discovered Scott Guthrie's excellent ViewManagerexample. I don't see anything fundamentally different from what he did there, which leads me to believe that what I am doing ought to work.

我搜索了一下,发现了 Scott Guthrie 的优秀ViewManager示例。我没有看到任何与他在那里所做的根本不同的事情,这让我相信我正在做的事情应该奏效。

采纳答案by Bela

Looks like the answer was buried in the comments for the ViewManager

看起来答案隐藏在ViewManager的评论中

You'll want a class that inherits from Page and overrides the check for server controls not in a form

您需要一个从 Page 继承的类并覆盖对不在表单中的服务器控件的检查

public class FormlessPage : Page
{
    public override void VerifyRenderingInServerForm(Control control)
    {
    }
}

Then when you render the control, use

然后在渲染控件时,使用

Page page = new FormlessPage();
UserControl ctl = (UserControl)page.LoadControl(controlName);
//etc

I'm assuming you'll lose the ability to have events firing from any controls rendered this way.

我假设您将失去从以这种方式呈现的任何控件触发事件的能力。

回答by ichiban

You could change your GetControl()method as follows:

你可以改变你的GetControl()方法如下:

private string GetControl(String controlName, String ClassId)
{
    Page page = new Page();
    StringWriter writer = new StringWriter();
    page.Server.Execute(controlName, writer, false);
    return writer.ToString();
}

回答by orandov

Instead of using an asp.net hidden control on your user control, just use a regular html hidden input with the code tags <% %> to fill the data like this:

不要在用户控件上使用 asp.net 隐藏控件,只需使用带有代码标签 <% %> 的常规 html 隐藏输入来填充数据,如下所示:

<input id="Hidden1"  type="hidden" value="<%= text %>"/>

"text" is a public variable in the code behind file.

“text”是代码隐藏文件中的公共变量。

This worked for me and didn't require a form with runat="server".

这对我有用,不需要带有runat="server".

回答by Javier Sanchez

    <System.Web.Services.WebMethod()> _
Public Shared Function GetDetails(ByVal filename As String) As String
    Dim page As Page = New Page()
    Dim ctl As recDetails = CType(page.LoadControl("~/Controles/recDetails.ascx"), recDetails)
    ctl.FileName = filename

    page.EnableEventValidation = False
    Dim _form As New HtmlForm()
    page.Controls.Add(_form)
    _form.Controls.Add(ctl)

    Dim writer As New System.IO.StringWriter()
    HttpContext.Current.Server.Execute(page, writer, False)
    Dim output As String = writer.ToString()
    writer.Close()
    Return output
End Function

You add the Form dinamically

您动态地添加表单

回答by Javier Sanchez

<System.Web.Services.WebMethod()> _
Public Shared Function GetDetails(ByVal filename As String) As String
    Dim page As Page = New Page()
    Dim ctl As recDetails = CType(page.LoadControl("~/Controles/recDetails.ascx"), recDetails)
    ctl.FileName = filename

    page.EnableEventValidation = False
    Dim _form As New HtmlForm()
    page.Controls.Add(_form)
    _form.Controls.Add(ctl)

    Dim writer As New System.IO.StringWriter()
    HttpContext.Current.Server.Execute(page, writer, False)
    Dim output As String = writer.ToString()
    writer.Close()
    Return output
End Function

You add the Form dinamically

您动态地添加表单