简体   繁体   English

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

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

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 ControlService.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将控件加载到一个页面中,用来自服务返回的HTML替换id为ContentPlaceholder:

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.

SimpleControl.ascx SimpleControl.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控件来解决此问题时,表单将呈现,但渲染器会重写控件中的表单标记,以便它回发到ASMX服务而不是aspx页面中定义的表单。

I googled around and discovered Scott Guthrie's excellent ViewManager example. 我用Google搜索并发现了Scott Guthrie出色的ViewManager示例。 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. 我没有看到与他在那里所做的任何根本不同的事情,这让我相信我正在做的事情应该起作用。

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. 我假设你将失去从任何以这种方式呈现的控件触发事件的能力。

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"并且不需要带有runat="server"的表单。

<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 您可以通过dinamically添加表单

    <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 您可以通过dinamically添加表单

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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