简体   繁体   中英

What's The Best Way To Load Widgets Asynchronously?

I'm trying to emulate iGoogle widgets for my company's internal website and I'm running into a problem when I do a post back. The error I receive is

Sys.WebForms.PageRequestManagerServerErrorException: Sys.WebForms.PageRequestManagerServerErrorException: The state information is invalid for this page and might be corrupted.

I receive this error when the LayoutButton1 button is clicked. The weird thing is that this error occurs on IE 9 and Chrome 16, but not in Firefox 9.

How can I load ASPX pages or user controls using my approach? Am I missing something? Is there a better way of doing this?

Here the desired behavior I want to have:

1) The user hits the page the first time and the page fully loads. Each widget displays an ajax loader gif. 2) After the page fully loads, I make asynchronous calls for each widget to load their content. The content of each widget contains ASP.NET server controls like grid view controls and tree view controls, so the widget content needs to be a user control or an ASPX page. In my proof of concept project, I'm using ASPX pages. 3) After the content of the widget is retrieved, the the ajax loader gif is replaced with the markup I received from the asynchronous call.

Here is the code I'm using. Please note I'm using Telerik ASP.NET server controls for the widgets (which shouldn't have any influence on my problem).

<script type="text/javascript">
    $(document).ready(function ()
    {
        // Wires a client-side click event to the layout buttons.
        $('.layouts').find('input').live('click', function ()
        {
            if ($(this).attr('class') == 'layoutSelected')
            {
                return false;
            }
            else
            {
                return true;
            }
        });
    });

    // Load content of all widgets.
    function loadAllWidgets()
    {
        loadWidget1();
    }

    // Loads widget #1.
    function loadWidget1()
    {
        $(document).ready(function ()
        {
            PageMethods.LoadWidgetContent1(onWidgetContentLoadSucceeded, onWidgetContentLoadFailed);
        });
    }

    // Widget content load success.
    function onWidgetContentLoadSucceeded(result, userContext, methodName)
    {
        switch (methodName)
        {
            case "LoadWidgetContent1":
                $('#Widget1').find('.widgetContent').html(result);
                break;
        }
    }

    // Widget content load fail.
    function onWidgetContentLoadFailed(error, userContext, methodName)
    {
        alert(error);
    }
</script>

<asp:ScriptManager ID="ScriptManager1" EnablePageMethods="true" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <ContentTemplate>
        <div style="padding-bottom: 10px;">
            <table class="layouts">
                <tr>
                    <td>Layout:</td>
                    <td><asp:Button ID="LayoutButton1" CommandArgument="1" OnClick="LayoutButton_Click" runat="server" /></td>
                </tr>
            </table>
        </div>
        <div>
            <telerik:RadDockLayout ID="RadDockLayout1" runat="server">
                <telerik:RadDockZone ID="RadDockZone1" CssClass="dockZone dockZoneWide" runat="server" />
            </telerik:RadDockLayout>
        </div>
    </ContentTemplate>
</asp:UpdatePanel>

protected void LayoutButton_Click(object sender, EventArgs e)
{
    var layoutNumber = Convert.ToInt32(((Button)sender).CommandArgument);

    switch (layoutNumber)
    {
        case 1:
            LayoutButton1.CssClass = "layoutSelected";
            LayoutButton2.CssClass = string.Empty;
            LayoutButton3.CssClass = string.Empty;
            LayoutButton4.CssClass = string.Empty;

            RadDockZone1.CssClass = "dockZone dockZoneWide";
            RadDockZone2.CssClass = "dockZone dockZoneNormal";
            RadDockZone3.CssClass = "dockZone dockZoneNormal";
            RadDockZone4.CssClass = "dockZone dockZoneNormal";
            RadDockZone5.CssClass = "dockZone dockZoneNormal";

            RadDockZone4.Visible = true;
            RadDockZone5.Visible = false;
            CreateWidgets();

            break;
    }
}

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        CreateWidgets();
    }
}

private void CreateWidgets()
{

    var radDock = new RadDock
    {
        Closed = false,
        DefaultCommands = DefaultCommands.All,
        ID = "Widget 1",
        Resizable = false,
        Skin = "Sitefinity",
        Title = "Widget 1",
    };

    var divTag = new HtmlGenericControl("div");
    divTag.Attributes.Add("class", "widgetContent");

    var image = new Image
    {
        CssClass = "ajaxLoader",
        ImageUrl = "~/images/transparent.gif",
        ToolTip = "loading..."
    };

    divTag.Controls.Add(image);
    radDock.ContentContainer.Controls.Add(divTag);
    RadDockLayout1.Controls.Add(radDock);
    radDock.Dock(RadDockZone1);


    ScriptManager.RegisterClientScriptBlock(this, typeof(Page), "LoadAllWidgets", "loadAllWidgets();", true);
}

[WebMethod]
public static string LoadWidgetContent1()
{
    var stringWriter = new StringWriter();
    HttpContext.Current.Server.Execute("~/WidgetContent/WidgetContent1.aspx", stringWriter);
    return stringWriter.ToString();
}

OK - after several hours and multiple approaches to this, I think I've found a good solution. The solution I found to work is to use user controls instead of ASPX pages. This will allow for server tags and prevents the invalid view state issue I was having. The only change I needed to make in the code-behind was to update the web method like so:

[WebMethod]
public static string LoadWidgetContent()
{
    Page pageHolder = new Page();
    UserControl viewControl = (UserControl)pageHolder.LoadControl("~/WidgetContent/WidgetContent.ascx");
    pageHolder.Controls.Add(viewControl);

    StringWriter output = new StringWriter();
    HttpContext.Current.Server.Execute(pageHolder, output, false);

    return output.ToString();
}

This approach allowed me to continue using the update panel and loaded the widgets once the page was completely loaded. I ended up using Scott Gu's blog as a reference . This is a bit outdated, but the basic principle of using web methods still holds.

The approach you are using is correct and could work. If you need an example of professional widgets - take a look at the Telerik Sitefinity. The product is commercial, but you can download a Trial version and test it.

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