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

C# 製作不規則視窗與按鈕:Region 與 GraphicsPath 完整實作教學

     編輯:關於C語言

核心原理

  • Control.Region 屬性決定控制項的可見/可點擊區域
  • 使用 GraphicsPath 描述不規則形狀
  • 以圖片左上角像素 (0,0) 作為透明色,掃描每一列連續不透明像素形成矩形,加入 GraphicsPath
  • 適用 24/32 位元色深圖片 (BMP/PNG/GIF)

核心類別:BitmapRegion


using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

public static class BitmapRegion
{
    public static void CreateControlRegion(Control control, Bitmap bitmap)
    {
        if (control == null || bitmap == null) return;

        control.Width = bitmap.Width;
        control.Height = bitmap.Height;

        if (control is Form form)
        {
            form.Width += 15;  // 補償邊框
            form.Height += 35;
            form.FormBorderStyle = FormBorderStyle.None;
            form.BackgroundImage = bitmap;
            form.Region = new Region(CalculateGraphicsPath(bitmap));
        }
        else if (control is Button btn)
        {
            btn.Text = "";
            btn.Cursor = Cursors.Hand;
            btn.BackgroundImage = bitmap;
            btn.Region = new Region(CalculateGraphicsPath(bitmap));
        }
        // 可擴充支援 Panel、PictureBox 等
    }

    private static GraphicsPath CalculateGraphicsPath(Bitmap bitmap)
    {
        var path = new GraphicsPath();
        Color transparentColor = bitmap.GetPixel(0, 0); // 左上角為透明色

        for (int y = 0; y < bitmap.Height; y++)
        {
            int xOpaqueStart = 0;
            for (int x = 0; x < bitmap.Width; x++)
            {
                if (bitmap.GetPixel(x, y) != transparentColor)
                {
                    xOpaqueStart = x;
                    int xNext = x;
                    // 尋找連續不透明區段結束
                    while (xNext < bitmap.Width && bitmap.GetPixel(xNext, y) != transparentColor)
                        xNext++;
                    // 加入矩形區段 (高度=1)
                    path.AddRectangle(new Rectangle(xOpaqueStart, y, xNext - xOpaqueStart, 1));
                    x = xNext; // 跳過已處理區段
                }
            }
        }
        return path;
    }
}

使用範例:不規則主視窗


public class MainForm : Form
{
    private readonly Bitmap _bg = new Bitmap(typeof(MainForm), "Resources.back.png");

    public MainForm()
    {
        InitializeComponent();
        BitmapRegion.CreateControlRegion(this, _bg);
    }

    // 拖曳移動視窗 (無標題列時必要)
    private Point _lastPos;
    private bool _dragging;

    protected override void OnMouseDown(MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            _lastPos = e.Location;
            _dragging = true;
        }
        base.OnMouseDown(e);
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        if (_dragging)
        {
            Location = new Point(
                Location.X + e.X - _lastPos.X,
                Location.Y + e.Y - _lastPos.Y);
        }
        base.OnMouseMove(e);
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        _dragging = false;
        base.OnMouseUp(e);
    }
}

使用範例:不規則按鈕 + 滑鼠進入/離開切換圖片


public class MainForm : Form
{
    private Button _btn;
    private Bitmap _btnNormal, _btnHover;

    public MainForm()
    {
        _btn = new Button { Location = new Point(100, 100) };
        _btnNormal = new Bitmap(typeof(MainForm), "Resources.btn_normal.png");
        _btnHover = new Bitmap(typeof(MainForm), "Resources.btn_hover.png");
        Controls.Add(_btn);

        BitmapRegion.CreateControlRegion(_btn, _btnNormal);

        _btn.MouseEnter += (s, e) => BitmapRegion.CreateControlRegion(_btn, _btnHover);
        _btn.MouseLeave += (s, e) => BitmapRegion.CreateControlRegion(_btn, _btnNormal);
        _btn.Click += (s, e) => MessageBox.Show("不規則按鈕被點擊!");
    }
}

效能優化建議

  1. 快取 GraphicsPath:同一張圖片重複呼叫時,先計算並快取 GraphicsPath,避免重複 GetPixel 掃描。
  2. 使用 LockBits:大圖片時 GetPixel 極慢,改用 Bitmap.LockBits 直接存取記憶體。
  3. 避免頻繁重建 Region:滑鼠進入/離開切換圖片時,可預先建立兩個 Region 物件直接切換。

進階:不規則控制項點擊測試

Region 自動處理點擊測試,只有在不透明區域點擊才會觸發 Click 事件,透明區域點擊會穿透給父容器。

注意事項

  • 表單設定 FormBorderStyle.None 後失去標題列,需自行實作移動/最小化/關閉。
  • 多螢幕 DPI 縮放下,圖片尺寸與實際像素可能不符,需處理 AutoScaleMode 或使用向量圖 (SVG/WMF)。
  • 透明色以 (0,0) 像素為準,設計圖片時確保四角為透明色。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved