LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

WinForms自定义控件:精美的旋转开关按钮实现详解

admin
2025年2月12日 20:44 本文热度 120

在Windows Forms应用程序开发中,自定义控件是提升用户界面体验的重要手段。本文将详细介绍一个具有精美视觉效果的旋转开关按钮控件(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(76217100);
        private readonly Color offColor = Color.FromArgb(2555948);
        private readonly Color knobColor = Color.White;

        private readonly Color gradientStart = Color.FromArgb(240240240);
        private readonly Color gradientEnd = Color.FromArgb(200200200);

        // 阴影相关属性  
        private bool enableShadow = true;
        private float shadowDepth = 5f;
        private float shadowOpacity = 0.3f;
        private Color shadowColor = Color.FromArgb(76000);
        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(100100);
            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(1f0.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.8f0.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(100255255255), 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(30255255255);
                    highlightBrush.SurroundColors = new[] { Color.FromArgb(0255255255) };
                    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.8f90);   // 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(100130130130), 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,
                        130130130), 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(30000)))
            {
                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(230230230) };
                    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(50255255255);
                highlightBrush.SurroundColors = new[] { Color.Transparent };
                g.FillPath(highlightBrush, new GraphicsPath());
            }

            // 绘制指示线  
            using (var indicatorPen = new Pen(Color.FromArgb(100000), 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+绘图技术实现各种视觉效果。

该控件特别适合用于:

  • 需要明确开关状态显示的场景

  • 追求精致视觉效果的专业软件界面

  • 模拟物理设备控制面板的应用程序

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


阅读原文:原文链接


该文章在 2025/2/13 8:44:37 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved