【问题标题】:export Gridview to Excel using ClosedXML without warning: the file you are trying to open is in a different format使用 ClosedXML 将 Gridview 导出到 Excel 而不会发出警告:您尝试打开的文件格式不同
【发布时间】:2016-05-22 23:13:58
【问题描述】:

我正在开发一个 ASP.NET 4.5 Webform,并且我有一个 Gridview(具有自定义 TemplateField 并从 sqlDataSource 获取数据)

我有这个事件将 gridview 内容导出到 excel 工作表,它的工作很好,除了创建的文件在用户打开它时发出警告(我理解这是因为创建的文件不是实际的excel文件):

"您尝试打开的文件的格式不同于 由文件扩展名指定”

protected void btnExport_Excel_Click(object sender, EventArgs e)
        {
            try
            {
                Response.Clear();
                Response.Buffer = true;
                Response.AddHeader("content-disposition", "attachment;filename=GV.xls");
                Response.Charset = "";
                Response.ContentType = "application/ms-excel";
                //Response.ContentType = "application/text";
                Response.ContentEncoding = System.Text.Encoding.Unicode;
                Response.BinaryWrite(System.Text.Encoding.Unicode.GetPreamble());

                using (StringWriter sw = new StringWriter())
                {
                    HtmlTextWriter hw = new HtmlTextWriter(sw);

                    //To Export all pages
                    GridView4.AllowPaging = false;
                    GridView4.AllowSorting = false;
                    GridView4.ShowFooter = false;
                    GridView4.DataBind();
                    //this.BindGrid();

                    GridView4.HeaderRow.BackColor = Color.White;
                    foreach (TableCell cell in GridView4.HeaderRow.Cells)
                    {
                        cell.BackColor = GridView4.HeaderStyle.BackColor;
                    }
                    foreach (GridViewRow row in GridView4.Rows)
                    {
                        row.BackColor = Color.White;
                        foreach (TableCell cell in row.Cells)
                        {
                            if (row.RowIndex % 2 == 0)
                            {
                                cell.BackColor = GridView4.AlternatingRowStyle.BackColor;
                            }
                            else
                            {
                                cell.BackColor = GridView4.RowStyle.BackColor;
                            }
                            cell.CssClass = "textmode";
                        }
                    }

                    GridView4.RenderControl(hw);

                    //style to format numbers to string
                    string style = @"<style> .textmode { } </style>";
                    Response.Write(style);
                    Response.Output.Write(sw.ToString());
                    Response.Flush();
                    Response.End();
                }

                //Display message
                InfoPanel.Visible = true;
                InfoPanel.CssClass = "panel panel-success";
                lblMessage.CssClass = "text text-sucess bold";
                lblMessage.Text = "File has been exported!";

            }
            catch (Exception ex)
            {
                //Display message
                InfoPanel.Visible = true;
                lblMessage.Text = "<b>An error has occurred. Please try again later!</b></br>" + ex.Message;
                lblMessage.CssClass = "text text-danger bold";
                InfoPanel.CssClass = "panel panel-danger";
                panelResult.Visible = false;
            }
        }

Excel .xls 文件中的结果很好(除了标题列之外没有样式,没有页脚,就像 Gridview 上显示的一样):


我正在寻找另一种方法来避免这个警告,所以我看到人们喜欢使用
ClosedXML,所以我将上面的那个事件替换为这个事件:

protected void ExportExcel(object sender, EventArgs e)
{
    DataTable dt = new DataTable("GridView_Data");
    foreach(TableCell cell in GridView4.HeaderRow.Cells)
    {
        dt.Columns.Add(cell.Text);
    }
    foreach (GridViewRow row in GridView4.Rows)
    {
        dt.Rows.Add();
        for (int i=0; i<row.Cells.Count; i++)
        {
            dt.Rows[dt.Rows.Count - 1][i] = row.Cells[i].Text;
        }
   }
    using (XLWorkbook wb = new XLWorkbook())
    {
        wb.Worksheets.Add(dt);

        Response.Clear();
        Response.Buffer = true;
        Response.Charset = "";
        Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
        Response.AddHeader("content-disposition", "attachment;filename=GV.xlsx");

        using (MemoryStream MyMemoryStream = new MemoryStream())
        {
            wb.SaveAs(MyMemoryStream);
            MyMemoryStream.WriteTo(Response.OutputStream);
            Response.Flush();
            Response.End();
        }
    }
}

结果很糟糕(唯一的好处是导出的文件是真正的 2007+ Excel 工作表,因此没有警告):

如何使用 closedXML 获得上述“好”结果?

【问题讨论】:

  • 在将数据表插入工作表之前,您是否检查过它?在将数据添加到 DataTable 之后,您可能需要先调用 dt.AcceptChanges();
  • 只是把它放进去,结果还是一样?
  • 如果我理解正确,您对第二个 excel (closedXML) 感到满意,但数据未显示?
  • 是的,我想使用 ClosedXML 导出一个真正的 excel 文件,但是没有显示标题和数据,代码有问题。
  • 您以错误的方式处理此问题,为什么要解析标头等。您可以使用 ClosedXML 用更少的代码完成此操作我将发布一个您可以遵循的工作示例。我使用ClosedXML 每日零问题。

标签: c# asp.net gridview export-to-excel closedxml


【解决方案1】:

我在按钮单击事件上调用 Export to Excel,如下所示

protected void btnPrint_Click(object sender, EventArgs e)
{
    fileName = string.Format(fileName, DateTime.Now.ToString("MMddyyyy_hhmmss"));
    Extensions.ExportToXcel_SomeReport(dt, fileName, this.Page);
}

从那里我有一个名为 Extensions 的实用程序类,我在其中定义了 ExportToExcel_SomeReport 方法

public static class Extensions
{
     internal static void ExportToXcel_SomeReport(DataTable dt, string fileName, Page page)
    {
        var recCount = dt.Rows.Count;
        RemoveHtmlSpecialChars(dt);
        fileName = string.Format(fileName, DateTime.Now.ToString("MMddyyyy_hhmmss"));
        var xlsx = new XLWorkbook();
        var ws = xlsx.Worksheets.Add("Some Report Name");
        ws.Style.Font.Bold = true;
        ws.Cell("C5").Value = "YOUR REPORT NAME";
        ws.Cell("C5").Style.Font.FontColor = XLColor.Black;
        ws.Cell("C5").Style.Font.SetFontSize(16.0);
        ws.Cell("E5").Value = DateTime.Now.ToString("MM/dd/yyyy HH:mm");
        ws.Range("C5:E5").Style.Font.SetFontSize(16.0);
        ws.Cell("A7").Value = string.Format("{0} Records", recCount);
        ws.Style.Font.Bold = false;
        ws.Cell(9, 1).InsertTable(dt.AsEnumerable());
        ws.Row(9).InsertRowsBelow(1);
       // ws.Style.Font.FontColor = XLColor.Gray;
        ws.Columns("1-9").AdjustToContents();
        ws.Tables.Table(0).ShowAutoFilter = true;
        ws.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
        DynaGenExcelFile(fileName, page, xlsx);
    }


    /// <summary>
    /// Remove all HTML special characters from datatable field if they are present 
    /// </summary>
    /// <param name="dt"></param>
    private static void RemoveHtmlSpecialChars(DataTable dt)
    {
        for (int rows = 0; rows < dt.Rows.Count; rows++)
        {
            for (int column = 0; column < dt.Columns.Count; column++)
            {
                dt.Rows[rows][column] = dt.Rows[rows][column].ToString().Replace("&nbsp;", string.Empty);
            }
        }
    }

    /// <summary>
    /// Call this Method to Generate the Excel Files from different Lap Reports depending on which one has been selected
    /// </summary>
    /// <param name="fileName"></param>
    /// <param name="page"></param>
    /// <param name="xlsx"></param>
    private static void DynaGenExcelFile(string fileName, Page page, XLWorkbook xlsx)
    {
        page.Response.ClearContent();
        page.Response.ClearHeaders();
        page.Response.ContentType = "application/vnd.ms-excel";
        page.Response.AppendHeader("Content-Disposition", string.Format("attachment;filename={0}.xlsx", fileName));

        using (MemoryStream memoryStream = new MemoryStream())
        {
            xlsx.SaveAs(memoryStream);
            memoryStream.WriteTo(page.Response.OutputStream);
        }
        page.Response.Flush();
        page.Response.End();
    }   

}

【讨论】:

  • 此方法提供具有 XLS 文件的 MIME 类型并使用 .XLS 文件扩展名的 XLSX 文件。最好使用正确的扩展名和 MIME 类型application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
  • @mason 我正在使用 ClosedXML,我很清楚.. 如果您有更好的工作版本,请将其发布为答案
  • 我不明白你的评论。您正在使用 XLS 文件使用的 MIME 类型提供 XLSX 文件。您需要使用正确的 MIME 类型。就这么简单。
  • @mason 我已经使用我正在测试的类型给出了示例,因为它也可以使用 xlsx 类型我将更新答案,就这么简单..!
  • 我看到您进行了 1 个字符的编辑,但它仍然具有错误的 MIME 类型。
【解决方案2】:

与流行的看法相反,您可以将文件的扩展名设置为 .html,Excel 可以打开它。

只需将扩展名设置为 HTML:

Response.AddHeader("content-disposition", "attachment;filename=GV.html");

并保留 Excel 作为内容类型:

Response.ContentType = "application/ms-excel"; 

编辑:哦,对了,忘了说,这应该可以让那个烦人的对话消失。

编辑 2:看起来原来的问题已经改变了......现在它正在谈论使用 ClosedXML......但我会在此处留下这个答案,以防其他人使用 HTML 和 Excel。

【讨论】:

  • 你可以并不意味着你应该这样做。 MIME 类型应准确描述内容。然后由用户的设置来决定用什么打开文件。
  • @mason:我指的是原始的 office HTML 规范。它曾经存在于 MSDN 上,但显然不再存在。但是,仍然受支持。因此,如果您决定将数据表保存为 HTML,然后在 Excel 中打开它,它会很容易地为您完成。
【解决方案3】:

我试过了,请找代码希望对你有帮助:

Index.aspx

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="ExportExcel.Index" %>

  <!DOCTYPE html>

  <html xmlns="http://www.w3.org/1999/xhtml">
  <head runat="server">
  <title></title>
  </head>
  <body>
<form id="form1" runat="server">
<div>
    <asp:GridView ID="GridView1" HeaderStyle-BackColor="#3AC0F2" HeaderStyle- ForeColor="White"
        runat="server" AutoGenerateColumns="false">
        <Columns>
            <asp:BoundField DataField="Id" HeaderText="Id" ItemStyle-Width="30"  />
            <asp:TemplateField HeaderText="Name">
                <ItemTemplate>
                    <asp:TextBox ID="txtName" runat="server" Text='<%#Eval("Name") %>'></asp:TextBox>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Country">
                <ItemTemplate>
                    <asp:Label ID="lblCountry" Text='<%# Eval("Country") %>' runat="server" />
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:GridView>
    <br />
    <asp:Button ID="btnExport" Text="Export" runat="server" OnClick="btnExport_Click" />
</div>
</form>

Index.aspx.cs

        using ClosedXML.Excel;
        using System;
        using System.Collections.Generic;
        using System.Data;
        using System.Drawing;
        using System.IO;
        using System.Linq;
        using System.Web;
        using System.Web.UI;
        using System.Web.UI.WebControls;

   namespace ExportExcel
    {
    public partial class Index : System.Web.UI.Page
    {
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!this.IsPostBack)
        {
            GetData();
        }
    }

    private void GetData()
    {
        DataTable dt = new DataTable();
        dt.Columns.AddRange(new DataColumn[3] { new DataColumn("Id", typeof(int)), new DataColumn("Name", typeof(string)), new DataColumn("Country", typeof(string)) });
        dt.Rows.Add(1, "abc", "UK");
        dt.Rows.Add(2, "def", "India");
        dt.Rows.Add(3, "ghi", "France");
        dt.Rows.Add(4, "jkl", "Russia");
        GridView1.DataSource = dt;
        GridView1.DataBind();
    }

    protected void btnExport_Click(object sender, EventArgs e)
    {
        try
        {
            DataTable dt = new DataTable("GridView_Data");
            foreach (TableCell cell in GridView1.HeaderRow.Cells)
            {
                dt.Columns.Add(cell.Text);
            }
            foreach (GridViewRow row in GridView1.Rows)
            {
                TextBox txtNameRow = (TextBox)row.FindControl("txtName");

                Label lblCountryRow = (Label)row.FindControl("lblCountry");

                DataRow drow = dt.NewRow();
                for (int i = 0; i < GridView1.Columns.Count; i++)
                {
                    drow[i] = row.Cells[i].Text;
                }
                drow["Name"] = txtNameRow.Text;
                drow["Country"] = lblCountryRow.Text;
                dt.Rows.Add(drow);
            }
            using (XLWorkbook wb = new XLWorkbook())
            {
                wb.Worksheets.Add(dt);

                Response.Clear();
                Response.Buffer = true;
                Response.Charset = "";
                Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                Response.AddHeader("content-disposition", "attachment;filename=GV.xlsx");

                using (MemoryStream MyMemoryStream = new MemoryStream())
                {
                    wb.SaveAs(MyMemoryStream);
                    MyMemoryStream.WriteTo(Response.OutputStream);
                    Response.Flush();
                    Response.End();
                }
            }

        }
        catch (Exception ex)
        {

            throw;
        }
    }




}
}

【讨论】:

    【解决方案4】:

    第二部分代码(使用 ClosedXML)的主要问题是,您试图将 GridViewRowText 属性用于 TemplateField 字段列。如您所见here,您可以通过Text 属性仅对BoundField 字段列和自动生成的字段列获取字段值。

    要从TemplateField 获取值,您应该导航到包含值的内部控件并从中获取值。

    如果您有以下列模板:

       <asp:TemplateField>
           <ItemTemplate>
               <asp:Label ID="labelName" runat="server" Text ='<%# Eval("ABC")%>' ></asp:Label>
           </ItemTemplate>
       </asp:TemplateField>
    

    你的代码应该是:

        for (int i=0; i<row.Cells.Count; i++)
        {
            dt.Rows[dt.Rows.Count - 1][i] = (row.Cells[i].FindControl("labelName") as Label).Text;
        }
    

    编辑

    你的代码应该如下:

    protected void ExportExcel(object sender, EventArgs e)
    {
        DataTable dt = new DataTable("GridView_Data");
        foreach (DataControlField col in GridView4.Columns)
        {
            dt.Columns.Add(col.HeaderText);
        }
        foreach (GridViewRow row in GridView4.Rows)
        {
            dt.Rows.Add();
            for (int i = 0; i < row.Cells.Count; i++)
            {
                dt.Rows[dt.Rows.Count - 1][i] = (FindControl(row.Cells[i].Controls, "lbl") as Label).Text;
            }
        }
        //your code below is not changed
    }
    
    protected Control FindControl(ControlCollection collection, string id)
    {
        foreach (Control ctrl in collection)
        {
            if (ctrl.ID == id)
                return ctrl;
        }
    
        return null;
    }
    

    确保TemplateField 中使用的所有Label 控件具有与"lbl" 相同的ID

       <asp:TemplateField HeaderText="ID">
           <ItemTemplate>
               <asp:Label ID="lbl" runat="server" Text ='<%# Eval("ID")%>' ></asp:Label>
           </ItemTemplate>
       </asp:TemplateField>
       <asp:TemplateField HeaderText="Name">
           <ItemTemplate>
               <asp:Label ID="lbl" runat="server" Text ='<%# Eval("Name")%>' ></asp:Label>
           </ItemTemplate>
       </asp:TemplateField>
       <asp:TemplateField HeaderText="Amount">
           <ItemTemplate>
               <asp:Label ID="lbl" runat="server" Text ='<%# Eval("Amount")%>' ></asp:Label>
           </ItemTemplate>
       </asp:TemplateField>
    

    【讨论】:

    • 如何获取所有标题,在我的代码中我有类似&lt;asp:TemplateField HeaderText="Amount" SortExpression="Amount" HeaderStyle-Width="8%"&gt;
    • 你也可以帮忙处理循环,所以我有7个ItemTemplate,大部分是标签,我如何获取数据?如果您可以显示一个完整的函数或将获取数据的代码/循环,那将非常有帮助。
    • 我还是不能让这个工作,你能举个例子吗,假设有5个TemplateField(和HeaderText),每个有1个ItemTemplate,其中包含1个label .你的函数是什么样子的?
    • 我改变了答案。我希望它能帮助你解决问题。
    • 对于标题列,出现错误:'object' does not contain a definition for 'HeaderText' and no extension method 'HeaderText' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-23
    • 1970-01-01
    • 2015-07-29
    • 1970-01-01
    相关资源
    最近更新 更多