WPF異步繪制曲線圖


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM