透過 Microsoft.Office.Interop.Excel 以 C# 程式化建立 Excel 圖表,需理解 Excel 物件模型:Application → Workbook → Worksheet → ChartObjects → Chart → SeriesCollection。本文示範完整流程:寫入資料、建立圖表、設定類型、資料來源、標題、圖例、座標軸、資料標籤、輸出圖片。
Microsoft.Office.Interop.Excel (NuGet: Microsoft.Office.Interop.Excel)using Excel = Microsoft.Office.Interop.Excel;
public class ExcelChartBuilder
{
public void CreateSalesChart(string outputXlsx, string outputChartImage = null)
{
Excel.Application excel = null;
Excel.Workbook wb = null;
Excel.Worksheet ws = null;
try
{
excel = new Excel.Application
{
Visible = false,
DisplayAlerts = false,
ScreenUpdating = false
};
wb = excel.Workbooks.Add();
ws = (Excel.Worksheet)wb.ActiveSheet;
ws.Name = "銷售報表";
// 1. 寫入標題列
string[] headers = { "月份", "產品A", "產品B", "產品C" };
for (int c = 0; c < headers.Length; c++)
ws.Cells[1, c + 1] = headers[c];
// 2. 寫入資料 (範例 12 筆)
var data = new object[,]
{
{ "1月", 120, 95, 80 },
{ "2月", 135, 110, 85 },
{ "3月", 150, 105, 90 },
{ "4月", 165, 120, 95 },
{ "5月", 180, 130, 100 },
{ "6月", 200, 145, 110 },
{ "7月", 220, 160, 120 },
{ "8月", 210, 155, 115 },
{ "9月", 195, 140, 105 },
{ "10月", 180, 130, 100 },
{ "11月", 160, 115, 90 },
{ "12月", 140, 100, 80 }
};
ws.Range["A2", "D13"].Value2 = data;
// 3. 設定儲存格格式
ws.Range["A1", "D1"].Font.Bold = true;
ws.Range["A1", "D1"].Interior.Color = ColorTranslator.ToOle(Color.LightBlue);
ws.Columns.AutoFit();
// 4. 建立圖表
Excel.ChartObjects chartObjs = (Excel.ChartObjects)ws.ChartObjects(Type.Missing);
Excel.ChartObject chartObj = chartObjs.Add(Left: 100, Top: 50, Width: 600, Height: 400);
Excel.Chart chart = chartObj.Chart;
// 5. 設定圖表類型:群組長條圖
chart.ChartType = Excel.XlChartType.xlColumnClustered;
// 6. 設定資料來源 (包含標題列,依欄為系列)
Excel.Range sourceRange = ws.Range["A1", "D13"];
chart.SetSourceData(sourceRange, Excel.XlRowCol.xlColumns);
// 7. 圖表美化
chart.HasTitle = true;
chart.ChartTitle.Text = "2024 年月度產品銷售比較";
chart.ChartTitle.Font.Size = 14;
chart.ChartTitle.Font.Bold = true;
chart.HasLegend = true;
chart.Legend.Position = Excel.XlLegendPosition.xlLegendPositionBottom;
chart.Legend.Font.Size = 10;
// 座標軸
Excel.Axis categoryAxis = (Excel.Axis)chart.Axes(Excel.XlAxisType.xlCategory);
categoryAxis.HasTitle = true;
categoryAxis.AxisTitle.Text = "月份";
categoryAxis.TickLabels.Font.Size = 9;
Excel.Axis valueAxis = (Excel.Axis)chart.Axes(Excel.XlAxisType.xlValue);
valueAxis.HasTitle = true;
valueAxis.AxisTitle.Text = "銷售額 (千元)";
valueAxis.MajorGridlines.Format.Line.ForeColor.RGB = ColorTranslator.ToOle(Color.LightGray);
// 資料標籤 (顯示數值)
foreach (Excel.Series series in chart.SeriesCollection())
{
series.HasDataLabels = true;
series.DataLabels().Font.Size = 8;
series.DataLabels().NumberFormat = "#,##0";
}
// 8. (選擇性) 另存圖表為圖片
if (!string.IsNullOrEmpty(outputChartImage))
{
chart.Export(outputChartImage, "PNG");
}
// 9. 儲存活頁簿
wb.SaveAs(outputXlsx, Excel.XlFileFormat.xlOpenXMLWorkbook);
}
finally
{
// 10. 釋放 COM 物件 (逆序)
ReleaseCom(ws);
if (wb != null) { wb.Close(false); ReleaseCom(wb); }
if (excel != null) { excel.Quit(); ReleaseCom(excel); }
GC.Collect(); GC.WaitForPendingFinalizers();
}
}
private void ReleaseCom(object obj)
{
if (obj != null)
{
try { Marshal.ReleaseComObject(obj); } catch { }
}
}
}
| 物件 | 用途 | 常用屬性/方法 |
|---|---|---|
ChartObjects | 工作表上的圖表容器集合 | Add(Left, Top, Width, Height) |
ChartObject | 單一圖表容器 (可移動/調整大小) | .Chart 取得實際 Chart 物件 |
Chart | 圖表核心 | ChartType, SetSourceData, SeriesCollection(), Export |
SeriesCollection | 資料系列集合 | .Item(index), .NewSeries() |
Series | 單一資料系列 | .Name, .Values, .XValues, .HasDataLabels |
Axes | 座標軸集合 | AxisTitle, .MajorGridlines, .MinimumScale |
// 柱狀/長條
xlColumnClustered = 51 // 群組長條圖
xlColumnStacked = 52 // 堆疊長條圖
xlColumnStacked100 = 53 // 100% 堆疊長條圖
xlBarClustered = 57 // 群組橫條圖
// 折線
xlLine = 4 // 折線圖
xlLineMarkers = 65 // 帶標記折線圖
xlLineStacked = 63 // 堆疊折線圖
// 圓餅
xlPie = 5 // 圓餅圖
xlPieExploded = 69 // 分離圓餅圖
xlDoughnut = -4120 // 環狀圖
// 散佈/氣泡
xlXYScatter = -4169 // 散佈圖
xlBubble = 15 // 氣泡圖
// 組合圖需手動建立多個 Series 並設定 ChartType
// 方式 1: SetSourceData (最簡單)
chart.SetSourceData(ws.Range["A1:D13"], Excel.XlRowCol.xlColumns); // 欄為系列
// 方式 2: 手動加入 Series (彈性最高)
Excel.Series seriesA = chart.SeriesCollection().NewSeries();
seriesA.Name = "=Sheet1!$B$1";
seriesA.Values = "=Sheet1!$B$2:$B$13";
seriesA.XValues = "=Sheet1!$A$2:$A$13";
seriesA.ChartType = Excel.XlChartType.xlColumnClustered;
// 方式 3: 多區域非連續範圍
Excel.Range multiRange = excel.Union(ws.Range["A1:A13"], ws.Range["C1:C13"]);
chart.SetSourceData(multiRange, Excel.XlRowCol.xlColumns);
// 假設已有 chart 與資料
Excel.Series seriesLine = chart.SeriesCollection().NewSeries();
seriesLine.Name = "=Sheet1!$E$1"; // 達成率
seriesLine.Values = "=Sheet1!$E$2:$E$13";
seriesLine.ChartType = Excel.XlChartType.xlLineMarkers;
seriesLine.AxisGroup = Excel.XlAxisGroup.xlSecondary; // 指派給次要 Y 軸
// 顯示次要 Y 軸
Excel.Axis secAxis = (Excel.Axis)chart.Axes(Excel.XlAxisType.xlValue, Excel.XlAxisGroup.xlSecondary);
secAxis.HasTitle = true;
secAxis.AxisTitle.Text = "達成率 (%)";
secAxis.MaximumScale = 1.2;
secAxis.MinimumScale = 0;
secAxis.TickLabels.NumberFormat = "0%";
Range.Value2 = object[,] 一次寫入二維陣列,避免逐 Cell 呼叫 COM。excel.ScreenUpdating = false; excel.DisplayAlerts = false;Marshal.ReleaseComObject 逆序釋放,GC.Collect() 強制回收 RCW。for (int i=1; i<=n; i++) ws.Cells[i,1] = ... 極慢,改用陣列或 Range.Resize。| 套件 | 支援圖表 | 特點 |
|---|---|---|
EPPlus (商業授權) | 完整支援 | 純 .NET、無 Office 相依、效能極佳 |
ClosedXML (MIT) | 基本支援 | API 簡潔、開源、社群活躍 |
NPOI (Apache 2.0) | 支援 (較複雜) | Java POI 移植、支援 .xls/.xlsx |
Syncfusion XlsIO (商業) | 企業級完整 | 功能最全、含轉 PDF/圖片 |
建議:伺服器端/容器化部署請改用 EPPlus/ClosedXML,避免安裝 Office 與 COM 相容性問題。