在圖表 Y 軸標籤、直式排版、印刷預覽等場景需繪製垂直文字。本文對比兩種做法:傳統座標變換(平移→旋轉→平移)與現代 StringFormatFlags.DirectionVertical 一行解決。
using System.Drawing;
using System.Drawing.Drawing2D;
public void DrawVerticalTextLegacy(Graphics g, string text, Font font, Brush brush, float x, float y)
{
// 1. 儲存當前變換狀態
GraphicsState state = g.Save();
try
{
// 2. 將原點移至旋轉中心 (文字中心點)
g.TranslateTransform(x, y);
// 3. 逆時針旋轉 90 度 (或 270 度順時針)
g.RotateTransform(-90); // -90 = 逆時針 90 度
// 4. 繪製文字 (現原點為旋轉中心)
g.DrawString(text, font, brush, 0, 0);
}
finally
{
// 5. 恢復原始變換 (關鍵:避免影響後續繪圖)
g.Restore(state);
}
}
// 使用範例:Y 軸標籤
public void DrawYAxisLabels(Graphics g, Rectangle plotArea)
{
using (Font font = new Font("Microsoft JhengHei", 10))
using (Brush brush = Brushes.Black)
{
for (int i = 0; i <= 100; i += 10)
{
float y = plotArea.Bottom - (i / 100f) * plotArea.Height;
string label = i.ToString();
// 計算文字大小以置中
SizeF textSize = g.MeasureString(label, font);
float x = plotArea.Left - textSize.Height - 5; // 左側留白
DrawVerticalTextLegacy(g, label, font, brush, x, y);
}
}
}
原始座標系: 旋轉後座標系: Y ↑ X' ↑ (原 -Y 方向) | | +--→ X +--→ Y' (原 X 方向) 步驟: 1. Translate(x, y) : 原點移到 (x,y) 2. Rotate(-90) : 座標系逆時針轉 90° 3. DrawString(0,0) : 在新原點繪製 4. Restore() : 還原座標系
public void DrawVerticalTextModern(Graphics g, string text, Font font, Brush brush, float x, float y)
{
// 關鍵:StringFormatFlags.DirectionVertical
var format = new StringFormat(StringFormatFlags.DirectionVertical)
{
Alignment = StringAlignment.Center, // 水平置中 (旋轉後為垂直置中)
LineAlignment = StringAlignment.Center // 垂直置中 (旋轉後為水平置中)
};
// 直接在指定位置繪製,GDI+ 內部處理旋轉
g.DrawString(text, font, brush, x, y, format);
}
// 更簡潔的單行版
g.DrawString(text, font, brush, x, y, new StringFormat(StringFormatFlags.DirectionVertical));
public void DrawChartWithVerticalLabels(Graphics g, Rectangle clientRect)
{
var plotArea = new Rectangle(80, 40, clientRect.Width - 100, clientRect.Height - 80);
// 1. 繪製座標軸
using (var axisPen = new Pen(Color.Black, 2))
{
g.DrawLine(axisPen, plotArea.Left, plotArea.Bottom, plotArea.Right, plotArea.Bottom); // X 軸
g.DrawLine(axisPen, plotArea.Left, plotArea.Top, plotArea.Left, plotArea.Bottom); // Y 軸
}
// 2. 繪製垂直 Y 軸標籤 (現代方法)
using (Font font = new Font("Microsoft JhengHei", 9))
using (Brush brush = Brushes.DarkBlue)
{
for (int i = 0; i <= 10; i++)
{
float y = plotArea.Bottom - (i / 10f) * plotArea.Height;
string label = $"{i * 10}%";
// 文字區域在 Y 軸左側
float x = plotArea.Left - 50;
g.DrawString(label, font, brush, x, y,
new StringFormat(StringFormatFlags.DirectionVertical)
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
});
}
}
// 3. 繪製水平 X 軸標籤 (對比)
using (Font font = new Font("Microsoft JhengHei", 9))
using (Brush brush = Brushes.DarkBlue)
{
for (int i = 0; i <= 10; i++)
{
float x = plotArea.Left + (i / 10f) * plotArea.Width;
string label = $"{i * 10}";
g.DrawString(label, font, brush, x, plotArea.Bottom + 5,
new StringFormat { Alignment = StringAlignment.Center });
}
}
}
| 特性 | 座標變換法 | StringFormat.Vertical |
|---|---|---|
| 代碼複雜度 | 高 (Save/Translate/Rotate/Restore) | 極低 (一行) |
| 相容性 | 所有 .NET 版本 | .NET Framework 4.5+ / .NET Core 3.0+ |
| 效能 | 稍高 (矩陣運算) | 內部優化,略快 |
| 文字度量 | MeasureString 需在變換後座標系 | 正常 MeasureString 即可 |
| 對齊控制 | 手動計算 | StringFormat Alignment/LineAlignment |
| 字型回退 | 正常 | 部分舊版字型可能不支援垂直排版 |
DirectionVertical,必須用座標變換MeasureString 回傳寬高在原始座標系,垂直文字實際佔用區域為 (Height, Width)g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit 獲得最佳品質
現代專案請直接使用 StringFormatFlags.DirectionVertical,代碼簡潔、可維護、效能佳。僅在舊版 .NET CF、特殊字型相容性場景才需手動座標變換。理解兩者原理有助於除錯複雜排版問題。