winform下刻度尺控件的实现

文章介绍了一个C#的自定义用户控件Ruler,用于创建刻度尺效果。该控件支持设置刻度间隔、开始和结束数值、背景色、线条颜色、字体等属性。在OnPaint方法中绘制刻度和数值,提供了灵活性和可配置性。代码中包含了错误检查和缩放比例调整等功能,适用于WindowsForms应用程序的界面设计。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.LinkLabel;

namespace CarQueueProgram
{
    /// <summary>
    /// 刻度尺控件
    /// </summary>
    public partial class Ruler : UserControl
    {
        private const int _monitorDPI = 96;

        public Ruler()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 刻度枚举
        /// </summary>
        public DegreeIntervalEnum DegreeInterval { get; set; } = DegreeIntervalEnum.PointOne;

        /// <summary>
        /// 开始数字
        /// </summary>
        public int StartNum { get; set; } = 0;

        /// <summary>
        /// 结束数字
        /// </summary>
        public int EndNum { get; set; } = 10;

        /// <summary>
        /// 指定数字的倍数, 依据数字的倍数来显示相关数字. 如: 此属性设置为3, 刻度尺上显示的数字就是0, 3, 6, 9...
        /// </summary>
        public int MultipleNum { get; set; } = 1;

        /// <summary>
        /// 背景颜色
        /// </summary>
        public override Color BackColor { get; set; } = Color.Transparent;

        /// <summary>
        /// 设置线条颜色
        /// </summary>
        public override Color ForeColor { get; set; } = Color.Black;

        /// <summary>
        /// 字体设置
        /// </summary>
        public override Font Font { get; set; } = new Font("Arial", 9);

        /// <summary>
        /// 刻度是否向上
        /// </summary>
        public bool IsDegreeUp { get; set; } = false;

        /// <summary>
        /// 缩放比例
        /// </summary>
        public float Scaling { get; set; } = 1f;

        /// <summary>
        /// 设置刻度尺到左侧的距离
        /// </summary>
        public int OffsetLeft { get; set; } = 6;

        /// <summary>
        /// 刻度尺长度
        /// </summary>
        public int RulerLength
        {
            get
            {
                if (EndNum <= StartNum)
                {
                    throw new ArgumentOutOfRangeException(nameof(EndNum), $"{nameof(EndNum)}数值不可以小于等于{nameof(StartNum)}数值!");
                }
                return EndNum - StartNum;
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (EndNum <= StartNum)
            {
                throw new ArgumentOutOfRangeException(nameof(EndNum), $"{nameof(EndNum)}数值不可以小于等于{nameof(StartNum)}数值!");
            }
            
            var g = e.Graphics;
            using (var pen = new Pen(ForeColor))
            {
                //如果刻度向上则取客户端设置的高度数值, 如果刻度向下, 需要减去设置的字体高度和2(该数字是刻度线和刻度数值的间距)
                int rectH = Height - (IsDegreeUp ? 0 : (Font.Height + 2));
                //绘制矩形
                Rectangle rectangle = new Rectangle(0, 0, Width, Height);
                //填充矩形颜色(通过BackColor设置)
                Brush brush = new SolidBrush(BackColor);
                g.FillRectangle(brush, rectangle);
                //绘制一条直线
                float lineY = rectH / 2f;
                g.DrawLine(pen, new PointF(0, lineY), new PointF(Width, lineY));

                //绘制刻度
                var degreesCount = GetDegreesCount();
                var degreeInterval = GetDegreeInterval();
                decimal num = StartNum;
                for (int i = 0; i < degreesCount; i++)
                {
                    //刻度线的x坐标(依据索引值(i)不同而变化)
                    float x = GetXCoordinate(i, Scaling, OffsetLeft);

                    PointF startPoint;
                    PointF endPoint;
                    //如果是需要显示的倍数(举例: MultipleNum为2, 那么需要显示的数值为0, 2, 4, 6...)
                    if (num % MultipleNum == 0M)
                    {
                        startPoint = GetStartPoint(x, 1f, lineY);
                        endPoint = GetEndPoint(x, 1f, lineY);

                        string numStr = ((int)num).ToString();
                        //获取字符串大小信息
                        SizeF strSize = g.MeasureString(numStr, Font);
                        //字符串宽度
                        float strWidth = strSize.Width;
                        //字体宽度
                        float fontWidth = strWidth / numStr.Length;
                        //获取字符串的节点位置
                        PointF strPoint = GetStrPoint(x, fontWidth, numStr.Length, 1f, lineY);
                        g.DrawString(numStr, Font, new SolidBrush(ForeColor), strPoint);
                    }
                    //如果刻度线的索引值是5的倍数(比如0-1之间的0.5刻度线)
                    else if (num % 5M == 0M)
                    {
                        startPoint = GetStartPoint(x, 0.5f, lineY);
                        endPoint = GetEndPoint(x, 0.5f, lineY);
                    }
                    else
                    {
                        startPoint = GetStartPoint(x, 0.25f, lineY);
                        endPoint = GetEndPoint(x, 0.25f, lineY);
                    }
                    g.DrawLine(pen, startPoint, endPoint);

                    num += degreeInterval;
                }
            }
        }

        /// <summary>
        /// 获取刻度尺总数
        /// </summary>
        /// <returns></returns>
        private int GetDegreesCount()
        {
            int num = EndNum - StartNum;
            int degreesCount;
            if (DegreeInterval == DegreeIntervalEnum.One)
            {
                degreesCount = (num / 1) + 1;
            }
            else
            {
                degreesCount = (int)(num / 0.1M) + 1;
            }
            return degreesCount;
        }

        /// <summary>
        /// 获取每刻度代表多少数值
        /// </summary>
        /// <returns></returns>
        private decimal GetDegreeInterval()
        {
            decimal degreeInterval;
            if (DegreeInterval == DegreeIntervalEnum.One)
            {
                degreeInterval = 1M;
            }
            else
            {
                degreeInterval = 0.1M;
            }
            return degreeInterval;
        }

        /// <summary>
        /// 获取刻度线的起始节点(为画刻度线准备)
        /// </summary>
        /// <param name="x">刻度线的x坐标</param>
        /// <param name="multiple">
        ///     刻度线长度的倍数, 
        ///     如果是需要显示数值的刻度线, 倍数是1;
        ///     如果刻度线的索引值是5的倍数, 倍数是0.5;
        ///     如果是普通的刻度线, 倍数是0.25;
        /// </param>
        /// <param name="lineY">刻度尺直线条的y坐标</param>
        /// <returns></returns>
        private PointF GetStartPoint(float x, float multiple, float lineY)
        {
            PointF startPoint;
            if (IsDegreeUp)
            {
                startPoint = new PointF(x, (1 - multiple) * lineY);
            }
            else
            {
                startPoint = new PointF(x, lineY);
            }
            return startPoint;
        }

        /// <summary>
        /// 获取刻度线的结束节点(为画刻度线准备)
        /// </summary>
        /// <param name="x">刻度线的x坐标</param>
        /// <param name="multiple">
        ///     刻度线长度的倍数, 
        ///     如果是需要显示数值的刻度线, 倍数是1;
        ///     如果刻度线的索引值是5的倍数, 倍数是0.5;
        ///     如果是普通的刻度线, 倍数是0.25;
        /// </param>
        /// <param name="lineY">刻度尺直线条的y坐标</param>
        /// <returns></returns>
        private PointF GetEndPoint(float x, float multiple, float lineY)
        {
            PointF endPoint;
            if (IsDegreeUp)
            {
                endPoint = new PointF(x, lineY);
            }
            else
            {
                endPoint = new PointF(x, (1 + multiple) * lineY);
            }
            return endPoint;
        }

        /// <summary>
        /// 获取字符串的节点位置
        /// </summary>
        /// <param name="x">刻度线的x坐标</param>
        /// <param name="fontWidth">字体宽度</param>
        /// <param name="strLen">字符串长度</param>
        /// <param name="multiple">
        ///     刻度线长度的倍数, 
        ///     如果是需要显示数值的刻度线, 倍数是1;
        ///     如果刻度线的索引值是5的倍数, 倍数是0.5;
        ///     如果是普通的刻度线, 倍数是0.25;
        /// </param>
        /// <param name="lineY">刻度尺直线条的y坐标</param>
        /// <returns></returns>
        private PointF GetStrPoint(float x, float fontWidth, int strLen, float multiple, float lineY)
        {
            //字符串的x坐标是刻度线坐标值减去半个长度的字符串乘以字体宽度的积
            float xVal = x - fontWidth * (strLen * 1.0f / 2);
            float yVal;
            if (IsDegreeUp)
            {
                yVal = lineY + 2;
            }
            else
            {
                yVal = (1 + multiple) * lineY + 2;
            }
            var point = new PointF(xVal, yVal);
            return point;
        }

        /// <summary>
        /// 获取x轴坐标
        /// </summary>
        /// <param name="degree">刻度</param>
        /// <returns></returns>
        public static float GetXCoordinate(float degree, float scaling, int offset)
        {
            float x = _monitorDPI / 25.4f * degree * scaling + offset;
            return x;
        }

        /// <summary>
        /// 刻度枚举
        /// </summary>
        public enum DegreeIntervalEnum
        { 
            /// <summary>
            /// 0.1
            /// </summary>
            PointOne,
            /// <summary>
            /// 1
            /// </summary>
            One
        }
    }
}

一定程度上参考了“C#窗体加标尺的两种方式及显现效果_c#标尺_冰的学习时光的博客-CSDN博客”这个博客。对此博主表示一定的感谢,也希望我的实现能够帮助到大家,同时也欢迎大家提出改进的建议

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小鹏云友

您的鼓励是我创作的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值