热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WPF开发心率检测大数据曲线图的高性能实现方法

本文介绍了在WPF开发中实现心率检测大数据曲线图的高性能方法。作者尝试过使用Canvas和第三方开源库,但性能和功能都不理想。最终作者选择使用DrawingVisual对象,并结合局部显示的方式实现了自己想要的效果。文章详细介绍了实现思路和具体代码,对于不熟悉DrawingVisual的读者可以去微软官网了解更多细节。

一、前言

项目中涉及到了心率检测,而且数据量达到了百万级别,通过WPF实现大数据曲线图时,尝试过最基础的Canvas来实现,但是性能堪忧,而且全部画出来也不实际。同时也尝试过找第三方的开源库,但是因为曲线图涉及到很多细节功能,第三方的肯定也没法满足。没办法,只能自己实现,上网查找后发现DrawingVisual这个玩意可以实现高性能画图,同时再搭配局部显示,这样就能实现自己想要的效果。话不多说,今天把大致的实现思路写一下,就不直接把项目的源码贴出来,写个简单的Demo就好了。


二、正文

1、首先新建个项目,然后创建个自定义控件,命名为CurveChartDrawingVisual,然后让它继承FrameworkElement。因为要使用DrawingVisual对象的话,需要为它创建一个主机容器。关于其他相关DrawingVisual的细节这里不做过多阐述,不明白的可以去微软官网看。

2、实现的具体代码如下,相关细节有备注标注了。这里记得要重写一下VisualChildrenCount属性和重写GetVisualChild()方法,不然图画不出来

public class CruveChartDrawingVisual : FrameworkElement
{
private List visuals = new List();
private DrawingVisual Layer;
private double offset_x = 0;//滑动条偏移值
private double y_scale;//y轴放心缩放比例
private List<int> list_points;//曲线数据
private static int Top_Val_Max = 100;//y轴最大值
private static int Top_Val_Min = 0;//y轴最小值
private static int X_Sex = 20;//x轴分度值
private static int Y_Sex = 20;//x轴分度值
private static int Bottom = 30;//底部x轴坐标显示高度

Pen pen
= new Pen(Brushes.Green, 2);
Pen primarygrid_pen
= new Pen(Brushes.Black, 1);
Pen secondgrid_pen
= new Pen(Brushes.Gray, 1);
public CruveChartDrawingVisual()
{
pen.Freeze();
//冻结笔,提高性能关键所在
primarygrid_pen.Freeze();
secondgrid_pen.Freeze();
Layer
= new DrawingVisual();
visuals.Add(Layer);
}
public void SetupData(List<int> points)
{
list_points
= points;
offset_x
= 0;
DrawContent();
}
public void OffsetX(double offset)
{
offset_x
= offset;
DrawContent();
InvalidateVisual();
}
private void DrawContent()
{
var dc = Layer.RenderOpen();
y_scale
= (RenderSize.Height - Bottom) / (Top_Val_Max - Top_Val_Min);
var mat = new Matrix();
mat.ScaleAt(
1, -1, 0, RenderSize.Height / 2);
mat.OffsetX
= -offset_x;
dc.PushTransform(
new MatrixTransform(mat));
//横线
for (int y = 0; y <= Top_Val_Max - Top_Val_Min; y += 10)
{
Point point1
= new Point(offset_x, y * y_scale + Bottom);
Point point2
= new Point(offset_x + RenderSize.Width, y * y_scale + Bottom);
if (y % Y_Sex == 0)
{
dc.DrawLine(primarygrid_pen, point1, point2);
continue;
}
dc.DrawLine(secondgrid_pen, point1, point2);
}
//竖线与文字
for (int i = 0; i <= (offset_x + RenderSize.Width); i += X_Sex * 2)
{
if (i < offset_x)
{
continue;
}
var point1 = new Point(i, Bottom);
var point2 = new Point(i, (Top_Val_Max - Top_Val_Min) * y_scale + Bottom);
//y轴文字
if (i % 100 == 0)
{
var text1 = new FormattedText(i + "", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Verdana"), 16, Brushes.Black);
var mat3 = new Matrix();
mat3.ScaleAt(
1, -1, i - text1.Width / 2, 8 + text1.Height / 2);
dc.PushTransform(
new MatrixTransform(mat3));
dc.DrawText(text1,
new Point(i - text1.Width / 2, 8));
dc.Pop();
}
//表格刻度文字
if (i % 100 == 0)
{
for (int y = Top_Val_Min; y <= Top_Val_Max; y += 10)
{
if (y % Y_Sex == 0)
{
var text1 = new FormattedText(y + "", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Verdana"), 12, Brushes.Black);
var mat3 = new Matrix();
mat3.ScaleAt(
1, -1, i + 1, (y - Top_Val_Min) * y_scale + Bottom + text1.Height / 2);
dc.PushTransform(
new MatrixTransform(mat3));
dc.DrawText(text1,
new Point(i + 1, (y - Top_Val_Min) * y_scale + Bottom));
dc.Pop();
}
}
//深色竖线
dc.DrawLine(primarygrid_pen, point1, point2);
continue;
}
//浅色竖线
dc.DrawLine(secondgrid_pen, point1, point2);
}
if (list_points != null)
{
for (int i = (int)offset_x; i 1; i++)
{
if (i > offset_x + RenderSize.Width)
{
break;
}
dc.DrawLine(pen,
new Point(i, list_points[i] * y_scale + Bottom), new Point(i + 1, list_points[i + 1] * y_scale + Bottom));
}
}
dc.Pop();
dc.Close();
}
protected override int VisualChildrenCount => visuals.Count;
protected override Visual GetVisualChild(int index)
{
return visuals[index];
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
DrawContent();
base.OnRenderSizeChanged(sizeInfo);
}
protected override void OnRender(DrawingContext drawingContext)
{
drawingContext.DrawRectangle(Brushes.White,
null, new Rect(0, 0, RenderSize.Width, RenderSize.Height));
base.OnRender(drawingContext);
}
}

3、接着测试一下,打开MainWindow,添加我们的自定义控件,这里局部显示需要搭配一个ScrollViewer来实现,代码如下


"curve" Margin="0,15,0,20" />
<ScrollViewer
Name
="scroll"
HorizontalScrollBarVisibility
="Auto"
ScrollChanged
="ScrollViewer_ScrollChanged"
VerticalScrollBarVisibility
="Disabled">
"canvas" 1" />

4、接着就是后台代码,比较简单,就是自动生成一个List,然后传给自定义控件,Canvas的宽度直接设置为List的长度,因为这里是水平方向一个像素点画一个点

public partial class MainWindow : Window
{
private bool isAdd = true;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List
<int> lists = new List<int>();
int temp = 20;
for (int i = 0; i <60 * 60; i++)
{
if (isAdd)
{
lists.Add(temp);
temp
++;
}
else
{
lists.Add(temp);
temp
--;
}
if (temp == 90) isAdd = false;
if (temp == 10) isAdd = true;
}
canvas.Width
= lists.Count;
curve.SetupData(lists);
}
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
curve.OffsetX(scroll.HorizontalOffset);
}
}

5、运行效果如下,滑动条拖到哪里就显示哪里,这样就算数据量再大也没问题

 



推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 使用eclipse创建一个Java项目的步骤
    本文介绍了使用eclipse创建一个Java项目的步骤,包括启动eclipse、选择New Project命令、在对话框中输入项目名称等。同时还介绍了Java Settings对话框中的一些选项,以及如何修改Java程序的输出目录。 ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
  • Tkinter Frame容器grid布局并使用Scrollbar滚动原理
    本文介绍了如何使用Tkinter实现Frame容器的grid布局,并通过Scrollbar实现滚动效果。通过将Canvas作为父容器,使用滚动Canvas来滚动Frame,实现了在Frame中添加多个按钮,并通过Scrollbar进行滚动。同时,还介绍了更新Frame大小和绑定滚动按钮的方法,以及配置Scrollbar的相关参数。 ... [详细]
  • 颜色迁移(reinhard VS welsh)
    不要谈什么天分,运气,你需要的是一个截稿日,以及一个不交稿就能打爆你狗头的人,然后你就会被自己的才华吓到。------ ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
author-avatar
高振Andy
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有