WPF 使用DrawingVisual绘制高性能曲线图
一、前言
項目中涉及到了心率監測,而且數據量達到了百萬級別,通過WPF實現大數據曲線圖時,嘗試過最基礎的Canvas來實現,但是性能堪憂,而且全部畫出來也不實際。同時也嘗試過找第三方的開源庫,但是因為曲線圖涉及到很多細節功能,第三方的肯定也沒法滿足。沒辦法,只能自己實現,上網查找后發現DrawingVisual這個玩意可以實現高性能畫圖,同時再搭配局部顯示,這樣就能實現自己想要的效果。話不多說,今天把大致的實現思路寫一下,就不直接把項目的源碼貼出來,寫個簡單的Demo就好了。
二、正文
1、首先新建個項目,然后創建個自定義控件,命名CurveChartDrawingVisual,然后讓它繼承FrameworkElement。因為要使用DrawingVisual對象的話,需要為它創建一個主機容器。關于其他相關DrawingVisual的細節這里不做過多闡述,不明白的可以去微軟官網看。
2、實現的具體代碼如下,相關細節有備注標注了。這里記得要重寫一下VisualChildrenCount屬性和重寫 GetVisualChild() 方法,不然圖畫不出來
public?class?CruveChartDrawingVisual?:?FrameworkElement {private?List<Visual>?visuals?=?new?List<Visual>();private?DrawingVisual?Layer;private?double?offset_x?=?0;//滑動條偏移值private?double?y_scale;//y軸方向縮放比例private?List<int>?list_points;//曲線數據private?static?int?Top_Val_Max?=?100;//y軸最大值private?static?int?Top_Val_Min?=?0;//y軸最小值private?static?int?X_Sex?=?20;//x軸分度值private?static?int?Y_Sex?=?20;//y軸分度值private?static?int?Bottom?=?30;//底部x軸坐標顯示高度Pen?pen?=?new?Pen(Brushes.Green,?2);Pen?primarygrid_pen?=?new?Pen(Brushes.Black,?1);Pen?secondgrid_pen?=?new?Pen(Brushes.Gray,?1);public?CruveChartDrawingVisual(){pen.Freeze();//凍結筆,提高性能關鍵所在primarygrid_pen.Freeze();secondgrid_pen.Freeze();Layer?=?new?DrawingVisual();visuals.Add(Layer);}public?void?SetupData(List<int>?points){list_points?=?points;offset_x?=?0;DrawContent();}public?void?OffsetX(double?offset){offset_x?=?offset;DrawContent();InvalidateVisual();}private?void?DrawContent(){var?dc?=?Layer.RenderOpen();y_scale?=?(RenderSize.Height?-?Bottom)?/?(Top_Val_Max?-?Top_Val_Min);var?mat?=?new?Matrix();mat.ScaleAt(1,?-1,?0,?RenderSize.Height?/?2);mat.OffsetX?=?-offset_x;dc.PushTransform(new?MatrixTransform(mat));//橫線for?(int?y?=?0;?y?<=?Top_Val_Max?-?Top_Val_Min;?y?+=?10){Point?point1?=?new?Point(offset_x,?y?*?y_scale?+?Bottom);Point?point2?=?new?Point(offset_x?+?RenderSize.Width,?y?*?y_scale?+?Bottom);if?(y?%?Y_Sex?==?0){dc.DrawLine(primarygrid_pen,?point1,?point2);continue;}dc.DrawLine(secondgrid_pen,?point1,?point2);}//豎線與文字for?(int?i?=?0;?i?<=?(offset_x?+?RenderSize.Width);?i?+=?X_Sex?*?2){if?(i?<?offset_x){continue;}var?point1?=?new?Point(i,?Bottom);var?point2?=?new?Point(i,?(Top_Val_Max?-?Top_Val_Min)?*?y_scale?+?Bottom);//y軸文字if?(i?%?100?==?0){var?text1?=?new?FormattedText(i?+?"",?CultureInfo.CurrentCulture,?FlowDirection.LeftToRight,?new?Typeface("Verdana"),?16,?Brushes.Black);var?mat3?=?new?Matrix();mat3.ScaleAt(1,?-1,?i?-?text1.Width?/?2,?8?+?text1.Height?/?2);dc.PushTransform(new?MatrixTransform(mat3));dc.DrawText(text1,?new?Point(i?-?text1.Width?/?2,?8));dc.Pop();}//表格刻度文字if?(i?%?100?==?0){for?(int?y?=?Top_Val_Min;?y?<=?Top_Val_Max;?y?+=?10){if?(y?%?Y_Sex?==?0){var?text1?=?new?FormattedText(y?+?"",?CultureInfo.CurrentCulture,?FlowDirection.LeftToRight,?new?Typeface("Verdana"),?12,?Brushes.Black);var?mat3?=?new?Matrix();mat3.ScaleAt(1,?-1,?i?+?1,?(y?-?Top_Val_Min)?*?y_scale?+?Bottom?+?text1.Height?/?2);dc.PushTransform(new?MatrixTransform(mat3));dc.DrawText(text1,?new?Point(i?+?1,?(y?-?Top_Val_Min)?*?y_scale?+?Bottom));dc.Pop();}}//深色豎線dc.DrawLine(primarygrid_pen,?point1,?point2);continue;}//淺色豎線dc.DrawLine(secondgrid_pen,?point1,?point2);}if?(list_points?!=?null){for?(int?i?=?(int)offset_x;?i?<?list_points.Count?-?1;?i++){if?(i?>?offset_x?+?RenderSize.Width){break;}dc.DrawLine(pen,?new?Point(i,?list_points[i]?*?y_scale?+?Bottom),?new?Point(i?+?1,?list_points[i?+?1]?*?y_scale?+?Bottom));}}dc.Pop();dc.Close();}protected?override?int?VisualChildrenCount?=>?visuals.Count;protected?override?Visual?GetVisualChild(int?index){return?visuals[index];}protected?override?void?OnRenderSizeChanged(SizeChangedInfo?sizeInfo){DrawContent();base.OnRenderSizeChanged(sizeInfo);}protected?override?void?OnRender(DrawingContext?drawingContext){drawingContext.DrawRectangle(Brushes.White,?null,?new?Rect(0,?0,?RenderSize.Width,?RenderSize.Height));base.OnRender(drawingContext);} }3、接著測試一下,打開MainWindow,添加我們的自定義控件,這里局部顯示需要搭配一個ScrollViewer來實現,記得這里沒有將我們的自定義控件嵌入ScrollViewer,而是放一個Canvas來填充,代碼如下
<Grid><local:CruveChartDrawingVisual?x:Name="curve"?Margin="0,15,0,20"?/><ScrollViewerName="scroll"HorizontalScrollBarVisibility="Auto"ScrollChanged="ScrollViewer_ScrollChanged"VerticalScrollBarVisibility="Disabled"><Canvas?x:Name="canvas"?Height="1"?/></ScrollViewer> </Grid>4、接著就是后臺代碼,比較簡單,就是自動生成一個List,然后傳給自定義控件,Canvas的寬度直接設置為List的長度,因為這里是水平方向一個像素點畫一個點,然后滑動條滑動時再將對應的偏移值傳遞到控件,再通過偏移值更新視圖
public?partial?class?MainWindow?:?Window {private?bool?isAdd?=?true;public?MainWindow(){InitializeComponent();}private?void?Window_Loaded(object?sender,?RoutedEventArgs?e){List<int>?lists?=?new?List<int>();int?temp?=?20;for?(int?i?=?0;?i?<?60?*?60;?i++){if?(isAdd){lists.Add(temp);temp?++;}else{lists.Add(temp);temp?--;}if?(temp?==?90)?isAdd?=?false;if?(temp?==?10)?isAdd?=?true;}canvas.Width?=?lists.Count;curve.SetupData(lists);}private?void?ScrollViewer_ScrollChanged(object?sender,?ScrollChangedEventArgs?e){curve.OffsetX(scroll.HorizontalOffset);} }5、運行效果如下,滑動條拖到哪里就顯示哪里,這樣就算數據量再大也沒問題,這種曲線圖跟常規的曲線圖有點差別,這里更多的是提供一種思路。
[1]
參考資料
[1]
鏈接: https://www.cnblogs.com/cong2312/p/15921146.html
總結
以上是生活随笔為你收集整理的WPF 使用DrawingVisual绘制高性能曲线图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Xamarin效果第十二篇之控制祖传PL
- 下一篇: .Net Core手撸一个基于Token