WPF利用Interactive Data Display實現示波器(C#多執行緒和WPF多執行緒)
2018.8.7(已實現)
首先,今天還沒有實現示波器,專案中需要這個功能,在探索中有了一點進展,先記錄下來。
實現的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: 這個小程式還有一個問題就是,如果直接關掉視窗,其實繪圖的執行緒還在執行,程式沒有真正結束,但這個問題並不是文章的重點,所以忽略。