从UpdatePanel编程强制执行完整的PostBack

6
我有一个在UpdatePanel中的GridView。它是继承自asp:GridView的,所以它有一个小的“导出我”图标,可以将它导出为XLS文件。它通过响应“导出”点击来工作。问题在于,如果你把这个智能GridView放在一个UpdatePanel中,ASP.NET会认为XLS文件应该写入面板中,这显然不是我们想要的。因此,每次都需要完全的后台处理。
我的所有更新面板都是通过编程生成的。
以下是在这种情况下不起作用的解决方案(其中许多在其他SO中已经涵盖):
1. 在ASP.NET 4之前的版本中,如果您在控件上留下ID,即使在UpdatePanel内部也会进行完全的后台处理。我的问题只针对最新和最伟大的.NET 4。
2. ScriptManager.RegisterPostBackControl看起来很有前途。它确实使控件后台处理使用正确的面板ID作为事件目标,但没有其他帮助。
3. 向UpdatePanel添加PostBackTrigger。我的更新面板是通过编程生成的,MS表示不支持这样做。我的测试表明他们是正确的:我试过了各种方式,但这不起作用。
4. 我不太喜欢聪明的GridView必须从自己身上挣脱出来的想法,但我试图在这些情况下让它在updatePanel外面放一个额外的控件。这个想法是在面板内部使客户端单击我的导出按钮,然后通过客户端JavaScript将其重定向到模拟面板外部的那个按钮上。然而,这不起作用,因为我似乎不能将“外部”控件添加到页面上——我得到了“The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases.” 错误。
5. 使用jQuery将“导出”控件移动到面板外部。MS一定有一些他们认为是“在”面板中的控件的列表,而DOM中的物理位置并不重要。
有人有什么想法如何使它工作吗?我知道很多东西应该可以工作,但那不完全是同一件事。
4个回答

4

对我来说,解决方案3是有效的。

我动态地向页面添加了一个UpdatePanel,在内容模板中添加了一个Button并关联了一个PostBackTrigger。我添加了一个Click事件处理程序,在其中使用当前日期/时间更新一个标签(位于UpdatePanel之外)。

请参见下面的测试设置。

标记

<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:Panel ID="Panel1" runat="server"></asp:Panel>
<asp:Label ID="Label1" runat="server"></asp:Label>

代码后端

protected void Page_Load(object sender, EventArgs e)
{
    string buttonId = "Button1";

    UpdatePanel updatePanel = new UpdatePanel();
    updatePanel.ID = "UpdatePanel1";

    Button button = new Button();
    button.ID = "Button1";
    button.Text = "Post back";
    button.Click += new EventHandler(button_Click);

    updatePanel.Triggers.Add(new PostBackTrigger() { ControlID = buttonId});

    updatePanel.ContentTemplateContainer.Controls.Add(button);

    Panel1.Controls.Add(updatePanel);
}

void button_Click(object sender, EventArgs e)
{
    Label1.Text = string.Format("Current date/time: {0}", DateTime.Now.ToString());
}

希望这有所帮助。

对我来说,它不起作用。如果我在创建面板时添加“postBackTrigger”,则如果按钮.ID不存在,则会失败(合理),但是如果我在自定义控件中添加它,则没有任何效果-这表明存在某些生命周期问题。我会继续努力。 - philw
如果我将按钮控件放入包含更新面板的控件的OnInit()中,那么它将按建议工作。相反,如果我将它放在需要它的自定义控件的OnInit()中(向上搜索控件层次结构以查找更新面板),那么它就不起作用。需要进行更多实验。 - philw
不确定这是否有帮助,但请查看此问题(特别是答案):在UpdatePanel中的FormView中使用FileUpload - jdavies
我看了大部分那些问题,但没有看到那个答案的微妙之处。我明天会试试。目前,我在所有我的更新面板中放置了一个不可见的链接按钮。然后在面板内的自定义GridView中找到它,并将导出事件处理程序连接到它。最后,我使用jQuery将控件移动到网格中。这很完美,但这是违反自然规律的行为! - philw

3

我知道这个问题是一年前提出的,但如果对其他人有帮助的话,解决方法2对我有效。

((ScriptManager)this.Page.Master.FindControl("scrptmgr")).RegisterPostBackControl(lnkbtn);

唯一让它工作的方法是,当上述代码运行时,控件(在本例中为 lnkbtn)必须可见。

编辑 - 经过更多测试,现在似乎只有在注册控件进行完整的提交并实际点击该按钮之间没有postback时,此方法才起作用。解决方案或hack是在每次页面加载时注册控件以进行完整的提交。


有趣的是 - 我用另一种方式解决了它(不记得是怎么做的)。"postbacks" 的注意事项可能是为什么这对我没有起作用的原因。 - philw
挽救了我的晚上。在我的情况下,我有自己的事件,它只绑定到包含UpdatePanel的主控件。我的页面OnInit代码如下: anlagenSelector.SelectionChanged += anlagenSelector_SelectionChanged; ((ScriptManager)this.Page.Master.FindControl("scriptManager")).RegisterPostBackControl(anlagenSelector); - Tillito

0

我建立了一个类似的继承GridView控件,具有CSV导出功能。我处理它的方式是将导出按钮设置为当前页面的简单<a href>,并带有特别制作的查询字符串参数"?SortablePagableGridViewExportToCSV=XXX",其中XXX是我的控件的UniqueID。我重写了我的控件的Render方法,在那里我手动检查请求参数,看看是否发送了该导出参数。如果是这样,我就像下面一样编写CSV内容类型头和响应。

Me.Page.Response.Clear()
Me.Page.Response.ClearContent()
Me.Page.Response.ClearHeaders()
Me.Page.Response.ContentType = "text/csv"
Me.Page.Response.AddHeader("Content-Disposition", "attachment;filename=" & Me.ExportToCsvFileName)
Me.Page.Response.End()

关键是调用Page.Response.End(),它会停止所有进一步的处理。这是一个GET请求,所以我不必担心UpdatePanel异步回发或其他类似的问题。浏览器在这种情况下表现得很好,弹出下载链接但保持用户在当前页面上。浏览器的URL不会改变,用户甚至可以右键点击下载链接并选择“另存为...”如果他们愿意的话。

你肯定需要“response”这个东西,但我的问题更具体。问题在于微软处理更新面板的代码会“窃取”下载的文件。在更新面板之外,它可以像你描述的那样工作,但在里面却不行。 - philw
@philw,你自己尝试过这个技术吗?在使用.NET 4.0的UpdatePanel中,它对我有效。在这种情况下,UpdatePanel并不重要,因为它根本没有回传,而是执行了一个正常的GET请求,这不会被任何UpdatePanel AJAX代码所捕获。 - Jordan Rieger
当然,响应内容也需要添加内容长度,否则您将无法获得任何进度指示器。我想(从记忆中)我实际上不记得我们是如何解决这个问题的! - philw

0
如果应该强制页面回发的控件(假设是一个按钮)在 GridView 的模板字段内部,您可能会遇到一些问题设置 UpdatePanel 触发器的 controlId... 在这种情况下,您可以简单地转到模板字段按钮并将其 "PostBackUrl" 属性设置为... 相同的页面本身!
这样,点击它,它将强制进行完整的回发...

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