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

二、实现效果:

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. 安卓手机改造服务器——基本环境配置(CentOS7 arm32)

    安装好CentOS系统之后,我们需要对环境进行一些基本的配置,让Linux更好用 写在前面 注意:本文章是针对arm32的CentOS7进行配置的,其他系统或不同架构不要尝试. 配置yum镜像源 1. ...

  2. 用java实现斐波那契数列

    斐波那契数列源于数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入的计算问题.假设某种兔子兔子,出生第一个月变成大兔子,大兔子再过一个月能生下一对小兔子,且一年 ...

  3. Spring boot中相关的注解

    一.相关类中使用的注解 @RestController:REST风格的控制器 @RequestMapping:配置URL和方法之间的映射 @SpringBootApplication:应用程序入口类 ...

  4. ROS2学习之旅(20)——创建一个动作消息

    本文用来自定义一个动作消息类型. 以下命令用来创建一个工作空间并建立一个功能包: mkdir -p action_ws/src cd action_ws/src ros2 pkg create act ...

  5. nginx的基本使用

    下载: https://nginx.org/en/download.html  Window下安装: 下载好了之后直接解压就行了.(解压目录切记别含有中文) 启动:1️⃣直接双击nginx.exe2️ ...

  6. 基于YARP实现的FastGithub

    前言 最近开源的两个项目,先是FastGithub,旨在解决访问github抽风的问题.然后开发HttpMouse项目,基于yarp的http公网反向代理到内网的服务端与客户端库,在开发HttpMou ...

  7. Django基础-004 上下文管理器&中间件&前端公共代码复用

    一.上下文管理器 在views中重复使用的代码,可以在上下文管理器中实现 上下文管理器的处理流程如下: 1.先走完views里面的代码,将结果返回给前端 2.然后再将上下文的结果返回给前端 3.上下文 ...

  8. Linux- RPM与yum软件包安装

    Linux安装及管理程序一.Linux应用程序基础1)应用程序与系统命令的关系2)典型应用程序的目录结构3)常见的软件包封装类型二.RPM包管理工具① RPM软件包管理器Red-Hat Package ...

  9. DNS配置【正向解析】

    DNS配置.正向解析                一.BIND域名服务基础                  1)DNS的定义                  2)域名结构             ...

  10. C语言:赋值语句

    赋值语句 1.赋值号:= 2.赋值号具有方向性,只能将右边的常数 变量的值  表达式的值赋值给左边的变量 3.赋值号左边只能是变量,不能是表达式.常数.符号常量.常量 如下列是非法的语句:a+b=3; ...