[原創]FineUI秘密花園(二十二) — 表格之導出Excel文件


將表格內容導出為Excel文件是實際項目中的常見需求,怎么來實現呢?

 

導出文件的格式

首先我們需要理解的一點是,導出的文件其實一個HTML片段,只不過Excel會按照自身的格式自動格式化而已。

來看一個導出文件的典型示例:

   1:  <table border="1">
   2:   <tr><th>姓名</th><th>性別</th></tr>
   3:   <tr><th>張三</th><th></th></tr>
   4:   <tr><th>李四</th><th></th></tr>
   5:   <tr><th>春花</th><th></th></tr>
   6:  </table>

將此文件后綴改成xls,並用Excel打開后可見:

image

 

 

將GridView導出為Excel文件

首先來看下如何將Asp.Net的GridView導出為Excel文件,網上已經有很好的參考資料,這是博客園中的中文譯本

概括說來有如下幾個技巧:

  1. 必須重載VerifyRenderingInServerForm函數,函數體留空,否則會報錯;
  2. 如果GridView中包含CheckBox,LinkButton等控件或者分頁時,需要設置頁面屬性EnableEventValidation="false",否則會報錯;
  3. 可以在導出數據之前將GridView中的CheckBox等控件用Literal控件代替。

 

下面來看一個示例,示例的ASPX標簽結構如下:

   1:  <asp:GridView ID="GridView1" Width="900px" DataKeyNames="Id,Name" AutoGenerateColumns="False"
   2:      runat="server">
   3:      <Columns>
   4:          <asp:TemplateField>
   5:              <ItemTemplate>
   6:                  <asp:Label ID="Label1" runat="server" Text='<%# Container.DataItemIndex + 1 %>'></asp:Label>
   7:              </ItemTemplate>
   8:          </asp:TemplateField>
   9:          <asp:BoundField DataField="Name" HeaderText="姓名" />
  10:          <asp:TemplateField HeaderText="性別">
  11:              <ItemTemplate>
  12:                  <asp:Label ID="Label2" runat="server" Text='<%# GetGender(Eval("Gender")) %>'></asp:Label>
  13:              </ItemTemplate>
  14:          </asp:TemplateField>
  15:          <asp:BoundField DataField="EntranceYear" HeaderText="入學年份" />
  16:          <asp:CheckBoxField DataField="AtSchool" HeaderText="是否在校" />
  17:          <asp:HyperLinkField HeaderText="所學專業" DataTextField="Major" DataTextFormatString="{0}"
  18:              DataNavigateUrlFields="Major" DataNavigateUrlFormatString="http://gsa.ustc.edu.cn/search?q={0}"
  19:              Target="_blank" />
  20:          <asp:ImageField DataImageUrlField="Group" DataImageUrlFormatString="~/images/16/{0}.png"
  21:              HeaderText="分組">
  22:          </asp:ImageField>
  23:      </Columns>
  24:  </asp:GridView>

表格初始化代碼省略,我們來看下和導出相關的代碼:

   1:  public override void VerifyRenderingInServerForm(Control control)
   2:  {
   3:   
   4:  }
   5:   
   6:  protected void Button2_Click(object sender, EventArgs e)
   7:  {
   8:      // ResolveGridView(GridView1);
   9:   
  10:      Response.ClearContent();
  11:      Response.AddHeader("content-disposition", "attachment; filename=MyExcelFile.xls");
  12:      Response.ContentType = "application/excel";
  13:   
  14:      StringWriter sw = new StringWriter();
  15:      HtmlTextWriter htw = new HtmlTextWriter(sw);
  16:      GridView1.RenderControl(htw);
  17:      
  18:      Response.Write(sw.ToString());
  19:      Response.End();
  20:  }

這里做了如下幾件事情:

    1. 在ASPX標簽中設置頁面的EnableEventValidation屬性為false;
    2. 重載VerifyRenderingInServerForm函數並留空;
    3. 在導出Excel的按鈕事件中,首先設置響應頭content-disposition,這樣瀏覽器才會將此響應作為文件下載;
    4. 將GridView控件重新渲染到HtmlTextWriter流中;
    5. 讀出HtmlTextWriter流的內容,並作為響應體的正文。

 

我們來看下頁面的UI顯示和導出的Excel的內容:

image

 

image

 

優化GridView導出的Excel文件

在上面導出的Excel表格中,有兩個地方需要進一步優化:

  1. “是否在校”列不應該使用復選框,而應該是靜態文本;
  2. “分組”列的圖片沒有顯示出來,因為表格中的圖片路徑是相對路徑,這里需要轉換為絕對路徑。

 

實現起來也很簡單,只需要遍歷GridView實例,修改其中的復選框控件和圖片控件即可,如下所示:

   1:  private void ResolveGridView(Control ctrl)
   2:  {
   3:      for (int i = 0; i < ctrl.Controls.Count; i++)
   4:      {
   5:          // 圖片的完整URL
   6:          if (ctrl.Controls[i].GetType() == typeof(AspNet.Image))
   7:          {
   8:              AspNet.Image img = ctrl.Controls[i] as AspNet.Image;
   9:              img.ImageUrl = Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, Page.ResolveUrl(img.ImageUrl));
  10:          }
  11:   
  12:          // 將CheckBox控件轉化為靜態文本
  13:          if (ctrl.Controls[i].GetType() == typeof(AspNet.CheckBox))
  14:          {
  15:              Literal lit = new Literal();
  16:              lit.Text = (ctrl.Controls[i] as AspNet.CheckBox).Checked ? "√" : "×";
  17:              ctrl.Controls.RemoveAt(i);
  18:              ctrl.Controls.AddAt(i, lit);
  19:          }
  20:   
  21:          if (ctrl.Controls[i].HasControls())
  22:          {
  23:              ResolveGridView(ctrl.Controls[i]);
  24:          }
  25:      }
  26:  }

最終的結果:

image

 

 

將Grid導出為Excel文件

前面介紹了如何將Asp.Net的GridView控件導出為Excel,簡單來說就是將GridView重新渲染到HtmlTextWriter流中,然后將流內容輸入為響應正文。

那么我們是否也可以照葫蘆畫瓢來實現將Grid導出為Excel文件呢?

下面就來試一試,首先來看ASPX標簽結構:

   1:  <ext:Grid ID="Grid1" Title="表格" ShowBorder="true" ShowHeader="true" Width="900px"
   2:      AutoHeight="true" runat="server" DataKeyNames="Id,Name">
   3:      <Columns>
   4:          <ext:TemplateField Width="60px">
   5:              <ItemTemplate>
   6:                  <asp:Label ID="Label1" runat="server" Text='<%# Container.DataItemIndex + 1 %>'></asp:Label>
   7:              </ItemTemplate>
   8:          </ext:TemplateField>
   9:          <ext:BoundField Width="100px" DataField="Name" DataFormatString="{0}" HeaderText="姓名" />
  10:          <ext:TemplateField Width="60px" HeaderText="性別">
  11:              <ItemTemplate>
  12:                  <asp:Label ID="Label3" runat="server" Text='<%# GetGender(Eval("Gender")) %>'></asp:Label>
  13:              </ItemTemplate>
  14:          </ext:TemplateField>
  15:          <ext:BoundField Width="60px" DataField="EntranceYear" HeaderText="入學年份" />
  16:          <ext:CheckBoxField Width="60px" RenderAsStaticField="true" DataField="AtSchool" HeaderText="是否在校" />
  17:          <ext:HyperLinkField HeaderText="所學專業" DataToolTipField="Major" DataTextField="Major"
  18:              DataTextFormatString="{0}" DataNavigateUrlFields="Major" DataNavigateUrlFormatString="http://gsa.ustc.edu.cn/search?q={0}"
  19:              DataNavigateUrlFieldsEncode="true" Target="_blank" ExpandUnusedSpace="True" />
  20:          <ext:ImageField Width="60px" DataImageUrlField="Group" DataImageUrlFormatString="~/images/16/{0}.png"
  21:              HeaderText="分組"></ext:ImageField>
  22:          <ext:BoundField Width="100px" DataField="LogTime" DataFormatString="{0:yy-MM-dd}"
  23:              HeaderText="注冊日期" />
  24:      </Columns>
  25:  </ext:Grid>
  26:  <ext:Button ID="Button1" EnableAjax="false" DisableControlBeforePostBack="false"
  27:      runat="server" Text="將Grid導出為Excel文件" OnClick="Button1_Click">
  28:  </ext:Button>

請注意這里ext:Button的屬性:

  1. EnableAjax=false,由於在按鈕的點擊事件中手工修改了響應頭和響應正文,就不能使用FineUI默認的Ajax回發;
  2. DisableControlBeforePostBack=false,這個屬性本來是讓按鈕在點擊后立即變灰,然后在Ajax響應后再次啟用,放置多次點擊,這里就不需要了。

 

來看下后台按鈕事件處理函數:

   1:  protected void Button1_Click(object sender, EventArgs e)
   2:  {
   3:      Response.ClearContent();
   4:      Response.AddHeader("content-disposition", "attachment; filename=MyExcelFile.xls");
   5:      Response.ContentType = "application/excel";
   6:   
   7:      StringWriter sw = new StringWriter();
   8:      HtmlTextWriter htw = new HtmlTextWriter(sw);
   9:      Grid1.RenderControl(htw);
  10:   
  11:      Response.Write(sw.ToString());
  12:      Response.End();
  13:  }

 

點擊導出按鈕,用記事本打開生成的Excel文件:

   1:  <div id="Grid1_wrapper">
   2:      <div id="Grid1_tpls" class="x-grid-tpls x-hide-display">
   3:          <div class="x-grid-tpl" id="Grid1_c0r0">
   4:              <span id="Grid1_c0r0_Label1">1</span>
   5:          </div>
   6:          <div class="x-grid-tpl" id="Grid1_c2r0">
   7:              <span id="Grid1_c2r0_Label3"></span>
   8:          </div>
   9:          <div class="x-grid-tpl" id="Grid1_c0r1">
  10:              <span id="Grid1_c0r1_Label1">2</span>
  11:          </div>
  12:          // 省略類似的部分...
  13:      </div>
  14:  </div>

結果只看到一些DIV,而非Table結構。仔細觀察這些DIV,你會發現它們是模板列渲染后的值,其他列的值哪去了?

 

如果你理解extjs的工作原理,這個結果並不奇怪。

Grid渲染到頁面中的只有一些簡單的DIV標簽,至於內部的內容則是通過JavaScript來生成的,這個JavaScript就隱藏在頁面的底部,如果你觀察生成的頁面源代碼的話,就能看到類似的代碼:

image 

 

而這些Values值正是渲染后的HTML片段,並且我們可以通過行GridRow的Values屬性拿到這些值!

如此一來就好辦了,拿到這些值后手工拼裝成一個表格不就可以了,最終的代碼如下所示:

   1:  protected void Button1_Click(object sender, EventArgs e)
   2:  {
   3:      Response.ClearContent();
   4:      Response.AddHeader("content-disposition", "attachment; filename=MyExcelFile.xls");
   5:      Response.ContentType = "application/excel";
   6:      Response.Write(GetGridTableHtml(Grid1));
   7:      Response.End();
   8:  }
   9:   
  10:  private string GetGridTableHtml(Grid grid)
  11:  {
  12:      StringBuilder sb = new StringBuilder();
  13:   
  14:      sb.Append("<table cellspacing=\"0\" rules=\"all\" border=\"1\" style=\"border-collapse:collapse;\">");
  15:   
  16:      sb.Append("<tr>");
  17:      foreach (GridColumn column in grid.Columns)
  18:      {
  19:          sb.AppendFormat("<td>{0}</td>", column.HeaderText);
  20:      }
  21:      sb.Append("</tr>");
  22:   
  23:   
  24:      foreach (GridRow row in grid.Rows)
  25:      {
  26:          sb.Append("<tr>");
  27:          foreach (object value in row.Values)
  28:          {
  29:              string html = value.ToString();
  30:              // 處理CheckBox
  31:              if (html.Contains("box-grid-static-checkbox"))
  32:              {
  33:                  if (html.Contains("box-grid-static-checkbox-uncheck"))
  34:                  {
  35:                      html = "×";
  36:                  }
  37:                  else
  38:                  {
  39:                      html = "√";
  40:                  }
  41:              }
  42:   
  43:              // 處理圖片
  44:              if (html.Contains("<img"))
  45:              {
  46:                  string prefix = Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, "");
  47:                  html = html.Replace("src=\"", "src=\"" + prefix);
  48:              }
  49:   
  50:              sb.AppendFormat("<td>{0}</td>", html);
  51:          }
  52:          sb.Append("</tr>");
  53:      }
  54:   
  55:      sb.Append("</table>");
  56:   
  57:      return sb.ToString();
  58:  }

正如你看到的,這里面也對復選框和圖片進行了處理:

  1. 因為FineUI最終將復選框渲染成類似 <div class="box-grid-static-checkbox"></div>的標簽,所以我們要將其替換成靜態文本;
  2. 對圖片的處理則是在圖片路徑前面加載前綴,以生成圖片的絕對路徑。

 

來看最終的效果:

image

 

 

小結

將表格內容導出為Excel文件在實際項目中經常會遇到,本篇文章從導出Asp.Net的GridView開始,循序漸進地講解如何導出FineUI的Grid控件,最終得到我們滿意的結果。

下一篇文章我們會開始新的征程,接着講解樹控件、選項卡控件、手風琴控件以及窗體控件。

 

注:《FineUI秘密花園》系列文章由三生石上原創,博客園首發,轉載請注明出處。文章目錄 官方論壇


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM