这篇文章有两个内容,第一个是绘制曲线图(OxyPlot),第二个是异步执行( Task.Run)
1. 绘制曲线的控件有很多,最近研究了一下OxyPlot,自适应很实用,缩放的实现方便,而且可以有多个纵轴,坐标轴可以是线性或者指数级增长方式
a. 第一步是引用OxyPlot.dll和OxyPlot.Wpf.dll
并在后台程序中引用并申明数据源
using OxyPlot;
using OxyPlot.Axes;
PlotModel _mPlotModelData = new PlotModel();
public PlotModel PlotModelData
{
get { return _mPlotModelData; }
set
{
_mPlotModelData = value;
OnPropertyChanged("PlotModelData");
}
}
b. 在前端界面引用xmlns:oxy="clr-namespace:OxyPlot.Wpf;assembly=OxyPlot.Wpf"
直接插入绘图控件:<oxy:PlotView x:Name="_ploter" Grid.Row="1" Model="{Binding PlotModelData}" />
c. 申明坐标轴,包括横坐标和纵坐标
public LogarithmicAxis _X_LinearAxis; // X轴
public LinearAxis _YL_LinearAxis; // 自然增长左侧Y轴
public LinearAxis _YR_LinearAxis; // 右侧Y轴
d. 初始化坐标轴,并添加到数据源的坐标轴列表(注意:纵坐标的Key值是各个点识别的关键,添加点的时候通过Key值识别是哪条坐标轴)
PlotModelData = new PlotModel(); _X_LinearAxis = new LogarithmicAxis() { Title = "频率(Hz)", Position = AxisPosition.Bottom, MajorGridlineStyle = LineStyle.None, FontSize = 13, MajorGridlineColor = OxyColor.FromArgb(40, 0, 0, 139), MinorGridlineColor = OxyColor.FromArgb(20, 0, 0, 139), MinorGridlineStyle = LineStyle.Solid, }; _YL_LinearAxis = new LinearAxis() { Title = "功率值(W)", Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.None, FontSize = 13, PositionTier = 6, Key = "Limit", MajorGridlineColor = OxyColor.FromArgb(40, 10, 10, 139), MinorGridlineColor = OxyColor.FromArgb(20, 5, 5, 139), MinorGridlineStyle = LineStyle.Solid, }; _YR_LinearAxis = new LinearAxis() { Title = "幅度值(dBm)", Position = AxisPosition.Right, MajorGridlineStyle = LineStyle.None, FontSize = 13, PositionTier = 6, Key = "Range", MajorGridlineColor = OxyColor.FromArgb(40, 10, 10, 139), MinorGridlineColor = OxyColor.FromArgb(20, 5, 5, 139), MinorGridlineStyle = LineStyle.Solid, }; PlotModelData.Axes.Add(_X_LinearAxis); PlotModelData.Axes.Add(_YR_LinearAxis); PlotModelData.Axes.Add(_YL_LinearAxis);e
e. 初始化曲线,并添加到数据源的Series列表
var lineSeriesStandardLimit = new OxyPlot.Series.LineSeries() { MarkerType = MarkerType.Circle, StrokeThickness = 2, YAxisKey = "Limit", Title = "Limit", Color = OxyColors.Green }; var lineSeriesStandardRange = new OxyPlot.Series.LineSeries() { MarkerType = MarkerType.Circle, StrokeThickness = 2, YAxisKey = "Range", Title = "Range", Color = OxyColors.Red }; var lineSeriesStandardVol = new OxyPlot.Series.LineSeries() { MarkerType = MarkerType.Circle, StrokeThickness = 2, YAxisKey = "Limit", Title = "Vol", Color = OxyColors.Blue }; PlotModelData.Series.Add(lineSeriesStandardLimit); PlotModelData.Series.Add(lineSeriesStandardRange); PlotModelData.Series.Add(lineSeriesStandardVol);
f. 添加坐标点到曲线上,并绘制曲线
for (int i = 0; i < 10000; i++)
{
dicSeriesLimit["Limit"].Points.Add(new DataPoint(i, i * 10));
dicSeriesRange["Range"].Points.Add(new DataPoint(i, i * 8));
dicSeriesVol["Vol"].Points.Add(new DataPoint(i, i * 5));
Thread.Sleep(1000);
PlotModelData.InvalidatePlot(true);
}
这样就完成了曲线的绘制,但这种方式有个问题,就是如果在主线程中直接调用绘图部分,因为耗时比较长,所以就会使主线程卡死。为了解决这个问题,就需要考虑到异步线程。
注意:最后一行代码,是更新曲线的方式,想要动态效果就得每添加一个点就调用一次。
2. 异步线程有很多种方式,我用到的主要有三种,第一种是Async/Await的方式,第二种是AutoResetEvent(主要是用来同步资源的,但也可以用来防止线程卡死的问题),第三种是Task.Run的方式。
a. 个人认为最简单的Task.Run方式
这种方式很简单,比如上面绘制曲线图的例子中的使用,可以用下面的方式:
var t = Task.Run(() =>
{
AddPoints();
});
b. AutoResetEvent的用法
1. 首先定义一个AutoResetEvent变量:
private AutoResetEvent autoReset = new AutoResetEvent(false);
2. 然后在需要同步数据的地方判断是否需要等待信号
for (int i = 0; i < 100; i++)
{
if (isStop)
autoReset.WaitOne();
dicSeriesLimit["Limit"].Points.Add(new DataPoint(i, i * 10));
dicSeriesRange["Range"].Points.Add(new DataPoint(i, i * 8));
dicSeriesVol["Vol"].Points.Add(new DataPoint(i, i * 5));
Thread.Sleep(500);
_YL_LinearAxis.Maximum = i * 10 + 100;
_YL_LinearAxis.Minimum = -100;
_YR_LinearAxis.Minimum = -100;
PlotModelData.InvalidatePlot(true);
}
3. 在合适的地方重置autoReset
private void TestBtn_Click(object sender, RoutedEventArgs e)
{
isStop = isStop == true ? false : true;
if (!isStop)
autoReset.Set();
}
效果就是当第一次点击TestBtn时曲线绘制将暂停,当再次点击button时又开始绘制曲线