ASP.NET AJAX Advance Tips & Tricks (10) 解决使用AJAX Extender时的页面导出(Word/Excel)问题(Extender control 'XXX' is not a registered extender control)
前言:
有客户问到这么个问题:我们可以使用Response容易地将ASP.NET页面导出为Excel或Word。然而,如果有AJAX Control Toolkit的Extender在页面上的话,则会发生错误(Extender control 'XXX' is not a registered extender control)。我搜索了一下,ASP.NET forum里有很多这样的问题未能解决,故写了这个解决方案,与大家分享。
问题重现:
首先,使用Response将ASP.NET页面导出为Excel或Word,代码比较简单,如下所示:
{
if (type == DocumentType.Excel)
{
//Excel
Response.AppendHeader("Content-Disposition", "attachment;filename="+name+".xls");
Response.ContentType = "application/ms-excel";
}
else if (type == DocumentType.Word)
{
//Word
Response.AppendHeader("Content-Disposition", "attachment;filename=" + name + ".doc");
Response.ContentType = "application/ms-word";
}
Response.Charset = "UTF-8";
Response.ContentEncoding = System.Text.Encoding.UTF8;
source.Page.EnableViewState = false;
System.IO.StringWriter writer = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htmlWriter = new System.Web.UI.HtmlTextWriter(writer);
source.RenderControl(htmlWriter);
Response.Write(writer.ToString());
Response.End();
}
public enum DocumentType
{
Word,
Excel
}
然而,在下面的例子中,我们将会得到一个令人厌烦的错误:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Button2_Click(object sender, EventArgs e)
{
ExportControl(this, DocumentType.Word, "ExportWord");
}
public override void VerifyRenderingInServerForm(Control control)
{
}
public void ExportControl(System.Web.UI.Control source, DocumentType type,string name)
{
if (type == DocumentType.Excel)
{
//Excel
Response.AppendHeader("Content-Disposition", "attachment;filename="+name+".xls");
Response.ContentType = "application/ms-excel";
}
else if (type == DocumentType.Word)
{
//Word
Response.AppendHeader("Content-Disposition", "attachment;filename=" + name + ".doc");
Response.ContentType = "application/ms-word";
}
Response.Charset = "UTF-8";
Response.ContentEncoding = System.Text.Encoding.UTF8;
source.Page.EnableViewState = false;
System.IO.StringWriter writer = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htmlWriter = new System.Web.UI.HtmlTextWriter(writer);
source.RenderControl(htmlWriter);
Response.Write(writer.ToString());
Response.End();
}
public enum DocumentType
{
Word,
Excel
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" />
</div>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<ajaxToolkit:CalendarExtender ID="TextBox1_CalendarExtender" runat="server" Enabled="True"
TargetControlID="TextBox1">
</ajaxToolkit:CalendarExtender>
<br />
<br />
<br />
<asp:Panel ID="Panel1" runat="server" Height="100px" Width="194px" BackColor="Chocolate">
</asp:Panel>
<ajaxToolkit:RoundedCornersExtender ID="Panel1_RoundedCornersExtender" runat="server"
Enabled="True" TargetControlID="Panel1">
</ajaxToolkit:RoundedCornersExtender>
<br />
</p>
<asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Download" />
</form>
</body>
</html>
错误信息如下:
Server Error in '/AjaxControlToolkitWebSite2' Application.
--------------------------------------------------------------------------------
Extender control 'TextBox1_CalendarExtender' is not a registered extender control. Extender controls must be registered using RegisterExtenderControl() before calling RegisterScriptDescriptors().
Parameter name: extenderControl
分析:
显然,问题是出现在AJAX Extender上,如果把页面上的AJAX Extender全部删除,则工作正常。
有此,该问题的解决思路为“在导出之前移除页面上所有的Extender”
解决方案:
使用递归方式,在导出之前移除所有AJAX Extenders:
{
for (int i = 0; i < parent.Controls.Count; i++)
{
if (parent.Controls[i].GetType().ToString().IndexOf("Extender") != -1 && parent.Controls[i].ID != null)
{
parent.Controls.RemoveAt(i);
parent.Controls[i].Dispose();
}
if (parent.Controls[i].Controls.Count > 0)
{
DisableAJAXExtenders(parent.Controls[i]);
}
}
}
如下例所示,页面可成功导出为Word/Excel:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Button2_Click(object sender, EventArgs e)
{
//DisableAJAXExtenders(this);
ExportControl(this, DocumentType.Word, "ExportWord");
}
private void DisableAJAXExtenders(Control parent)
{
for (int i = 0; i < parent.Controls.Count; i++)
{
if (parent.Controls[i].GetType().ToString().IndexOf("Extender") != -1 && parent.Controls[i].ID != null)
{
parent.Controls.RemoveAt(i);
parent.Controls[i].Dispose();
}
if (parent.Controls[i].Controls.Count > 0)
{
DisableAJAXExtenders(parent.Controls[i]);
}
}
}
public override void VerifyRenderingInServerForm(Control control)
{
}
public void ExportControl(System.Web.UI.Control source, DocumentType type,string name)
{
if (type == DocumentType.Excel)
{
//Excel
Response.AppendHeader("Content-Disposition", "attachment;filename="+name+".xls");
Response.ContentType = "application/ms-excel";
}
else if (type == DocumentType.Word)
{
//Word
Response.AppendHeader("Content-Disposition", "attachment;filename=" + name + ".doc");
Response.ContentType = "application/ms-word";
}
Response.Charset = "UTF-8";
Response.ContentEncoding = System.Text.Encoding.UTF8;
source.Page.EnableViewState = false;
System.IO.StringWriter writer = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htmlWriter = new System.Web.UI.HtmlTextWriter(writer);
source.RenderControl(htmlWriter);
Response.Write(writer.ToString());
Response.End();
}
public enum DocumentType
{
Word,
Excel
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" />
</div>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<ajaxToolkit:CalendarExtender ID="TextBox1_CalendarExtender" runat="server" Enabled="True"
TargetControlID="TextBox1">
</ajaxToolkit:CalendarExtender>
<br />
<br />
<br />
<asp:Panel ID="Panel1" runat="server" Height="100px" Width="194px" BackColor="Chocolate">
</asp:Panel>
<ajaxToolkit:RoundedCornersExtender ID="Panel1_RoundedCornersExtender" runat="server"
Enabled="True" TargetControlID="Panel1">
</ajaxToolkit:RoundedCornersExtender>
<br />
<br />
<asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Download" />
</form>
</body>
</html>
PS:
一般情况下,如果想要移除页面上的所有AJAX Extender,也可以采用JavaScript的方式:
相关Case:
http://forums.asp.net/t/1405746.aspx
http://forums.asp.net/p/1078866/2082230.aspx
http://forums.asp.net/p/1242924/2275572.aspx