這篇文章有兩個內容,第一個是繪制曲線圖(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時又開始繪制曲線