目的:绘制简单轻量级的曲线视图

二、实现效果:

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 图表控件之曲线绘制与移动的更多相关文章

  1. Visifire For WPF 图表控件 如何免费

    可能用WPF生成过图表的开发人员都知道,WPF虽然本身的绘图能力强大,但如果每种图表都自己去实现一次的话可能工作量就大了, 尤其是在开发时间比较紧的情况下.这时候有必要借助一种专业的图表工具. Vis ...

  2. 一款开源免费的WPF图表控件ModernuiCharts

    一款简洁好看的Chart控件  支持WPF.silverlight.Windows8  ,基本够用,主要是开源免费的.(商业控件ComponentOne for WPF要4w多呢) This proj ...

  3. WPF Visifire 图表控件

    Visifire WPF 图表控件 破解 可能用WPF生成过图表的开发人员都知道,WPF虽然本身的绘图能力强大,但如果每种图表都自己去实现一次的话可能工作量就大了, 尤其是在开发时间比较紧的情况下.这 ...

  4. WPF 曲线图表控件(自制)(二)

    原文:WPF 曲线图表控件(自制)(二) 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/koloumi/article/details/775218 ...

  5. WPF 曲线图表控件(自制)(一)

    原文:WPF 曲线图表控件(自制)(一) 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/koloumi/article/details/775092 ...

  6. 【WPF】 OxyPlot图表控件学习

    最近在学习OxyPlot图表控件,一些基本的学习心得,在这里记录一下,方便以后进行查找.   一.引用 OxyPlot控件可以直接在VS的 " Nuget " 里面下载   选择: ...

  7. WPF 截图控件之绘制方框与椭圆(四) 「仿微信」

    前言 接着上周写的截图控件继续更新 绘制方框与椭圆. 1.WPF实现截屏「仿微信」 2.WPF 实现截屏控件之移动(二)「仿微信」 3.WPF 截图控件之伸缩(三) 「仿微信」 正文 有开发者在B站反 ...

  8. WPF 截图控件之绘制箭头(五)「仿微信」

    前言 接着上周写的截图控件继续更新 绘制箭头. 1.WPF实现截屏「仿微信」 2.WPF 实现截屏控件之移动(二)「仿微信」 3.WPF 截图控件之伸缩(三) 「仿微信」 4.WPF 截图控件之绘制方 ...

  9. C# WPF DevExpress 图表控件之柱状图

    说明:DevExpress版本是17.1.VS是2015. XAML: <!--#region 图表控件--> <dxc:ChartControl x:Name="char ...

随机推荐

  1. POJ 3984 迷宫(BFS)

    入门BFS,第一次做,部分借鉴了大牛的 #include <iostream> #include <cstdio> #include <queue> using n ...

  2. 【重学Java】Set集合

    Set集合 Set集合概述和特点[应用] 无序不可重复 没有索引,不能使用普通for循环遍历.可以使用迭代器或者增强foreach语句遍历 TreeSet集合 TreeSet集合概述和特点[应用] 无 ...

  3. 1.3.1、datetime时间-Before、After、Between

    server: port: 8080 spring: application: name: gateway cloud: gateway: routes: - id: guo-system1 uri: ...

  4. NetCore微服务简单流程审批数据库设计及后台服务开发

    1.数据库设计 -- ---------------------------- -- Table structure for TBase_Flow -- ----------------------- ...

  5. asp.net mvc中的安全性

    1.重复提交攻击:通过Bind特性指定要绑定和不绑定的值. 2.Cookie盗窃:阻止脚本对站点中Cookie的访问,webconfig文件中添加<HttpCookies domain=&quo ...

  6. 深入理解C++11 阅读笔记

    二 保证稳定性和兼容性保持与C99兼容 预定义宏 C99语言标准增加的一些预定义宏,C++11同样增加了对这些宏的支持 __func__预定义标识符 功能是返回所在函数的名字,在C++11中,标准甚至 ...

  7. WIN10 报错及解决方法

    你的电脑/设备需要修复,无法加载操作系统,原因是关键系统驱动程序丢失或包含错误 \windows\system32\drivers\zklvv.sys 错误代码:0XC000007b 解决方法: 用U ...

  8. iframe跨域访问出现的cookie问题,提供两种解决方案

    最近在java项目对接时出现的一个问题.A系统嵌入B系统页面时,使用iframe去嵌入B系统页面丢失sessionid,导致B系统认为是未进行登录的请求,从而跳转到了B系统登录页. 解决方法查看此博客 ...

  9. ES6 对象定义简写及常用的扩展方法

    1.ES6 对象定义简写 es6提供了对象定义里的属性,方法简写方式: 假如属性和变量名一样,可以省略,包括定义对象方法function也可以省略 <script type="text ...

  10. python之数据驱动ddt操作(方法四)

    from ddt import ddt,data,unpackfrom selenium import webdriverfrom selenium.webdriver.common.by impor ...