在Windows Forms应用程序开发中,自定义控件是提升用户界面体验的重要手段。本文将详细介绍一个具有精美视觉效果的旋转开关按钮控件(RotatingSwitchButton)的实现。这个控件融合了现代UI设计元素,包括平滑动画、阴影效果、渐变和金属质感等特性。
控件概述 RotatingSwitchButton是一个模拟物理旋转开关的自定义控件,具有以下主要特征:
旋转动画:开关状态切换时具有平滑的旋转动画效果
视觉反馈:通过颜色变化和位置移动清晰指示开关状态
阴影效果:可自定义的控件阴影,提供深度感
金属质感:运用渐变和光泽效果营造金属质感
精细刻度:环形刻度显示,增强专业性
让我生成一个这个控件的示意图:
核心功能实现 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); } } }
自定义选项 控件提供多个可自定义属性:
- EnableShadow:控制阴影效果 ShadowDepth:设置阴影深度
ShadowOpacity:设置阴影透明度
ShadowColor:设置阴影颜色
ShadowBlur:设置阴影模糊程度
总结 RotatingSwitchButton是一个结合了现代UI设计理念和经典开关外观的自定义控件。通过精心设计的视觉效果和流畅的动画,为Windows Forms应用程序提供了一个既美观又实用的用户界面元素。该控件的实现展示了如何在.NET环境下创建高质量的自定义控件,以及如何运用GDI+绘图技术实现各种视觉效果。
该控件特别适合用于:
需要明确开关状态显示的场景
追求精致视觉效果的专业软件界面
模拟物理设备控制面板的应用程序
通过合理运用渐变、阴影和动画等效果,这个控件成功地将实用性和美观性结合在一起,为Windows Forms应用程序开发提供了一个优秀的UI控件范例。
阅读原文:原文链接
该文章在 2025/2/13 8:44:37 编辑过