Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)?

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

ControlService.asmx

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

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:

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:

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

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.

I googled around and discovered Scott Guthrie's excellent ViewManager example. 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.

like image 965
Jeff Leonard Avatar asked Jun 10 '09 15:06

Jeff Leonard


2 Answers

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:

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

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

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

like image 34
orandov Avatar answered Sep 29 '22 12:09

orandov


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

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

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.

like image 118
Bela Avatar answered Sep 29 '22 13:09

Bela