关于WPF画图性能问题

最近用wpf画心电图,尝试了wpf所有的方法,性能依然不能满足要求,后来发现舍本逐末了,现在记录下来,以免以后再走弯路。

首先要明白wpf管理的机制,如果你往canvas画一条线,一般就是 new Line() 然后添加到canvas里面,这样做的话就算你用轻量级的Polyline,或者使用DrawingVisual的方法。对于高频数据来说(比如心电波形)都会很卡,这里面使用inkcanvas添加stroke的方法效果最好。但是如果你过多执行strokes的clear()和Add,时间一长,内存就会爆掉,InkCanvas 只能胜任轻量级的工作,WPF 会管理 InkCanvas 中的所有元素,例如位置和尺寸, 所以它会消耗大量的内存和 CPU处理时间。InkCanvas 会渲染所有的stroke 在其装饰器层(Adorner Layer)。

可能我对wpf的认识还不足,硬生的把wpf和GDI+隔离开来看了,后来在Just Wang的热心帮助下,终于找到了方法,看他的代码:

[csharp]  view plain copy
  1. public class WriteableBitmapTrendLine : FrameworkElement  
  2.     {  
  3.         #region DependencyProperties  
  4.   
  5.         public static readonly DependencyProperty LatestQuoteProperty =  
  6.             DependencyProperty.Register("LatestQuote"typeof(MinuteQuoteViewModel), typeof(WriteableBitmapTrendLine),  
  7.             new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, OnLatestQuotePropertyChanged));  
  8.   
  9.         private static void OnLatestQuotePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)  
  10.         {  
  11.             WriteableBitmapTrendLine trendLine = (WriteableBitmapTrendLine)d;  
  12.             MinuteQuoteViewModel latestQuote = (MinuteQuoteViewModel)e.NewValue;  
  13.             if (latestQuote != null)  
  14.             {  
  15.                 trendLine.DrawTrendLine(latestQuote.Ordinal, (float)latestQuote.LastPx);  
  16.             }  
  17.         }  
  18.   
  19.         public MinuteQuoteViewModel LatestQuote  
  20.         {  
  21.             get { return (MinuteQuoteViewModel)GetValue(LatestQuoteProperty); }  
  22.             set { SetValue(LatestQuoteProperty, value); }  
  23.         }  
  24.  
  25.         #endregion  
  26.   
  27.         private const int COLS = 723;  
  28.         private const int ROWS = 41;  
  29.   
  30.         private WriteableBitmap bitmap;  
  31.         private float maxPrice = 0.0F;  
  32.         private static int dx = 3;  
  33.         private float[] prices = new float[COLS / dx];  
  34.   
  35.         public WriteableBitmapTrendLine()  
  36.         {  
  37.             this.bitmap = new WriteableBitmap(COLS, ROWS, 96, 96, PixelFormats.Rgb24, null);  
  38.   
  39.             this.bitmap.Lock();  
  40.   
  41.             using (Bitmap backBufferBitmap = new Bitmap(COLS, ROWS,  
  42.                this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb,  
  43.                this.bitmap.BackBuffer))  
  44.             {  
  45.                 using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap))  
  46.                 {  
  47.                     backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke);  
  48.                     backBufferGraphics.Flush();  
  49.                 }  
  50.             }  
  51.   
  52.             this.bitmap.AddDirtyRect(new Int32Rect(0, 0, COLS, ROWS));  
  53.             this.bitmap.Unlock();  
  54.         }  
  55.   
  56.         private void DrawTrendLine(int ordinal, float latestPrice)  
  57.         {  
  58.             if (double.IsNaN(latestPrice))  
  59.                 return;  
  60.   
  61.             this.prices[ordinal] = latestPrice;  
  62.             bool redraw = false;  
  63.             if (ordinal == 0)  
  64.             {  
  65.                 this.maxPrice = latestPrice;  
  66.             }  
  67.             else  
  68.             {  
  69.                 if (latestPrice > this.maxPrice)  
  70.                 {  
  71.                     this.maxPrice = latestPrice;  
  72.                     redraw = true;  
  73.                 }  
  74.             }  
  75.   
  76.             if (ordinal == 0)  
  77.             {  
  78.                 int width = this.bitmap.PixelWidth;  
  79.                 int height = this.bitmap.PixelHeight;  
  80.   
  81.                 this.bitmap.Lock();  
  82.   
  83.                 using (Bitmap backBufferBitmap = new Bitmap(width, height,  
  84.                     this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb,  
  85.                     this.bitmap.BackBuffer))  
  86.                 {  
  87.                     using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap))  
  88.                     {  
  89.                         backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke);  
  90.                         backBufferGraphics.Flush();  
  91.                     }  
  92.                 }  
  93.   
  94.                 this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));  
  95.                 this.bitmap.Unlock();  
  96.             }  
  97.             else  
  98.             {  
  99.                 System.Drawing.Point[] points = new System.Drawing.Point[ordinal + 1];  
  100.                 float dy = (float)(ROWS / (this.maxPrice * 1.3));  
  101.                 for (int i = 0; i <= ordinal; i++)  
  102.                 {  
  103.                     points[i].X = i * dx;  
  104.                     points[i].Y = (int)(this.prices[i] * dy);  
  105.                 }  
  106.   
  107.                 int width = ordinal * dx + 1;  
  108.                 int height = this.bitmap.PixelHeight;  
  109.   
  110.                 this.bitmap.Lock();  
  111.   
  112.                 using (Bitmap backBufferBitmap = new Bitmap(width, height,  
  113.                     this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb,  
  114.                     this.bitmap.BackBuffer))  
  115.                 {  
  116.                     using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap))  
  117.                     {  
  118.                         backBufferGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;  
  119.                         backBufferGraphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;  
  120.   
  121.                         if (redraw)  
  122.                             backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke);  
  123.                         backBufferGraphics.DrawLines(System.Drawing.Pens.Green, points);  
  124.                         backBufferGraphics.Flush();  
  125.                     }  
  126.                 }  
  127.   
  128.                 this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));  
  129.                 this.bitmap.Unlock();  
  130.             }  
  131.         }  
  132.   
  133.         private void DrawTrendLineF(int ordinal, float latestPrice)  
  134.         {  
  135.             if (double.IsNaN(latestPrice))  
  136.                 return;  
  137.   
  138.             this.prices[ordinal] = latestPrice;  
  139.             if (ordinal == 0)  
  140.             {  
  141.                 this.maxPrice = latestPrice;  
  142.             }  
  143.             else  
  144.             {  
  145.                 if (latestPrice > this.maxPrice)  
  146.                 {  
  147.                     this.maxPrice = latestPrice;  
  148.                 }  
  149.             }  
  150.   
  151.             if (ordinal == 0)  
  152.             {  
  153.                 int width = this.bitmap.PixelWidth;  
  154.                 int height = this.bitmap.PixelHeight;  
  155.   
  156.                 this.bitmap.Lock();  
  157.   
  158.                 using (Bitmap backBufferBitmap = new Bitmap(width, height,  
  159.                     this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb,  
  160.                     this.bitmap.BackBuffer))  
  161.                 {  
  162.                     using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap))  
  163.                     {  
  164.                         backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke);  
  165.                         backBufferGraphics.Flush();  
  166.                     }  
  167.                 }  
  168.   
  169.                 this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));  
  170.                 this.bitmap.Unlock();  
  171.             }  
  172.             else  
  173.             {  
  174.                 int count = this.prices.Length;  
  175.                 PointF[] points = new PointF[ordinal + 1];  
  176.                 float dy = (float)(ROWS / this.maxPrice);  
  177.                 for (int i = 0; i <= ordinal; i++)  
  178.                 {  
  179.                     points[i].X = i;  
  180.                     points[i].Y = (float)Math.Floor(this.prices[i] * dy);  
  181.   
  182.                     if (float.IsNaN(points[i].Y))  
  183.                         points[i].Y = 0.0F;  
  184.                 }  
  185.   
  186.                 int width = ordinal + 1;  
  187.                 int height = this.bitmap.PixelHeight;  
  188.   
  189.                 this.bitmap.Lock();  
  190.   
  191.                 using (Bitmap backBufferBitmap = new Bitmap(width, height,  
  192.                     this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb,  
  193.                     this.bitmap.BackBuffer))  
  194.                 {  
  195.                     using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap))  
  196.                     {  
  197.                         backBufferGraphics.DrawLines(System.Drawing.Pens.Green, points);  
  198.                         backBufferGraphics.Flush();  
  199.                     }  
  200.                 }  
  201.   
  202.                 this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));  
  203.                 this.bitmap.Unlock();  
  204.             }  
  205.         }  
  206.   
  207.         protected override void OnRender(DrawingContext drawingContext)  
  208.         {  
  209.             drawingContext.PushTransform(new ScaleTransform(1, -1, 0, RenderSize.Height / 2));  
  210.             drawingContext.DrawImage(bitmap, new Rect(0, 0, RenderSize.Width, RenderSize.Height));  
  211.         }  
  212.     }  
[csharp]  view plain copy
  1. public class MinuteQuoteViewModel : INotifyPropertyChanged  
  2.     {  
  3.         private int ordinal;  
  4.         public int Ordinal  
  5.         {  
  6.             get { return this.ordinal; }  
  7.             set { if (this.ordinal != value) { this.ordinal = value; this.OnPropertyChanged("Ordinal"); } }  
  8.         }  
  9.   
  10.         private DateTime quoteTime;  
  11.         public DateTime QuoteTime  
  12.         {  
  13.             get { return this.quoteTime; }  
  14.             set { if (this.quoteTime != value) { this.quoteTime = value; this.OnPropertyChanged("QuoteTime"); } }  
  15.         }  
  16.   
  17.         private double lastPx = double.NaN;  
  18.         public double LastPx  
  19.         {  
  20.             get { return this.lastPx; }  
  21.             set { if (this.lastPx != value) { this.lastPx = value; this.OnPropertyChanged("LastPx"); } }  
  22.         }  
  23.   
  24.         private double avgPx = double.NaN;  
  25.         public double AvgPx  
  26.         {  
  27.             get { return this.avgPx; }  
  28.             set { if (this.avgPx != value) { this.avgPx = value; this.OnPropertyChanged("AvgPx"); } }  
  29.         }  
  30.   
  31.         private int volume;  
  32.         public int Volume  
  33.         {  
  34.             get { return this.volume; }  
  35.             set { if (this.volume != value) { this.volume = value; this.OnPropertyChanged("Volume"); } }  
  36.         }  
  37.   
  38.         private double amount = double.NaN;  
  39.         public double Amount  
  40.         {  
  41.             get { return this.amount; }  
  42.             set { if (this.amount != value) { this.amount = value; this.OnPropertyChanged("Amount"); } }  
  43.         }  
  44.  
  45.         #region INotifyPropertyChanged 成员  
  46.   
  47.         public event PropertyChangedEventHandler PropertyChanged;  
  48.         protected virtual void OnPropertyChanged(string propertyName)  
  49.         {  
  50.             if (this.PropertyChanged != null)  
  51.                 this.PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));  
  52.         }  
  53.  
  54.         #endregion  
  55.     }  


他这个应该是直接从项目上拔下来的,看着比较费劲,我在vs2013下写了一个简单的示例,取消了固定区域,转为自适应界面。

代码下载:http://download.csdn.net/detail/waleswood/7079287

相关帖子地址:http://social.msdn.microsoft.com/Forums/zh-CN/3baebf07-5a0e-4e3a-a588-b79d869d6d47/inkcanvasstrokesclear?forum=wpfzhchs#7ce0efd9-7254-4d98-9d86-427d820cd827

http://social.msdn.microsoft.com/Forums/zh-CN/febcee07-dc8b-44b4-8c0a-246daffdbe2b/wpf-?forum=wpfzhchs#bfadef47-ab7b-4f8b-9340-e3c7a2782b76

http://social.msdn.microsoft.com/Forums/zh-CN/b156e12b-bc52-44a9-b2f9-26fff723cff5/wpf-inkcanvas?forum=wpfzhchs#de6c4b50-7036-4823-bbec-4e9ba309a600

(转自:http://blog.csdn.net/waleswood/article/details/21744131)

你可能感兴趣的:(关于WPF画图性能问题)