1. 程式人生 > 實用技巧 >wpf 高效能自定義chart

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(); } } }