博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WPF 自定义的图表(适用大量数据绘制)下
阅读量:6984 次
发布时间:2019-06-27

本文共 26679 字,大约阅读时间需要 88 分钟。

原文:

上一篇文章中讲了WPF中自定义绘制大量数据的图标,思路是先将其绘制在内存,然后一次性加载到界面,在后续的调试过程中,发现当数据量到达10W时,移动鼠标显示数据有明显的延迟。经过思考,我采用了以下两个办法解决这个问题:
1.将数据显示的文本与图表分离,作为一个单独的canvas,这样,显示文本数据的时候就不需要重画图表了
2.计算鼠标移动速度,当移动速度过快时,不绘制文本,减少数据文本的绘制频率
3.使用START_INDEX 和 END_INDEX来表示绘制数据的其实位置和结束位置,减少对整个数据的遍历
首先看前台代码
在lineChart中分别加入图表的Canvas和文本数据的Canvas,修改之前的DrawingCanvas,把绘制数据文本的代码分离出来
DrawingCanvas.CS
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Media;using System.Windows.Threading;namespace Zero_Gjy.UserControls{    public class DrawingCanvas : Canvas    {        private List
visuals = new List
(); public const double YZero = 50; public const double YMargin = 50; private double YLabelLen = 5; private double yHeight; private Brush line1Color = Brushes.Black; private Brush labelColor = Brushes.Black; private Brush axisColor = Brushes.Black; private Brush line2Color = Brushes.Black; private double thinkness = 1; private double canvasWidth = 0; private double xWidth; private const int yLinesCount = 12; List
allDatas; private double canvasHeight; int dataCount = 0; int rtCount = 0; public enum MouseMode { ZOOM, VIEW } public MouseMode mMode = MouseMode.VIEW; public DrawingCanvas() { this.Background = Brushes.Transparent; this.HorizontalAlignment = HorizontalAlignment.Left; this.VerticalAlignment = VerticalAlignment.Top; this.Margin = new Thickness(0, 0, 0, YMargin); AllDatas = new List
(); } //获取Visual的个数 protected override int VisualChildrenCount { get { return visuals.Count; } } //获取Visual protected override Visual GetVisualChild(int index) { return visuals[index]; } //添加Visual public void AddVisual(Visual visual) { visuals.Add(visual); base.AddVisualChild(visual); base.AddLogicalChild(visual); } //删除Visual public void RemoveVisual(Visual visual) { visuals.Remove(visual); base.RemoveVisualChild(visual); base.RemoveLogicalChild(visual); } //命中测试 public DrawingVisual GetVisual(Point point) { HitTestResult hitResult = VisualTreeHelper.HitTest(this, point); return hitResult.VisualHit as DrawingVisual; } //使用DrawVisual画Polyline //DrawingVisual lineVisual; public void addData(string title,double min,double max,DoubleCollection xData, DoubleCollection mData, DoubleCollection sData) { AllDatas.Add(new LineDatas(title, min, max, xData,mData, sData)); dataCount++; rtCount = mData.Count; } public void removeFirst() { for(int i = 0; i < AllDatas.Count; i++) { AllDatas[i].RtData.RemoveAt(0); AllDatas[i].ThData.RemoveAt(0); } } public void removeLast() { for (int i = 0; i < AllDatas.Count; i++) { AllDatas[i].RtData.RemoveAt(AllDatas[i].RtData.Count - 1); AllDatas[i].ThData.RemoveAt(AllDatas[i].ThData.Count - 1); } } ///
/// 添加数据点 /// ///
x坐标 ///
实测数据 ///
理论数据 public void addPoint(double[] xdatas,double[] rtdatas,double[] thdatas) { if (rtdatas.Length != allDatas.Count) throw new Exception() { Source="数据个数不匹配"}; for (int i = 0; i < allDatas.Count; i++) { AllDatas[i].XData.Insert(0, xdatas[i]); AllDatas[i].RtData.Insert(0,rtdatas[i]); AllDatas[i].ThData.Insert(0,thdatas[i]); if (allDatas[i].Min > rtdatas[i]) allDatas[i].Min = rtdatas[i]; if (allDatas[i].Max < rtdatas[i]) allDatas[i].Max = rtdatas[i]; } } //清空所有数据 public void clearData() { AllDatas.Clear(); } //将数据清零 public void cleanData() { foreach(LineDatas item in allDatas) { for(int i = 0; i < item.RtData.Count; i++) { item.RtData[i] = 0; item.ThData[i] = 0; } item.Min = -1; item.Max = 1; } } public void Polyline() { //如果虚画布没有数据 if(this.visuals.Count == 0) { for(int i = 0; i < AllDatas.Count; i++) { this.visuals.Add(new DrawingVisual()); } } for(int count = 0; count < AllDatas.Count; count++){ //计算基础坐标系 double x0, y0; x0 = YZero; y0 = (count + 1) * YMargin + count * yHeight; string title = AllDatas[count].Title; LineDatas datas = allDatas[count]; DrawingVisual lineVisual = (DrawingVisual)this.visuals[count]; DrawingContext dc = lineVisual.RenderOpen(); Pen penAxis = new Pen(AxisColor, Thinkness); penAxis.Freeze(); int yLabelMax = (int)AllDatas[count].Max + 1; int yLabelMin = (int)AllDatas[count].Min - 1; //画标题 FormattedText fttitle = new FormattedText(title, new System.Globalization.CultureInfo("zh-CHS", false), FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"), 15, Brushes.Black); dc.DrawText(fttitle, new Point(xWidth / 2+x0 - fttitle.Width / 2, y0 - fttitle.Height)); //画Y轴 dc.DrawLine(penAxis, new Point(x0, y0), new Point(x0, y0 + yHeight)); dc.DrawLine(penAxis, new Point(x0 - YLabelLen, y0), new Point(x0, y0)); dc.DrawLine(penAxis, new Point(x0 - YLabelLen, y0 + yHeight / 2), new Point(x0, y0 + yHeight / 2)); dc.DrawLine(penAxis, new Point(x0 - YLabelLen, y0 + yHeight), new Point(x0, y0 + yHeight)); //y轴文本 FormattedText ft1 = new FormattedText(yLabelMax.ToString(), new System.Globalization.CultureInfo("zh-CHS", false), FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"), 10, Brushes.Black); dc.DrawText(ft1, new Point(x0 - YLabelLen - ft1.Width, y0 - ft1.Height / 2)); FormattedText ft2 = new FormattedText(((yLabelMax + yLabelMin) / 2).ToString(), new System.Globalization.CultureInfo("zh-CHS", false), FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"), 10, Brushes.Black); dc.DrawText(ft2, new Point(x0 - YLabelLen - ft2.Width, y0 + YHeight / 2 - ft2.Height / 2)); FormattedText ft3 = new FormattedText(yLabelMin.ToString(), new System.Globalization.CultureInfo("zh-CHS", false), FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"), 10, Brushes.Black); dc.DrawText(ft3, new Point(x0 - YLabelLen - ft3.Width, y0 + yHeight - ft3.Height / 2)); //画线 Pen linePen1 = new Pen(Line1Color, Thinkness); linePen1.Freeze(); Pen linePen2 = new Pen(Line2Color, Thinkness); linePen2.Freeze(); double ratio = (yLabelMax - yLabelMin) / yHeight; double step = xWidth / (datas.EndIndex - datas.StartIndex +1); //Console.WriteLine("=======datastep:" + step); for (int i = datas.StartIndex; i < datas.EndIndex; i++) { //将数值转换成位置 //理论值 Point p3 = new Point(x0 + (i - datas.StartIndex)*step, y0 + (yLabelMax - datas.ThData[i]) / ratio); Point p4 = new Point(x0 + (i - datas.StartIndex + 1)*step, y0 + (yLabelMax - datas.ThData[i + 1]) / ratio); dc.DrawLine(linePen2, p3, p4); //实测值 Point p1 = new Point(x0 +(i - datas.StartIndex) *step, y0 + (yLabelMax - datas.RtData[i]) / ratio); Point p2 = new Point(x0 + (i - datas.StartIndex + 1)*step, y0 + (yLabelMax - datas.RtData[i + 1]) / ratio); dc.DrawLine(linePen1, p1, p2); } //绘制纵向网格和x轴文本 Pen pen3 = new Pen(new SolidColorBrush((Color)ColorConverter.ConvertFromString("#666666")), 1); pen3.DashStyle = new DashStyle(new double[] { 2.5, 2.5 }, 0); pen3.Freeze(); double stepX = xWidth / yLinesCount; int stepData = (datas.EndIndex - datas.StartIndex + 1) / yLinesCount; if (stepData < 1) stepData = 1; for (int i = 1; i < yLinesCount; i++) { //纵向网格 Point p1 = new Point(x0 + i * stepX, y0 + yHeight); Point p2 = new Point(x0 + i * stepX, y0); dc.DrawLine(pen3, p1, p2); //x轴文本 FormattedText ftX = new FormattedText(datas.XData[i * stepData].ToString(), new System.Globalization.CultureInfo("zh-CHS", false), FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"), 10, Brushes.Black); Point p3 = new Point(x0 + i * stepX - ftX.Width / 2, yHeight + y0); dc.DrawText(ftX, p3); } dc.Close(); } this.InvalidateVisual(); } public double YHeight { get { return yHeight; } set { yHeight = value; } } public Brush LabelColor { get { return labelColor; } set { labelColor = value; } } public Brush AxisColor { get { return axisColor; } set { axisColor = value; } } public double Thinkness { get { return thinkness; } set { thinkness = value; } } public static double YZero1 { get { return YZero; } } public double CanvasWidth { get { return canvasWidth; } set { canvasWidth = value; this.Width = value; xWidth = value - YMargin - YZero; } } public Brush Line2Color { get { return line2Color; } set { line2Color = value; } } public Brush Line1Color { get { return line1Color; } set { line1Color = value; } } public double CanvasHeight { get { return canvasHeight; } set { canvasHeight = value; } } public int DataCount { get { return dataCount; } set { dataCount = value; } } public int RtCount { get { return rtCount; } set { rtCount = value; } } internal List
AllDatas { get { return allDatas; } set { allDatas = value; } } }}
DrawingLine.CS
using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Media;using System.Windows.Threading;namespace Zero_Gjy.UserControls{    class DrawingLine : Canvas    {        private List
visuals = new List
(); DrawingCanvas drawingCanvas; private double x0; private double y0; private double mWidth; private double mHeight; public enum MouseMode { ZOOM, VIEW } public MouseMode mMode = MouseMode.VIEW; private double canvasWidth; public DrawingLine(DrawingCanvas _dc) { this.drawingCanvas = _dc; this.Background = Brushes.Transparent; this.HorizontalAlignment = HorizontalAlignment.Left; this.VerticalAlignment = VerticalAlignment.Top; this.Height = drawingCanvas.CanvasHeight; this.Width = drawingCanvas.CanvasWidth; x0 = DrawingCanvas.YZero; y0 = DrawingCanvas.YMargin; mHeight = _dc.AllDatas.Count * (DrawingCanvas.YMargin + _dc.YHeight); this.PreviewMouseLeftButtonDown += DrawingCanvas_MouseLeftButtonDown; this.PreviewMouseLeftButtonUp += DrawingCanvas_MouseLeftButtonUp; this.MouseMove += DrawingCanvas_MouseMove; this.MouseLeave += DrawingCanvas_MouseLeave; } //鼠标离开画布 private void DrawingCanvas_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e) { if (mMode == MouseMode.VIEW) { this.RemoveVisual(textVisual); this.InvalidateVisual(); } } //选中放大区域完成,显示放大区域 private void DrawingCanvas_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) { isMouseDown = false; mMode = MouseMode.VIEW; endup = e.GetPosition(this).X; if (endup < x0) endup = x0; if (endup > x0+mWidth) endup = x0 + mWidth; //重画 选中区域 int len = drawingCanvas.AllDatas[0].EndIndex - drawingCanvas.AllDatas[0].StartIndex+1; if (drawingCanvas.AllDatas.Count < 1 || len < 1) return; double step = mWidth / len; if (isMouseMoved && Math.Abs(endup - startDown) > step) { int startIndex = 0; int endIndex = 0; if (endup > startDown) { startIndex = (int)((startDown - x0) / step) + 1; endIndex = (int)((endup - x0) / step); } else { startIndex = (int)((endup - x0) / step) + 1; endIndex = (int)((startDown - x0) / step); } foreach (LineDatas data in drawingCanvas.AllDatas) { data.EndIndex = data.StartIndex + endIndex; data.StartIndex = data.StartIndex + startIndex; } drawingCanvas.Polyline(); this.RemoveVisual(rectVisual); this.InvalidateVisual(); } isMouseMoved = false; } double preMoved = DrawingCanvas.YZero; System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); //long preTime = 0; private const long MIN_TIME= 15000; private void DrawingCanvas_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) { double positionX = e.GetPosition(this).X; if (mMode == MouseMode.VIEW) { int len = drawingCanvas.AllDatas[0].EndIndex - drawingCanvas.AllDatas[0].StartIndex+1; if (positionX > x0 && positionX < x0+mWidth && Math.Abs(positionX - preMoved) >= mWidth / len) { //计算速度鼠标移动速度,如果速度过快 ,则不绘制 if (stopwatch.IsRunning) { stopwatch.Stop(); } long curTime = stopwatch.ElapsedTicks; //Console.WriteLine(curTime); if(curTime > MIN_TIME) { this.PolyText(positionX); } stopwatch.Restart(); preMoved = positionX; } } else if (isMouseDown && startDown > x0 && startDown < mWidth+x0) { isMouseMoved = true; this.PolyRect(startDown, positionX); } //stopwatch.Start(); } private bool isMouseDown = false; private bool isMouseMoved = false; private double startDown; private double endup; private int clickTimes = 0; private void DrawingCanvas_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { isMouseDown = true; mMode = MouseMode.ZOOM; startDown = e.GetPosition(this).X; this.RemoveVisual(textVisual); this.InvalidateVisual(); //双击 clickTimes++; DispatcherTimer timer = new DispatcherTimer(); timer.Interval = new TimeSpan(0, 0, 0, 0, 300); timer.Tick += (s, e1) => { timer.IsEnabled = false; clickTimes = 0; }; timer.IsEnabled = true; if (clickTimes % 2 == 0) { //Console.WriteLine("double"); timer.IsEnabled = false; clickTimes = 0; foreach (LineDatas data in drawingCanvas.AllDatas) { data.StartIndex = 0; data.EndIndex = data.RtData.Count - 1; } drawingCanvas.Polyline(); drawingCanvas.InvalidateVisual(); //this.InvalidateVisual(); } } //获取Visual的个数 protected override int VisualChildrenCount { get { return visuals.Count; } } public double MWidth { get { return mWidth; } set { mWidth = value; } } public double MHeight { get { return mHeight; } set { mHeight = value; } } public double CanvasWidth { get { return canvasWidth; } set { canvasWidth = value; this.Width = value; mWidth = value - x0 - y0; } } //获取Visual protected override Visual GetVisualChild(int index) { return visuals[index]; } //添加Visual public void AddVisual(Visual visual) { visuals.Add(visual); base.AddVisualChild(visual); base.AddLogicalChild(visual); } //删除Visual public void RemoveVisual(Visual visual) { visuals.Remove(visual); base.RemoveVisualChild(visual); base.RemoveLogicalChild(visual); //visual = null; } //命中测试 public DrawingVisual GetVisual(Point point) { HitTestResult hitResult = VisualTreeHelper.HitTest(this, point); return hitResult.VisualHit as DrawingVisual; } //绘制鼠标选择放大的矩形 DrawingVisual rectVisual; public void PolyRect(double startx, double endx) { if (rectVisual != null) { this.RemoveVisual(rectVisual); } rectVisual = new DrawingVisual(); DrawingContext dc = rectVisual.RenderOpen(); Pen pen = new Pen(new SolidColorBrush(Color.FromArgb(100, 255, 200, 200)), 1); dc.DrawRectangle(new SolidColorBrush(Color.FromArgb(100, 255, 200, 200)), pen, new Rect(new Point(startx, y0), new Point(endx, y0+mHeight))); dc.Close(); this.AddVisual(rectVisual); this.InvalidateVisual(); } //绘制显示选中的数值 DrawingVisual textVisual; public void PolyText(double xPosition) { if (drawingCanvas.AllDatas.Count < 1) return; if (textVisual != null) { this.RemoveVisual(textVisual); } textVisual = new DrawingVisual(); DrawingContext dc = textVisual.RenderOpen(); int len = drawingCanvas.AllDatas[0].EndIndex - drawingCanvas.AllDatas[0].StartIndex+1; double step = mWidth / len; // Console.WriteLine("*****linestep:" + step); int index = (int)((xPosition - x0) / step); double ax = x0 + index * step; //竖线 Pen pen = new Pen(Brushes.Transparent, 3); pen.Freeze(); FormattedText[] ftXs = new FormattedText[drawingCanvas.AllDatas.Count]; for (int i = 0; i < drawingCanvas.AllDatas.Count; i++) { int mIndex = index + drawingCanvas.AllDatas[i].StartIndex; ftXs[i] = new FormattedText("X:" + drawingCanvas.AllDatas[i].XData[mIndex] + " 实测:" + drawingCanvas.AllDatas[i].RtData[mIndex] + " 设计:" + drawingCanvas.AllDatas[i].ThData[mIndex], new System.Globalization.CultureInfo("zh-CHS", false), FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"), 15, Brushes.White); //计算是否超出范围 if (ax + ftXs[i].Width < x0+mWidth) { dc.DrawRectangle(new SolidColorBrush(Color.FromArgb(200, 0, 150, 179)), pen, new Rect(new Point(ax, (i+1)*y0+i*drawingCanvas.YHeight), new Point(ax + ftXs[i].Width, (i + 1) * y0 + i * drawingCanvas.YHeight + ftXs[i].Height))); dc.DrawText(ftXs[i], new Point(ax, (i + 1) * y0 + i * drawingCanvas.YHeight)); } else { dc.DrawRectangle(new SolidColorBrush(Color.FromArgb(200, 0, 150, 179)), pen, new Rect(new Point(ax - ftXs[i].Width, (i + 1) * y0 + i * drawingCanvas.YHeight), new Point(ax, (i + 1) * y0 + i * drawingCanvas.YHeight + ftXs[i].Height))); dc.DrawText(ftXs[i], new Point(ax - ftXs[i].Width, (i + 1) * y0 + i * drawingCanvas.YHeight)); } } Pen penLine = new Pen(new SolidColorBrush(Color.FromArgb(200, 0, 150, 179)), 3); penLine.DashStyle = new DashStyle(new double[] { 2.5, 2.5 }, 0); penLine.Freeze(); dc.DrawLine(penLine, new Point(ax,y0), new Point(ax, y0+mHeight)); dc.Close(); this.AddVisual(textVisual); this.InvalidateVisual(); } }}
上面两段代码中,我把需要绘制的数据属性单独抽象出来
LineDatas.CS
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Media;namespace Zero_Gjy.UserControls{    class LineDatas    {        string title;        double min;        double max;        DoubleCollection xData;        DoubleCollection rtData;        DoubleCollection thData;        int startIndex;        int endIndex;        public double Min        {            get            {                return min;            }            set            {                min = value;            }        }        public double Max        {            get            {                return max;            }            set            {                max = value;            }        }                public string Title        {            get            {                return title;            }            set            {                title = value;            }        }        public DoubleCollection RtData        {            get            {                return rtData;            }            set            {                rtData = value;                min = value.Min();                max = value.Max();            }        }        public DoubleCollection ThData        {            get            {                return thData;            }            set            {                thData = value;                            }        }        public int StartIndex        {            get            {                return startIndex;            }            set            {                startIndex = value;            }        }        public int EndIndex        {            get            {                return endIndex;            }            set            {                endIndex = value;            }        }        public DoubleCollection XData        {            get            {                return xData;            }            set            {                xData = value;            }        }        public LineDatas()        {            this.title = "";            this.Max = 0;            this.Min = 0;            this.XData = new DoubleCollection();            this.RtData = new DoubleCollection();            this.ThData = new DoubleCollection();        }        ///         ///         ///         /// 数据标题        /// 数据最小值        /// 数据最大值        /// x坐标        /// 实测数据        /// 理论数据        public LineDatas(string _title,double min, double max, DoubleCollection _xData, DoubleCollection rtdata,DoubleCollection thdata)        {            this.title = _title;            this.Min = min;            this.Max = max;            this.XData = _xData;            this.RtData = rtdata ;            this.ThData = thdata;            this.StartIndex = 0;            this.EndIndex = rtData.Count - 1;        }        public void clear()        {            this.XData.Clear();            this.RtData.Clear();            this.ThData.Clear();        }    }}
经过这样的改进,鼠标在界面移动时显示数据明显顺畅了很多。

转载地址:http://prtpl.baihongyu.com/

你可能感兴趣的文章
读《白帽子讲Web安全》之安全意识篇(一)
查看>>
Session问题
查看>>
运用 autoconf 和 automake 自动生成 Makefile 实例讲解
查看>>
OpenSSL 之 RSA 相关命令学习笔记
查看>>
GLSL三种修饰符区别与用途(uniform,attribute和varying)
查看>>
python django django-debug-toolbar 加载缓慢,不能使用。
查看>>
操作系之进程调度及算法详解
查看>>
PHPexcel实列
查看>>
Butterknife 的简单使用 和 配合 Butterknife的插件 Zelezny
查看>>
Magento利用input type=”file”上传图片
查看>>
Android音频开发(4):如何存储和解析wav文件
查看>>
Handler延迟事件使用
查看>>
【DG】Oracle 19c使用dbca来搭建物理DG
查看>>
Cython安装
查看>>
StringBuilder 、StringBuffer 、 String
查看>>
brew install php55 报错 clang: error
查看>>
ubuntu18.4 安装swoole 和 php 扩展 swoole
查看>>
pcDuino入门心得+HDMI声音+蓝牙功放
查看>>
面向对象2
查看>>
c++测试题2016-6-2
查看>>