WPF開發(fā)之利用DrawingVisual繪制高性能曲線圖
前言
項目中涉及到了心率檢測,而且數(shù)據(jù)量達(dá)到了百萬級別,通過WPF實現(xiàn)大數(shù)據(jù)曲線圖時,嘗試過最基礎(chǔ)的Canvas來實現(xiàn),但是性能堪憂,而且全部畫出來也不實際。同時也嘗試過找第三方的開源庫,但是因為曲線圖涉及到很多細(xì)節(jié)功能,第三方的肯定也沒法滿足。沒辦法,只能自己實現(xiàn),上網(wǎng)查找后發(fā)現(xiàn)DrawingVisual這個玩意可以實現(xiàn)高性能畫圖,同時再搭配局部顯示,這樣就能實現(xiàn)自己想要的效果。話不多說,今天把大致的實現(xiàn)思路寫一下,就不直接把項目的源碼貼出來,寫個簡單的Demo就好了。
正文
1、首先新建個項目,然后創(chuàng)建個自定義控件,命名為CurveChartDrawingVisual,然后讓它繼承FrameworkElement。因為要使用DrawingVisual對象的話,需要為它創(chuàng)建一個主機容器。關(guān)于其他相關(guān)DrawingVisual的細(xì)節(jié)這里不做過多闡述,不明白的可以去微軟官網(wǎng)看。
2、實現(xiàn)的具體代碼如下,相關(guān)細(xì)節(jié)有備注標(biāo)注了。這里記得要重寫一下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;//曲線數(shù)據(jù) 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;//x軸分度值 private static int Bottom = 30;//底部x軸坐標(biāo)顯示高度 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();//凍結(jié)筆,提高性能關(guān)鍵所在 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來實現(xiàn),記得這里沒有將我們的自定義控件嵌入ScrollViewer,而是放一個Canvas來填充,代碼如下
<Grid> <local:CruveChartDrawingVisual x:Name="curve" Margin="0,15,0,20" /> <ScrollViewer Name="scroll" HorizontalScrollBarVisibility="Auto" ScrollChanged="ScrollViewer_ScrollChanged" VerticalScrollBarVisibility="Disabled"> <Canvas x:Name="canvas" Height="1" /> </ScrollViewer> </Grid>
4、接著就是后臺代碼,比較簡單,就是自動生成一個List,然后傳給自定義控件,Canvas的寬度直接設(shè)置為List的長度,因為這里是水平方向一個像素點畫一個點,然后滑動條滑動時再將對應(yīng)的偏移值傳遞到控件,再通過偏移值更新視圖
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、運行效果如下,滑動條拖到哪里就顯示哪里,這樣就算數(shù)據(jù)量再大也沒問題,這種曲線圖跟常規(guī)的曲線圖有點差別,這里更多的是提供一種思路
以上就是WPF開發(fā)之利用DrawingVisual繪制高性能曲線圖的詳細(xì)內(nèi)容,更多關(guān)于WPF DrawingVisual繪制曲線圖的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何傳值在2個頁面之間 要求不刷新父頁面,并且不能用Querystring傳值
通過Cookie,因為它既可以在服務(wù)器端對其進行操作,也可在客戶端對其進行操作但是缺點是不安全,而且有時客戶端會由于安全問題禁用Cookie!2008-12-12ASP.NET MVC4中使用Html.DropDownListFor的方法示例
這篇文章主要介紹了ASP.NET MVC4中使用Html.DropDownListFor的方法,結(jié)合實例形式分析了控制器數(shù)據(jù)源及Html.DropDownListFor顯示操作的相關(guān)技巧,需要的朋友可以參考下2016-08-08asp.net?core?MVC?全局過濾器之ExceptionFilter過濾器(1)
這篇文章主要為大家詳細(xì)介紹了asp.net?core?MVC?全局過濾器之ExceptionFilter過濾器,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08Visual Studio 2017 15.5 正式發(fā)布!性能再提升
Visual Studio 2017 15.5 正式發(fā)布!性能再提升,時發(fā)布的還有 Visual Studio for Mac 7.3,亮點如下2017-12-12基于.net core微服務(wù)的另一種實現(xiàn)方法
這篇文章主要給大家介紹了基于.net core微服務(wù)的另一種實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07使用VS2022在ASP.NET?Core中構(gòu)建輕量級服務(wù)
本文詳細(xì)講解了使用VS2022在ASP.NET?Core中構(gòu)建輕量級服務(wù)的方法,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-12-12ASP.NET下使用xml反序列化、緩存依賴實現(xiàn)個性化配置文件的實時生效
本文主要介紹了ASP.NET下使用xml反序列化、緩存依賴實現(xiàn)個性化配置文件的實時生效的方法。具有一定的參考價值,下面跟著小編一起來看下吧2017-01-01