Вызов метода из основной формы для запуска в дочерней форме / потоках

#c# #lightningchart

#c# #lightningchart

Вопрос:

Я использую WinForms C # .NET 4.6

У меня есть 3 формы. Основная, Form1 и Form2 Каждая форма выполняется в другом потоке. Form1 и Form2 имеют элемент управления Arction LightningChart.. Каждая из этих форм имеет метод с именем «PlotUpdate» для обновления диаграмм случайными значениями.

Основная форма имеет таймер, который запускается каждые 100 мс. Я хочу вызвать PlotUpdate из события основного таймера, чтобы обновить диаграммы в Form1 и Form2. Метод PlotUpdate работает, если элемент управления Arction LightningChart помещен в основную форму, в противном случае это не так.

Как я могу этого добиться?

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();

        }
    }
}
  

Комментарии:

1. По-видимому, именно так вы открываете свои формы, которые вас привлекают, нет необходимости запускать формы в разных потоках и вызывать Application.Run . Просто создайте форму и вызовите ShowDialog

2. @TheGeneral, ну, я хочу запускать формы в разных потоках, потому что рендеринг позже будет интенсивным для запуска в том же потоке пользовательского интерфейса. Использование ShowDialog() только без создания нового потока приведет к запуску всех форм в одном потоке. Это то, чего я хочу избежать

3. Что ж, это имеет больше смысла. Ну, ваша первая проблема — это область ваших форм Form1 f1 = new Form1(); , вам нужно будет хранить их глобально (как члены экземпляра)

4. Отредактировал это. Что еще?

5. «Основная форма имеет таймер, который запускается каждые 100 мс. Я хочу вызвать PlotUpdate из события основного таймера, чтобы обновить диаграммы в Form1 и Form2. » Почему бы просто не установить таймер в Form1 и Form2 и не обновить его самостоятельно? Тогда вам не нужно беспокоиться о том, что все они выполняются в разных потоках…

Ответ №1:

Вы на правильном пути. Код

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

создаст для вас форму Windows, работающую в собственном потоке. Только не забывайте, что потоки пользовательского интерфейса должны выполняться как AppartmentState.STA . Поэтому включите строку thread1.SetApartmentState(ApartmentState.STA);
Это не единственный способ создать несколько потоков пользовательского интерфейса в WinForms.

Теперь о элементе управления / компоненте LightningChart. LightningChart (R) .NET должен быть обновлен в главном потоке, как и для большинства элементов управления пользовательского интерфейса. При использовании другого (фонового) потока в приложении все обновления пользовательского интерфейса из потока должны проходить через Invoke (Control .Вызвать () в WinForms). Уже существует решение WPF с несколькими LightningChart и несколькими потоками пользовательского интерфейса. Ваше приложение WinForms должно / может следовать очень похожей логике. Первое, что вам нужно, это список диаграмм, которыми вы планируете управлять / обращаться к ним позже. Во-вторых, не забывайте эту диаграмму.Должен быть установлен родительский элемент, который является самой формой или панелью в этой форме. Наконец, вы вызываете диаграмму вызовов.Вызовите () для каждого экземпляра LightningChart, который вы хотите обновить в обработчике тиков. Например,

     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 });
            });
        }
    }
  

Первая часть фактически использует (пустой) поток формы для создания ChartWindow (в основном создает диаграмму и устанавливает родительскую форму в определенную форму) внутри нее.

Вторая часть добавляет новую случайную точку к каждой диаграмме. Там пользовательский метод AddSeriesPoints определен в классе ChartWindow для добавления массива точек и прокрутки. Вы также можете использовать методы LightningChart напрямую.