using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
/// <summary>
/// 自定义按钮控件(支持正常/悬停/点击状态颜色、文字、圆角、hover效果)
/// </summary>
namespace WindowsFormsTest.FECLS.Controls
{
/// <summary>
/// 半圆角自定义按钮(4个角为标准半圆,支持颜色/文字/hover/点击效果)
/// </summary>
public class SemiCircleButton : Control
{
#region 可自定义属性(设计时/代码中均可设置)
/// <summary>
/// 正常状态背景色(默认:深蓝色 #2563EB)
/// </summary>
[Category("自定义样式")]
[Description("按钮正常状态的背景色")]
public Color NormalBackColor { get; set; } = Color.FromArgb(37, 99, 235);
/// <summary>
/// 鼠标悬停状态背景色(默认:深蓝色加深 #1D4ED8)
/// </summary>
[Category("自定义样式")]
[Description("鼠标悬停时的背景色")]
public Color HoverBackColor { get; set; } = Color.FromArgb(29, 78, 216);
/// <summary>
/// 鼠标点击状态背景色(默认:深蓝色更深 #1E40AF)
/// </summary>
[Category("自定义样式")]
[Description("鼠标点击时的背景色")]
public Color ClickBackColor { get; set; } = Color.FromArgb(30, 64, 175);
/// <summary>
/// 正常状态文字色(默认:白色)
/// </summary>
[Category("自定义样式")]
[Description("按钮正常状态的文字颜色")]
public Color NormalForeColor { get; set; } = Color.White;
/// <summary>
/// 鼠标悬停状态文字色(默认:白色)
/// </summary>
[Category("自定义样式")]
[Description("鼠标悬停时的文字颜色")]
public Color HoverForeColor { get; set; } = Color.White;
/// <summary>
/// 鼠标点击状态文字色(默认:白色)
/// </summary>
[Category("自定义样式")]
[Description("鼠标点击时的文字颜色")]
public Color ClickForeColor { get; set; } = Color.White;
/// <summary>
/// 按钮边框颜色(默认:透明,不显示边框)
/// </summary>
[Category("自定义样式")]
[Description("按钮边框颜色")]
public Color BorderColor { get; set; } = Color.Transparent;
/// <summary>
/// 按钮边框宽度(默认:1px)
/// </summary>
[Category("自定义样式")]
[Description("按钮边框宽度")]
public int BorderWidth { get; set; } = 1;
/// <summary>
/// 按钮文字(默认:"按钮")
/// </summary>
[Category("自定义样式")]
[Description("按钮显示的文字")]
public new string Text { get; set; } = "按钮";
/// <summary>
/// 按钮文字字体(默认:宋体 12号 加粗)
/// </summary>
[Category("自定义样式")]
[Description("按钮文字字体")]
public new Font Font { get; set; } = new Font("宋体", 12F, FontStyle.Bold);
/// <summary>
/// 是否强制半圆(默认:true,4个角为标准半圆;false时使用自定义半径)
/// </summary>
[Category("自定义样式")]
[Description("是否强制4个角为半圆(true=半圆,false=自定义半径)")]
public bool ForceSemiCircle { get; set; } = true;
/// <summary>
/// 自定义圆角半径(仅ForceSemiCircle=false时生效)
/// </summary>
[Category("自定义样式")]
[Description("自定义圆角半径(仅ForceSemiCircle=false时生效)")]
public int CustomCornerRadius { get; set; } = 6;
#endregion
#region 私有字段
// 当前按钮状态(正常/悬停/点击)
private ButtonState _currentState = ButtonState.Normal;
// 按钮状态枚举
private enum ButtonState
{
Normal,
Hover,
Click
}
#endregion
public SemiCircleButton()
{
// 基础设置(.NET 4.8兼容)
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
DoubleBuffered = true; // 双缓冲防止闪烁
Size = new Size(120, 40); // 默认大小(宽度>高度,确保半圆效果)
Cursor = Cursors.Hand; // 鼠标悬停时显示手型
// 绑定鼠标事件(C# 7.3兼容)
MouseEnter += SemiCircleButton_MouseEnter;
MouseLeave += SemiCircleButton_MouseLeave;
MouseDown += SemiCircleButton_MouseDown;
MouseUp += SemiCircleButton_MouseUp;
Paint += SemiCircleButton_Paint;
}
#region 鼠标事件处理(状态切换)
private void SemiCircleButton_MouseEnter(object sender, EventArgs e)
{
if (_currentState != ButtonState.Click)
{
_currentState = ButtonState.Hover;
Invalidate();
}
}
private void SemiCircleButton_MouseLeave(object sender, EventArgs e)
{
_currentState = ButtonState.Normal;
Invalidate();
}
private void SemiCircleButton_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_currentState = ButtonState.Click;
Invalidate();
}
}
private void SemiCircleButton_MouseUp(object sender, MouseEventArgs e)
{
_currentState = ClientRectangle.Contains(e.Location) ? ButtonState.Hover : ButtonState.Normal;
Invalidate();
}
#endregion
#region 核心修改:半圆角绘制逻辑
private void SemiCircleButton_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias; // 抗锯齿(半圆更平滑)
g.Clear(Parent?.BackColor ?? Color.Transparent);
// 1. 获取当前状态的颜色
(Color backColor, Color foreColor) = GetCurrentStateColors();
// 2. 计算半圆半径(核心:半径=按钮高度的1/2,且不超过宽度的1/2)
int radius = CalculateSemiCircleRadius();
// 3. 绘制半圆背景
using (var brush = new SolidBrush(backColor))
using (var path = CreateSemiCirclePath(ClientRectangle, radius))
{
g.FillPath(brush, path);
}
// 4. 绘制边框(如果边框颜色非透明)
if (BorderColor != Color.Transparent && BorderWidth > 0)
{
using (var pen = new Pen(BorderColor, BorderWidth))
using (var path = CreateSemiCirclePath(GetBorderRectangle(radius), radius - BorderWidth / 2))
{
g.DrawPath(pen, path);
}
}
// 5. 绘制居中文字
using (var brush = new SolidBrush(foreColor))
{
var stringFormat = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center,
FormatFlags = StringFormatFlags.NoWrap
};
g.DrawString(Text, Font, brush, ClientRectangle, stringFormat);
}
}
/// <summary>
/// 计算半圆半径(核心逻辑)
/// </summary>
private int CalculateSemiCircleRadius()
{
if (!ForceSemiCircle)
return Math.Max(0, CustomCornerRadius); // 非强制半圆时使用自定义半径
// 强制半圆:半径=按钮高度的1/2,且不超过宽度的1/2(避免左右半圆超出控件)
int heightHalf = ClientRectangle.Height / 2;
int widthHalf = ClientRectangle.Width / 2;
return Math.Min(heightHalf, widthHalf);
}
/// <summary>
/// 创建半圆角路径(4个角均为半圆)
/// </summary>
private GraphicsPath CreateSemiCirclePath(Rectangle rect, int radius)
{
var path = new GraphicsPath();
radius = Math.Max(0, radius);
if (radius == 0)
{
path.AddRectangle(rect);
return path;
}
// 关键:4个角均为半圆(圆弧角度=90度,半径=控件高度的1/2)
// 左上角半圆:圆心(rect.X+radius, rect.Y+radius),半径radius,角度180→270
path.AddArc(rect.X, rect.Y, radius * 2, radius * 2, 180, 90);
// 右上角半圆:圆心(rect.Right-radius, rect.Y+radius),半径radius,角度270→360
path.AddArc(rect.Right - radius * 2, rect.Y, radius * 2, radius * 2, 270, 90);
// 右下角半圆:圆心(rect.Right-radius, rect.Bottom-radius),半径radius,角度0→90
path.AddArc(rect.Right - radius * 2, rect.Bottom - radius * 2, radius * 2, radius * 2, 0, 90);
// 左下角半圆:圆心(rect.X+radius, rect.Bottom-radius),半径radius,角度90→180
path.AddArc(rect.X, rect.Bottom - radius * 2, radius * 2, radius * 2, 90, 90);
path.CloseAllFigures();
return path;
}
/// <summary>
/// 获取边框绘制区域(避免边框超出半圆范围)
/// </summary>
private Rectangle GetBorderRectangle(int radius)
{
int offset = BorderWidth / 2;
// 边框区域向内偏移,且左右预留半圆半径空间
// 修复后(正确代码)
return new Rectangle(
ClientRectangle.X + offset,
ClientRectangle.Y + offset,
ClientRectangle.Width - BorderWidth,
ClientRectangle.Height - BorderWidth
);
}
#endregion
#region 辅助方法与重写事件
/// <summary>
/// 根据当前状态获取颜色
/// </summary>
private (Color BackColor, Color ForeColor) GetCurrentStateColors()
{
switch (_currentState)
{
case ButtonState.Hover:
return (HoverBackColor, HoverForeColor);
case ButtonState.Click:
return (ClickBackColor, ClickForeColor);
default:
return (NormalBackColor, NormalForeColor);
}
}
/// <summary>
/// 控件大小变化时重新计算半圆半径
/// </summary>
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Invalidate(); // 刷新半圆效果
}
/// <summary>
/// 禁用状态颜色处理
/// </summary>
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
if (!Enabled)
{
NormalBackColor = DarkenColor(NormalBackColor, 0.5f);
HoverBackColor = DarkenColor(HoverBackColor, 0.5f);
ClickBackColor = DarkenColor(ClickBackColor, 0.5f);
}
Invalidate();
}
/// <summary>
/// 颜色变暗(禁用状态使用)
/// </summary>
private Color DarkenColor(Color color, float factor)
{
return Color.FromArgb(
color.A,
(int)(color.R * (1 - factor)),
(int)(color.G * (1 - factor)),
(int)(color.B * (1 - factor))
);
}
/// <summary>
/// 暴露点击事件(兼容标准用法)
/// </summary>
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
}
#endregion
}
}
// 圆形按钮(宽=高=60px)
var circleBtn = new SemiCircleButton
{
Text = "开始",
Size = new Size(300, 100), // 宽=高→圆形
Location = new Point(80, 80),
ForceSemiCircle = true,
NormalBackColor = Color.FromArgb(239, 68, 68),
HoverBackColor = Color.FromArgb(220, 38, 38),
ClickBackColor = Color.FromArgb(185, 28, 28),
BorderColor = Color.White,
BorderWidth = 2,
Font = new Font("宋体", 11F, FontStyle.Bold)
};
circleBtn.Click += (s, args) => MessageBox.Show("圆形按钮被点击!");
this.Controls.Add(circleBtn);
return;
效果


© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...