前言
在winform开发过程中,有时候会需要开发曲线图相关功能,本文将分享一个自己开发的一个自定义曲线功能,如坐标轴的动态改变,以及曲线点的拖动和曲线动态绘制功能。
正文
首先准备一张精准的ps坐标图,由于我们要实现坐标轴可动态变化,所以这张ps图不含具体坐标轴的值。
然后在winform窗口的panel里添加这张图作为背景,并且在坐标轴的位置添加lable用于显示坐标轴数值。
接下来是坐标点,我用的是12个坐标点,方法是添加12个pictureBox,然后给每一个pictureBox添加红色的背景,并缩到最小大小;同时添加12个textbox用于显示每个坐标点的坐标(相对于坐标轴)效果如图。
曲线图功能核心是要实现点与点之间的连线,这个通过DrawLine方法即可实现,但是要实现坐标点的拖动,就必须是实时更新绘制,我的解决方法是:
1、每次有点在拖动的时候清空已经绘制的线条。
2、坐标点移动到新的位置以后,重新绘制界面。
3、在界面绘制事件里加入所有曲线点之间线条绘制的功能。
首先实现坐标点拖动功能,由于12个坐标的拖动功能都一样,给12个pictureBox添加相同的事件,贴上坐标点的拖动功能代码:
Point downPoint;//记录鼠标按下时的坐标(相对于panel)
Point centerPoint = new Point(5, 505);//坐标轴原点(相对于panel)
List<TextBox> textXList = new List<TextBox>();//横坐标集合
List<TextBox> textYList = new List<TextBox>();//纵坐标集合
int iMaxX=1000;//横纵最大值
int iMaxY=500;//纵轴最大值
private void PictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
PictureBox current = (PictureBox)sender;
current.Location = new Point(current.Location.X + e.Location.X - downPoint.X, current.Location.Y + e.Location.Y - downPoint.Y);
}
}
private void PictureBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
downPoint = e.Location;
}
}
private void PictureBox_Move(object sender, EventArgs e)//限制每个点的移动位置
{
PictureBox current = (PictureBox)sender;
#region 限制位置,根据自己的panel分辨率和坐标轴分辨率来定,我的panel分辨率为1020*520,坐标轴分辨率为1000*500,坐标轴居中显示
int index = int.Parse(current.Tag.ToString()) - 1;//每个pictureBox都有一个tag值,从1到12,用于区分对象
int x, y;
if (index == 0)
{
x = current.Location.X <= panel1.Controls[index + 1].Location.X ? current.Location.X : panel1.Controls[index + 1].Location.X;
x = x >= 5 ? x : 5;
}
else if (index == 11)
{
x = current.Location.X >= panel1.Controls[index - 1].Location.X ? current.Location.X : panel1.Controls[index - 1].Location.X;
x = x <= 1005 ? x : 1005;
}
else
{
x = current.Location.X;
if (current.Location.X <= panel1.Controls[index - 1].Location.X)
{
x = panel1.Controls[index - 1].Location.X;
}
if (current.Location.X >= panel1.Controls[index + 1].Location.X)
{
x = panel1.Controls[index + 1].Location.X;
}
}
y = current.Location.Y;
if (current.Location.Y < 5)
{
y = 5;
}
if (current.Location.Y > 505)
{
y = 505;
}
current.Location = new Point(x, y);
#endregion
string x1 = (Math.Round((float)(current.Location.X- centerPoint.X)* iMaxX / 1000 ,1)).ToString();
string y1 = (Math.Round((float)(centerPoint.Y - current.Location.Y )* iMaxY / 500 ,1)).ToString();
for (int i = 0; i < 12; i++)//记录所有点的横纵坐标
{
if (current.Tag == textXList[i].Tag)
{
textXList[i].Text = x1;
}
if (current.Tag == textYList[i].Tag)
{
textYList[i].Text = y1;
panel1.Invalidate();//用于重绘界面
return;
}
}
}
然后就是绘制坐标点之间的连线:
private void Panel1_Paint(object sender, PaintEventArgs e)
{
for (int i = 1; i < 12; i++)
{
Point current = new Point(panel1.Controls[i].Location.X + panel1.Controls[i].Width / 2, panel1.Controls[i].Location.Y + panel1.Controls[i].Height / 2);//当前点
Point last = new Point(panel1.Controls[i - 1].Location.X + panel1.Controls[i - 1].Width / 2, panel1.Controls[i - 1].Location.Y + panel1.Controls[i - 1].Height / 2);//上一个点
e.Graphics.DrawLine(new Pen(Color.Red, 2), current, last);//当前点和上一个点连线
}
}
最后显示坐标轴数值:
public void ChangXValue()//卸载窗体的load事件里
{
label1.Text = (iMaxX / 10).ToString();
label2.Text = (2 * iMaxX / 10).ToString();
label3.Text = (3 * iMaxX / 10).ToString();
label4.Text = (4 * iMaxX / 10).ToString();
label5.Text = (5 * iMaxX / 10).ToString();
label6.Text = (6 * iMaxX / 10).ToString();
label7.Text = (7 * iMaxX / 10).ToString();
label8.Text = (8 * iMaxX / 10).ToString();
label9.Text = (9 * iMaxX / 10).ToString();
label10.Text = (10 * formShip.curveClass.iMaxX / 10).ToString();
label11.Text = (iMaxY / 10).ToString();
label12.Text = (2 * iMaxY / 10).ToString();
label13.Text = (3 * iMaxY / 10).ToString();
label14.Text = (4 * iMaxY / 10).ToString();
label15.Text = (5 * iMaxY / 10).ToString();
label16.Text = (6 * iMaxY / 10).ToString();
label17.Text = (7 * iMaxY / 10).ToString();
label18.Text = (8 * iMaxY / 10).ToString();
label19.Text = (9 * iMaxY / 10).ToString();
label20.Text = (10 *iMaxY / 10).ToString();
}
此时,已经完成曲线点的绘制以及点的拖动功能,效果图如下:
最后,如果想实现通过直接配置textbox来实现点的坐标变化,可以加上如下代码:
private void X2_LostFocus(object sender, EventArgs e)//失去焦点事件
{
TextBox tempTxt = (TextBox)sender;
int index = int.Parse(tempTxt.Tag.ToString()) - 1;
int x = (int)(float.Parse(tempTxt.Text) * 1000 / formShip.curveClass.iMaxX + centerPoint.X);
panel1.Controls[index].Location = new Point(x, panel1.Controls[index].Location.Y);
}
private void Y12_LostFocus(object sender, EventArgs e)//失去焦点事件
{
TextBox tempTxt = (TextBox)sender;
int index = int.Parse(tempTxt.Tag.ToString()) - 1;
int y = (int)(centerPoint.Y - float.Parse(tempTxt.Text) * 500 / formShip.curveClass.iMaxY);
panel1.Controls[index].Location = new Point(panel1.Controls[index].Location.X, y);
}
private void X2_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar != 8 && !Char.IsDigit(e.KeyChar))//退格键和数字
{
e.Handled = true;
}
if (e.KeyChar == 13 )//回车键
{
pictureBox1.Focus();
}
}
自此,功能全部完成。
以上仅供参考,如有更好的方案,可以提供交流一下。
总结
本方案通过组合基础控件和GDI+绘图实现了完整的动态曲线系统,关键创新点包括:
1、分离坐标计算与界面渲染逻辑
2、采用事件驱动实现实时交互
3、提供双重输入方式(拖拽/文本输入)
4、实现自适应分辨率的边界控制
实际应用中可根据需求扩展数据绑定、动画效果或导出功能,建议结合双缓冲技术优化绘制性能。
关键词
WinForm、曲线图、动态绘制、坐标轴、GDI+、控件拖拽、实时重绘、数据可视化
最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。
也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!
作者:橘猫不太胖
出处:cnblogs.com/cat-not-fat/p/13717866.html
声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!