【C#】学习之图形绘制:圆形卡尺

  • 摘要
    • 本文描述了如何在Form窗体程序上的控件绘制圆形带卡尺图形。支持调整位置、大小、卡尺数量、卡尺宽度、卡尺长度。

  • 前言

    • 实现思路:

      • 通过提取卡尺的基本属性,通过窗体界面控件调整参数值,实现卡尺工具的动态绘制。
      • 继承Panel控件设置DoubleBuffered = true减少绘制时闪烁,实现卡尺显示。
      • 通过滑动块调整卡尺的主要参数。
    • 主要参数:

      • Center 圆心
      • Radius 圆半径
      • CaliperCount 圆半径
      • CaliperLength 卡尺长度
      • CaliperWidth 卡尺宽度
      • InnerLengthRatio 内卡尺长度比例
    • 主要方法

      • 绘制圆和卡尺
        • public void Draw(Graphics g)
      • 绘制十字准心(大小3x3,线宽一个像素)
        • private void DrawCrosshair(Graphics g)
      • 绘制卡尺
        • private void DrawCalipers(Graphics g)
      • 绘制卡尺矩形区域
        • private void DrawCaliperRectangle(Graphics g, PointF center, float angle)

  • 运行环境

    • 操作系统:Windows 11
    • 编程软件:Visual Stduio 2022

  • 预览

    • 运行效果

在这里插入图片描述


一、代码

  • MainForm 窗体代码

    • 主窗体添加四个参数调节控件(滑动块),实现动态调节卡尺参数:圆形半径,卡尺数量,卡尺宽度、卡尺高度。
    using CustomControls;
    using System.Drawing;
    using System.Windows.Forms;
    namespace CS学习之创建圆形卡尺工具
    {
        public partial class MainForm : WinFormBase
        {
            private CircleCaliper circleFinder;
            private Bitmap image;
            private bool isDrag = false;
            public MainForm()
            {
                InitializeComponent();
                this.CenterToParent();
                this.DoubleBuffered = true;
            }
            /// <summary>
            /// 窗体加载
            /// </summary>
            private void MainForm_Load(object sender, System.EventArgs e)
            {
                // 创建圆卡尺
                circleFinder = new CircleCaliper
                {
                    Center = new PointF(400, 300),
                    Radius = 150,
                    CaliperCount = 24,
                    CaliperLength = 30,
                    CaliperWidth = 10
                };
                canvas.BackColor = Color.Gray;
                canvas.Paint += Control_Paint;
                canvas.MouseDown += Control_MouseDown;
                canvas.MouseMove += Control_MouseMove;
                canvas.MouseUp += Control_MouseUp;
                UpdateControlShowValue();
                trackb_CallipersNumber.Scroll += trackb_CallipersNumber_Scroll;
                trackb_CallipersWidth.Scroll += trackb_CallipersWidth_Scroll;
                trackb_CallipersLength.Scroll += trackb_CallipersLength_Scroll;
                trackb_CircleRadios.Scroll += trackb_CircleRadio_Scroll;
            }
    
            #region 画布事件
            private void Control_Paint(object sender, PaintEventArgs e)
            {
                // 绘制圆查找器
                circleFinder.Draw(e.Graphics);
            }
            private void Control_MouseDown(object sender, MouseEventArgs e)
            {
                isDrag = true;
                canvas.Invalidate();
            }
            private void Control_MouseMove(object sender, MouseEventArgs e)
            {
                if (isDrag)
                {
                    circleFinder.Center = e.Location;
                }
                canvas.Invalidate();
            }
            private void Control_MouseUp(object sender, MouseEventArgs e)
            {
                isDrag = false;
                canvas.Invalidate();
            }
            #endregion
    
            #region 卡尺参数
            private void trackb_CallipersNumber_Scroll(object sender, System.EventArgs e)
            {
                circleFinder.CaliperCount = trackb_CallipersNumber.Value;
                UpdateControlShowValue();
                canvas.Invalidate();
            }
            private void trackb_CallipersWidth_Scroll(object sender, System.EventArgs e)
            {
                circleFinder.CaliperWidth = trackb_CallipersWidth.Value;
                UpdateControlShowValue();
                canvas.Invalidate();
            }
            private void trackb_CallipersLength_Scroll(object sender, System.EventArgs e)
            {
                circleFinder.CaliperLength = trackb_CallipersLength.Value;
                UpdateControlShowValue();
                canvas.Invalidate();
            }
            private void trackb_CircleRadio_Scroll(object sender, System.EventArgs e)
            {
                circleFinder.Radius = trackb_CircleRadios.Value;
                UpdateControlShowValue();
                canvas.Invalidate();
            }
            #endregion
    
            /// <summary>
            /// 值更新显示
            /// </summary>
            private void UpdateControlShowValue()
            {
                lab_CircleRadios.Text = $"圆半径:({trackb_CircleRadios.Value})";
                labNumber.Text = $"卡尺数量:({trackb_CallipersNumber.Value})";
                lab_CallipersWidth.Text = $"卡尺宽度:({trackb_CallipersWidth.Value})";
                lab_CallipersLength.Text = $"卡尺长度:({trackb_CallipersLength.Value})";
            }
        }
    }
    

  • MainForm设计器 代码

    • 窗体、控件的一些初始化参数(添加控件时自带)。
    namespace CS学习之创建圆形卡尺工具
    {
        partial class MainForm
        {
            /// <summary>
            /// 必需的设计器变量。
            /// </summary>
            private System.ComponentModel.IContainer components = null;
    
            /// <summary>
            /// 清理所有正在使用的资源。
            /// </summary>
            /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
    
            #region Windows 窗体设计器生成的代码
    
            /// <summary>
            /// 设计器支持所需的方法 - 不要修改
            /// 使用代码编辑器修改此方法的内容。
            /// </summary>
            private void InitializeComponent()
            {
                this.canvas = new CS学习之创建圆形卡尺工具.Canvas();
                this.panel1 = new System.Windows.Forms.Panel();
                this.lab_CircleRadios = new System.Windows.Forms.Label();
                this.trackb_CircleRadios = new System.Windows.Forms.TrackBar();
                this.lab_CallipersLength = new System.Windows.Forms.Label();
                this.trackb_CallipersLength = new System.Windows.Forms.TrackBar();
                this.lab_CallipersWidth = new System.Windows.Forms.Label();
                this.trackb_CallipersWidth = new System.Windows.Forms.TrackBar();
                this.labNumber = new System.Windows.Forms.Label();
                this.trackb_CallipersNumber = new System.Windows.Forms.TrackBar();
                this.panel1.SuspendLayout();
                ((System.ComponentModel.ISupportInitialize)(this.trackb_CircleRadios)).BeginInit();
                ((System.ComponentModel.ISupportInitialize)(this.trackb_CallipersLength)).BeginInit();
                ((System.ComponentModel.ISupportInitialize)(this.trackb_CallipersWidth)).BeginInit();
                ((System.ComponentModel.ISupportInitialize)(this.trackb_CallipersNumber)).BeginInit();
                this.SuspendLayout();
                // 
                // canvas
                // 
                this.canvas.BackColor = System.Drawing.Color.Gray;
                this.canvas.Dock = System.Windows.Forms.DockStyle.Fill;
                this.canvas.Location = new System.Drawing.Point(3, 40);
                this.canvas.Name = "canvas";
                this.canvas.Size = new System.Drawing.Size(1140, 727);
                this.canvas.TabIndex = 4;
                // 
                // panel1
                // 
                this.panel1.Controls.Add(this.lab_CircleRadios);
                this.panel1.Controls.Add(this.trackb_CircleRadios);
                this.panel1.Controls.Add(this.lab_CallipersLength);
                this.panel1.Controls.Add(this.trackb_CallipersLength);
                this.panel1.Controls.Add(this.lab_CallipersWidth);
                this.panel1.Controls.Add(this.trackb_CallipersWidth);
                this.panel1.Controls.Add(this.labNumber);
                this.panel1.Controls.Add(this.trackb_CallipersNumber);
                this.panel1.Dock = System.Windows.Forms.DockStyle.Left;
                this.panel1.Location = new System.Drawing.Point(3, 40);
                this.panel1.Name = "panel1";
                this.panel1.Size = new System.Drawing.Size(296, 727);
                this.panel1.TabIndex = 6;
                // 
                // lab_CircleRadios
                // 
                this.lab_CircleRadios.AutoSize = true;
                this.lab_CircleRadios.Location = new System.Drawing.Point(35, 51);
                this.lab_CircleRadios.Name = "lab_CircleRadios";
                this.lab_CircleRadios.Size = new System.Drawing.Size(62, 18);
                this.lab_CircleRadios.TabIndex = 7;
                this.lab_CircleRadios.Text = "圆半径";
                // 
                // trackb_CircleRadios
                // 
                this.trackb_CircleRadios.Location = new System.Drawing.Point(27, 93);
                this.trackb_CircleRadios.Maximum = 500;
                this.trackb_CircleRadios.Minimum = 5;
                this.trackb_CircleRadios.Name = "trackb_CircleRadios";
                this.trackb_CircleRadios.Size = new System.Drawing.Size(225, 69);
                this.trackb_CircleRadios.TabIndex = 6;
                this.trackb_CircleRadios.Value = 36;
                // 
                // lab_CallipersLength
                // 
                this.lab_CallipersLength.AutoSize = true;
                this.lab_CallipersLength.Location = new System.Drawing.Point(35, 460);
                this.lab_CallipersLength.Name = "lab_CallipersLength";
                this.lab_CallipersLength.Size = new System.Drawing.Size(80, 18);
                this.lab_CallipersLength.TabIndex = 5;
                this.lab_CallipersLength.Text = "卡尺长度";
                // 
                // trackb_CallipersLength
                // 
                this.trackb_CallipersLength.Location = new System.Drawing.Point(27, 502);
                this.trackb_CallipersLength.Maximum = 100;
                this.trackb_CallipersLength.Minimum = 10;
                this.trackb_CallipersLength.Name = "trackb_CallipersLength";
                this.trackb_CallipersLength.Size = new System.Drawing.Size(225, 69);
                this.trackb_CallipersLength.TabIndex = 4;
                this.trackb_CallipersLength.Value = 36;
                // 
                // lab_CallipersWidth
                // 
                this.lab_CallipersWidth.AutoSize = true;
                this.lab_CallipersWidth.Location = new System.Drawing.Point(35, 329);
                this.lab_CallipersWidth.Name = "lab_CallipersWidth";
                this.lab_CallipersWidth.Size = new System.Drawing.Size(80, 18);
                this.lab_CallipersWidth.TabIndex = 3;
                this.lab_CallipersWidth.Text = "卡尺宽度";
                // 
                // trackb_CallipersWidth
                // 
                this.trackb_CallipersWidth.Location = new System.Drawing.Point(27, 371);
                this.trackb_CallipersWidth.Maximum = 100;
                this.trackb_CallipersWidth.Minimum = 10;
                this.trackb_CallipersWidth.Name = "trackb_CallipersWidth";
                this.trackb_CallipersWidth.Size = new System.Drawing.Size(225, 69);
                this.trackb_CallipersWidth.TabIndex = 2;
                this.trackb_CallipersWidth.Value = 36;
                // 
                // labNumber
                // 
                this.labNumber.AutoSize = true;
                this.labNumber.Location = new System.Drawing.Point(35, 192);
                this.labNumber.Name = "labNumber";
                this.labNumber.Size = new System.Drawing.Size(80, 18);
                this.labNumber.TabIndex = 1;
                this.labNumber.Text = "卡尺数量";
                // 
                // trackb_CallipersNumber
                // 
                this.trackb_CallipersNumber.Location = new System.Drawing.Point(27, 234);
                this.trackb_CallipersNumber.Maximum = 100;
                this.trackb_CallipersNumber.Minimum = 10;
                this.trackb_CallipersNumber.Name = "trackb_CallipersNumber";
                this.trackb_CallipersNumber.Size = new System.Drawing.Size(225, 69);
                this.trackb_CallipersNumber.TabIndex = 0;
                this.trackb_CallipersNumber.Value = 36;
                // 
                // MainForm
                // 
                this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.ClientSize = new System.Drawing.Size(1146, 770);
                this.Controls.Add(this.panel1);
                this.Controls.Add(this.canvas);
                this.Name = "MainForm";
                this.Text = "MainForm";
                this.Load += new System.EventHandler(this.MainForm_Load);
                this.Controls.SetChildIndex(this.canvas, 0);
                this.Controls.SetChildIndex(this.panel1, 0);
                this.panel1.ResumeLayout(false);
                this.panel1.PerformLayout();
                ((System.ComponentModel.ISupportInitialize)(this.trackb_CircleRadios)).EndInit();
                ((System.ComponentModel.ISupportInitialize)(this.trackb_CallipersLength)).EndInit();
                ((System.ComponentModel.ISupportInitialize)(this.trackb_CallipersWidth)).EndInit();
                ((System.ComponentModel.ISupportInitialize)(this.trackb_CallipersNumber)).EndInit();
                this.ResumeLayout(false);
    
            }
    
            #endregion
            private Canvas canvas;
            private System.Windows.Forms.Panel panel1;
            private System.Windows.Forms.Label lab_CircleRadios;
            private System.Windows.Forms.TrackBar trackb_CircleRadios;
            private System.Windows.Forms.Label lab_CallipersLength;
            private System.Windows.Forms.TrackBar trackb_CallipersLength;
            private System.Windows.Forms.Label lab_CallipersWidth;
            private System.Windows.Forms.TrackBar trackb_CallipersWidth;
            private System.Windows.Forms.Label labNumber;
            private System.Windows.Forms.TrackBar trackb_CallipersNumber;
        }
    }
    
  • Canvas 画布代码

    • 创建一个控件作为画布,主要是需要启用双缓冲,防止绘制时闪烁。
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace CS学习之创建圆形卡尺工具
    {
        public class Canvas : Panel
        {
            public Canvas()
            {
                this.DoubleBuffered = true;
                this.BackColor = Color.Gray;
            }
        }
    }
    
  • (四)圆卡尺类代码

    • 创建圆卡尺工具的基本属性,以及一些绘制方法。绘制圆、卡尺、准心。
    using System;
    using System.Drawing;
    namespace CS学习之创建圆形卡尺工具
    {
        public class CircleCaliper
        {
            #region 属性
            /// <summary>
            /// 圆心
            /// </summary>
            public PointF Center { get; set; }
            /// <summary>
            /// 圆半径
            /// </summary>
            public float Radius { get; set; }
            /// <summary>
            /// 卡尺个数
            /// </summary>
            public int CaliperCount { get; set; } = 36;
            /// <summary>
            /// 卡尺长度
            /// </summary>
            public float CaliperLength { get; set; } = 20;
            /// <summary>
            /// 卡尺宽度
            /// </summary>
            public float CaliperWidth { get; set; } = 5;
            /// <summary>
            /// 内卡尺长度比例
            /// </summary>
            public float InnerLengthRatio { get; set; } = 0.5f;
            #endregion
    
            #region 绘制方法
            /// <summary>
            /// 绘制圆和卡尺
            /// </summary>
            public void Draw(Graphics g)
            {
                // 绘制拟合的圆
                using (Pen circlePen = new Pen(Color.Red, 2))
                {
                    g.DrawEllipse(circlePen, Center.X - Radius, Center.Y - Radius,
                                 Radius * 2, Radius * 2);
                }
                // 绘制圆形十字准心
                DrawCrosshair(g);
                // 绘制卡尺
                DrawCalipers(g);
            }
    
            /// <summary>
            /// 绘制十字准心,大小(3,3),线宽一个像素
            /// </summary>
            private void DrawCrosshair(Graphics g)
            {
                float size = 3f; // 十字准心大小
                using (Pen crossPen = new Pen(Color.Yellow, 1))
                {
                    // 绘制水平线+垂直线
                    g.DrawLine(crossPen, Center.X - size, Center.Y, Center.X + size, Center.Y);
                    g.DrawLine(crossPen, Center.X, Center.Y - size, Center.X, Center.Y + size);
                }
            }
    
            /// <summary>
            /// 绘制卡尺
            /// </summary>
            private void DrawCalipers(Graphics g)
            {
                using (Pen outerPen = new Pen(Color.Lime, 1))
                using (Pen innerPen = new Pen(Color.Cyan, 1))
                {
                    for (int i = 0; i < CaliperCount; i++)
                    {
                        float angle = (float)(2 * Math.PI * i / CaliperCount);
                        float cos = (float)Math.Cos(angle);
                        float sin = (float)Math.Sin(angle);
    
                        // 理论边缘点
                        PointF edgePoint = new PointF(
                            Center.X + Radius * cos,
                            Center.Y + Radius * sin);
    
                        // 外卡尺
                        PointF outerEnd = new PointF(
                            edgePoint.X + CaliperLength * cos,
                            edgePoint.Y + CaliperLength * sin);
    
                        g.DrawLine(outerPen, edgePoint, outerEnd);
    
                        // 内卡尺
                        PointF innerEnd = new PointF(
                               edgePoint.X - CaliperLength * InnerLengthRatio * cos,
                               edgePoint.Y - CaliperLength * InnerLengthRatio * sin);
                        g.DrawLine(innerPen, edgePoint, innerEnd);
    
                        // 绘制卡尺矩形区域
                        DrawCaliperRectangle(g, edgePoint, angle);
                    }
                }
            }
    
            /// <summary>
            /// 绘制卡尺矩形区域
            /// </summary>
            private void DrawCaliperRectangle(Graphics g, PointF center, float angle)
            {
                float perpAngle = angle + (float)(Math.PI / 2);
                float dx = (float)(CaliperWidth / 2 * Math.Cos(perpAngle));
                float dy = (float)(CaliperWidth / 2 * Math.Sin(perpAngle));
    
                // 卡尺外矩形
                PointF[] outerRect = new PointF[4];
                outerRect[0] = new PointF(center.X - dx, center.Y - dy);
                outerRect[1] = new PointF(center.X + dx, center.Y + dy);
                outerRect[2] = new PointF(center.X + dx + (float)(CaliperLength * Math.Cos(angle)),
                                      center.Y + dy + (float)(CaliperLength * Math.Sin(angle)));
                outerRect[3] = new PointF(center.X - dx + (float)(CaliperLength * Math.Cos(angle)),
                                      center.Y - dy + (float)(CaliperLength * Math.Sin(angle)));
    
                // 卡尺内矩形
                PointF[] innerRect = null;
                innerRect = new PointF[4];
                innerRect[0] = new PointF(center.X - dx, center.Y - dy);
                innerRect[1] = new PointF(center.X + dx, center.Y + dy);
                innerRect[2] = new PointF(center.X + dx - (float)(CaliperLength * InnerLengthRatio * Math.Cos(angle)),
                                      center.Y + dy - (float)(CaliperLength * InnerLengthRatio * Math.Sin(angle)));
                innerRect[3] = new PointF(center.X - dx - (float)(CaliperLength * InnerLengthRatio * Math.Cos(angle)),
                                      center.Y - dy - (float)(CaliperLength * InnerLengthRatio * Math.Sin(angle)));
    
                // 绘制外卡尺区域
                using (Brush outerBrush = new SolidBrush(Color.FromArgb(50, Color.Blue)))
                {
                    g.FillPolygon(outerBrush, outerRect);
                }
                using (Pen outerPen = new Pen(Color.Blue, 1))
                {
                    g.DrawPolygon(outerPen, outerRect);
                }
    
                // 绘制内卡尺区域
                if (innerRect != null)
                {
                    using (Brush innerBrush = new SolidBrush(Color.FromArgb(50, Color.Blue)))
                    {
                        g.FillPolygon(innerBrush, innerRect);
                    }
                    using (Pen innerPen = new Pen(Color.Blue, 1))
                    {
                        g.DrawPolygon(innerPen, innerRect);
                    }
                }
            }
            #endregion
        }
    }
    

  • 最后

    • 如果你觉得这篇文章对你有帮助,不妨点个赞再走呗!
    • 如有疑问,欢迎评论区留言讨论!
    • 也可以加入微信公众号 [编程笔记in] ,一起交流学习!
    • 项目地址gitee.com/incodenotes/cshape-demos
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程笔记in

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值