最佳异步加载小部件的方法是什么?

3
我正在尝试为公司的内部网站模拟iGoogle小部件,但当我进行回传时遇到了问题。我收到的错误是Sys.WebForms.PageRequestManagerServerErrorException: Sys.WebForms.PageRequestManagerServerErrorException: 该页面的状态信息无效,可能已损坏。
当单击LayoutButton1按钮时,我会收到此错误。奇怪的是,在IE9和Chrome16上出现此错误,但在Firefox9中没有出现此错误。
如何使用我的方法加载ASPX页面或用户控件?我错过了什么吗?有更好的方法吗?
以下是我想要实现的期望行为:
1) 用户第一次访问页面并且页面完全加载。每个小部件显示ajax的加载动画。 2) 页面完全加载后,我对每个小部件进行异步调用以加载其内容。每个小部件的内容包含ASP.NET服务器控件,例如网格视图控件和树形视图控件,因此小部件内容需要是用户控件或ASPX页面。在我的概念验证项目中,我正在使用ASPX页面。 3) 在检索到小部件的内容后,将用从异步调用中获取的标记替换ajax的加载动画。
这是我正在使用的代码。请注意,我正在为小部件使用Telerik ASP.NET服务器控件(这不应该对我的问题产生任何影响)。
<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();
}

UpdatePanel 无法同时处理多个更新,你可能会这样做,即在上一个更新完成之前就更新面板,导致视图状态损坏。此外,UpdatePanel 有自己的 ready 函数,但你却使用了 jQuery 的 ready 函数,这也会导致问题。 - Aristos
我移除了更新面板,但是出现了错误:“此页面的状态信息无效,可能已损坏。” - Halcyon
2个回答

1

好的 - 经过数小时和多种尝试,我认为我已经找到了一个好的解决方案。我发现可行的解决方案是使用用户控件而不是ASPX页面。这将允许使用服务器标记并防止我遇到的无效视图状态问题。我在代码后台需要做的唯一更改是更新Web方法,如下所示:

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

这种方法使我能够继续使用更新面板,并在页面完全加载后加载小部件。 最终我参考了Scott Gu的博客。这有点过时,但使用Web方法的基本原则仍然适用。


0

你所采用的方法是正确的,可能会起作用。如果你需要专业小部件的例子,请看看Telerik Sitefinity。该产品是商业化的,但你可以下载试用版本并进行测试。


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