调用线程必须是 STA 线程,因为它被许多 UI 个具有 livechart 的组件所需要
The calling thread must be an STA thread because it is required by many UI components with livechart
也许我的问题是重复的,但实际上我尝试了其他解决方案,但无法解决问题。 Plot 窗体不是我的主窗体,但它从主窗体打开。在主程序中,我在 Main() 之前有 [STAThread]。如果 sql table 中的数据发生任何变化,我想定期 运行 Ploting() 更新绘图。
我的代码如下:
namespace MainProject1{
public partial class Plot : Form
{
System.Timers.Timer timer = new System.Timers.Timer();
BackgroundWorker PlotWorker;
public Plot()
{
InitializeComponent();
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
PlotWorker = new BackgroundWorker();
PlotWorker.DoWork += new DoWorkEventHandler(PlotWorker_DoWork);
PlotWorker.WorkerSupportsCancellation = true;
//Ploting(); //here it works
}
void PlotWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
//Ploting(); //here also receive the error.
if (PlotWorker.CancellationPending)
{
e.Cancel = true;
return;
}
}
}
private void timer_Elapsed(object sender, ElapsedEventArgs elapsedEventArg)
{
Ploting(); //here i receive the error
}
private void test_Click(object sender, EventArgs e)
{
timer.AutoReset = true;
timer.Interval = 100;
timer.Start();
}
private void Ploting()
{
try
{
var list1 = new List<double>();
var list2 = new List<double>();
SqlResultTable rt = new SqlResultTable();
rt.ReadtoPlot(84, "readingS", ref list1, ref list2); //here i read the data from sql. it works corrrect.
cartesianChart1.Series = new SeriesCollection
{
new ScatterSeries
{
Title = "Setpoints",
Values = new ChartValues<double>(list1),
PointGeometry = DefaultGeometries.Diamond,
StrokeThickness = 2,
Fill = Brushes.Transparent,
},
new LineSeries
{
Title = "UUT readings",
Values = new ChartValues<double>(list2),
LineSmoothness = 0,
PointGeometrySize = 10
},
};
cartesianChart1.DataClick += CartesianChart1OnDataClick;
}
catch(Exception err)
{
MessageBox.Show(err.Message);
}
}
private void CartesianChart1OnDataClick(object sender, ChartPoint chartPoint)
{
MessageBox.Show("You clicked (" + chartPoint.X + "," + chartPoint.Y + ")");
}
}
}
当我在构造函数中使用 Ploting() 方法时,它可以工作,但是当我在 worker 或 timer_Elapsed 中使用它时,我再次收到错误。我知道它与线程有关,但不知道如何解决。我试图在后台工作程序或 timer_Elapsed 中创建新线程,但收到另一个错误。
很抱歉这个问题。我是 C# 新手!
新更新:委托代码:
namespace MainProject1{
public partial class Plot : Form
{
public delegate void plotDel(); //**** new added ***
System.Timers.Timer timer = new System.Timers.Timer();
BackgroundWorker PlotWorker;
public Plot()
{
InitializeComponent();
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
PlotWorker = new BackgroundWorker();
PlotWorker.DoWork += new DoWorkEventHandler(PlotWorker_DoWork);
PlotWorker.WorkerSupportsCancellation = true;
plotDel pl1 = OnPlotNeeded; //**** new added ***
//Ploting(); //here it works
}
protected virtual void OnPlotNeeded() //**** new added ***
{
Ploting();
}
void PlotWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
//Ploting(); //here also receive the error.
if (PlotWorker.CancellationPending)
{
e.Cancel = true;
return;
}
}
}
private void timer_Elapsed(object sender, ElapsedEventArgs elapsedEventArg)
{
System.Threading.SynchronizationContext.Current.Post(new System.Threading.SendOrPostCallback(o => Ploting()), null); //when i add this line, it does not go to next line(no error, no action) and without it, again receive the error as before!
OnPlotNeeded();
}
private void test_Click(object sender, EventArgs e)
{
timer.AutoReset = true;
timer.Interval = 100;
timer.Start();
}
private void Ploting()
{
// the inside code is same as first code
}
private void CartesianChart1OnDataClick(object sender, ChartPoint chartPoint)
{
MessageBox.Show("You clicked (" + chartPoint.X + "," + chartPoint.Y + ")");
}
}
}
问题大概是 timer_Elapsed
没有在 UiThread 上执行(实际上这就是异常试图告诉你的)。但是您可以将对 Ploting()
的调用委托回 UiThread。
试试这个 timer_Elapsed
的变体
private void timer_Elapsed(object sender, ElapsedEventArgs elapsedEventArg)
{
System.Threading.SynchronizationContext.Current.Post(delegate { this.Ploting(); }, null);
}
并且还将对 Plotting()
的所有其他调用替换为延迟。
编辑:
在创建时将 SynchronizationContext
保存在 class 变量中,如
private readonly SynchronizationContext syncContext;
public Plot()
{
InitializeComponent();
this.syncContext = System.Threading.SynchronizationContext.Current;
...
}
然后使用此上下文调用 Plotting()
,如下所示:
private void Timer_Elapsed(object sender, ElapsedEventArgs e) => this.DelagetePloting();
private void DelagetePloting()
=> this.syncContext.Post(delegate
{
this.Ploting();
}, null);
private void Ploting()
{
// your work
}
也许我的问题是重复的,但实际上我尝试了其他解决方案,但无法解决问题。 Plot 窗体不是我的主窗体,但它从主窗体打开。在主程序中,我在 Main() 之前有 [STAThread]。如果 sql table 中的数据发生任何变化,我想定期 运行 Ploting() 更新绘图。 我的代码如下:
namespace MainProject1{
public partial class Plot : Form
{
System.Timers.Timer timer = new System.Timers.Timer();
BackgroundWorker PlotWorker;
public Plot()
{
InitializeComponent();
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
PlotWorker = new BackgroundWorker();
PlotWorker.DoWork += new DoWorkEventHandler(PlotWorker_DoWork);
PlotWorker.WorkerSupportsCancellation = true;
//Ploting(); //here it works
}
void PlotWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
//Ploting(); //here also receive the error.
if (PlotWorker.CancellationPending)
{
e.Cancel = true;
return;
}
}
}
private void timer_Elapsed(object sender, ElapsedEventArgs elapsedEventArg)
{
Ploting(); //here i receive the error
}
private void test_Click(object sender, EventArgs e)
{
timer.AutoReset = true;
timer.Interval = 100;
timer.Start();
}
private void Ploting()
{
try
{
var list1 = new List<double>();
var list2 = new List<double>();
SqlResultTable rt = new SqlResultTable();
rt.ReadtoPlot(84, "readingS", ref list1, ref list2); //here i read the data from sql. it works corrrect.
cartesianChart1.Series = new SeriesCollection
{
new ScatterSeries
{
Title = "Setpoints",
Values = new ChartValues<double>(list1),
PointGeometry = DefaultGeometries.Diamond,
StrokeThickness = 2,
Fill = Brushes.Transparent,
},
new LineSeries
{
Title = "UUT readings",
Values = new ChartValues<double>(list2),
LineSmoothness = 0,
PointGeometrySize = 10
},
};
cartesianChart1.DataClick += CartesianChart1OnDataClick;
}
catch(Exception err)
{
MessageBox.Show(err.Message);
}
}
private void CartesianChart1OnDataClick(object sender, ChartPoint chartPoint)
{
MessageBox.Show("You clicked (" + chartPoint.X + "," + chartPoint.Y + ")");
}
}
}
当我在构造函数中使用 Ploting() 方法时,它可以工作,但是当我在 worker 或 timer_Elapsed 中使用它时,我再次收到错误。我知道它与线程有关,但不知道如何解决。我试图在后台工作程序或 timer_Elapsed 中创建新线程,但收到另一个错误。 很抱歉这个问题。我是 C# 新手!
新更新:委托代码:
namespace MainProject1{
public partial class Plot : Form
{
public delegate void plotDel(); //**** new added ***
System.Timers.Timer timer = new System.Timers.Timer();
BackgroundWorker PlotWorker;
public Plot()
{
InitializeComponent();
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
PlotWorker = new BackgroundWorker();
PlotWorker.DoWork += new DoWorkEventHandler(PlotWorker_DoWork);
PlotWorker.WorkerSupportsCancellation = true;
plotDel pl1 = OnPlotNeeded; //**** new added ***
//Ploting(); //here it works
}
protected virtual void OnPlotNeeded() //**** new added ***
{
Ploting();
}
void PlotWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
//Ploting(); //here also receive the error.
if (PlotWorker.CancellationPending)
{
e.Cancel = true;
return;
}
}
}
private void timer_Elapsed(object sender, ElapsedEventArgs elapsedEventArg)
{
System.Threading.SynchronizationContext.Current.Post(new System.Threading.SendOrPostCallback(o => Ploting()), null); //when i add this line, it does not go to next line(no error, no action) and without it, again receive the error as before!
OnPlotNeeded();
}
private void test_Click(object sender, EventArgs e)
{
timer.AutoReset = true;
timer.Interval = 100;
timer.Start();
}
private void Ploting()
{
// the inside code is same as first code
}
private void CartesianChart1OnDataClick(object sender, ChartPoint chartPoint)
{
MessageBox.Show("You clicked (" + chartPoint.X + "," + chartPoint.Y + ")");
}
}
}
问题大概是 timer_Elapsed
没有在 UiThread 上执行(实际上这就是异常试图告诉你的)。但是您可以将对 Ploting()
的调用委托回 UiThread。
试试这个 timer_Elapsed
private void timer_Elapsed(object sender, ElapsedEventArgs elapsedEventArg)
{
System.Threading.SynchronizationContext.Current.Post(delegate { this.Ploting(); }, null);
}
并且还将对 Plotting()
的所有其他调用替换为延迟。
编辑:
在创建时将 SynchronizationContext
保存在 class 变量中,如
private readonly SynchronizationContext syncContext;
public Plot()
{
InitializeComponent();
this.syncContext = System.Threading.SynchronizationContext.Current;
...
}
然后使用此上下文调用 Plotting()
,如下所示:
private void Timer_Elapsed(object sender, ElapsedEventArgs e) => this.DelagetePloting();
private void DelagetePloting()
=> this.syncContext.Post(delegate
{
this.Ploting();
}, null);
private void Ploting()
{
// your work
}