#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 напрямую.