ASP.NET WebForms の GridView で一覧を作っていると、画面に表示した内容をそのまま Excel や PDF としてダウンロードさせたい場面がよくあります。この記事では VB.NET を例に、Response の設定・ページングへの対応・iTextSharp を使った PDF 生成まで、つまずきやすい点も含めて実装手順をまとめます。
ASP.NET WebForms の GridView を「ファイルとしてダウンロード」させる考え方
WebForms のエクスポートは、最終的に HTTP レスポンスを「ファイル」扱いにして返すのが基本です。つまり、Response に対して次の要素を正しく設定し、コンテンツをそのまま書き出します。
| 設定項目 | 役割 | よく使う値・ポイント |
|---|---|---|
| ContentType | ブラウザに「これは何のファイルか」を伝える | Excel: application/vnd.ms-excel / PDF: application/pdf |
| Content-Disposition | ダウンロード扱いにしてファイル名を指定する | attachment; filename=...(日本語ファイル名は別途対策推奨) |
| Encoding | 文字化け対策(特に Excel の簡易方式) | UTF-8 を基本にしつつ、メタタグや BOM を併用すると安定 |
| 出力内容 | Excel なら HTML、PDF なら PDF バイナリ | GridView を HTML として描画、PDF はライブラリで生成して BinaryWrite |
また、WebForms 特有の制約として、GridView を Response に直接レンダリングする場合は “おまじない” が必要です。これを忘れると、実行時に例外で止まります。
共通で押さえるべきポイント
VerifyRenderingInServerForm を空実装でオーバーライドする
GridView を RenderControl で直接書き出す場合、WebForms のランタイム検証を回避するために VerifyRenderingInServerForm をオーバーライドします。ページ(.aspx のコードビハインド)に次を追加してください。
Public Overrides Sub VerifyRenderingInServerForm(ByVal control As Control)
' GridView を Response に直接描画するためのおまじない
End Sub
ページング中なら、エクスポート前に全件を再バインドする
GridView でページングを有効にしている場合、そのまま出力すると 表示中のページ分だけが出力されます。全件出したい場合は、エクスポート直前に以下の流れにします。
AllowPaging = Falseにする- 同じ条件でデータを取り直して
DataBind()(もしくは既存のバインドメソッド) - 出力(RenderControl / PDF 生成)
- 必要なら
AllowPaging = Trueに戻して再バインド
Response.End は避け、CompleteRequest を使うと安定しやすい
サンプルでよく見かける Response.End() は、内部的にスレッド中断が発生して例外(ThreadAbortException)が出ることがあります。動作自体は問題ないケースも多いですが、ログが汚れたり運用で嫌われがちです。可能なら次のように CompleteRequest を使う構成にしておくと安心です。
HttpContext.Current.ApplicationInstance.CompleteRequest()
UpdatePanel 配下では「非同期ポストバックだとダウンロードできない」ことがある
ボタンが UpdatePanel 内にあると、クリックが非同期ポストバックになり、ファイルダウンロードが失敗することがあります。その場合は、エクスポートボタンを PostBackTrigger にします。
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Button ID="Export2Excel" runat="server" Text="Export to Excel" OnClick="Export2Excel_Click" />
</ContentTemplate>
<Triggers>
<asp:PostBackTrigger ControlID="Export2Excel" />
</Triggers>
</asp:UpdatePanel>
GridView を Excel にエクスポートする方法(HTML 方式)
WebForms で一番手軽なのが、GridView を HTML として描画し、Excel に「Excel っぽく開かせる」方法です。厳密には .xls のバイナリではありませんが、社内ツールや簡易出力としては今でも使われます。
HTML 方式のメリット・デメリット
| 観点 | 内容 |
|---|---|
| メリット | 実装が軽い/GridView の見た目(ある程度)をそのまま出せる/ライブラリ不要 |
| デメリット | 拡張子は .xls が多いが中身は HTML/Excel で「ファイル形式と拡張子が一致しない」警告が出ることがある/高度な書式や .xlsx の厳密出力は不向き |
ボタン配置(ASPX)
<asp:Button Text="Export to Excel"
ID="Export2Excel"
runat="server"
OnClick="Export2Excel_Click" />
クリックイベント実装(VB.NET)
以下は実務向けに少し安定化した例です。ポイントは、文字化け対策と、全件出力(ページング解除)、そして 余計な HTML が混ざらないよう Response をクリアすることです。
Imports System.IO
Imports System.Text
Imports System.Web
Protected Sub Export2Excel_Click(sender As Object, e As EventArgs)
' 1) 全件出力したい場合はページングを解除して再バインド
Dim originalPaging As Boolean = GV1.AllowPaging
GV1.AllowPaging = False
BindGrid()
' 2) テンプレート列など、セル内コントロールを文字に置き換える(必要に応じて)
PrepareGridViewForExport(GV1)
' 3) Response 設定
Dim fileName As String = "GridViewExport.xls"
Response.Clear()
Response.ClearContent()
Response.ClearHeaders()
Response.Buffer = True
' Excel が文字化けしにくいように UTF-8 を明示
Response.Charset = "UTF-8"
Response.ContentEncoding = Encoding.UTF8
Response.ContentType = "application/vnd.ms-excel"
' 日本語ファイル名を使う場合は filename* を検討(対応ブラウザ向け)
Dim encodedFileName As String = HttpUtility.UrlEncode(fileName, Encoding.UTF8)
Response.AddHeader("Content-Disposition", "attachment; filename*=UTF-8''" & encodedFileName)
' 4) HTML として GridView を描画して書き出す
Using sw As New StringWriter()
Using hw As New HtmlTextWriter(sw)
' Excel 向けの最低限の HTML ヘッダー
Response.Write("<html><head>")
Response.Write("<meta http-equiv=""Content-Type"" content=""text/html; charset=utf-8"" />")
' 数値が勝手に指数表記/日付変換されるのを抑えたい場合の例(必要に応じて)
Response.Write("<style> .text { mso-number-format:\@; } </style>")
Response.Write("</head><body>")
GV1.RenderControl(hw)
Response.Write("</body></html>")
Response.Output.Write(sw.ToString())
Response.Flush()
End Using
End Using
' 5) 後処理(元に戻す)
GV1.AllowPaging = originalPaging
BindGrid()
HttpContext.Current.ApplicationInstance.CompleteRequest()
End Sub
Private Sub PrepareGridViewForExport(gv As GridView)
For Each row As GridViewRow In gv.Rows
For Each cell As TableCell In row.Cells
For i As Integer = cell.Controls.Count - 1 To 0 Step -1
Dim c As Control = cell.Controls(i)
' よくあるコントロールは Text を拾って Literal に差し替え
Dim text As String = ExtractText(c)
If text IsNot Nothing Then
cell.Controls.RemoveAt(i)
cell.Controls.Add(New LiteralControl(HttpUtility.HtmlEncode(text)))
End If
Next
Next
Next
End Sub
Private Function ExtractText(c As Control) As String
If TypeOf c Is ITextControl Then
Return CType(c, ITextControl).Text
End If
' 例:CheckBox を「TRUE/FALSE」や「〇/空」に変換したい場合
If TypeOf c Is CheckBox Then
Dim cb = CType(c, CheckBox)
Return If(cb.Checked, "TRUE", "FALSE")
End If
' 子コントロールを掘る(テンプレート列で Panel 等に入っているケース)
If c.HasControls() Then
Dim sb As New StringBuilder()
For Each child As Control In c.Controls
Dim t As String = ExtractText(child)
If Not String.IsNullOrEmpty(t) Then
If sb.Length > 0 Then sb.Append(" ")
sb.Append(t)
End If
Next
If sb.Length > 0 Then Return sb.ToString()
End If
Return Nothing
End Function
Excel 側で警告が出る・表示がおかしいときの対策
- 「ファイル形式と拡張子が一致しません」:HTML を .xls として開く方式の仕様です。社内運用で許容されない場合は、後述の Open XML 方式(.xlsx)へ移行します。
- 文字化け:UTF-8 の指定に加え、
<meta charset>を出力する/必要なら BOM 付与(BinaryWrite で UTF-8 BOM を先頭に書く)を検討します。 - 先頭ゼロが消える、勝手に日付になる:CSS の
mso-number-formatを使う、または出力前に文字列化しておきます(例:郵便番号、社員番号など)。
.xlsx の「本格的な Excel」を出したい場合の現実的な選択肢
HTML 方式は手軽な反面、厳密な Excel ファイルではありません。帳票に近い用途(関数・書式・複数シート・正しい .xlsx)なら、次の方向性が候補になります。
| 方式 | 特徴 | 向いているケース |
|---|---|---|
| Open XML SDK | Microsoft 公式系の低レベル API。自由度は高いが記述量が増えやすい | 完全に .xlsx を組み立てたい/依存を抑えたい |
| ClosedXML / EPPlus / NPOI など | 高レベル API で書きやすい(ただしライセンスや配布条件は必ず確認) | 開発工数を抑えて整った Excel を出したい |
GridView を PDF にエクスポートする方法(iTextSharp 方式)
PDF は Excel のように「HTML を開かせる」手は効きにくいため、PDF 生成ライブラリで 表データを PDF のテーブルとして組み立てるのが定番です。ここでは iTextSharp(iText 5 系)を使う例を紹介します。
事前準備:iTextSharp の導入(NuGet)
Visual Studio の [ツール] → [NuGet パッケージ マネージャー] → [パッケージ マネージャー コンソール] を開き、次を実行します。
Install-Package iTextSharp
説明文に書かれている「Execute command …」まで含めて入力してしまうと、Execute コマンドが見つからないエラーになります。コンソールに入れるのは コマンド本体だけです。
ボタン配置(ASPX)
<asp:Button Text="Export to PDF"
ID="Export2PDF"
runat="server"
OnClick="Export2PDF_Click" />
クリックイベント実装(VB.NET / 日本語フォント対応付き)
PDF で多い落とし穴が 日本語が豆腐(□)になる問題です。iTextSharp はデフォルトだと日本語フォントが入らないため、サーバーに存在するフォントを指定して埋め込み(Embed)する構成にします。
Imports System.IO
Imports System.Text
Imports System.Web
Imports iTextSharp.text
Imports iTextSharp.text.pdf
Protected Sub Export2PDF_Click(sender As Object, e As EventArgs)
' 1) 全件出力したい場合はページング解除して再バインド
Dim originalPaging As Boolean = GV1.AllowPaging
GV1.AllowPaging = False
BindGrid()
' 2) PDF をメモリ上で生成してから Response に流す
Using ms As New MemoryStream()
' PDF 文書(A4 横など、用途に応じて変更)
Dim pdfDoc As New Document(PageSize.A4, 20, 20, 20, 20)
Dim writer As PdfWriter = PdfWriter.GetInstance(pdfDoc, ms)
pdfDoc.Open()
' 3) 日本語フォント(サーバーに存在するフォントを指定)
' 例:Windows サーバーなら Fonts フォルダにあることが多い
Dim fontPath As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "meiryo.ttc")
Dim bf As BaseFont = BaseFont.CreateFont(fontPath & ",0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED)
Dim normalFont As New iTextSharp.text.Font(bf, 9, iTextSharp.text.Font.NORMAL)
Dim headerFont As New iTextSharp.text.Font(bf, 9, iTextSharp.text.Font.BOLD)
' 4) タイトル(任意)
pdfDoc.Add(New Paragraph("GridView Export", headerFont))
pdfDoc.Add(New Paragraph(" ", normalFont))
' 5) GridView の列数でテーブルを作る
Dim colCount As Integer = GV1.HeaderRow.Cells.Count
Dim table As New PdfPTable(colCount)
table.WidthPercentage = 100
' 6) ヘッダー行
For Each cell As TableCell In GV1.HeaderRow.Cells
Dim text As String = CleanCellText(cell.Text)
Dim pdfCell As New PdfPCell(New Phrase(text, headerFont))
pdfCell.HorizontalAlignment = Element.ALIGN_CENTER
pdfCell.Padding = 4
table.AddCell(pdfCell)
Next
' 7) データ行
For Each row As GridViewRow In GV1.Rows
For Each cell As TableCell In row.Cells
Dim text As String = GetGridCellText(cell)
Dim pdfCell As New PdfPCell(New Phrase(text, normalFont))
pdfCell.HorizontalAlignment = Element.ALIGN_LEFT
pdfCell.Padding = 4
table.AddCell(pdfCell)
Next
Next
pdfDoc.Add(table)
pdfDoc.Close()
writer.Close()
' 8) Response へ PDF を返す
Dim fileName As String = "GridViewExport.pdf"
Response.Clear()
Response.ClearContent()
Response.ClearHeaders()
Response.Buffer = True
Response.ContentType = "application/pdf"
Dim encodedFileName As String = HttpUtility.UrlEncode(fileName, Encoding.UTF8)
Response.AddHeader("Content-Disposition", "attachment; filename*=UTF-8''" & encodedFileName)
Response.BinaryWrite(ms.ToArray())
Response.Flush()
End Using
' 9) 後処理(元に戻す)
GV1.AllowPaging = originalPaging
BindGrid()
HttpContext.Current.ApplicationInstance.CompleteRequest()
End Sub
Private Function GetGridCellText(cell As TableCell) As String
' BoundField 等なら cell.Text で十分なことが多い
' TemplateField の場合は Controls から拾う必要がある
If cell.Controls IsNot Nothing AndAlso cell.Controls.Count > 0 Then
Dim sb As New StringBuilder()
For Each c As Control In cell.Controls
Dim t As String = ExtractText(c)
If Not String.IsNullOrEmpty(t) Then
If sb.Length > 0 Then sb.Append(" ")
sb.Append(t)
End If
Next
If sb.Length > 0 Then Return sb.ToString()
End If
Return CleanCellText(cell.Text)
End Function
Private Function CleanCellText(text As String) As String
If text Is Nothing Then Return ""
Dim decoded As String = HttpUtility.HtmlDecode(text)
decoded = decoded.Replace(" ", " ").Replace(ChrW(&H00A0), " ")
Return decoded.Trim()
End Function
Private Function ExtractText(c As Control) As String
If TypeOf c Is ITextControl Then
Return CType(c, ITextControl).Text
End If
If TypeOf c Is CheckBox Then
Dim cb = CType(c, CheckBox)
Return If(cb.Checked, "TRUE", "FALSE")
End If
If c.HasControls() Then
Dim sb As New StringBuilder()
For Each child As Control In c.Controls
Dim t As String = ExtractText(child)
If Not String.IsNullOrEmpty(t) Then
If sb.Length > 0 Then sb.Append(" ")
sb.Append(t)
End If
Next
If sb.Length > 0 Then Return sb.ToString()
End If
Return Nothing
End Function
PDF 出力で実務的に効く改善ポイント
- 列幅の調整:列数が多い一覧は A4 横(Landscape)や列幅指定(
table.SetWidths)を検討します。 - 長文セルの折り返し:
PdfPCellは基本的に折り返しますが、見栄えが悪いなら文字サイズを下げる/列幅を広げるなどで調整します。 - TemplateField の値取得:
cell.Textだけでは空になることが多いので、FindControlで個別に拾うか、上のような “掘って拾う” ヘルパーを用意します。 - フォント問題:サーバーに対象フォントがないと例外になります。運用環境に合わせてフォント名を固定するか、配置するフォントを運用設計に組み込みます。
iTextSharp を DLL 参照追加で入れたい場合(NuGet を使わない補足)
すでにダウンロード済みの DLL を使いたい場合は、プロジェクトを右クリックして [参照の追加] から DLL を選ぶ方法もあります。ただし依存関係の管理やアップデートを考えると、基本は NuGet 管理のほうがトラブルが減ります。
DocumentFormat.OpenXml.Framework の参照エラーが出る場合の切り分け
「iTextSharp を入れて PDF 出力コードを組み込んだ後に、次のような参照エラーが出た」というケースがあります。
Reference required to assembly ‘DocumentFormat.OpenXml.Framework, Version=3.2.0.0, … containing the type ‘OpenXmlCompositeElement’
このエラーは、PDF 出力(iTextSharp)そのものというより、プロジェクト内のどこかで Open XML 系の型を参照している可能性が高い症状です。対処は大きく2つです。
Open XML SDK を使うなら:必要なパッケージ参照を追加する
Open XML を使うコードが存在する(または今後使う)なら、NuGet で追加します。
Install-Package DocumentFormat.OpenXml
加えて、古い参照が残っている場合は、参照一覧から古い DLL を外して、NuGet 側に統一するほうがバージョン衝突を避けやすいです。
Open XML を使うつもりがないなら:参照・Imports を削除する
- プロジェクト内で
DocumentFormat.OpenXml名前空間を Imports(C# なら using)している箇所を探して削除 OpenXmlCompositeElementなど Open XML 型を使っているクラス・機能が不要ならコメントアウト/削除
PDF/Excel エクスポートと無関係な参照が原因でビルドが止まっているだけ、ということも多いので、まずは「どのファイルが OpenXML 型を使っているか」を検索して切り分けるのが近道です。
よくあるトラブルと対策(チェック表)
| 症状 | 原因の可能性 | 対策 |
|---|---|---|
| 「GridView は server form 内でレンダリングされる必要があります」系の例外 | VerifyRenderingInServerForm の未オーバーライド | ページのコードビハインドに空実装を追加 |
| Excel 出力が表示中のページ分しか出ない | ページング解除せずに Render している | AllowPaging=False → 再バインド → 出力 |
| Excel が文字化けする | エンコード指定不足、meta タグ無し | UTF-8 指定+ meta 出力(必要に応じて BOM) |
| Excel で「拡張子と形式が一致しない」警告 | HTML を .xls として開いている方式の仕様 | 許容する/本格的に .xlsx(Open XML 等)へ移行 |
| PDF の日本語が □ になる | 日本語フォント未指定 | BaseFont を Identity-H で作成し、フォントを埋め込み |
| PDF/Excel のダウンロードが反応しない(特に UpdatePanel) | 非同期ポストバックになっている | PostBackTrigger を付けてフルポストバックにする |
| TemplateField の値が空になる | cell.Text では拾えない(中身がコントロール) | FindControl で拾う/Controls を掘るヘルパーを作る |
| 「Execute コマンドが見つからない」 | NuGet の説明文を含めて入力している | Install-Package iTextSharp だけを入力 |
運用面で意識したいポイント
- 大量データの一括出力:全件出力は便利ですが、件数が大きいとメモリや応答時間が急増します。業務要件として必要なら、上限件数・期間指定・非同期キュー化なども検討します。
- 検索条件の一致:画面表示と同じ条件(絞り込み・並び順)で出力するには、エクスポート時に同じデータ取得ロジックを通すのが確実です。GridView の状態だけに依存すると差異が出やすくなります。
- ファイル名:日本語ファイル名は環境差が出ることがあります。まずは ASCII の安全な名前で運用し、必要に応じて
filename*を採用するのが堅実です。
まとめ
- GridView の Excel 出力は、まずは HTML 方式(ContentType を Excel にして RenderControl)が最短ルートです。
- PDF 出力は iTextSharp で PdfPTable を組み立てるのが基本で、日本語フォント指定が成功の鍵になります。
VerifyRenderingInServerFormのオーバーライド、ページング解除、UpdatePanel のトリガー設定など、WebForms 特有の罠を押さえるとトラブルが激減します。DocumentFormat.OpenXml.Frameworkの参照エラーは PDF/Excel 出力そのものとは別件のことが多く、Open XML SDK の導入/不要参照の削除で切り分けます。

コメント