WPF 自定义的图表(适用大量数据绘制)

简介: 原文:WPF 自定义的图表(适用大量数据绘制) 在WPF中绘制图表比较简单,有很多的第三方控件,但是在绘制大量数据的时候,就显得有些吃力,即便是自己用StreamGeometry画也达不到理想的效果,要达到绘制大量数据而不会顿卡现象,只有一个途径,就是首先在内存中绘制好所有的图形,再一次性加载(或者说绘制)到界面控件Canvas或Grid中。
原文: WPF 自定义的图表(适用大量数据绘制)

在WPF中绘制图表比较简单,有很多的第三方控件,但是在绘制大量数据的时候,就显得有些吃力,即便是自己用StreamGeometry画也达不到理想的效果,要达到绘制大量数据而不会顿卡现象,只有一个途径,就是首先在内存中绘制好所有的图形,再一次性加载(或者说绘制)到界面控件Canvas或Grid中。

废话不多说,直接看效果吧


选中放大效果


源代码:

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<Visual> visuals = new List<Visual>();
        public enum MouseMode
        {
            ZOOM,
            VIEW
        }
        public MouseMode mMode = MouseMode.VIEW;
        public DrawingCanvas()
        {
            this.Background = Brushes.Transparent;
            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 && textVisual !=null)
            {
                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 < YZero)
                endup = YZero;
            if (endup > canvasWidth)
                endup = canvasWidth;
            //重画 选中区域
            double step = (canvasWidth - YZero) / sData.Count;
            if (isMouseMoved && Math.Abs(endup - startDown) >  step)
            {
                
                int startIndex = 0;
                int endIndex = 0;
                if (endup > startDown)
                {
                    startIndex = (int)((startDown - YZero) / step)+1;
                    endIndex = (int)((endup - YZero) / step);
                }
                else
                {
                    startIndex = (int)((endup - YZero) / step)+1;
                    endIndex = (int)((startDown - YZero) / step);
                }
                //暂存
                DoubleCollection tempM = new DoubleCollection(mData.Count);
                DoubleCollection tempS = new DoubleCollection(sData.Count);
                for(int i = 0;i < mData.Count;i++)
                {
                    tempM.Add(mData[i]);
                    tempS.Add(sData[i]);
                }
                mData.Clear();
                sData.Clear();
                for(int i = startIndex; i <= endIndex;i++)
                {
                    mData.Add(tempM[i]);
                    sData.Add(tempS[i]);
                }
                this.Polyline();
                this.RemoveVisual(rectVisual);
                this.InvalidateVisual();
            }
            isMouseMoved = false;
        }
        double preMoved = YZero;
        private void DrawingCanvas_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {           
            double positionX = e.GetPosition(this).X;
            if(mMode == MouseMode.VIEW)
            {
                Console.WriteLine("Moving" + positionX);
                if (positionX > YZero && positionX < canvasWidth && Math.Abs(positionX - preMoved) >= (canvasWidth-YZero)/mData.Count)
                {

                    Console.WriteLine("" + positionX);
                    this.PolyText(positionX);
                    
                    //this.InvalidateVisual();
                }
            }
            else if (isMouseDown && startDown > YZero && startDown < canvasWidth)
            {
               
                isMouseMoved = true;
                
                this.PolyRect(startDown, positionX);
                this.InvalidateVisual();
                
            }
        }

        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;

            if (textVisual != null)
                this.RemoveVisual(textVisual);
            //双击
            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;

                mData.Clear();
                sData.Clear();
                for (int i = 0 ; i < rtData.Count; i++)
                {
                    mData.Add(rtData[i]);
                    sData.Add(thData[i]);
                }
                this.Polyline();
                this.InvalidateVisual();
            }
        }

        
        //获取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;
        }
        
        private const double YZero = 50;
        private const double YMargin = 10;
        private const 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 yLabelMax = 100;
        private double yLabelMin = -100;
        private double canvasWidth = 0;
        private const int yLinesCount = 12;
        DoubleCollection rtData;
        DoubleCollection thData;
        DoubleCollection mData;
        DoubleCollection sData;
         //使用DrawVisual画Polyline
         DrawingVisual lineVisual;
        public DrawingVisual Polyline()
        {
            if (lineVisual != null)
                this.RemoveVisual(lineVisual);
            lineVisual = new DrawingVisual();
            DrawingContext dc = lineVisual.RenderOpen();
            Pen penAxis = new Pen(AxisColor,Thinkness);
            penAxis.Freeze();
            //画Y轴
            dc.DrawLine(penAxis, new Point(YZero,YMargin),new Point(YZero,YHeight-YMargin));
            dc.DrawLine(penAxis, new Point(YZero - YLabelLen, YMargin),new Point(YZero,YMargin));
            dc.DrawLine(penAxis, new Point(YZero - YLabelLen, YHeight/2), new Point(YZero, YHeight / 2));
            dc.DrawLine(penAxis, new Point(YZero - YLabelLen, YHeight-YMargin), new Point(YZero, YHeight - YMargin));
            
            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(YZero - YLabelLen - ft1.Width, YMargin - 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(YZero - YLabelLen - ft2.Width, 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(YZero - YLabelLen - ft3.Width, YHeight - YMargin - ft3.Height / 2));
            //画线
            Pen linePen1 = new Pen(Line1Color, Thinkness);
            linePen1.Freeze();
            Pen linePen2 = new Pen(Line2Color, Thinkness);
            linePen2.Freeze();
            double ratio = (yLabelMax - yLabelMin) / (YHeight - 2 * YMargin);
            for (int i = 0; i < mData.Count - 1; i++)
            {
                //将数值转换成位置   
                //理论值 
                Point p3 = new Point(YZero + i * (canvasWidth - YZero) / sData.Count, YMargin + (yLabelMax - sData[i]) / ratio);
                Point p4 = new Point(YZero + (i + 1) * (canvasWidth - YZero) / sData.Count, YMargin + (yLabelMax - sData[i + 1]) / ratio);
                dc.DrawLine(linePen2, p3, p4);
                //实测值
                Point p1 = new Point(YZero + i * (canvasWidth - YZero) / mData.Count, YMargin + (yLabelMax - mData[i]) / ratio);
                Point p2 = new Point(YZero + (i+1) * (canvasWidth - YZero) / mData.Count, YMargin + (yLabelMax - mData[i + 1]) / ratio);
                dc.DrawLine(linePen1, p1, p2);                             
            }
            //绘制纵向网格和x轴文本
            Pen pen3 = new Pen(new SolidColorBrush((Color)ColorConverter.ConvertFromString("#666666")),4);
            pen3.DashStyle = new DashStyle(new double[] {2.5,2.5 },0);
            pen3.Freeze();
            double stepX = (canvasWidth - YZero) / yLinesCount;
            int stepData = mData.Count / yLinesCount;
            if (stepData < 1)
                stepData = 1;
            for (int i = 1; i < yLinesCount; i++)
            {
                //纵向网格
                Point p1 = new Point(YZero + i * stepX, YHeight - YMargin);
                Point p2 = new Point(YZero + i * stepX,YMargin);
                dc.DrawLine(pen3, p1, p2);
                //x轴文本
                FormattedText ftX = new FormattedText((i*stepData).ToString(), new System.Globalization.CultureInfo("zh-CHS", false), FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"), 10, Brushes.Black);
                Point p3 = new Point(YZero + i * stepX - ftX.Width/2,yHeight - YMargin);
                dc.DrawText(ftX, p3);
            }

            dc.Close();

            this.AddVisual(lineVisual);
            return lineVisual;
        }
        //绘制鼠标选择放大的矩形
        DrawingVisual rectVisual;
        public DrawingVisual 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, YMargin), new Point(endx, YHeight - YMargin)));

            dc.Close();
            this.AddVisual(rectVisual);

            return rectVisual;
        }
        //绘制显示选中的数值
        DrawingVisual textVisual;
        public void PolyText(double xPosition)
        {
            if (textVisual != null)
            {
                this.RemoveVisual(textVisual);
                //textVisual = new DrawingVisual();
                //this.AddVisual(textVisual);
            }
                
            
            textVisual = new DrawingVisual();
            DrawingContext dc = textVisual.RenderOpen();

            double step = (canvasWidth - YZero) / mData.Count;
            int index = (int)((xPosition - YZero) / step);
            double ax = YZero + index * step;
            //竖线
            Pen pen = new Pen(Brushes.Transparent, 3);
            pen.Freeze();
            FormattedText ftX = new FormattedText("里程:"+index+" 实测:"+mData[index]+" 设计:"+sData[index], new System.Globalization.CultureInfo("zh-CHS", false), FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"),15, Brushes.White);
            //计算是否超出范围
            if(ax + ftX.Width < canvasWidth)
            {
                dc.DrawRectangle(new SolidColorBrush(Color.FromArgb(200, 0, 150, 179)), pen, new Rect(new Point(ax, YMargin), new Point(ax + ftX.Width, YMargin + ftX.Height)));
                dc.DrawText(ftX, new Point(ax, YMargin));
            }
            else
            {
                dc.DrawRectangle(new SolidColorBrush(Color.FromArgb(200, 0, 150, 179)), pen, new Rect(new Point(ax-ftX.Width, YMargin), new Point(ax, YMargin + ftX.Height)));
                dc.DrawText(ftX, new Point(ax-ftX.Width, YMargin));
            }
            
            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, YMargin), new Point(ax, YHeight - YMargin));
            dc.Close();
            this.AddVisual(textVisual);
        }

        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 double YLabelMax
        {
            get
            {
                return yLabelMax;
            }

            set
            {
                yLabelMax = value;
            }
        }

        public double YLabelMin
        {
            get
            {
                return yLabelMin;
            }

            set
            {
                yLabelMin = value;
            }
        }

        public static double YZero1
        {
            get
            {
                return YZero;
            }
        }

        public double CanvasWidth
        {
            get
            {
                return canvasWidth;
            }

            set
            {
                canvasWidth = value;
            }
        }

        public Brush Line2Color
        {
            get
            {
                return line2Color;
            }

            set
            {
                line2Color = value;
            }
        }

        public Brush Line1Color
        {
            get
            {
                return line1Color;
            }

            set
            {
                line1Color = value;
            }
        }

        public DoubleCollection RtData
        {
            get
            {
                return rtData;
            }

            set
            {
                rtData = value;
                mData = new DoubleCollection(value.Count);
                foreach (double temp in value)
                {
                    mData.Add(temp);
                }
            }
        }

        public DoubleCollection ThData
        {
            get
            {
                return thData;
            }

            set
            {
                thData = value;
                sData = new DoubleCollection(value.Count);
                foreach(double temp in value)
                {
                    sData.Add(temp);
                }
            }
        }
    }
}


目录
相关文章
|
4月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
|
4月前
|
开发框架 前端开发 JavaScript
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(10) -- 在DataGrid上直接编辑保存数据
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(10) -- 在DataGrid上直接编辑保存数据
|
4月前
|
开发框架 前端开发 搜索推荐
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(4) -- 实现DataGrid数据的导入和导出操作
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(4) -- 实现DataGrid数据的导入和导出操作
|
4月前
|
传感器 C# 监控
硬件交互新体验:WPF与传感器的完美结合——从初始化串行端口到读取温度数据,一步步教你打造实时监控的智能应用
【8月更文挑战第31天】本文通过详细教程,指导Windows Presentation Foundation (WPF) 开发者如何读取并处理温度传感器数据,增强应用程序的功能性和用户体验。首先,通过`.NET Framework`的`Serial Port`类实现与传感器的串行通信;接着,创建WPF界面显示实时数据;最后,提供示例代码说明如何初始化串行端口及读取数据。无论哪种传感器,只要支持串行通信,均可采用类似方法集成到WPF应用中。适合希望掌握硬件交互技术的WPF开发者参考。
86 0
|
4月前
|
C# 开发者 数据处理
WPF开发者必备秘籍:深度解析数据网格最佳实践,轻松玩转数据展示与编辑大揭秘!
【8月更文挑战第31天】数据网格控件是WPF应用程序中展示和编辑数据的关键组件,提供排序、筛选等功能,显著提升用户体验。本文探讨WPF中数据网格的最佳实践,通过DevExpress DataGrid示例介绍其集成方法,包括添加引用、定义数据模型及XAML配置。通过遵循数据绑定、性能优化、自定义列等最佳实践,可大幅提升数据处理效率和用户体验。
70 0
|
4月前
|
数据处理 开发者 C#
WPF数据绑定实战:从零开始,带你玩转数据与界面同步,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据绑定是核心技能之一,它能实现界面元素与数据源的同步更新。本文详细介绍了WPF数据绑定的概念与实现方法,包括属性绑定、元素绑定及路径绑定等技术,并通过示例代码展示了如何创建数据绑定。通过数据绑定,开发者不仅能简化代码、提高可维护性,还能提升用户体验。无论初学者还是有经验的开发者,都能从中受益,更好地掌握WPF数据绑定技巧。
105 0
|
4月前
|
开发框架 前端开发 JavaScript
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(3)--自定义用户控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(3)--自定义用户控件
|
4月前
|
C#
WPF 自定义可拖动标题栏
WPF 自定义可拖动标题栏
57 0
|
4月前
|
开发框架 .NET C#
WPF/C#:显示分组数据的两种方式
WPF/C#:显示分组数据的两种方式
67 0
|
4月前
|
XML C# 数据格式
WPF/C#:如何将数据分组显示
WPF/C#:如何将数据分组显示
41 0