从主窗体调用方法以在子窗体/线程上运行

问题描述

我正在使用WinForms C#.NET 4.6

我有3种表格。 Main,Form1和Form2每个窗体都在不同的线程上运行。 Form1和Form2具有Arction LightningChart控件。每个这些Forms都有一个名为“ PlotUpdate”的方法,用于使用随机值更新图表。

主窗体具有一个计时器,该计时器每100毫秒触发一次。我想从Main Timer的滴答事件中调用PlotUpdate来更新Form1和Form2上的图表。如果将Arction LightningChart控件放置在“主窗体”中,则PlotUpdate方法有效,否则不起作用。

我该如何实现?

MainForm.cs

using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;

namespace MultipleUIThreads
{
    public partial class MainForm : Form
    {
        const int FormSeriesCount = 3;
        static System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();

        public MainForm()
        {
            InitializeComponent();

            myTimer.Interval = 100;
            myTimer.Tick += myTimer_Tick;
            myTimer.Start();
        }

        private void Form1_Load(object sender,EventArgs e)
        {

            // Start new UI thread for Form3
            Thread thread1 = new Thread(() =>
            {
                Form1 f1 = new Form1();
                Application.Run(f1);
            });
            thread1.Start();

            // Start new UI thread for Form2
            Thread thread2 = new Thread(() =>
            {
                Form2 f2 = new Form2(); 
                Application.Run(f2);
            });
            thread2.Start();

        }

        private void myTimer_Tick(object sender,EventArgs e)
        {
            //What shall I write here
        }
    }
}

Form1.cs / Form2.cs相似

using System;
using System.Windows.Forms;
using Arction.WinForms.Charting;

namespace MultipleUIThreads
{
    public partial class Form1 : Form
    {

        int _pointsAdded = 0;
        Random _rand = new Random();
        public Form1()
        {
            InitializeComponent();
            ChartCreator.InitChart(LChart);

        }

        public void PlotUpdate()
        {
            LChart.BeginUpdate();

            double x = _pointsAdded * 0.1;
            for (int i = 0; i < 1; i++)
            {
                SeriesPoint[] points = new SeriesPoint[1];
                points[0].X = x;
                points[0].Y = _rand.Next(-100,100);
                LChart.ViewXY.PointLineseries[i].AddPoints(points,false);
            }

            LChart.ViewXY.XAxes[0].ScrollPosition = x;
            LChart.EndUpdate();

            _pointsAdded++;
        }
    }
}

ChartCreator.cs

using Arction.WinForms.Charting;
using Arction.WinForms.Charting.Axes;
using Arction.WinForms.Charting.SeriesXY;
using Arction.WinForms.Charting.Views.ViewXY;

namespace MultipleUIThreads
{
     class ChartCreator
    {
        public static void InitChart(LightningChartUltimate ulChart)
        {
            ulChart.BeginUpdate();
            ViewXY view = ulChart.ViewXY;

            AxisX xAxis = view.XAxes[0];
            xAxis.SetRange(0,20);
            xAxis.ScrollMode = XAxisScrollMode.Scrolling;

            view.YAxes.Clear();

            for (int seriesIndex = 0; seriesIndex < 1; seriesIndex++)
            {
                AxisY yAxis = new AxisY(view);
                yAxis.SetRange(-100,100);
                view.YAxes.Add(yAxis);

                PointLineseries line = new PointLineseries(view,xAxis,yAxis);
                line.PointsVisible = false;
                line.Linestyle.Color = DefaultColors.SeriesForBlackBackground[seriesIndex];
                view.PointLineseries.Add(line);
            }

            view.DropOldSeriesData = true;
            view.AxisLayout.YAxesLayout = YAxesLayout.Stacked;


            ulChart.EndUpdate();

        }
    }
}

解决方法

您在正确的轨道上。代码

            // Start new UI thread for Form3
        Thread thread1 = new Thread(() =>
        {
            Form1 f1 = new Form1();
            Application.Run(f1);
        });
        thread1.Start();

将为您创建一个在其自己的线程上运行的Windows窗体。只是不要忘记,UI线程应作为AppartmentState.STA运行。因此,请包含第thread1.SetApartmentState(ApartmentState.STA);行 这不是创建Multiple UI threads in WinForms的唯一方法。

现在有关LightningChart控件/组件。与大多数UI控件一样,应在主线程中更新LightningChart(R).NET。在应用程序中使用另一个(后台)线程时,该线程的所有UI更新都必须通过Invoke(WinForms中的Control.Invoke())进行。 已经有WPF解决方案Multiple LightningChart and multiple UI threads。您的WinForms应用程序应该/可以遵循非常相似的逻辑。 您需要做的第一件事是计划在以后处理/访问的图表列表。其次,不要忘记应该设置Chart.Parent,它是Form本身或该表单中的Panel。最后,您为要在Tick处理程序中更新的每个LightningChart实例调用Chart.Invoke()。例如,

    private void myTimer_Tick(object sender,EventArgs e)
    {
        if(_chartContainers.Count == 0)
        {
            for (int i = 0; i < _chartForms.Count; i++)
            {
                //ChartWindow cw = new ChartWindow(_chartForms[i]);
                _chartForms[i].Invoke((System.Action)delegate 
                {
                    ChartWindow cw = new ChartWindow(_chartForms[i]);
                    _chartContainers.Add(cw);
                });

            }
        }

        if (_chartForms.Count == 0)
            return;

        for (int iChart = 0; iChart < _chartForms.Count; iChart++)
        {
            ChartWindow cw = _chartContainers[iChart];
            cw.Chart.Invoke((System.Action)delegate 
            {
                cw.AddSeriesPoints(new double[] { random.NextDouble()*10 });
            });
        }
    }

第一部分实际上是使用(空)Form的线程在其中创建ChartWindow(基本上是创建Chart并将父级设置为特定的Form)。

第二部分向每个图表添加新的随机点。在那里,在ChartWindow类中定义了 AddSeriesPoints 定制方法,以添加点数组和滚动。您也可以直接使用LightningChart方法。