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