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

二、实现效果:

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. lms微服务的rpc通信框架

    RPC的概念 RPC 全称 Remote Procedure Call--远程过程调用.是为了解决远程调用服务的一种技术,使得调用者像调用本地服务一样方便透明.简单的说,RPC就是从一台机器(客户端) ...

  2. Java Collecion的常用方法

    import java.util.*; /** * 数组存储数据的特点: * ①一旦初始化,大小确定 * ②一旦定义,存储的类型确定且相同. * 数组存储的弊端: * ①大小无法改变. * ②方法较少 ...

  3. 1.3.5、通过Method匹配

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

  4. 两个有序数组合并为一个有序数组---python

    def merge(a, b): """ 合并2个有序数组,默认a,b都是从小到大的有序数组 """ # 1.临时变量 i, j = 0, ...

  5. 排序---python版

    冒泡排序: 比较相邻的元素.如果第一个比第二个大,就交换它们两个: 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数: 针对所有的元素重复以上的步骤,除了最 ...

  6. Linux常用基础命令(二)

    Linux常用基础命令 一.-ls--列表显示目录内容 二.-alias--设置别名 三.-du--统计目录及文件空间占用情况 四.-mkdir--创建新目录 五.-touch--创建空文件 六.-l ...

  7. Kubernetes全栈架构师(二进制高可用安装k8s集群扩展篇)--学习笔记

    目录 二进制Metrics&Dashboard安装 二进制高可用集群可用性验证 生产环境k8s集群关键性配置 Bootstrapping: Kubelet启动过程 Bootstrapping: ...

  8. C语言:带参数的宏与函数的区别

    带参数的宏和函数很相似,但有本质上的区别:宏展开仅仅是字符串的替换,不会对表达式进行计算:宏在编译之前就被处理掉了,它没有机会参与编译,也不会占用内存.而函数是一段可以重复使用的代码,会被编译,会给它 ...

  9. ThinkPHP3.2.3使用PHPExcel类操作excel导入读取excel

    方法一: 1. 下载PHPExcel并保存在如下位置: 2. 在控制器中引用 vendor("PHPExcel.PHPExcel"); $objReader = \PHPExcel ...

  10. 每天五分钟Go - 指针

    什么是指针 一个指向内存地址的变量,称为指针变量,指针是一个特殊的变量,他的值存储的是另一个值的内存地址 指针变量的声明 var var_name *type var_name 是指针变量的名称,ty ...