控制項架構
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
public class CrystalButton : Button {
private bool isPressed = false;
private bool isMouseOver = false;
private Color baseBackColor;
public CrystalButton() {
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
baseBackColor = Color.FromArgb(
Math.Min(BackColor.R + 0x22, 255),
Math.Min(BackColor.G + 0x22, 255),
Math.Min(BackColor.B + 0x22, 255));
}
幾何路徑:圓角矩形
protected GraphicsPath GetRoundRectPath(Rectangle rect) {
GraphicsPath path = new GraphicsPath();
if (rect.Width <= 0) rect.Width = 1;
if (rect.Height <= 0) rect.Height = 1;
int radius = rect.Height;
path.AddArc(rect.Left, rect.Top, radius, radius, 90, 180);
path.AddArc(rect.Right - radius, rect.Top, radius, radius, 270, 180);
path.CloseFigure();
return path;
}
protected GraphicsPath GetHighlightPath(Rectangle rect) {
GraphicsPath path = new GraphicsPath();
if (rect.Width <= 0) rect.Width = 1;
if (rect.Height <= 0) rect.Height = 1;
int radius = rect.Height;
path.AddArc(rect.Left, rect.Top, radius, radius, 190, 80);
path.AddArc(rect.Right - radius, rect.Top, radius, radius, 270, 80);
path.CloseFigure();
return path;
}
繪製邏輯:三層疊加
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.HighQuality;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
Rectangle rect = ClientRectangle;
GraphicsPath mainPath = GetRoundRectPath(rect);
// 1. 基底填滿
g.FillRectangle(new SolidBrush(baseBackColor), rect);
g.FillPath(new SolidBrush(baseBackColor), mainPath);
this.Region = new Region(mainPath); // 點擊區域同步圓角
// 2. 陰影/立體感(多層漸變疊加)
DrawShadow(g, isPressed);
// 3. 高光(垂直線性漸層)
DrawHighlight(g, isPressed);
// 4. 文字(按下時下移 4px,滑鼠懸停底線)
DrawText(g, isPressed);
// 5. 焦點框
if (Focused)
g.DrawPath(new Pen(Color.FromArgb(0x22, Color.White), 3), mainPath);
}
陰影繪製
private void DrawShadow(Graphics g, bool pressed) {
Rectangle r = ClientRectangle;
r.Inflate(-r.Width / 10, -r.Height / 4);
float stepX = r.Width / 100f, stepY = r.Height / 100f;
r.Y += ClientRectangle.Height / 4;
if (pressed) r.Y += 4;
for (int a = 1; a < 33; a++) {
Rectangle layer = r;
layer.Inflate(-(int)(stepX * a), -(int)(stepY * a));
GraphicsPath p = GetRoundRectPath(layer);
int R = Math.Min(baseBackColor.R + 3 * a, 255);
int G = Math.Min(baseBackColor.G + 3 * a, 255);
int B = Math.Min(baseBackColor.B + 3 * a, 255);
g.FillPath(new SolidBrush(Color.FromArgb(R, G, B)), p);
}
}
高光繪製
private void DrawHighlight(Graphics g, bool pressed) {
Rectangle r = ClientRectangle;
r.Inflate(-4, -4);
if (pressed) r.Y += 4;
GraphicsPath p = GetHighlightPath(r);
RectangleF bounds = p.GetBounds();
bounds.Height += 1;
g.FillPath(new LinearGradientBrush(bounds,
Color.FromArgb(0xFF, Color.White),
Color.FromArgb(0xFF, baseBackColor),
LinearGradientMode.Vertical), p);
}
文字繪製
private void DrawText(Graphics g, bool pressed) {
Rectangle r = ClientRectangle;
Rectangle r1 = ClientRectangle;
StringFormat fmt = new StringFormat {
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
r.Y = ClientRectangle.Height / 5;
r1.Y = r.Y;
if (pressed) { r.Y += 4; r1.Y += 4; }
Font font = isMouseOver ? new Font(Font, FontStyle.Underline) : Font;
g.DrawString(Text, font, new SolidBrush(Color.FromArgb(0x66, baseBackColor)), r, fmt); // 陰影字
g.DrawString(Text, font, new SolidBrush(ForeColor), r1, fmt); // 主字
}
互動狀態管理
protected override void OnMouseDown(MouseEventArgs e) {
if (!isPressed) { isPressed = true; Invalidate(); }
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e) {
if (isPressed) { isPressed = false; Invalidate(); }
base.OnMouseUp(e);
}
protected override void OnMouseEnter(EventArgs e) {
if (!isMouseOver) { isMouseOver = true; Invalidate(); }
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(EventArgs e) {
if (isMouseOver) { isMouseOver = false; Invalidate(); }
base.OnMouseLeave(e);
}
protected override void OnKeyDown(KeyEventArgs e) {
if (!isPressed && e.KeyCode == Keys.Space) { isPressed = true; Invalidate(); }
base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyEventArgs e) {
if (isPressed && e.KeyCode == Keys.Space) { isPressed = false; Invalidate(); }
base.OnKeyUp(e);
}
protected override void OnBackColorChanged(EventArgs e) {
baseBackColor = Color.FromArgb(
Math.Min(BackColor.R + 0x22, 255),
Math.Min(BackColor.G + 0x22, 255),
Math.Min(BackColor.B + 0x22, 255));
Invalidate();
base.OnBackColorChanged(e);
}
效果總結
- 無圖片依賴:純 GDI+ 向量繪製,任意縮放不失真
- 三態視覺回饋:正常、懸停(底線)、按下(下沉 + 光影變化)
- 鍵盤無障礙:Space 鍵觸發按下效果
- 焦點指示:白色半透明外框
- 自動適應色系:依
BackColor 動態計算陰影/高光色