首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# GDI+ 自繪圖表實戰:長條圖與折線圖完整原始碼解析

     編輯:關於C#

前言

統計報表功能常需繪製圖表,若不想引入 ZedGraph 等第三方元件,透過 System.Drawing 命名空間的 GDI+ 功能即可自行實作長條圖與折線圖。本文提供兩個完整、可直接套用的 Render 方法,支援自訂標題、尺寸、資料來源(DataTable),並內建座標軸、圖例、數值標籤等繪圖細節。

一、長條圖實作

長條圖效果圖
長條圖示例:雙資料系列並排顯示

方法簽章

public Image Render(string title, int width, int height, DataTable chartTable)

參數說明: chartTable 需包含至少三欄:第 0 欄與第 1 欄為兩組數值,第 2 欄為 X 軸標籤文字。

核心邏輯流程

  1. 建立 Bitmap 與 Graphics,清除背景
  2. 驗證繪圖區域是否過小
  3. 掃描資料找出最大值 highPoint,作為 Y 軸縮放基準
  4. 繪製主標題與垂直方向圖例(紅/藍對應兩數列)
  5. 計算長條寬度 barWidth 與起始座標 barOrigin
  6. 迴圈逐列繪製:紅色長條 → 底部標籤 → 頂部數值 → 藍色長條 → 頂部數值
  7. 繪製座標軸邊框
  8. 釋放資源並回傳 Bitmap

關鍵程式碼片段

// 計算最大值
float highPoint = 1;
foreach (DataRow dr in dt.Rows)
{
    highPoint = Math.Max(highPoint, Convert.ToSingle(dr[0]));
    highPoint = Math.Max(highPoint, Convert.ToSingle(dr[1]));
}

// 長條寬度計算
float barWidth = (width - left) / (dt.Rows.Count * 3 + 1);
PointF barOrigin = new PointF(left + barWidth, 0);

// 迴圈繪製雙系列長條
for (int i = 0; i < dt.Rows.Count; i++)
{
    // 系列 1(紅)
    float barHeight = Convert.ToSingle(dt.Rows[i][0]) * (height - 2 * top) / highPoint;
    barOrigin.Y = height - barHeight - top;
    g.FillRectangle(Brushes.Red, barOrigin.X, barOrigin.Y, barWidth, barHeight);
    g.DrawString(dt.Rows[i][2].ToString(), fontBottom, Brushes.Black, barOrigin.X, height - top); // 底部標籤
    g.DrawString(dt.Rows[i][0].ToString(), fontTop, Brushes.Red, barOrigin.X, barOrigin.Y - 3 * fontTop.Size / 2); // 頂部數值
    barOrigin.X += barWidth;

    // 系列 2(藍)
    barHeight = Convert.ToSingle(dt.Rows[i][1]) * (height - 2 * top) / highPoint;
    barOrigin.Y = height - barHeight - top;
    g.FillRectangle(Brushes.Blue, barOrigin.X, barOrigin.Y, barWidth, barHeight);
    g.DrawString(dt.Rows[i][1].ToString(), fontTop, Brushes.Blue, barOrigin.X, barOrigin.Y - 3 * fontTop.Size / 2);
    barOrigin.X += barWidth * 2; // 留出間距
}

二、折線圖實作

折線圖效果圖
折線圖示例:雙曲線重疊對比

核心差異點

  • 不繪製長條,改以 FillEllipse 繪製資料點圓點
  • 收集所有點座標至 PointF[] 陣列
  • 迴圈結束後呼叫 g.DrawLines 連線成曲線
  • X 軸刻度線:資料點超過 10 個時自動稀疏顯示,避免擁擠

關鍵程式碼片段

PointF[] pt1 = new PointF[dt.Rows.Count];
PointF[] pt2 = new PointF[dt.Rows.Count];

for (int i = 0; i < dt.Rows.Count; i++)
{
    // 系列 1 點位
    float barHeight = Convert.ToSingle(dt.Rows[i][0]) * (height - 2 * top) / highPoint;
    barOrigin.Y = height - barHeight - top;
    g.FillEllipse(Brushes.Red, barOrigin.X - 3, barOrigin.Y - 3, 6, 6);
    pt1[i] = new PointF(barOrigin.X, barOrigin.Y);
    g.DrawString(dt.Rows[i][0].ToString(), fontTop, Brushes.Red, barOrigin.X, barOrigin.Y - 2 * fontTop.Size);

    // 系列 2 點位
    barHeight = Convert.ToSingle(dt.Rows[i][1]) * (height - 2 * top) / highPoint;
    barOrigin.Y = height - barHeight - top;
    g.FillEllipse(Brushes.Blue, barOrigin.X - 3, barOrigin.Y - 3, 6, 6);
    pt2[i] = new PointF(barOrigin.X, barOrigin.Y);
    g.DrawString(dt.Rows[i][1].ToString(), fontTop, Brushes.Blue, barOrigin.X, barOrigin.Y - 2 * fontTop.Size);

    barOrigin.X += barWidth;
}

// 繪製連線
g.DrawLines(new Pen(Brushes.Red, 1), pt1);
g.DrawLines(new Pen(Brushes.Blue, 1), pt2);

三、共用輔助邏輯

功能實作方式
動態字型大小根據長條寬度/資料位數自動計算,並限制最大/最小值
座標軸繪製藍色粗線(2px)繪製 Y 軸與 X 軸
資料來源驗證折線圖額外檢查 chartTable == null 顯示「無資料」
例外處理統一 try-catch 包裹,發生錯誤回傳空白圖片

四、使用範例

DataTable dt = new DataTable();
dt.Columns.Add("系列A", typeof(double));
dt.Columns.Add("系列B", typeof(double));
dt.Columns.Add("標籤", typeof(string));

dt.Rows.Add(80, 60, "1月");
dt.Rows.Add(95, 70, "2月");
dt.Rows.Add(110, 85, "3月");
// ... 更多資料

Image barChart = Render("月度業績對比", 600, 400, dt);
pictureBox1.Image = barChart;

// 或存檔
barChart.Save("chart.png", ImageFormat.Png);

五、效能與擴充建議

  • 雙緩衝:WinForm 直接指派 PictureBox.Image 已具雙緩衝,無閃爍
  • 大量資料:超過 500 點建議改用虛擬化繪製或第三方元件
  • 互動功能:滑鼠懸停提示、點選鑽取需自行處理 MouseMove 事件與座標換算
  • 向量輸出:需匯出 SVG/PDF 時可改用 System.Drawing.Drawing2D.Metafile

作者:半支煙阿傑 | 出處:CSDN 部落格

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved