WPF實(shí)現(xiàn)繪制折線圖的示例代碼
WPF 實(shí)現(xiàn)折線圖
框架支持.NET4 至 .NET8
;
Visual Studio 2022
;
實(shí)現(xiàn)代碼
1)新增 ChartBase
代碼如下:
1.繪制X
軸:根據(jù)控件的寬度和數(shù)據(jù)的數(shù)量計(jì)算出圖表的寬度,并在底部繪制X軸。
2.繪制Y
軸虛線:繪制一系列垂直的短線來(lái)代表Y軸的虛線。
3.繪制Y
軸數(shù)值文本:在Y軸虛線的旁邊繪制對(duì)應(yīng)的數(shù)值文本。
4.計(jì)算刻度值:根據(jù)數(shù)據(jù)的最大值和設(shè)定的行數(shù)來(lái)計(jì)算Y
軸上每個(gè)刻度的值。
5.繪制Y
軸線:在每個(gè)刻度值的位置繪制一條線來(lái)代表Y軸。
6.繪制Y
軸數(shù)值文本:在每個(gè)刻度的位置繪制對(duì)應(yīng)的數(shù)值文本。
xAxiHeight
設(shè)定 X
軸的高度
StartY
設(shè)定 Y
軸的起始位置
width
計(jì)算圖表的寬度
IntervalY
Y
軸的間隔初始化為0
x
當(dāng)前 X
軸的位置
y
當(dāng)前 Y
軸的位置加上畫筆高度
drawingContext.DrawSnappedLinesBetweenPoints(myPen, myPen.Thickness, new Point(StartX, StartY), new Point(width, StartY));
繪制X
軸
drawingContext.DrawSnappedLinesBetweenPoints(myPen, myPen.Thickness, points.ToArray());
繪制底部 X
軸的齒距
drawingContext.DrawText(formattedText, new Point(StartX - formattedText.Width - 10, yAxis - formattedText.Height / 2));
繪制 Y
軸的數(shù)值
drawingContext.DrawSnappedLinesBetweenPoints(xAxisPen, xAxisPen.Thickness, points.ToArray());
繪制 Y
軸上的線
public class ChartBase : Control { public static readonly DependencyProperty DatasProperty = DependencyProperty.Register("Datas", typeof(IEnumerable<KeyValuePair<string, double>>), typeof(ChartBase), new UIPropertyMetadata(DatasChanged)); static ChartBase() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ChartBase), new FrameworkPropertyMetadata(typeof(ChartBase))); } protected double Rows { get; } = 5; protected double Interval { get; } = 120; protected short ScaleFactor { get; private set; } = 80; protected Brush ChartFill { get; private set; } protected double StartX { get; private set; } protected double StartY { get; private set; } protected double MaxY { get; } protected double IntervalY { get; private set; } protected Brush NormalBrush => ControlsHelper.PrimaryNormalBrush; public IEnumerable<KeyValuePair<string, double>> Datas { get => (IEnumerable<KeyValuePair<string, double>>) GetValue(DatasProperty); set => SetValue(DatasProperty, value); } private static void DatasChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = d as ChartBase; if (e.NewValue != null) ctrl.InvalidateVisual(); } protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); if (Datas == null || Datas.Count() == 0) return; SnapsToDevicePixels = true; UseLayoutRounding = true; ChartFill = Application.Current.TryFindResource("WD.ChartFillSolidColorBrush") as Brush; var myPen = new Pen { Thickness = 1, Brush = ChartFill }; myPen.Freeze(); var xAxiHeight = 4; StartY = ActualHeight - (xAxiHeight + myPen.Thickness) - 20; var w = ActualWidth; StartX = 40; var width = Datas.Count() * Interval + StartX; IntervalY = 0; var x = StartX; var y = StartY + myPen.Thickness; drawingContext.DrawSnappedLinesBetweenPoints(myPen, myPen.Thickness, new Point(StartX, StartY), new Point(width, StartY)); var points = new List<Point>(); for (var i = 0; i < Datas.Count() + 1; i++) { points.Add(new Point(x, y)); points.Add(new Point(x, y + xAxiHeight)); x += Interval; } drawingContext.DrawSnappedLinesBetweenPoints(myPen, myPen.Thickness, points.ToArray()); var formattedText = DrawingContextHelper.GetFormattedText(IntervalY.ToString(), ChartFill, FlowDirection.LeftToRight); drawingContext.DrawText(formattedText, new Point(StartX - formattedText.Width * 2, StartY - formattedText.Height / 2)); var xAxisPen = new Pen { Thickness = 1, Brush = Application.Current.TryFindResource("WD.ChartXAxisSolidColorBrush") as Brush }; xAxisPen.Freeze(); var max = Convert.ToInt32(Datas.Max(kvp => kvp.Value)); var min = Convert.ToInt32(Datas.Min(kvp => kvp.Value)); ScaleFactor = Convert.ToInt16(StartY / Rows); var yAxis = StartY - ScaleFactor; points.Clear(); var average = Convert.ToInt32(max / Rows); var result = Enumerable.Range(0, (Convert.ToInt32(max) - average) / average + 1) .Select(i => average + i * average); foreach (var item in result) { points.Add(new Point(StartX, yAxis)); points.Add(new Point(width, yAxis)); IntervalY = item; formattedText = DrawingContextHelper.GetFormattedText(IntervalY.ToString(), ChartFill, FlowDirection.LeftToRight); drawingContext.DrawText(formattedText, new Point(StartX - formattedText.Width - 10, yAxis - formattedText.Height / 2)); yAxis -= ScaleFactor; } drawingContext.DrawSnappedLinesBetweenPoints(xAxisPen, xAxisPen.Thickness, points.ToArray()); } }
2)新增 ChartLine
代碼如下:
- 1.計(jì)算比例和位置:根據(jù)第一個(gè)數(shù)據(jù)點(diǎn)的值計(jì)算其在
Y
軸上的比例和位置。 - 2.繪制數(shù)據(jù)點(diǎn)標(biāo)簽:遍歷數(shù)據(jù)集中的每一個(gè)數(shù)據(jù)點(diǎn),為每個(gè)數(shù)據(jù)點(diǎn)繪制其標(biāo)簽,并計(jì)算標(biāo)簽的繪制位置。
- 3.繪制線條和橢圓:對(duì)于每個(gè)數(shù)據(jù)點(diǎn),計(jì)算其在
Y
軸上的位置,并繪制從上一個(gè)數(shù)據(jù)點(diǎn)到當(dāng)前數(shù)據(jù)點(diǎn)的線。同時(shí),繪制一個(gè)橢圓來(lái)表示當(dāng)前的數(shù)據(jù)點(diǎn)。 - 4.更新位置和狀態(tài):更新起始點(diǎn)和
X
軸位置,為繪制下一個(gè)數(shù)據(jù)點(diǎn)做準(zhǔn)備。
using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Media; namespace WPFDevelopers.Controls { public class ChartLine : ChartBase { private const double _size = 10; protected override void OnRender(DrawingContext drawingContext) { if (Datas == null || Datas.Count() == 0) return; base.OnRender(drawingContext); var x = StartX; var interval = Interval; var drawingPen = new Pen { Thickness = 1, Brush = NormalBrush }; drawingPen.Freeze(); var firstDataPoint = Datas.FirstOrDefault(); if (firstDataPoint.Equals(default(KeyValuePair<string, double>))) return; double proportion = firstDataPoint.Value / IntervalY; double yPositionFromBottom = StartY - proportion * (ScaleFactor * Rows); var startPoint = new Point(x + Interval / 2, yPositionFromBottom); foreach (var item in Datas) { var formattedText = DrawingContextHelper.GetFormattedText(item.Key, ChartFill, FlowDirection.LeftToRight); var point = new Point(x + interval / 2 - formattedText.Width / 2, StartY + 4); drawingContext.DrawText(formattedText, point); var y = StartY - (item.Value / IntervalY) * (ScaleFactor * Rows); var endPoint = new Point(x + Interval / 2, y); drawingContext.DrawLine(drawingPen, startPoint, endPoint); var ellipsePoint = new Point(endPoint.X - _size / 2, endPoint.Y - _size / 2); var rect = new Rect(ellipsePoint, new Size(_size, _size)); var ellipseGeom = new EllipseGeometry(rect); drawingContext.DrawGeometry(drawingPen.Brush, drawingPen, ellipseGeom); startPoint = endPoint; x += interval; } } } }
3)新增 ChartLineExample.xaml
示例代碼如下:
<Grid Background="{DynamicResource WD.BackgroundSolidColorBrush}"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"> <Border Height="500" Margin="30,0" Background="{DynamicResource WD.BackgroundSolidColorBrush}"> <wd:ChartLine Datas="{Binding Datas, RelativeSource={RelativeSource AncestorType=local:ChartLineExample}}" /> </Border> </ScrollViewer> <Button Grid.Row="1" Width="200" VerticalAlignment="Bottom" Click="Button_Click" Content="刷新" Style="{StaticResource WD.PrimaryButton}" /> </Grid>
3)新增 ChartLineExample.xaml.cs
示例代碼如下:
public partial class ChartLineExample : UserControl { public IEnumerable<KeyValuePair<string, double>> Datas { get { return (IEnumerable<KeyValuePair<string, double>>)GetValue(DatasProperty); } set { SetValue(DatasProperty, value); } } public static readonly DependencyProperty DatasProperty = DependencyProperty.Register("Datas", typeof(IEnumerable<KeyValuePair<string, double>>), typeof(ChartLineExample), new PropertyMetadata(null)); private Dictionary<string, IEnumerable<KeyValuePair<string, double>>> keyValues = new Dictionary<string, IEnumerable<KeyValuePair<string, double>>>(); private int _index = 0; public ChartLineExample() { InitializeComponent(); var models1 = new[] { new KeyValuePair<string, double>("Mon", 120), new KeyValuePair<string, double>("Tue", 530), new KeyValuePair<string, double>("Wed", 1060), new KeyValuePair<string, double>("Thu", 140), new KeyValuePair<string, double>("Fri", 8000) , new KeyValuePair<string, double>("Sat", 200) , new KeyValuePair<string, double>("Sun", 300) , }; var models2 = new[] { new KeyValuePair<string, double>("(1)月", 120), new KeyValuePair<string, double>("(2)月", 170), new KeyValuePair<string, double>("(3)月", 30), new KeyValuePair<string, double>("(4)月", 200), new KeyValuePair<string, double>("(5)月", 100) , new KeyValuePair<string, double>("(6)月", 180) , new KeyValuePair<string, double>("(7)月", 90) , }; keyValues.Add("1", models1); keyValues.Add("2", models2); Datas = models1; } private void Button_Click(object sender, RoutedEventArgs e) { _index++; if (_index >= keyValues.Count) { _index = 0; } Datas = keyValues.ToList()[_index].Value; } }
效果圖
以上就是WPF實(shí)現(xiàn)繪制折線圖的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于WPF折線圖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#操作本地文件及保存文件到數(shù)據(jù)庫(kù)的基本方法總結(jié)
C#使用System.IO中的文件操作方法在Windows系統(tǒng)中處理本地文件相當(dāng)順手,這里我們還總結(jié)了在Oracle中保存文件的方法,嗯,接下來(lái)就來(lái)看看整理的C#操作本地文件及保存文件到數(shù)據(jù)庫(kù)的基本方法總結(jié)2016-05-05C#?實(shí)例解釋面向?qū)ο缶幊讨械膯我还δ茉瓌t(示例代碼)
本文我介紹了?SOLID?原則中的單一功能原則(single-responsibility?principle),并通過(guò)?C#?代碼示例簡(jiǎn)明地詮釋了它的含意和實(shí)現(xiàn),對(duì)C#?面向?qū)ο缶幊淘瓌t感興趣的朋友跟隨小編一起看看吧2022-02-02C#自定義導(dǎo)出數(shù)據(jù)到Excel的類實(shí)例
這篇文章主要介紹了C#自定義導(dǎo)出數(shù)據(jù)到Excel的類,實(shí)例分析了C#操作Excel的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-03-03Visual C#.Net 網(wǎng)絡(luò)程序開發(fā)-Socket篇
Visual C#.Net 網(wǎng)絡(luò)程序開發(fā)-Socket篇...2007-03-03C#中的隨機(jī)數(shù)函數(shù)Random()
這篇文章介紹了C#生成隨機(jī)數(shù)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05詳解如何獲取C#類中發(fā)生數(shù)據(jù)變化的屬性信息
這篇文章主要介紹了詳解如何獲取C#類中發(fā)生數(shù)據(jù)變化的屬性信息,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05