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