WPF利用Interactive Data Display实现示波器(C#多线程和WPF多线程)

2018.8.7

首先,今天还没有实现示波器,项目中需要这个功能,在探索中有了一点进展,先记录下来。

首先选择绘图的控件,经过筛选选择了Interactive Data Display,这是微软官方的开源库,github网址为https://github.com/Microsoft/InteractiveDataDisplay.WPF

实现的chart控件,能够相应滑轮、鼠标拖动、放大缩小,很适合作为示波器的背景。

关于Interactive Data Display的引用,可以考虑下载dll文件,或者直接用VS自带的Nuget包管理工具安装,具体教程可百度。

我试着修改了Interactive Data Display项目中的事例,增加了多线程来绘图。

当然还不是示波器期望的图,不过之后的重心放在绘图即可。

xaml代码如下

<Window x:Class="BarChartSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d3="clr-namespace:InteractiveDataDisplay.WPF;assembly=InteractiveDataDisplay.WPF"
        mc:Ignorable="d"
        Title="Bar chart" Height="600" Width="800">
    <Grid>
        <d3:Chart Name="plotter">
            <d3:Chart.Title>
                <TextBlock HorizontalAlignment="Center" FontSize="18" Margin="0,5,0,5">Bar chart sample</TextBlock>
            </d3:Chart.Title>
            <d3:BarGraph Name="barChart" Color="Green" />
        </d3:Chart>
    </Grid>
</Window>

后台代码如下:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Thread t = new Thread(tem);
            t.Start();
  
        }
        public void tem()
        {
            int N = 100;
            double[] y = new double[N];

            Random r = new Random();
            double k;
            long j = 100;
            while (j > 0)
            {
                System.Threading.Thread.Sleep(100);
                for (int i = 0; i < N; i++)
                {
                    y[i] = y[i] + 10;
                }
                Dispatcher.Invoke(new Action(delegate { barChart.PlotBars(y); }
                    ));
               
                j--;
            }
        }
    }

效果如下:

 其中有两个问题,一是绘图的函数需要使用多线程,二是绘图函数中修改控件的时候,WPF中只有UI线程才能操作UI元素,非UI线程要访问UI时就会报异常。可以使用Dispatcher.BeginInvoke()与Invoke()方法。BeginInvoke()异步执行,不等待委托结束就更新,Invoke()同步执行,需等待委托执行完,具体用法见上。

示波器的具体实现等进一步探索。

今天来把坑补上,根据之前的分析,我们只需要更换chart中的内容,即更换绘制的图即可。

我们用来绘制曲线的控件是LineGraph,同样是在Interactive Data Display中实现的。由于以前没有绘制过图像,天真的我看到这个控件的名字还以为是只能画直线,所以我的确先是实现了直线。如下图

 其实呢,无论是什么曲线,关键在于x、y的坐标而已。因此采用数学方法实现x y 的坐标即可。

最终的代码在下:

xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d3="clr-namespace:InteractiveDataDisplay.WPF;assembly=InteractiveDataDisplay.WPF"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="示波器模拟" Margin="20,10,0,0"
                       FontSize="15" FontWeight="Bold"/>
        </StackPanel>
        <d3:Chart x:Name="chart" Margin="10,10,20,10" Grid.Row="1">

        </d3:Chart>
    </Grid>
</Window>

后台C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using InteractiveDataDisplay.WPF;
using System.Threading;
namespace WpfApplication1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        
        LineGraph aaa = new LineGraph();
        List<Double> b = new List<Double>();
        List<Double> c = new List<Double>();
        Double i = 0, q = 0;
        public MainWindow()
        {
            InitializeComponent();
            trychart();
            Thread t = new Thread(tem);
            t.Start();
          
        }

        public void trychart()
        {
            aaa.Stroke = new SolidColorBrush(Color.FromRgb(29, 80, 162));
            chart.Content = aaa;
            
        }
        public void tem()//line graph
        {
           
              while(i<10000){
                 System.Threading.Thread.Sleep(1000);
                b.Add(i);
                c.Add(q);
                i = i + 10;
                q = q + 5;

                Dispatcher.BeginInvoke(new Action(delegate
                {
                    List<Double> a = new List<Double>();
                    List<Double> d = new List<Double>();
                    int j,m;
                    for (m= 0; m < b.Count(); m++) a.Add(b[m]);
                    for ( j = 0; j < b.Count(); j++) d.Add(b[j]);
                    aaa.Plot(d,a);
         
                }));
            }
        }

        public void temqq()
        {
            while (i < 10000)
            {
                System.Threading.Thread.Sleep(100);
                b.Add(i);
                c.Add(q);
                q = Math.Sin(i*0.4)*10;
                i = i + 1;

                Dispatcher.BeginInvoke(new Action(delegate
                {
                    List<Double> a = new List<Double>();
                    List<Double> d = new List<Double>();
                    int j, m;
                    for (m = 0; m < c.Count(); m++) a.Add(c[m]);
                    for (j = 0; j < b.Count(); j++) d.Add(b[j]);
                    aaa.Plot(d, a);
 
                }));
            }
        }

        
    }
}

最终效果

最后一点说明:

1、关于最后代码,这不是项目中的代码,而是我在探索Interactive Data Display中试验,所以代码可能比较乱,命名随意,请谅解,我也懒得再修改了,有问题可以留言。 在c#代码中,tem方法是绘制直线的,temqq是绘制曲线的,在线程中修改相应的调用即可。

2、我在学习Interactive Data Display的过程中,基本没有什么资料,仅有github的源码,既然没有文档...所以写下这个供后来使用Interactive Data Display的人参考一下。

PS: 这个小程序还有一个问题就是,如果直接关掉窗口,其实绘图的线程还在运行,程序没有真正结束,但这个问题并不是文章的重点,所以忽略。

猜你喜欢

转载自blog.csdn.net/tengfeipu/article/details/81480045