前言:
有客户问到这么个问题:我们可以使用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的方式:
function removeall() {
var currentBehavior = null;
var allBehaviors = Sys.Application.getComponents();
for (var loopIndex = 0; loopIndex < allBehaviors.length; loopIndex++) {
currentBehavior = allBehaviors[loopIndex];
currentBehavior.dispose();
}
}
相关Case:
http://forums.asp.net/t/1405746.aspx