从另一个线程更新oxyplot模型

时间:2017-11-09 21:29:50

标签: c# wpf multithreading user-interface oxyplot

这是我第一次使用WPF而且我遇到了问题。 我无法从另一个线程更新我的oxyplot模型。 我可以在另一个线程中得出点,但是当我试图在另一个线程中这样做时,没有任何事情发生。 现在我有了这段代码:

 private void Button_Click(object sender, RoutedEventArgs e)
    {
        doComputingThread compute = new doComputingThread();
        Thread _MainThread = new Thread(new ThreadStart(compute.MainThread));
        _MainThread.Start();
    }

class doComputingThread{
   public doComputingThread()
    {
        DataPlot = new PlotModel();
        DataPlot.Series.Add(new LineSeries());
    }
    public void MainThread()
    {
        bool flag;

        _timer = new System.Timers.Timer();
        _timer.Interval = 10;
        _timer.Elapsed += (sender, e) => { GuiRefresher(true); };
        _timer.Enabled = true;

        Thread _ComputeThread = new Thread(new ThreadStart(ProducerThread));
        _ComputeThread.Start();
    }
    public void ProducerThread()
    {
        //populate queue

        int X = 0;
        int Y = 0;

        for (double t = 0; t < 2 * 3.14; t = t + 0.1)
        {
            X = (int)(Math.Cos(t) * 5000);
            Y = (int)(Math.Sin(t) * 5000);


            Coordinate.X = X;
            Coordinate.Y = Y;
            _queue.Enqueue(Coordinate);
        }
    public void GuiRefresher(object flag)
    {

        if (_queue.TryDequeue(out Coordinate))
        {
            //this part didn't refresh my oxyplot
            Dispatcher.CurrentDispatcher.Invoke(() =>
           {
               (DataPlot.Series[0] as LineSeries).Points.Add(new DataPoint(Coordinate.X, Coordinate.Y));
               DataPlot.InvalidatePlot(true);
           });

}

所有工作都按预期工作,但Dispatcher.CurrentDispatcher时的部分除外。我不明白为什么我的情节没有刷新。

我有一些想法,我不明白在这种情况下使用WPF的UI线程是什么线程,也许我应该在doComputingThread构造函数中启动我的线程。

的Xaml:

<ui:WslMainWindow x:Class="fpga_control.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:oxy="http://oxyplot.org/wpf"
    xmlns:local="clr-namespace:fpga_control"
    xmlns:ui="clr-namespace:Keysight.Ccl.Wsl.UI;assembly=Keysight.Ccl.Wsl"
    xmlns:DynamicVectorImages="clr-namespace:Keysight.Ccl.Wsl.UI.Controls.DynamicVectorImages;assembly=Keysight.Ccl.Wsl" 
    Title="Example 1 (WPF)" Height="461.311" Width="621.393">
<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>
<Grid >
    <oxy:PlotView Model="{Binding DataPlot}" Margin="10,10,152,0" Height="418" VerticalAlignment="Top"/>
    <Button Content="Button" HorizontalAlignment="Left" Margin="464,10,0,0" VerticalAlignment="Top" Width="137" Height="38" RenderTransformOrigin="0.303,1.929" Click="Button_Click"/>
    <TextBox x:Name="txb" HorizontalAlignment="Left" Height="23" Margin="468,53,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="133"/>
</Grid>

1 个答案:

答案 0 :(得分:2)

您的代码似乎是在循环中向队列添加相同的坐标。您每次在循环期间都不会创建新的Coordinate实例。

另外,_queue.TryDequeue(out Coordinate)行会为我产生语法错误,因为它试图将Coordinate用作变量而不是类。

如果没有Coordinate的定义,我就无法确认这一点。

另外,我无法看到您将DataPlot实际添加到表单中的位置或表单上的任何控件。您似乎没有#&#t} 39;无论如何都要显示任何内容。

最重要的是,您的示例代码不是最小,完整且可验证的示例。所以我只能猜测问题是什么。

我打算给你一个替代方案,我想会给你你想要的东西。

首先,我提供Coordinate的简单定义:

public class Coordinate
{
    public int X;
    public int Y;
}

现在我将使用Microsoft的Reactive Framework来完成代码的所有线程,计算和调度 - 这就是我如何编写Button_Click处理程序的方法:

private void Button_Click(object sender, RoutedEventArgs e)
{
    DataPlot = new PlotModel();
    DataPlot.Series.Add(new LineSeries());

    int steps = 60;
    IObservable<Coordinate> query =
        Observable
            .Generate(
                0,
                n => n < steps,
                n => n + 1,
                n => n * 2.0 * Math.PI / steps,
                n => TimeSpan.FromMilliseconds(10.0))
            .Select(t => new Coordinate()
            {
                X = (int)(Math.Cos(t) * 5000),
                Y = (int)(Math.Sin(t) * 5000),
            });

    IDisposable subscription =
        query
            .ObserveOnDispatcher()
            .Subscribe(c =>
            {
                (DataPlot.Series[0] as LineSeries).Points.Add(new DataPoint(c.X, c.Y));
                DataPlot.InvalidatePlot(true);
            });
}

使用此代码,您根本不再需要doComputingThread课程。

Observable.Generate代码为原始代码生成的t数据点生成60值,但每次只10.0一次只生成一个值t毫秒。此代码实际上是一个计时器和.Select步骤的生成者。

tCoordinate值映射为subscription值。

query.ObserveOnDispatcher()的执行。第一步是使用.Subscribe调用将值复制回UI线程,然后c => ...获取每个值并在UI线程上运行subscription.Dispose()委托。

这应该很好地更新情节。

如果你想提前停止密谋,只需致电using,它就会停止。

你需要NuGet&#34; System.Reactive&#34;和&#34; System.Reactive.Windows.Threading&#34;获得位。您还需要以下using System.Reactive; using System.Reactive.Linq; 语句来编译代码:

DataPlot

请勿忘记 - 您仍需要以某种方式将user添加到表单中。