WPF 图表控件之曲线绘制与移动
目的:绘制简单轻量级的曲线视图
二、实现效果:
1,绘制标准基准线
2,可拖动
三、用到控件
1,Canvas
2,Ellipse
XAML代码:
<Canvas Background="#232323" Grid.Row="1" x:Name="MainCanvas" SizeChanged="LiveChar_SizeChanged"
Width="600" Height="300" MouseMove="MainCanvas_MouseMove"
ClipToBounds="True" MouseLeftButtonDown="MainCanvas_MouseLeftButtonDown"
MouseLeftButtonUp="MainCanvas_MouseLeftButtonUp"/>
CS代码:
1,定义变量
/// <summary>
/// 画板宽度
/// </summary>
public double BoardWidth { get; set; }
/// <summary>
/// 画板高度
/// </summary>
public double BoardHeight { get; set; }
/// <summary>
/// 平行(横向)边距(画图区域距离左右两边长度)
/// </summary>
public double HorizontalMargin { get; set; }
/// <summary>
/// 垂直(纵向)边距(画图区域距离左右两边长度)
/// </summary>
public double VerticalMargin { get; set; }
/// <summary>
/// 水平刻度间距像素
/// </summary>
public double horizontalBetween { get; set; }
/// <summary>
/// 垂直刻度间距像素
/// </summary>
public double verticalBetween { get; set; }
/// <summary>
/// 图表区域宽度
/// </summary>
public double ChartWidth;
/// <summary>
/// 图表区域高度
/// </summary>
public double ChartHeight;
/// <summary>
/// <summary>
/// 坐标点数据源
/// </summary>
public PointCollection DataSourse;
/// <summary>
/// 画图区域起点
/// </summary>
public Point StartPostion;
/// <summary>
/// 画图区域终点
/// </summary>
public Point EndPostion;
/// <summary>
/// x轴最大值
/// </summary>
public double MaxX { get; set; }
/// <summary>
/// y轴最大值
/// </summary>
public double MaxY { get; set; }
/// <summary>
/// x轴最小值
/// </summary>
public double MinX { get; set; }
/// <summary>
/// y轴最小值
/// </summary>
public double MinY { get; set; }
double MapLocationX = 0;
double MapLocationY = 0;
TextBox moushPonit;
Point pBefore = new Point();//鼠标点击前坐标
Point eBefore = new Point();//圆移动前坐标
bool isMove = false;//是否需要移
Path path2 = null;
2,定义类型
public enum DrawType
{
//Line
L,
//Horizontal line
H,
//Vertical line
V,
//Cubic Bezier curve
C,
//Quadratic Bezier curve
Q,
//Smooth cubic Bezier curve
S,
//smooth quadratic Bezier curve
T
} public class SpeedOrPower
{ public double SpeedNum { get; set; }
public double PowerNum { get; set; }
}
3,方法
private PointCollection GetCollection()
{
PointCollection myPointCollection = new PointCollection()
{
new Point(0,50),
new Point(10,50),
new Point(20,50),
new Point(100,100)
}; return myPointCollection;
}
private void LiveChar_SizeChanged(object sender, SizeChangedEventArgs e)
{
Refresh();
} private void Refresh()
{
InitCanvas(); //获取y最大值
if (MaxY < 0.0001)
{
MaxY = 100;
}
//MinY = DataSourse.Min(m => m.Y); if (MaxX < 0.0001)
{
MaxX = 100;
}
//MinX = DataSourse.Min(m => m.X);
if (Math.Abs(MaxX) < 0.000001 || Math.Abs(MaxY) < 0.000001)
{
return;
}
DrawAxis();
DrawXAxisScale();
DrawYAxisScale();
DrawPolyLine();
}
List<Point> Points = new List<Point>(); /// <summary>
/// 初始化计算点的相对坐标
/// </summary>
private void DrawPolyLine()
{ foreach (var item in DataSourse)
{ Point point = GetRealPoint(item);
Points.Add(point);
} PaintLine(Points);
PaintEllipse(Points);
}
/// <summary>
/// 绘制控制点
/// </summary>
/// <param name="points"></param>
private void PaintEllipse(List<Point> points)
{
int i = 0;
foreach (var item in points)
{
Ellipse ellipse = new Ellipse();
ellipse.Fill = Brushes.Yellow;
ellipse.Width = 5;
ellipse.Height = 5;
ellipse.Tag = i;
MainCanvas.Children.Add(ellipse);
Canvas.SetTop(ellipse, item.Y-(ellipse.Width/2));
Canvas.SetLeft(ellipse, item.X - (ellipse.Height / 2));
i++;
} } /// <summary>
/// h绘制曲线
/// </summary>
/// <param name="points"></param>
private void PaintLine(List<Point> points, DrawType drawType = DrawType.C)
{
StringBuilder data = new StringBuilder("M");
switch (drawType)
{
case DrawType.L:
data.AppendFormat("{0},{1} L", points[0].X, points[0].Y);
break;
case DrawType.H:
data.AppendFormat("{0},{1} H", points[0].X, points[0].Y);
break;
case DrawType.V:
data.AppendFormat("{0},{1} V", points[0].X, points[0].Y);
break;
case DrawType.C:
data.AppendFormat("{0},{1} C", points[0].X, points[0].Y);
break;
case DrawType.Q:
data.AppendFormat("{0},{1} Q", points[0].X, points[0].Y);
break;
case DrawType.S:
data.AppendFormat("{0},{1} S", points[0].X, points[0].Y);
break;
case DrawType.T:
data.AppendFormat("{0},{1} T", points[0].X, points[0].Y);
break;
default:
break;
} if (path2 != null)
{ for (int i = 1; i < points.Count; i++)
{
Point pre;
Point next;
if (i == 1)
{
var CurrentPoint = GetSpeedOrPower(points[i]);
var GetCurrentPoint= GetSpeedOrPower(points[i - 1]);
if (CurrentPoint.SpeedNum- GetCurrentPoint.SpeedNum < 10)
{
pre = new Point((points[i - 1].X + points[i].X) / 2, points[i ].Y); //控制点
next = new Point(points[i].X, points[i].Y); //控制点
}
else
{
pre = new Point((points[i - 1].X + points[i].X) / 2, points[i-1].Y); //控制点
next = new Point((points[i - 1].X + points[i].X) / 2, points[i-1].Y); //控制点
} }
else
{
pre = new Point((points[i - 1].X + points[i].X) / 2, points[i - 1].Y); //控制点
next = new Point((points[i - 1].X + points[i].X) / 2, points[i].Y); //控制点
}
//Point pre = new Point((points[i - 1].X + points[i].X) / 2, points[i - 1].Y); //控制点
//Point next = new Point((points[i - 1].X + points[i].X) / 2, points[i - 1].Y); //控制点 data.AppendFormat(" {0},{1} {2},{3} {4},{5}", pre.X, pre.Y, next.X, next.Y, points[i].X, points[i].Y);
}
path2.Data = Geometry.Parse(data.ToString());
}
else
{ // data.AppendFormat("{0},{1} C", points[0].X, points[0].Y);
for (int i = 1; i < points.Count; i++)
{
Point pre = new Point((points[i - 1].X + points[i].X) / 2, points[i - 1].Y); //控制点
Point next = new Point((points[i - 1].X + points[i].X) / 2, points[i].Y); //控制点
data.AppendFormat(" {0},{1} {2},{3} {4},{5}", pre.X, pre.Y, next.X, next.Y, points[i].X, points[i].Y);
} path2 = new Path { Stroke = Brushes.White, StrokeThickness = 2, Data = Geometry.Parse(data.ToString()) }; this.MainCanvas.Children.Add(path2);
} }
/// <summary>
/// 查询相对坐标
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
private Point GetRealPoint(Point point)
{
var realX = StartPostion.X + (point.X - MinX) * ChartWidth / (MaxX - MinX) + MapLocationX;
var realY = StartPostion.Y + (MaxY - point.Y) * ChartHeight / (MaxY - MinY) + MapLocationY;
return new Point(realX, realY);
}
/// <summary>
/// 绘制Y刻度
/// </summary>
private void DrawYAxisScale()
{
if (MinY > MaxY) return;
if (verticalBetween < 0.0001)
verticalBetween = (MaxY - MinY) / 10;
for (var i = MinY; i <= MaxY + 0.01; i += verticalBetween)
{
var y = EndPostion.Y - i * ChartHeight / (MaxY - MinY) + MapLocationY;
var marker = new Line
{
X1 = StartPostion.X - 5,
Y1 = y,
X2 = StartPostion.X,
Y2 = y,
Stroke = Brushes.Red
};
MainCanvas.Children.Add(marker); //绘制Y轴网格
var gridLine = new Line
{
X1 = StartPostion.X,
Y1 = y,
X2 = EndPostion.X,
Y2 = y,
StrokeThickness = 0.5,
Stroke = new SolidColorBrush(Colors.AliceBlue)
};
MainCanvas.Children.Add(gridLine); string text = i.ToString(CultureInfo.InvariantCulture);
var MarkerText = new TextBlock
{
Text = text,
Width = 30,
Foreground = Brushes.Yellow,
FontSize = 10,
HorizontalAlignment = HorizontalAlignment.Right,
TextAlignment = TextAlignment.Right
};
MainCanvas.Children.Add(MarkerText);
Canvas.SetTop(MarkerText, y - 10);
Canvas.SetLeft(MarkerText, 00); }
} /// <summary>
/// 绘制X刻度
/// </summary>
private void DrawXAxisScale()
{
if (MinX >= MaxX) return;
if (horizontalBetween < 0.0001)
horizontalBetween = (MaxX - MinX) / 10; for (var i = MinX; i <= MaxX + 0.01; i += horizontalBetween)
{
var x = StartPostion.X + i * ChartWidth / (MaxX - MinX) + MapLocationX; ///绘制X轴刻度
var marker = new Line
{
X1 = x,
Y1 = EndPostion.Y,
X2 = x,
Y2 = EndPostion.Y + 4,
Stroke = Brushes.Red
};
MainCanvas.Children.Add(marker); //绘制X轴网格
var gridLine = new Line
{
X1 = x,
Y1 = StartPostion.Y,
X2 = x,
Y2 = EndPostion.Y,
StrokeThickness = 0.5,
Stroke = new SolidColorBrush(Colors.AliceBlue)
};
MainCanvas.Children.Add(gridLine); //绘制X轴字符
var text = i.ToString(CultureInfo.InvariantCulture); var markText = new TextBlock
{
Text = text,
Width = 130,
Foreground = Brushes.Yellow,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Stretch,
TextAlignment = TextAlignment.Left,
FontSize = 10
};
MainCanvas.Children.Add(markText);
Canvas.SetTop(markText, EndPostion.Y + 5);
Canvas.SetLeft(markText, x - 5); }
} /// <summary>
/// 绘制线条
/// </summary>
private void DrawAxis()
{
var xaxis = new Line
{
X1 = StartPostion.X,
Y1 = EndPostion.Y,
X2 = EndPostion.X,
Y2 = EndPostion.Y,
Stroke = new SolidColorBrush(Colors.Black) };
MainCanvas.Children.Add(xaxis); var XaxisTop = new Line
{
X1 = StartPostion.Y,
Y1 = StartPostion.Y,
X2 = EndPostion.X,
Y2 = StartPostion.Y,
Stroke = new SolidColorBrush(Colors.Black)
};
MainCanvas.Children.Add(XaxisTop);
var yaxis = new Line
{
X1 = StartPostion.X,
Y1 = StartPostion.Y,
X2 = StartPostion.X,
Y2 = EndPostion.Y,
Stroke = new SolidColorBrush(Colors.Black)
};
MainCanvas.Children.Add(yaxis); var YaxisBottom = new Line
{
X1 = EndPostion.X,
Y1 = EndPostion.Y,
X2 = EndPostion.X,
Y2 = StartPostion.Y,
Stroke = new SolidColorBrush(Colors.Black)
};
MainCanvas.Children.Add(YaxisBottom); } /// <summary>
/// 初始化
/// </summary>
private void InitCanvas()
{
MainCanvas.Children.Clear(); BoardWidth = MainCanvas.ActualWidth - SystemParameters.VerticalScrollBarWidth;
BoardHeight = MainCanvas.ActualHeight - SystemParameters.HorizontalScrollBarHeight;
HorizontalMargin = 40;
VerticalMargin = 40; ChartWidth = BoardWidth - 2 * HorizontalMargin;//画图区域宽度
ChartHeight = BoardHeight - 2 * VerticalMargin; //画图区域高度 StartPostion = new Point(HorizontalMargin, VerticalMargin);
EndPostion = new Point(BoardWidth - HorizontalMargin, BoardHeight - VerticalMargin); moushPonit = new TextBox
{
Background = new SolidColorBrush(Colors.Transparent),
Height = 20,
Width = 200,
Foreground = Brushes.White,
BorderThickness = new Thickness(0),
VerticalAlignment = VerticalAlignment.Top
};
MainCanvas.Children.Add(moushPonit);
Canvas.SetTop(moushPonit, 0);
Canvas.SetLeft(moushPonit, 0);
}
/// <summary>
/// 查询当前速度与功率的值
/// </summary>
/// <param name="Point"></param>
/// <returns></returns>
public SpeedOrPower GetSpeedOrPower(Point Point)
{
double num2 = 100 - (Point.Y - VerticalMargin) / (EndPostion.Y - VerticalMargin) * 100;
double num3 = (Point.X - HorizontalMargin) / (EndPostion.X - HorizontalMargin) * 100;
double SpeedNum = Math.Round(num3, 1);
double PowerNum = Math.Round(num2, 1); return new SpeedOrPower() { SpeedNum=SpeedNum,PowerNum=PowerNum};
} /// <summary>
/// 鼠标移动时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MainCanvas_MouseMove(object sender, MouseEventArgs e)
{
Point currentMousePosition = e.GetPosition((UIElement)sender); var CurrentPoint = GetSpeedOrPower(currentMousePosition);
if (e.OriginalSource != null && e.OriginalSource.GetType() == typeof(Ellipse) && isMove)
{ Ellipse el = (Ellipse)e.OriginalSource;
Point p = e.GetPosition(null);//获取鼠标移动中的坐标
int index = Convert.ToInt32(el.Tag);
if (CurrentPoint.SpeedNum < 0 || CurrentPoint.PowerNum < 0)
isMove = false;
if (currentMousePosition.X > EndPostion.X || currentMousePosition.X < StartPostion.X)
isMove = false;
if (eBefore.Y + (p.Y - pBefore.Y) > EndPostion.Y || eBefore.Y + (p.Y - pBefore.Y) < StartPostion.Y)
isMove = false; if (index == 0)
{
var Getobj = GetSpeedOrPower(Points[index + 1]);
if(CurrentPoint.PowerNum>Getobj.PowerNum)
{
isMove = false; return;
}
Canvas.SetLeft(el, Points[index].X - (el.Width / 2));
Canvas.SetTop(el, eBefore.Y + (p.Y - pBefore.Y));
Points[index] = new Point(Points[index].X, currentMousePosition.Y);
PaintLine(Points);
}
else if (index == Points.Count() - 1)
{ var Getobj = GetSpeedOrPower(Points[index -1]);
if (CurrentPoint.PowerNum < Getobj.PowerNum)
{
isMove = false; return;
}
Canvas.SetLeft(el, Points[index].X - (el.Width / 2));
Canvas.SetTop(el, eBefore.Y + (p.Y - pBefore.Y));
Points[index] = new Point(Points[index].X, currentMousePosition.Y);
PaintLine(Points);
}
else
{
if (currentMousePosition.X > Points[index + 1].X) { isMove = false; return; }
if (currentMousePosition.Y < Points[index + 1].Y) { isMove = false; return; }
if (currentMousePosition.X < Points[index - 1].X) { isMove = false; return; }
if (currentMousePosition.Y > Points[index - 1].Y) { isMove = false; return; } Canvas.SetLeft(el, currentMousePosition.X-(el.Width/2));
Canvas.SetTop(el, currentMousePosition.Y - (el.Height / 2));
Points[index] = currentMousePosition;
PaintLine(Points);
} } if (CurrentPoint.SpeedNum < 0)
{
moushPonit.Visibility = Visibility.Collapsed;
CurrentPoint.SpeedNum = 0;
return;
}
if (CurrentPoint.SpeedNum > 100)
{
moushPonit.Visibility = Visibility.Collapsed;
return;
}
if (CurrentPoint.PowerNum < 0)
{
moushPonit.Visibility = Visibility.Collapsed;
CurrentPoint.PowerNum = 0;
return;
}
if (CurrentPoint.PowerNum > 100)
{
moushPonit.Visibility = Visibility.Collapsed;
return;
}
moushPonit.Visibility = Visibility.Visible;
moushPonit.Text = $"(Speed={CurrentPoint.SpeedNum}%:Power={CurrentPoint.PowerNum}%)";
Canvas.SetTop(moushPonit, currentMousePosition.Y - 20);
Canvas.SetLeft(moushPonit, currentMousePosition.X); } /// <summary>
/// 鼠标按下时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MainCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource.GetType() == typeof(Ellipse))
{
this.pBefore = e.GetPosition(null);//获取点击前鼠标坐标
Ellipse el = (Ellipse)e.OriginalSource;
el.Width = el.Width + 5;
el.Height = el.Height + 5;
this.eBefore = new Point(Canvas.GetLeft(el), Canvas.GetTop(el));//获取点击前圆的坐标
isMove = true;//开始移动了
el.CaptureMouse();//鼠标捕获此圆
}
}
/// <summary>
/// 鼠标释放时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MainCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource.GetType() == typeof(Ellipse))
{
Ellipse el = (Ellipse)e.OriginalSource;
int index = Convert.ToInt32(el.Tag);
if (el.Width >= 10)
{
el.Width = el.Width - 5;
el.Height = el.Height - 5;
} Canvas.SetLeft(el, Points[index].X - (el.Width / 2));
Canvas.SetTop(el, Points[index].Y - (el.Height / 2));
isMove = false;//结束移动了
el.ReleaseMouseCapture();//鼠标释放此圆 }
}
实现效果:

github:
https://github.com/zt199510/ChartingTest
WPF 图表控件之曲线绘制与移动的更多相关文章
- Visifire For WPF 图表控件 如何免费
可能用WPF生成过图表的开发人员都知道,WPF虽然本身的绘图能力强大,但如果每种图表都自己去实现一次的话可能工作量就大了, 尤其是在开发时间比较紧的情况下.这时候有必要借助一种专业的图表工具. Vis ...
- 一款开源免费的WPF图表控件ModernuiCharts
一款简洁好看的Chart控件 支持WPF.silverlight.Windows8 ,基本够用,主要是开源免费的.(商业控件ComponentOne for WPF要4w多呢) This proj ...
- WPF Visifire 图表控件
Visifire WPF 图表控件 破解 可能用WPF生成过图表的开发人员都知道,WPF虽然本身的绘图能力强大,但如果每种图表都自己去实现一次的话可能工作量就大了, 尤其是在开发时间比较紧的情况下.这 ...
- WPF 曲线图表控件(自制)(二)
原文:WPF 曲线图表控件(自制)(二) 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/koloumi/article/details/775218 ...
- WPF 曲线图表控件(自制)(一)
原文:WPF 曲线图表控件(自制)(一) 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/koloumi/article/details/775092 ...
- 【WPF】 OxyPlot图表控件学习
最近在学习OxyPlot图表控件,一些基本的学习心得,在这里记录一下,方便以后进行查找. 一.引用 OxyPlot控件可以直接在VS的 " Nuget " 里面下载 选择: ...
- WPF 截图控件之绘制方框与椭圆(四) 「仿微信」
前言 接着上周写的截图控件继续更新 绘制方框与椭圆. 1.WPF实现截屏「仿微信」 2.WPF 实现截屏控件之移动(二)「仿微信」 3.WPF 截图控件之伸缩(三) 「仿微信」 正文 有开发者在B站反 ...
- WPF 截图控件之绘制箭头(五)「仿微信」
前言 接着上周写的截图控件继续更新 绘制箭头. 1.WPF实现截屏「仿微信」 2.WPF 实现截屏控件之移动(二)「仿微信」 3.WPF 截图控件之伸缩(三) 「仿微信」 4.WPF 截图控件之绘制方 ...
- C# WPF DevExpress 图表控件之柱状图
说明:DevExpress版本是17.1.VS是2015. XAML: <!--#region 图表控件--> <dxc:ChartControl x:Name="char ...
随机推荐
- gRPC四种模式、认证和授权实战演示,必赞~~~
前言 上一篇对gRPC进行简单介绍,并通过示例体验了一下开发过程.接下来说说实际开发常用功能,如:gRPC的四种模式.gRPC集成JWT做认证和授权等. 正文 1. gRPC四种模式服务 以下案例演示 ...
- Nginx:Nginx日志切割方法
Nginx的日志文件是没有切割(rotate)功能的,但是我们可以写一个脚本来自动切割日志文件. 首先我们要注意两点: 1.切割的日志文件是不重名的,所以需要我们自定义名称,一般就是时间日期做文件名. ...
- vsftpd配置文件详解(转)
vsftpd配置文件详解 1.默认配置: 1>允许匿名用户和本地用户登陆. anonymous_enable=YES local_enable=YES 2> ...
- MySQL索引类型总结和使用技巧以及注意事项 (转)
在数据库表中,对字段建立索引可以大大提高查询速度.假如我们创建了一个 mytable表: 代码如下: CREATE TABLE mytable( ID INT NOT NULL, us ...
- BUU mrctf shit
吐槽:去年没写出的题,现在终于可以上手了,昂哥nb 动调发现直接卡着不动了,怀疑是反调试,果然有好几处反调试 这里选择就不先nop了,先让程序跑起来,然后attach,在输入函数下面下个断点,atta ...
- python 异常获取方法
import sys #第1:print(6/0) #直接运行该命令,出现异常,程序终止 #异常提示: '''Traceback (most recent call last): File " ...
- [刘阳Java]_InternalResourceViewResolver视图解析器_第6讲
SpringMVC在处理器方法中通常返回的是逻辑视图,如何定位到真正的页面,就需要通过视图解析器 InternalResourceViewResolver是SpringMVC中比较常用视图解析器. 网 ...
- JDK安装与环境搭建.
卸载JDK 1.删除Java安装目录 2.删除Java Home 3.删除path下Java的目录 4.打开cmd命令输入java-version 出现''不是内部或外部命令,也不是可运行的程序 或批 ...
- python基础之多线程的操作
# 多线程实例# 例1.单线程from time import sleep,ctimedef task1(taskName): for i in range(2): print("正在执行 ...
- 《面试八股文》之 Redis 16卷
微信公众号:moon聊技术 关注选择" 星标 ", 重磅干货,第一 时间送达! [如果你觉得文章对你有帮助,欢迎关注,在看,点赞,转发] 大家好,我是 moon. redis 作为 ...