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軸虛線:繪制一系列垂直的短線來代表Y軸的虛線。
3.繪制Y軸數(shù)值文本:在Y軸虛線的旁邊繪制對(duì)應(yīng)的數(shù)值文本。
4.計(jì)算刻度值:根據(jù)數(shù)據(jù)的最大值和設(shè)定的行數(shù)來計(jì)算Y軸上每個(gè)刻度的值。
5.繪制Y軸線:在每個(gè)刻度值的位置繪制一條線來代表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è)橢圓來表示當(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#中Winfrom默認(rèn)輸入法的設(shè)置方法
這篇文章主要介紹了C#中Winfrom默認(rèn)輸入法的設(shè)置方法,以實(shí)例形式較為詳細(xì)的分析了C#中輸入法設(shè)置的相關(guān)技巧,需要的朋友可以參考下2015-05-05
c#實(shí)現(xiàn)從字符串?dāng)?shù)組中把數(shù)字的元素找出來
下面小編就為大家分享一篇c#實(shí)現(xiàn)從字符串?dāng)?shù)組中把數(shù)字的元素找出來的方法,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12
C#實(shí)現(xiàn)排列組合算法完整實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)排列組合算法的完整實(shí)例,文中實(shí)例主要展示了排列循環(huán)方法和排列堆棧方法,需要的朋友可以參考下2014-09-09
使用策略模式實(shí)現(xiàn)報(bào)警服務(wù)示例詳解(短信報(bào)警)
服務(wù)的功能:這個(gè)服務(wù)就是能夠?qū)崿F(xiàn)多通路報(bào)警的服務(wù),比如郵件報(bào)警、客戶端報(bào)警、短信報(bào)警等,該服務(wù)靈活性還不錯(cuò),比較方便擴(kuò)展2014-01-01

