Skip to content

Instantly share code, notes, and snippets.

@mizuneko
Last active May 5, 2018 09:54
Show Gist options
  • Save mizuneko/604206bc12b767c3ae00ed2b236dff8a to your computer and use it in GitHub Desktop.
Save mizuneko/604206bc12b767c3ae00ed2b236dff8a to your computer and use it in GitHub Desktop.
[Excel帳票] EXCELで帳票出力するときに使えるかも。 #ClosedXML
using System.Data;
namespace SampleReport
{
public class ExcelReport
{
/// <summary>
/// レポートの管理クラス
/// </summary>
public class ReportControl
{
// ページの最大行数
public int PageMaxRows { get; set; }
// レポートヘッダ行数
public int ReportHeaderRows { get; set; }
// レポートフッタ行数
public int ReportFooterRows { get; set; }
// ページヘッダ行数
public int PageHeaderRows { get; set; }
// ページフッタ行数
public int PageFooterRows { get; set; }
}
/// <summary>
/// 行を取得する列挙体
/// </summary
public enum DetailsRowType
{
Middle = 0,
First,
Final
}
// 1ページの最大明細行数
int _rowMax;
// 現在の印刷行位置
int _rowPoint;
// ページ番号
int _pageNo;
// 行番号(最初からの位置)
int _lineNo;
// レポート印刷制御データ
ReportControl _repCtrl = new ReportControl();
/// <summary>
/// コンストラクタ
/// </summary>
public ExcelReport()
{
_pageNo = 1;
_lineNo = 1;
}
/// <summary>
/// ページ番号を取得します。
/// </summary>
public int PageNo
{
get { return _pageNo; }
}
/// <summary>
/// 先頭行からの行番号を取得します。
/// </summary>
public int LineNo
{
get { return _lineNo; }
}
/// <summary>
/// レポートの情報等を設定または取得します。
/// </summary>
public ReportControl RepCtrl
{
get { return _repCtrl; }
set { _repCtrl = value; }
}
/// <summary>
/// 1ページの最大明細行数を設定します。
/// </summary>
/// <param name="mode">0:通常ページ, 1:開始ページ, 2:終了ページ</param>
protected void SetDetailRows(DetailsRowType mode = DetailsRowType.Middle)
{
switch (mode)
{
case DetailsRowType.First:
_rowMax = RepCtrl.PageMaxRows
- (RepCtrl.PageHeaderRows + RepCtrl.PageFooterRows + RepCtrl.ReportHeaderRows);
_rowPoint = 0;
break;
case DetailsRowType.Final:
_rowMax = RepCtrl.PageMaxRows;
_rowPoint = 0;
break;
default:
_rowMax = RepCtrl.PageMaxRows
- (RepCtrl.PageHeaderRows + RepCtrl.PageFooterRows);
_rowPoint = 0;
break;
}
}
/// <summary>
/// 改ページをします。
/// </summary>
protected void WrPageBreak()
{
_pageNo += 1;
WritePageBreak();
SetDetailRows();
}
/// <summary>
/// ページヘッダが必要なら出力します。
/// </summary>
protected void WrNeedPageHeader()
{
if (_rowPoint == 0)
{
WrPageHeader();
}
}
/// <summary>
/// 次行に進み、ページフッタが必要なら出力して改ページします。
/// </summary>
protected void WrNextLine()
{
_rowPoint += 1;
_lineNo += 1;
if (_rowMax <= _rowPoint)
{
WrPageFooter();
WrPageBreak();
}
}
/// <summary>
/// レポートヘッダを出力します。
/// </summary>
protected void WrReportHeader()
{
if (RepCtrl.ReportHeaderRows <= 0)
{
return;
}
WriteReportHeader();
_lineNo += RepCtrl.ReportHeaderRows;
}
/// <summary>
/// レポートフッタを出力します。
/// </summary>
protected void WrReportFooter()
{
if (RepCtrl.ReportFooterRows <= 0)
{
return;
}
WriteReportFooter();
_lineNo += RepCtrl.ReportFooterRows;
int n = _rowMax - _rowPoint;
if (n < RepCtrl.ReportFooterRows)
{
WritePageBreak();
}
}
/// <summary>
/// ページヘッダを出力します。
/// </summary>
protected void WrPageHeader()
{
if (RepCtrl.PageHeaderRows <= 0)
{
return;
}
WritePageHeader();
_lineNo += RepCtrl.PageHeaderRows;
}
/// <summary>
/// ページフッタを出力します。
/// </summary>
protected void WrPageFooter()
{
if (RepCtrl.PageFooterRows <= 0)
{
return;
}
WritePageFooter();
_lineNo += RepCtrl.PageFooterRows;
}
/// <summary>
/// 明細の1行を出力します。
/// </summary>
protected void WrLinePrint(DataRow row)
{
WriteLinePrint(row);
}
/// <summary>
/// 明細内容を出力します。
/// </summary>
protected void WrDetail(DataTable table)
{
WriteDetail(table);
}
/// <summary>
/// レポートを出力します。
/// </summary>
protected void WrReport(DataTable table)
{
WriteReport(table);
}
#region 継承側で出力するコードを記述する関数
protected virtual void WritePageBreak() { }
protected virtual void WriteReportHeader() { }
protected virtual void WriteReportFooter() { }
protected virtual void WritePageHeader() { }
protected virtual void WritePageFooter() { }
protected virtual void WriteLinePrint(DataRow row) { }
protected virtual void WriteDetail(DataTable table)
{
if (table.Rows.Count == 0)
{
return;
}
foreach(DataRow row in table.Rows)
{
WrNeedPageHeader();
WrLinePrint(row);
WrNextLine();
}
if (0 < _rowPoint)
{
WrPageFooter();
}
}
protected virtual void WriteReport(DataTable table)
{
WrReportHeader();
WrDetail(table);
WrReportFooter();
}
#endregion
}
}
using ClosedXML.Excel;
using System;
using System.Data;
namespace SampleReport
{
public class MyReport : ExcelReport, IDisposable
{
XLWorkbook xlBook;
IXLWorksheet xlSheet;
XLColor color1 = XLColor.White;
XLColor color2 = XLColor.DarkBlue;
/// <summary>
/// コンストラクタ
/// </summary>
public MyReport()
{
RepCtrl.ReportHeaderRows = 14;
RepCtrl.ReportFooterRows = 0;
RepCtrl.PageHeaderRows = 1;
RepCtrl.PageFooterRows = 0;
RepCtrl.PageMaxRows = 50;
}
/// <summary>
/// レポートを出力します。
/// </summary>
public void Write()
{
xlBook = new XLWorkbook();
xlSheet = xlBook.AddWorksheet("請求書(明細)");
SetBaseDesign();
SetDetailRows(DetailsRowType.First);
var dt = GetSampleDetailData();
WriteReport(dt);
DefingPageSetup();
string outputPath = "./out_請求書.xlsx";
xlBook.SaveAs(outputPath);
}
/// <summary>
/// 印刷用設定
/// </summary>
private void DefingPageSetup()
{
xlSheet.PageSetup.SetPageOrientation(XLPageOrientation.Portrait);
xlSheet.PageSetup.SetPaperSize(XLPaperSize.A4Paper);
xlSheet.PageSetup.PrintAreas.Add(1, 1, LineNo, 28);
xlSheet.PageSetup.AddHorizontalPageBreak(RepCtrl.PageMaxRows);
xlSheet.PageSetup.AddVerticalPageBreak(28);
xlSheet.PageSetup.SetPagesWide(1);
xlSheet.PageSetup.SetShowGridlines(false);
}
/// <summary>
/// 既定のデザインを設定します。
/// </summary>
private void SetBaseDesign()
{
xlSheet.ColumnWidth = 4.0;
xlSheet.RowHeight = 18.0;
xlSheet.Row(13).Height = 30.0;
xlSheet.Row(13).Style.Alignment.SetVertical(XLAlignmentVerticalValues.Center);
xlSheet.Column(21).Width = 8.0;
xlSheet.Columns(18, 19).Width = 2.0;
// 請求書タイトル
xlSheet.Cell(2, 17).SetValue<string>("請 求 書");
xlSheet.Range(2, 17, 2, 23).Merge().Style
.Font.SetFontColor(color1)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center);
// 請求書No
xlSheet.Cell(2, 25).SetValue<string>("No.").Style
.Font.SetFontColor(color2)
.Border.SetBottomBorder(XLBorderStyleValues.Thin)
.Border.SetBottomBorderColor(color2);
xlSheet.Range(2, 26, 2, 28).Merge().Style
.Border.SetBottomBorder(XLBorderStyleValues.Thin)
.Border.SetBottomBorderColor(color2);
// お客様コード
xlSheet.Cell(8, 1).SetValue<string>("お客様コード").Style
.Font.SetFontColor(color2);
// 文言1
xlSheet.Cell(9, 1).SetValue<string>("毎度ありがとうございます。").Style
.Font.SetFontColor(color2);
// 文言2
xlSheet.Cell(10, 1).SetValue<string>("下記の通り御請求申し上げます。").Style
.Font.SetFontColor(color2);
// 前回繰越金額
xlSheet.Cell(12, 1).SetValue("前回繰越金額");
xlSheet.Range(12, 1, 12, 3).Merge().Style
.Font.SetFontColor(color1)
.Font.SetBold(true)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center);
// 御入金額
xlSheet.Cell(12, 4).SetValue<string>("御入金額");
xlSheet.Range(12, 4, 12, 6).Merge().Style
.Font.SetFontColor(color1)
.Font.SetBold(true)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center);
// 今月売上金額
xlSheet.Cell(12, 7).SetValue<string>("今月売上金額");
xlSheet.Range(12, 7, 12, 9).Merge().Style
.Font.SetFontColor(color1)
.Font.SetBold(true)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center);
// 消費税等
xlSheet.Cell(12, 10).SetValue<string>("消費税等");
xlSheet.Range(12, 10, 12, 12).Merge().Style
.Font.SetFontColor(color1)
.Font.SetBold(true)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center);
// 今回御請求額
xlSheet.Cell(12, 17).SetValue<string>("今回御請求額");
xlSheet.Range(12, 17, 12, 20).Merge().Style
.Font.SetFontColor(color1)
.Font.SetBold(true)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center);
xlSheet.Range(12, 1, 13, 12).Style
.Border.SetTopBorder(XLBorderStyleValues.Thin)
.Border.SetBottomBorder(XLBorderStyleValues.Thin)
.Border.SetRightBorder(XLBorderStyleValues.Thin)
.Border.SetLeftBorder(XLBorderStyleValues.Thin);
xlSheet.Range(12, 17, 13, 20).Style
.Border.SetTopBorder(XLBorderStyleValues.Thin)
.Border.SetBottomBorder(XLBorderStyleValues.Thin)
.Border.SetRightBorder(XLBorderStyleValues.Thin)
.Border.SetLeftBorder(XLBorderStyleValues.Thin);
// 捺印欄
xlSheet.Range(12, 25, 13, 26).Style
.Border.SetOutsideBorder(XLBorderStyleValues.Thin)
.Border.SetOutsideBorderColor(color2);
xlSheet.Range(12, 27, 13, 28).Style
.Border.SetOutsideBorder(XLBorderStyleValues.Thin)
.Border.SetOutsideBorderColor(color2);
}
/// <summary>
/// レポートヘッダを出力します。
/// </summary>
protected override void WriteReportHeader()
{
base.WriteReportHeader();
var data = GetSampleReportHeaderData();
foreach (DataRow row in data.Rows)
{
var xlCell = xlSheet.Cell((int)row["R"], (int)row["C"]);
xlCell.Value = row["V"].ToString();
}
SetReportHeaderDesign();
}
/// <summary>
/// レポートヘッダのスタイルを設定します。
/// </summary>
private void SetReportHeaderDesign()
{
xlSheet.Cell(5, 1).Style
.Font.SetBold(true)
.Font.SetUnderline(XLFontUnderlineValues.Single);
xlSheet.Cell(6, 28).Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right);
xlSheet.Cell(8, 28).Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right);
xlSheet.Cell(9, 28).Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right);
xlSheet.Range(13, 1, 13, 3).Merge().Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right);
xlSheet.Range(13, 4, 13, 6).Merge().Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right);
xlSheet.Range(13, 7, 13, 9).Merge().Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right);
xlSheet.Range(13, 10, 13, 12).Merge().Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right);
xlSheet.Range(13, 17, 13, 20).Merge().Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right);
}
/// <summary>
/// ページヘッダを出力します。
/// </summary>
protected override void WritePageHeader()
{
base.WritePageHeader();
xlSheet.Cell(LineNo, 1).Value = "伝票日付";
xlSheet.Cell(LineNo, 4).Value = "伝票No.";
xlSheet.Cell(LineNo, 7).Value = "品番・品名";
xlSheet.Cell(LineNo, 18).Value = "数量";
xlSheet.Cell(LineNo, 21).Value = "単位";
xlSheet.Cell(LineNo, 22).Value = "単価";
xlSheet.Cell(LineNo, 25).Value = "金額";
SetPageHeaderDesign();
}
/// <summary>
/// ページヘッダのスタイルを設定します。
/// </summary>
private void SetPageHeaderDesign()
{
// 伝票日付
xlSheet.Range(LineNo, 1, LineNo, 3).Merge().Style
.Font.SetFontColor(color1)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
// 伝票No.
xlSheet.Range(LineNo, 4, LineNo, 6).Merge().Style
.Font.SetFontColor(color1)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
// 品番・品名
xlSheet.Range(LineNo, 7, LineNo, 17).Merge().Style
.Font.SetFontColor(color1)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
// 数量
xlSheet.Range(LineNo, 18, LineNo, 20).Merge().Style
.Font.SetFontColor(color1)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
// 単位
xlSheet.Cell(LineNo, 21).Style
.Font.SetFontColor(color1)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
// 単価
xlSheet.Range(LineNo, 22, LineNo, 24).Merge().Style
.Font.SetFontColor(color1)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
// 金額
xlSheet.Range(LineNo, 25, LineNo, 28).Merge().Style
.Font.SetFontColor(color1)
.Fill.SetBackgroundColor(color2)
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
}
/// <summary>
/// 明細1行分を出力します。
/// </summary>
/// <param name="row">1行データ</param>
protected override void WriteLinePrint(DataRow row)
{
base.WriteLinePrint(row);
xlSheet.Cell(LineNo, 1).Value = row["SLIP_DATE"];
xlSheet.Cell(LineNo, 4).Value = row["SLIP_NO"];
xlSheet.Cell(LineNo, 7).Value = row["PROD_NO"];
xlSheet.Cell(LineNo, 18).Value = row["QT"];
xlSheet.Cell(LineNo, 21).Value = row["UNIT"];
xlSheet.Cell(LineNo, 22).Value = row["PRICE"];
xlSheet.Cell(LineNo, 25).Value = row["AMT"];
SetLinePrintDesign();
}
/// <summary>
/// 明細1行のスタイルを設定します。
/// </summary>
private void SetLinePrintDesign()
{
xlSheet.Range(LineNo, 1, LineNo, 3).Merge().Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
xlSheet.Range(LineNo, 4, LineNo, 6).Merge().Style
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
xlSheet.Range(LineNo, 7, LineNo, 17).Merge().Style
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
xlSheet.Range(LineNo, 18, LineNo, 20).Merge().Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
xlSheet.Cell(LineNo, 21).Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
xlSheet.Range(LineNo, 22, LineNo, 24).Merge().Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
xlSheet.Range(LineNo, 25, LineNo, 28).Merge().Style
.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Right)
.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
}
#region サンプルデータ作成
private static void RegistSampleData(DataTable data, int row, int col, string value)
{
var r = data.NewRow();
r["R"] = row;
r["C"] = col;
r["V"] = value;
data.Rows.Add(r);
}
private DataTable GetSampleReportHeaderData()
{
var data = new DataTable();
DataColumn[] cols = new DataColumn[]
{
new DataColumn("R", typeof(int)), new DataColumn("C", typeof(int)), new DataColumn("V", typeof(string))
};
data.Columns.AddRange(cols);
RegistSampleData(data, 2, 26, "SK003329"); // 請求書No
RegistSampleData(data, 3, 1, "〒650-0031"); // POST
RegistSampleData(data, 4, 1, "兵庫県神戸市中央区東町 3-8-1 松井パークタワー1003"); // ADDRESS
RegistSampleData(data, 5, 1, "黒渦 一朗太 様");
RegistSampleData(data, 6, 28, "アドバンスソフトウェア株式会社");
RegistSampleData(data, 7, 22, "〒918-8239");
RegistSampleData(data, 8, 28, "福井県福井市成和01-2816");
RegistSampleData(data, 9, 28, "TEL:0776-21-9008/FAX:0776-21-9022");
RegistSampleData(data, 8, 5, "U00213");
RegistSampleData(data, 13, 1, "1,000");
RegistSampleData(data, 13, 4, "14,800");
RegistSampleData(data, 13, 7, "382,047");
RegistSampleData(data, 13, 10, "2,841");
RegistSampleData(data, 13, 17, "300,000");
return data;
}
private DataTable GetSampleDetailData()
{
var dt = new DataTable();
dt.Columns.Add("SLIP_DATE");
dt.Columns.Add("SLIP_NO");
dt.Columns.Add("PROD_NO");
dt.Columns.Add("QT");
dt.Columns.Add("UNIT");
dt.Columns.Add("PRICE");
dt.Columns.Add("AMT");
var row = dt.NewRow();
row["SLIP_DATE"] = "2018/05/02";
row["SLIP_NO"] = "S010042XXX";
row["PROD_NO"] = "PRD0012055011/テスト商品1";
row["QT"] = "10";
row["UNIT"] = "WTO";
row["PRICE"] = "184";
row["AMT"] = "88,320";
dt.Rows.Add(row);
row = dt.NewRow();
row["SLIP_DATE"] = "2018/05/03";
row["SLIP_NO"] = "S010042XYZ";
row["PROD_NO"] = "PRD0012055011/サンプル用商品の何か";
row["QT"] = "10";
row["UNIT"] = "WTO";
row["PRICE"] = "184";
row["AMT"] = "88,320";
dt.Rows.Add(row);
for (int i = 0; i < 100; i++)
{
row = dt.NewRow();
row["SLIP_DATE"] = "2018/05/04";
row["SLIP_NO"] = "Z010042" + i.ToString("000");
row["PROD_NO"] = "PRD001205TEST/商品ABCDEFGテスト";
row["QT"] = "10";
row["UNIT"] = "WTO";
row["PRICE"] = "184";
row["AMT"] = "88,320";
dt.Rows.Add(row);
}
return dt;
}
#endregion
#region IDisposable Support
private bool disposedValue = false; // 重複する呼び出しを検出するには
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
if (xlBook != null) xlBook.Dispose();
if (xlSheet != null) xlSheet.Dispose();
}
disposedValue = true;
}
}
~MyReport()
{
// このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
Dispose(false);
}
// このコードは、破棄可能なパターンを正しく実装できるように追加されました。
public void Dispose()
{
// このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}
using (var rep = new MyReport())
{
rep.Write();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment