C#自定义曲线绘图面板

一、实现功能

1、显示面板绘制。

2、拖动面板,X轴、Y轴都可以拖动。

3、显示面板缩放,放大或者缩小。

4、鼠标在面板中对应的XY轴数值。

5、自动生成的数据数组,曲线显示。

6、鼠标是否在曲线上检测。

二、界面

拖动面板

鼠标在曲线上识别

三、部分功能代码实现

1、图形面板

        /// <summary>
        /// 生成坐标系图片面板
        /// </summary>
        /// <returns></returns>
        public Bitmap DrawImage()
        {
            ImageBoardInit();

            return bitMap;
        }

        /// <summary>
        /// 创建绘图面板-显示坐标系
        /// </summary>
        private void ImageBoardInit()
        {
            //1、绘制X、Y坐标轴
            if ((int)height != 0 && (int)width != 0)  //做这个判断是因为在最小化的情况下width=0。
            {
                bitMap = new Bitmap((int)width, (int)height);  //根据给定的高度和宽度创建一个位图图像
            }            
            graphics = Graphics.FromImage(bitMap);    //从指定的 objBitmap 对象创建 objGraphics 对象 (即在objBitmap对象中画图)

            //根据给定颜色(LightGray)填充图像的矩形区域 (背景)
            graphics.DrawRectangle(new Pen(boardColor, 1), 0, 0, width - 1, height - 1);    //画边框
            graphics.FillRectangle(new SolidBrush(backColor), 1, 1, width - 2, height - 2); //填充边框

            //画X轴,注意图像的原始X轴和Y轴计算是以左上角为原点,向右和向下计算的
            xAxisPoint1.X = xSpace + xSliceBegin;
            xAxisPoint1.Y = (height / 2) + ySliceBegin;    //
            xAxisPoint2.X = width;
            xAxisPoint2.Y = xAxisPoint1.Y;
            graphics.DrawLine(new Pen(new SolidBrush(axisColor), 2), xAxisPoint1, xAxisPoint2);

            //画Y轴
            yAxisPoint1.X = xSpace + xSliceBegin;
            yAxisPoint1.Y = height ;
            yAxisPoint2.X = xSpace + xSliceBegin;
            yAxisPoint2.Y = 0 ;
            graphics.DrawLine(new Pen(new SolidBrush(axisColor), 2), yAxisPoint1, yAxisPoint2);

            //2、面板标题
            //graphics.DrawString("曲线编辑器", new Font("宋体", fontSize), new SolidBrush(Color.Blue), new PointF(width / 2, ySpace / 2));

            //3、画X轴上刻度、刻度说明            
            xSlice = (width - xSpace ) / xSliceCount;
            ySlice = (height / 2) / (ySliceCount / 2);
            
            int tempCountX1 = (int)(- xSliceBegin / xSlice);
            int tempCountY1 = (int)Math.Abs((ySliceBegin / ySlice));
            tempCountX = tempCountX1 + xSliceCount;
            tempCountY = tempCountY1 + ySliceCount/2;
            //画网格虚线
            Pen penDashed = new Pen(new SolidBrush(Color.Black));
            penDashed.DashStyle = DashStyle.Dash;
            for (int i = 1; i < tempCountX + 1; i++)
            {
                //X轴刻度虚线
                graphics.DrawLine(penDashed, new PointF(i * xSlice + xSpace + xSliceBegin, 0 ), new PointF(i * xSlice + xSpace + xSliceBegin, height));
                //X轴刻度值标识文字
                string xStr = (i * xSliceValue).ToString();
                int nStrLength = xStr.Length;
                graphics.DrawString(xStr, new Font("宋体", fontSize), new SolidBrush(Color.Black), new PointF(i * xSlice + xSpace - fontSize * nStrLength + xSliceBegin, height / 2 + fontSize + ySliceBegin));
            }
            //Y轴虚线、刻度文字-正半轴
            for (int i = 0; i < tempCountY + 1; i++)
            {
                graphics.DrawLine(penDashed, new PointF(xSpace + xSliceBegin, height / 2 - i * ySlice + ySliceBegin), new PointF(width, height / 2 - i * ySlice + ySliceBegin));
                //Y轴刻度值标识文字
                string yStr = (i * ySliceValue).ToString();
                int nStrLength = yStr.Length;
                if(i>0)
                    graphics.DrawString(yStr, new Font("宋体", fontSize), new SolidBrush(Color.Black), new PointF(xSpace - fontSize * nStrLength + xSliceBegin, height / 2 - i * ySlice + ySliceBegin));
                
            }
            //Y轴虚线、刻度文字-负半轴
            for (int i = 1; i < tempCountY  + 1; i++)
            {
                graphics.DrawLine(penDashed, new PointF(xSpace + xSliceBegin, height / 2 + i * ySlice + ySliceBegin), new PointF(width, height / 2 + i * ySlice + ySliceBegin));
                //Y轴刻度值标识文字
                string yStr = (-i * ySliceValue).ToString();
                int nStrLength = yStr.Length;
                graphics.DrawString(yStr, new Font("宋体", fontSize), new SolidBrush(Color.Black), new PointF(xSpace - fontSize * nStrLength + xSliceBegin, height / 2 + i * ySlice + ySliceBegin));
            }
            //4、原点刻度说明
            graphics.DrawString("0", new Font("宋体", fontSize + 2), new SolidBrush(Color.Black), new PointF(xSpace - fontSize * 2 + xSliceBegin, height / 2 + ySliceBegin));
            
        }

2、数据点曲线显示

        /// <summary>
        /// 绘制XY轴曲线
        /// </summary>
        /// <param name="xDatas">X轴数据</param>
        /// <param name="yDatas">Y轴数据</param>
        /// <param name="splineColor">曲线颜色</param>
        /// <param name="tension">曲线系数:0.0f-1.0f,默认0.5f,0.0f是直线</param>
        /// <param name="isPointFill">是否标出点</param>
        public void DrawXY(float[] xDatas,float[] yDatas,Color splineColor,float tension,bool isPointFill)
        {
            int xLength = xDatas.Length;
            int yLength = yDatas.Length;
            if(xLength!=yLength)
            {
                graphics.DrawString("X和Y数据长度不相等",new Font("宋体", fontSize + 5), new SolidBrush(Color.Blue), new Point((int)xSpace, (int)(height / 2)));
                return;
            }
            PointF[] splinePoints = new PointF[xDatas.Length];
            float xSlicePos = 0, ySlicePos = 0; //转换后的点在Image面板上像素点位置
            Brush brushPoint = new SolidBrush(splineColor);        //数据点画刷
            for(int i=0;i<xDatas.Length;i++)
            {
                xSlicePos = xSliceBegin + xSpace + xDatas[i] * xSlice / xSliceValue;
                ySlicePos = ySliceBegin + height / 2 - yDatas[i] * ySlice / ySliceValue;
                splinePoints[i] = new PointF(xSlicePos, ySlicePos);
                if (isPointFill)
                {
                    //graphics.FillEllipse(brushPoint, xSlicePos - 4, ySlicePos - 4, 8, 8); //原点
                    graphics.FillRectangle(brushPoint, xSlicePos - 4, ySlicePos - 4, 8, 8); //矩形
                }                
            }
            graphics.DrawCurve(new Pen(splineColor, 2.0f), splinePoints, tension);
            //graphics.Dispose();
        }

3、鼠标当前所在面板位置对应的坐标系统XY值

        int ex = 0, ey = 0;
        int mouseToValueX = 0;
        float mouseToValueY = 0;   //鼠标位置当前值
        float fMouseToValueX = 0;
        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            ex = e.X;
            ey = e.Y;
            //1、显示鼠标在坐标系内X、Y对应的值
            fMouseToValueX =cureDraw.MousePosToValue_X(ex);
            mouseToValueX = (int)Math.Round(cureDraw.MousePosToValue_X(ex));
            //mouseToValueY = (float)Math.Round(cureDraw.MousePosToValue_Y(ey));
            mouseToValueY = cureDraw.MousePosToValue_Y(ey);
            if (mouseToValueX<0)
            {
                mouseToValueX = 0;
            }
            if (mouseToValueX >= 0 && ex < pictureBox1.Width - 60)
            {                
                labMousePos.Text = "X:" + mouseToValueX.ToString() + ";" + "Y:" + mouseToValueY.ToString();
                labMousePos.Location = new Point(ex + 50, ey + 40);
            }
            if (mouseToValueX >= 0 && ex > pictureBox1.Width - 60)
            {                
                labMousePos.Text = "X:" + mouseToValueX.ToString() + ";" + "Y:" + mouseToValueY.ToString();
                labMousePos.Location = new Point(ex - 50, ey + 40);
            }


        }

4、鼠标是否在曲线上检测

        bool isMouseOn = false;
        private void timer1_Tick(object sender, EventArgs e)
        {
            //bool isMouseOn = cureDraw.IsMouseOnPointCheck(xDatas, yDatas, ex, ey);
            //bool isMouseOn = cureDraw.IsMouseOnPointCheck(xDatasExpand, yDatasExpand, ex, ey);

            float tempX = 0, tempY = 0;
            int n = 0;
            bool b1 = false, b2 = false;
            foreach (var xData in xDatasExpand)
            {
                if(Math.Abs(xData-fMouseToValueX)<0.02)
                {
                    tempX = xData;
                }
            }
            for (int i = 0; i < xDatasExpand.Length;i++ )
            {
                if(xDatasExpand[i]==tempX)
                {
                    n = i;
                }
            }
            if (Math.Abs(yDatasExpand[n] - mouseToValueY)<2.5)
            {
                isMouseOn = true;
            }
            else
            {
                isMouseOn = false;
            }

            txtValue1.Text = xDatasExpand[n].ToString();    //Y
            txtValue2.Text = fMouseToValueX.ToString();

            txtValue3.Text = yDatasExpand[n].ToString();    //X
            txtValue4.Text = mouseToValueY.ToString();

            txtValue5.Text = n.ToString();

            if (isMouseOn)
            {
                labMousePos.ForeColor = Color.Red;                
            }
            else
            {
                labMousePos.ForeColor = Color.Black;
            }
        }

四、、工程下载链接

https://download.csdn.net/download/panjinliang066333/89775386

相关推荐
飞人博尔特的摄影师16 分钟前
WPF绑定Bind方法合集,实时更新
visualstudio·c#·wpf·xaml·maui·xamarin·技巧
冷眼Σ(-᷅_-᷄๑)2 小时前
WPF异步UI交互功能的实现方法
c#·wpf
CV大法好6 小时前
刘铁猛C#入门 027 抽象和开闭原则
开发语言·c#
离歌漠7 小时前
C#调用C++ DLL方法之P/Invoke
c++·c#·p/invoke
公子小六8 小时前
在WPF程序中实现PropertyGrid功能
windows·microsoft·c#·.net·wpf
dangoxiba8 小时前
[Unity Demo]从零开始制作空洞骑士Hollow Knight第二十集:制作专门渲染HUD的相机HUD Camera和画布HUD Canvas
游戏·unity·c#·游戏引擎·playmaker
小吴同学·16 小时前
.NET6 WebApi第1讲:VSCode开发.NET项目、区别.NET5框架【两个框架启动流程详解】
c#·.netcore·.net core
bluefox197921 小时前
使用 Oracle.DataAccess.Client 驱动 和 OleDB 调用Oracle 函数的区别
开发语言·c#
鲤籽鲲1 天前
C# MethodTimer.Fody 使用详解
开发语言·c#·mfc