编辑
2025-02-26
C# 应用
00
请注意,本文编写于 69 天前,最后修改于 69 天前,其中某些信息可能已经过时。

目录

控件概述
核心功能实现
1. 状态管理与动画
2. 阴影效果
3. 视觉元素绘制
外环绘制
刻度线绘制
旋钮绘制
完整代码
自定义选项
总结

在Windows Forms应用程序开发中,自定义控件是提升用户界面体验的重要手段。本文将详细介绍一个具有精美视觉效果的旋转开关按钮控件(RotatingSwitchButton)的实现。这个控件融合了现代UI设计元素,包括平滑动画、阴影效果、渐变和金属质感等特性。

控件概述

RotatingSwitchButton是一个模拟物理旋转开关的自定义控件,具有以下主要特征:

  1. 旋转动画:开关状态切换时具有平滑的旋转动画效果
  2. 视觉反馈:通过颜色变化和位置移动清晰指示开关状态
  3. 阴影效果:可自定义的控件阴影,提供深度感
  4. 金属质感:运用渐变和光泽效果营造金属质感
  5. 精细刻度:环形刻度显示,增强专业性

让我生成一个这个控件的示意图:

核心功能实现

1. 状态管理与动画

控件使用布尔值isOn追踪当前状态,并通过currentAngle控制旋钮的旋转角度。动画效果通过Timer实现,使用缓动函数使动画更自然:

C#
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创建径向渐变,实现柔和的阴影效果:

C#
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. 视觉元素绘制

外环绘制

外环采用渐变效果和高光,创造金属质感:

C#
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, ...); } }

刻度线绘制

通过循环绘制不同长度的刻度线,增强专业感:

C#
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; // 主要刻度线 // 绘制刻度线... }

旋钮绘制

旋钮采用多层渲染,包括基础形状、阴影、高光和指示线:

C#
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应用程序中:

C#
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); } } }

image.png

自定义选项

控件提供多个可自定义属性:

  • IsOn:控制/获取开关状态
  • EnableShadow:控制阴影效果
  • ShadowDepth:设置阴影深度
  • ShadowOpacity:设置阴影透明度
  • ShadowColor:设置阴影颜色
  • ShadowBlur:设置阴影模糊程度

总结

RotatingSwitchButton是一个结合了现代UI设计理念和经典开关外观的自定义控件。通过精心设计的视觉效果和流畅的动画,为Windows Forms应用程序提供了一个既美观又实用的用户界面元素。该控件的实现展示了如何在.NET环境下创建高质量的自定义控件,以及如何运用GDI+绘图技术实现各种视觉效果。

该控件特别适合用于:

  • 需要明确开关状态显示的场景
  • 追求精致视觉效果的专业软件界面
  • 模拟物理设备控制面板的应用程序

通过合理运用渐变、阴影和动画等效果,这个控件成功地将实用性和美观性结合在一起,为Windows Forms应用程序开发提供了一个优秀的UI控件范例。

本文作者:rick

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!