wpf 高性能自定义chart

上周五,公司要求我们用wpf做一个上波形图数据的控件,试了n个不收费的三方chart控件,功能是真好,但是性能不能,好一点的最多一次能加载几千个点,最后决定自己画。在QA论坛上问了一下,人家推荐用WriteableBitmap画,最后写完发现性能真的还行,10几万个点还算比较流畅,代码记录一下。


using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace _3.测试自定义控件
{
    /// <summary>
    /// CustomerChart.xaml 的交互逻辑
    /// </summary>
    public partial class CustomerChart : UserControl
    {
    
        /// <summary>
        /// 垂直(纵向)边距(画图区域距离左右两边长度)
        /// </summary>
      public  double VerticalMargin { get; set; }
        /// <summary>
        /// 平行(横向)边距(画图区域距离左右两边长度)
        /// </summary>
      public  double HorizontalMargin { get; set; }
        /// <summary>
        /// 水平刻度间距像素
        /// </summary>
       public double horizontalBetween { get; set; }
        /// <summary>
        /// 垂直刻度间距像素
        /// </summary>
       public   double verticalBetween { get; set; }

        /// <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; }

        ///// 画图区域起点
        ///// </summary>
      public  Point StartPostion { get; set; }
        /// <summary>
        /// 画图区域终点
        /// </summary>
      public  Point EndPostion { get; set; }

/// <summary>
/// 点的集合
/// </summary>
       public  System.Drawing.PointF[] points { get; set; }
        /// <summary>
        /// 缩放值
        /// </summary>
        public int Scale;
        public CustomerChart()
        {
            InitializeComponent();
            HorizontalMargin = 40;
            VerticalMargin = 40;
            StartPostion = new Point(HorizontalMargin, VerticalMargin);
            points = new System.Drawing.PointF[0];
        }
        public void Refresh()
        {
            InitCanvas();

            //获取y最大值
            if (MaxY < 0.0001)
            {
                if (points.Length < 2) return;
                MaxY = points.Max(m => m.Y);
            }

            if (MaxX < 0.0001)
            {
                if (points.Length < 2) return;
                MaxX = points.Max(m => m.X);
            }

            DrawXAxisTicks();
            DrawYAxisTicks();
            DrawAxis();

            DrawPolyline();
        }

        private void InitCanvas()
        {
            MainCanvas.Children.Clear();
            EndPostion = new Point(MainCanvas.ActualWidth - HorizontalMargin, MainCanvas.ActualHeight - VerticalMargin);
        }

        private void DrawPolyline()
        {
            WriteableBitmap _plotBitmap = new WriteableBitmap((int)(MainCanvas.ActualWidth - HorizontalMargin), (int)(MainCanvas.ActualHeight - VerticalMargin), 96, 96, PixelFormats.Rgb24, null);        
            Image image = new Image();
            image.MouseWheel += Image_MouseWheel;
            _plotBitmap.Lock();

            var b = new System.Drawing.Bitmap(_plotBitmap.PixelWidth,
                               _plotBitmap.PixelHeight,
                               _plotBitmap.BackBufferStride,
                               System.Drawing.Imaging.PixelFormat.Format24bppRgb,
                               _plotBitmap.BackBuffer);

            using (var bitmapGraphics = System.Drawing.Graphics.FromImage(b))
            {

                bitmapGraphics.SmoothingMode = SmoothingMode.HighSpeed;
                bitmapGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
                bitmapGraphics.CompositingMode = CompositingMode.SourceCopy;
                bitmapGraphics.CompositingQuality = CompositingQuality.HighSpeed;
                System.Drawing.Pen myPen = new System.Drawing.Pen(System.Drawing.Color.Green);

                for (int t = 0; t < points.Length; t++)
                {

                    points[t].X = _plotBitmap.PixelWidth * (t * 1.0f / points.Length);
                    points[t].Y = _plotBitmap.PixelHeight *(((float)MaxY - points[t].Y)/(float)MaxY);
                }
                bitmapGraphics.DrawLines(myPen, points);
            }
            _plotBitmap.AddDirtyRect(new Int32Rect(0, 0, _plotBitmap.PixelWidth, _plotBitmap.PixelHeight));
            _plotBitmap.Unlock();
            image.Source = _plotBitmap;            
            MainCanvas.Children.Add(image);
            Canvas.SetBottom(image, VerticalMargin);
            Canvas.SetLeft(image, HorizontalMargin);
        }

        private void Image_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            double x = e.GetPosition(sender as Image).X;
            
        }

        private void DrawYAxisTicks()
        {
            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 * (MainCanvas.ActualHeight - VerticalMargin) / (MaxY - MinY);
                var marker = new Line
                {
                    X1 = StartPostion.X - 5,
                    Y1 = y,
                    X2 = StartPostion.X,
                    Y2 = y,
                    Stroke = Brushes.Red
                };
                MainCanvas.Children.Add(marker);

                var gridLine = new Line
                {
                    X1 = StartPostion.X,
                    Y1 = y,
                    X2 = EndPostion.X,
                    Y2 = y,
                    StrokeThickness = 1,
                    Stroke = new SolidColorBrush(Colors.AliceBlue)
                };
                MainCanvas.Children.Add(gridLine);

                //画y轴字符
                var markText = new TextBlock
                {
                    Text = i.ToString(CultureInfo.InvariantCulture),
                    Width = 30,
                    Foreground = Brushes.Black,
                    FontSize = 10,
                    HorizontalAlignment = HorizontalAlignment.Right,
                    TextAlignment = TextAlignment.Right
                };
                MainCanvas.Children.Add(markText);
                Canvas.SetTop(markText, y -3);
                Canvas.SetLeft(markText, 00);
            }
        }

        /// <summary>
        /// 画x轴标签
        /// </summary>
        private void DrawXAxisTicks()
        {
            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 * (MainCanvas.ActualWidth - HorizontalMargin) / (MaxX - MinX) ;
                var marker = new Line
                {
                    X1 = x,
                    Y1 = EndPostion.Y,
                    X2 = x,
                    Y2 = EndPostion.Y + 4,
                    Stroke = Brushes.Red
                };
                MainCanvas.Children.Add(marker);

                var gridLine = new Line
                {
                    X1 = x,
                    Y1 = StartPostion.Y,
                    X2 = x,
                    Y2 = EndPostion.Y,
                    StrokeThickness = 1,
                    Stroke = new SolidColorBrush(Colors.AliceBlue)
                };
                MainCanvas.Children.Add(gridLine);

                //画x轴字符
                var text =Convert.ToInt32(i).ToString(CultureInfo.InvariantCulture);
                var markText = new TextBlock
                {
                    Text = text,
                    Width = 130,
                    Foreground = Brushes.Black,
                    VerticalAlignment = VerticalAlignment.Top,
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    TextAlignment = TextAlignment.Left,
                    FontSize = 10
                };

                //Transform st = new SkewTransform(0, 0);
                //markText.RenderTransform = st;
                MainCanvas.Children.Add(markText);
                Canvas.SetTop(markText, EndPostion.Y + 5);
                Canvas.SetLeft(markText, x-15);
            }


        }

        /// <summary>
        /// X轴Y轴
        /// </summary>
        private void DrawAxis()
        {
            var xaxis = new Line
            {
                X1 = StartPostion.X,
                Y1 = EndPostion.Y,
                X2 = EndPostion.X+HorizontalMargin,
                Y2 = EndPostion.Y,
                Stroke = new SolidColorBrush(Colors.Black)
            };
            MainCanvas.Children.Add(xaxis);

            var yaxis = new Line
            {
                X1 = StartPostion.X,
                Y1 = StartPostion.Y-VerticalMargin,
                X2 = StartPostion.X,
                Y2 = EndPostion.Y,

                Stroke = new SolidColorBrush(Colors.Black)
            };
            MainCanvas.Children.Add(yaxis);
        }

        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Refresh();
        }


    }
}

 

posted @ 2020-12-14 20:51  当年小清新  阅读(1915)  评论(0编辑  收藏  举报