1. 程式人生 > >WPF利用Interactive Data Display實現示波器(C#多執行緒和WPF多執行緒)

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: 這個小程式還有一個問題就是,如果直接關掉視窗,其實繪圖的執行緒還在執行,程式沒有真正結束,但這個問題並不是文章的重點,所以忽略。