前言
在 WinForm 程序开发中,自定义控件是提升用户界面体验的重要手段。本文将详细介绍一个具有精美视觉效果的旋转开关按钮控件(RotatingSwitchButton)的实现。
该控件结合现代 UI 设计元素,包括平滑动画、阴影效果、渐变和金属质感等特性,帮助用户提供直观且吸引人的交互体验。
控件介绍
RotatingSwitchButton 是一个模拟物理旋转开关的自定义控件,具备以下主要特征:
1、旋转动画
开关状态切换时,控件会执行平滑的旋转动画,提供流畅的视觉过渡效果。
2、视觉反馈
通过颜色变化和位置移动清晰指示开关状态,确保用户能够直观地理解当前的操作结果。
3、阴影效果
支持可自定义的控件阴影,增加深度感和立体感,使控件在界面上更加突出。
4、金属质感
运用渐变和光泽效果营造出逼真的金属质感,提升整体视觉档次。
5、精细刻度
环形刻度显示增强了控件的专业性和精确度,适用于需要高精度操作的场景。
核心功能
1、状态管理与动画
控件使用布尔值isOn追踪当前状态,并通过currentAngle控制旋钮的旋转角度。
动画效果通过Timer实现,使用缓动函数使动画更自然:
private bool isOn = false;
private float currentAngle = 0f;
private readonly float targetOnAngle = -90f; // 向上位置
private readonly float targetOffAngle = 90f; // 向下位置
2、阴影效果
控件支持可自定义的阴影效果,通过以下属性控制:
EnableShadow:启用/禁用阴影
ShadowDepth:阴影深度
ShadowOpacity:阴影透明度
ShadowColor:阴影颜色
ShadowBlur:阴影模糊程度
阴影实现使用PathGradientBrush创建径向渐变,实现柔和的阴影效果:
private void DrawShadow(Graphics g, float centerX, float centerY, float radius)
{
using (var shadowPath = new GraphicsPath())
{
float shadowSize = radius * 2 + shadowBlur * 2;
shadowPath.AddEllipse(/*...*/);
using (var shadowBrush = new PathGradientBrush(shadowPath))
{
// 设置渐变参数
shadowBrush.CenterColor = centerShadowColor;
shadowBrush.SurroundColors = new[] { Color.FromArgb(0, shadowColor) };
g.FillPath(shadowBrush, shadowPath);
}
}
}
3、视觉元素绘制
外环绘制
外环采用渐变效果和高光,创造金属质感:
private void DrawOuterRing(Graphics g, float centerX, float centerY, float radius)
{
// 主体渐变
using (var gradientBrush = new LinearGradientBrush(...))
{
g.FillEllipse(gradientBrush, ...);
}
// 边缘高光
using (var pen = new Pen(...))
{
g.DrawEllipse(pen, ...);
}
}
刻度线绘制
通过循环绘制不同长度的刻度线,增强专业感:
for (int i = 0; i < 360; i += 6)
{
float scaleLength = (i % 30 == 0) ? 0.12f : 0.08f;
if (i == 90 || i == 270)
scaleLength = 0.15f; // 主要刻度线
// 绘制刻度线...
}
旋钮绘制
旋钮采用多层渲染,包括基础形状、阴影、高光和指示线:
private void DrawKnob(Graphics g, float centerX, float centerY, float radius)
{
// 计算旋钮位置
float knobRadius = radius * 0.3f;
float knobX = centerX + (float)(radius * 0.6f * Math.Cos(currentAngle * Math.PI / 180));
float knobY = centerY + (float)(radius * 0.6f * Math.Sin(currentAngle * Math.PI / 180));
// 绘制旋钮本体、阴影和高光
// ...
// 绘制指示线
using (var indicatorPen = new Pen(...))
{
// 绘制方向指示线
}
}
完整代码
控件可以轻松集成到Windows Forms应用程序中:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Timer = System.Windows.Forms.Timer;
namespace AppControls
{
public class RotatingSwitchButton : Control
{
private bool isOn = false;
private float currentAngle = 0f;
// 修改目标角度为90度(上)和-90度(下)
private readonly float targetOnAngle = -90f; // 负角度表示向上
private readonly float targetOffAngle = 90f; // 正角度表示向下
private Timer animationTimer;
// 定义颜色
private readonly Color onColor = Color.FromArgb(76, 217, 100);
private readonly Color offColor = Color.FromArgb(255, 59, 48);
private readonly Color knobColor = Color.White;
private readonly Color gradientStart = Color.FromArgb(240, 240, 240);
private readonly Color gradientEnd = Color.FromArgb(200, 200, 200);
// 阴影相关属性
private bool enableShadow = true;
private float shadowDepth = 5f;
private float shadowOpacity = 0.3f;
private Color shadowColor = Color.FromArgb(76, 0, 0, 0);
private float shadowBlur = 10f;
// 公开的属性设置
[Category("Appearance")]
[Description("启用或禁用控件阴影")]
public bool EnableShadow
{
get => enableShadow;
set
{
if (enableShadow != value)
{
enableShadow = value;
Invalidate();
}
}
}
[Category("Appearance")]
[Description("设置阴影深度")]
public float ShadowDepth
{
get => shadowDepth;
set
{
if (shadowDepth != value)
{
shadowDepth = value;
Invalidate();
}
}
}
[Category("Appearance")]
[Description("设置阴影透明度 (0.0 - 1.0)")]
public float ShadowOpacity
{
get => shadowOpacity;
set
{
value = Math.Max
(0, Math.Min(1, value));
if (shadowOpacity != value)
{
shadowOpacity = value;
Invalidate();
}
}
}
[Category("Appearance")]
[Description("设置阴影颜色")]
public Color ShadowColor
{
get => shadowColor;
set
{
if (shadowColor != value)
{
shadowColor = value;
Invalidate();
}
}
}
[Category("Appearance")]
[Description("设置阴影模糊程度")]
public float ShadowBlur
{
get => shadowBlur;
set
{
if (shadowBlur != value)
{
shadowBlur = value;
Invalidate();
}
}
}
public bool IsOn
{
get => isOn;
set
{
if (isOn != value)
{
isOn = value;
StartAnimation();
OnValueChanged(EventArgs.Empty);
}
}
}
public event EventHandler ValueChanged;
public RotatingSwitchButton()
{
// 构造函数内容保持不变
SetStyle(
ControlStyles.SupportsTransparentBackColor |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint, true);
Size = new Size(100, 100);
BackColor = Color.Transparent;
// 初始化角度为OFF位置(下方)
currentAngle = targetOffAngle;
animationTimer = new Timer();
animationTimer.Interval = 16;
animationTimer.Tick +=
AnimationTimer_Tick;
// 确保控件有足够的空间显示阴影
Padding = new Padding((int)(shadowDepth + shadowBlur));
}
private void StartAnimation()
{
animationTimer.Start();
}
private float EaseInOutQuad(float t)
{
return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
}
private void AnimationTimer_Tick(object sender, EventArgs e)
{
float targetAngle =
isOn ? targetOnAngle : targetOffAngle;
float totalDistance =
Math.Abs(targetAngle - currentAngle);
float progress =
Math.Min(1f, 0.15f);
// 控制动画速度
if (Math.Abs(currentAngle - targetAngle) < 0.1f)
{
currentAngle = targetAngle;
animationTimer.Stop();
}
else
{
float step =
totalDistance * EaseInOutQuad(progress);
if (currentAngle < targetAngle)
currentAngle += step;
else
currentAngle -= step;
}
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode =
SmoothingMode.AntiAlias;
e.Graphics.InterpolationMode =
InterpolationMode.HighQualityBicubic;
e.Graphics.CompositingQuality =
CompositingQuality.HighQuality;
// 计算中心点和半径
float centerX = Width / 2f;
float centerY = Height
/ 2f;
float outerRadius = Math.Min(Width, Height)
/ 2f - (shadowDepth + shadowBlur);
float innerRadius = outerRadius * 0.7f;
// 如果启用阴影,先绘制阴影
if (enableShadow)
{
DrawShadow(e.Graphics, centerX, centerY, outerRadius);
}
// 继续绘制其他部分
using (var path = new GraphicsPath())
{
DrawOuterRing(e.Graphics, centerX,
centerY, outerRadius);
DrawLabels(e.Graphics, centerX,
centerY, outerRadius);
DrawInnerRing(e.Graphics, centerX,
centerY, innerRadius);
DrawKnob(e.Graphics, centerX,
centerY, innerRadius);
}
}
// 阴影绘制方法
private void DrawShadow(Graphics g,
float centerX, float centerY, float radius)
{
// 创建阴影路径
using (var shadowPath = new GraphicsPath())
{
// 阴影椭圆的大小要略大于实际控件
float shadowSize =
radius * 2 + shadowBlur * 2;
float shadowX =
centerX - radius - shadowBlur + shadowDepth * 0.5f;
float shadowY =
centerY - radius - shadowBlur + shadowDepth;
shadowPath.AddEllipse(shadowX, shadowY, shadowSize, shadowSize);
// 创建径向渐变画刷来实现柔和阴影
using (var shadowBrush =
new PathGradientBrush(shadowPath))
{
// 设置中心色和边缘色
Color centerShadowColor =
Color.FromArgb(
(int)(255 * shadowOpacity),
shadowColor.R,
shadowColor.G,
shadowColor.B);
shadowBrush.CenterColor = centerShadowColor;
shadowBrush.SurroundColors = new[] {
Color.FromArgb(0, shadowColor) };
// 设置阴影的渐变焦点
shadowBrush.FocusScales =
new PointF(0.8f, 0.8f);
// 绘制阴影
g.FillPath(shadowBrush, shadowPath);
}
}
}
private void DrawOuterRing(Graphics g, float centerX,
float centerY, float radius)
{
// 主体渐变背景
using (var gradientBrush = new LinearGradientBrush(
new RectangleF(centerX - radius, centerY - radius,
radius * 2, radius * 2),
gradientStart,
gradientEnd,
45f))
{
g.FillEllipse(gradientBrush, centerX - radius,
centerY - radius, radius * 2, radius * 2);
}
// 添加边缘高光
using (var pen = new Pen(Color.FromArgb(100,
255, 255, 255), 1.5f))
{
g.DrawEllipse(pen, centerX - radius,
centerY - radius, radius * 2, radius * 2);
}
// 添加内部光泽效果
using (var highlightPath = new GraphicsPath())
{
highlightPath.AddEllipse(centerX - radius * 0.9f,
centerY - radius * 0.9f,
radius * 1.8f, radius * 1.8f);
using (var highlightBrush =
new PathGradientBrush(highlightPath))
{
highlightBrush.CenterColor = Color.FromArgb(30, 255, 255, 255);
highlightBrush.SurroundColors = new[] {
Color.FromArgb(0, 255, 255, 255) };
g.FillPath(highlightBrush, highlightPath);
}
}
}
private void DrawLabels(Graphics g,
float centerX,
float centerY,
float radius)
{
using (var font = 绘制旋转钮阴影 new Font("Arial",
12f, FontStyle.Bold))
{
// 绘制ON标签(上方)
DrawRotatedText(g, "ON", font,
isOn ? onColor : Color.Gray,
centerX, centerY, radius * 0.8f, -90); // -90度位置
// 绘制OFF标签(下方)
DrawRotatedText(g, "OFF", font,
!isOn ? offColor : Color.Gray,
centerX, centerY, radius * 0.8f, 90); // 90度位置
}
}
private void DrawRotatedText(Graphics g, string text,
Font font, Color color,
float centerX,
float centerY, float radius, float angle)
{
using (var brush = new SolidBrush(color))
{
var size = g.MeasureString(text, font);
// 计算文本位置,考虑垂直方向
float x = centerX + (float)(radius * Math.Cos(angle
* Math.PI / 180)) - size.Width / 2;
float y = centerY + (float)(radius * Math.Sin(angle
* Math.PI / 180)) - size.Height / 2;
// 保持文本水平显示
float rotationAngle = 0;
if (angle == 90)
// OFF位置
rotationAngle = 0;
else if (angle == -90)
// ON位置
rotationAngle = 0;
g.TranslateTransform(x + size.Width / 2,
y + size.Height / 2);
g.RotateTransform(rotationAngle);
g.DrawString(text, font, brush, -size.Width / 2,
-size.Height / 2);
g.ResetTransform();
}
}
private void DrawInnerRing(Graphics g,
float centerX, float centerY, float radius)
{
using (var pen = new Pen(Color.FromArgb(100, 130, 130, 130), 1f))
{
g.DrawEllipse(pen, centerX - radius,
centerY - radius, radius * 2, radius * 2);
// 绘制精细刻度线
for (int i = 0; i < 360; i += 6)
{
float scaleLength = (i % 30 == 0) ? 0.12f : 0.08f;
if (i == 90 || i == 270)
scaleLength = 0.15f;
// 主要刻度线
float startX = centerX + (float)(radius
* (1 - scaleLength) * Math.Cos(i
* Math.PI / 180));
float startY = centerY + (float)(radius
* (1 - scaleLength) * Math.Sin(i
* Math.PI / 180));
float endX = centerX + (float)(radius
* Math.Cos(i * Math.PI / 180));
float endY = centerY + (float)(radius
* Math.Sin(i * Math.PI / 180));
using (var scalePen = new Pen(Color.FromArgb(
i % 30 == 0 ? 120 : 80,
130, 130, 130), i % 30 == 0 ? 1.5f : 0.8f))
{
g.DrawLine(scalePen, startX,
startY, endX, endY);
}
}
}
}
private void DrawKnob(Graphics g, float centerX,
float centerY, float radius)
{
float knobRadius = radius * 0.3f;
float knobX = centerX + (float)(radius * 0.6f *
Math.Cos(currentAngle * Math.PI / 180));
float knobY = centerY + (float)(radius * 0.6f *
Math.Sin(currentAngle * Math.PI / 180));
// 绘制旋转钮阴影
using (var shadowBrush = new SolidBrush(Color.FromArgb(30, 0, 0, 0)))
{
g.FillEllipse(shadowBrush, knobX - knobRadius + 2,
knobY - knobRadius + 2,
knobRadius * 2, knobRadius * 2);
}
// 绘制金属质感旋转钮
using (var knobPath = new GraphicsPath())
{
knobPath.AddEllipse(knobX - knobRadius,
knobY - knobRadius,
knobRadius * 2, knobRadius * 2);
using (var knobBrush = new PathGradientBrush(knobPath))
{
knobBrush.CenterColor = Color.White;
knobBrush.SurroundColors = new[] {
Color.FromArgb(230, 230, 230) };
g.FillPath(knobBrush, knobPath);
}
}
// 添加高光效果
using (var highlightBrush = new PathGradientBrush(new PointF[] {
new PointF(knobX - knobRadius * 0.5f,
knobY - knobRadius * 0.5f),
new PointF(knobX + knobRadius * 0.5f,
knobY - knobRadius * 0.5f),
new PointF(knobX,
knobY + knobRadius * 0.5f)
}))
{
highlightBrush.CenterColor = Color.FromArgb(50, 255, 255, 255);
highlightBrush.SurroundColors = new[] {
Color.Transparent };
g.FillPath(highlightBrush,
new GraphicsPath());
}
// 绘制指示线
using (var indicatorPen = new Pen(Color.FromArgb(100, 0, 0, 0), 2f))
{
float lineLength = knobRadius * 0.8f;
float endX = knobX + (float)(lineLength
* Math.Cos(currentAngle * Math.PI / 180));
float endY = knobY + (float)(lineLength
* Math.Sin(currentAngle * Math.PI / 180));
g.DrawLine(indicatorPen, knobX, knobY, endX, endY);
}
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
IsOn = !IsOn;
}
protected virtual void OnValueChanged(EventArgs e)
{
ValueChanged?.Invoke(this, e);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
animationTimer?.Dispose();
}
base.Dispose(disposing);
}
}
}
自定义选项
控件提供多个可自定义属性:
IsOn:控制/获取开关状态
EnableShadow:控制阴影效果
ShadowDepth:设置阴影深度
ShadowOpacity:设置阴影透明度
ShadowColor:设置阴影颜色
ShadowBlur:设置阴影模糊程度
总结
RotatingSwitchButton 是一个融合了现代 UI 设计理念与经典开关外观的自定义控件。
通过精心设计的视觉效果和流畅的动画,它为 Windows Forms 应用程序提供了一个既美观又实用的用户界面元素。
该控件的实现不仅展示了在 .NET 环境下创建高质量自定义控件的方法,还详细介绍了如何运用 GDI+ 绘图技术来实现各种视觉效果。其主要特点包括:
明确的开关状态显示:通过颜色变化和位置移动,清晰指示开关状态,确保用户能够直观地理解当前的操作结果。
渐变与光泽:采用渐变色和光泽效果,营造出逼真的金属质感,提升整体视觉档次。
阴影效果:支持可自定义的控件阴影,增加深度感和立体感,使控件在界面上更加突出。
环形刻度:精细的环形刻度显示增强了控件的专业性和精确度,适用于需要高精度操作的场景。
平滑动画:开关状态切换时,控件执行平滑的旋转动画,提供流畅的视觉过渡效果,提升了用户体验。
RotatingSwitchButton 特别适合以下应用场景:
需要明确开关状态显示的场景:如设备控制面板、设置界面等,确保用户可以一目了然地了解当前状态。
追求精致视觉效果的专业软件界面:适用于高端应用和专业工具,提供一流的视觉体验。
模拟物理设备控制面板的应用程序:如工业控制系统、智能家居界面等,增强真实感和交互性。
通过合理运用渐变、阴影和动画等效果,RotatingSwitchButton 成功地将实用性和美观性结合在一起,为 Windows Forms 应用程序开发提供了一个优秀的 UI 控件范例。
最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。
也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!
作者:技术老小子
出处:mp.weixin.qq.com/s/7D6CyGvm3rACjWW6i64iBg
声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!