【发布时间】:2012-09-16 21:16:46
【问题描述】:
我正在使用 BackgroundWorker 定期检查硬件开关。由于它是通过慢速 RS485 网络连接的,我不得不延迟下一次状态更新。 在切换状态更改时,我想更新 OK/nOK 图片框。这被实现为一个绿色的 OK pictureBox 在一个 noOK pictureBox 上。这里没有做任何实际的工作。
为了可扩展性,我决定使用 Backgroundworker。最后我想要一个隐藏的工人,它
- 全局提供三个开关的状态和
- StatusChange 图片框的更新。
问题描述 一旦启动了 BackgroundWorker,它就会按预期工作。但是 GUI 冻结。
我尝试了什么? MSDN BackgroundWorker Class Note 1 说,GUI 应该通过 ProgressChanged 更新。我试图通过 Worker_Switch.ReportProgress(fakeProgress++) 引发此事件但失败了。 PictureBox 不再更新。
设计师的片段
this.Worker_Switch = new System.ComponentModel.BackgroundWorker();
//
// Worker_Switch
//
this.Worker_Switch.WorkerSupportsCancellation = true;
this.Worker_Switch.DoWork += new System.ComponentModel.DoWorkEventHandler(this.Worker_Switch_DoWork);
来自主窗体的片段
delegate void SetEventCallback(object sender, DoWorkEventArgs e); // Threadsafe calls for DoWork
private void btnBackgroundworker_Click(object sender, EventArgs e)
{
if (!Worker_Switch.IsBusy)
{
Worker_Switch.RunWorkerAsync();
}
}
private void Worker_Switch_DoWork(object sender, DoWorkEventArgs e)
{
// Worker Thread has no permission to change PictureBox "pictureBoxSwitchrightOK"
// Therefore this method calls itsself in the MainThread, if necessary.
while (!Worker_Switch.CancellationPending)
{
if (this.pictureBoxSwitchrightOK.InvokeRequired) // Worker Thread
{
System.Threading.Thread.Sleep(400);
SetEventCallback myCall = new SetEventCallback(Worker_Switch_DoWork);
this.Invoke(myCall, new object[] { sender, e });
}
else // Main Thread
{
// Turns OK Picture Box invisible, if nOk State (Switch pushed)
pictureBoxSwitchrightOK.Visible = SwitchOK("right"); // true: OK (green)
this.Refresh();
}
}
private bool SwitchOK(string rightOrLeft) // select one of the switches
{ (...)} // gets hardware switch status
编辑:特别感谢 laszlokiss88(3 种可能性)和 JMK(为了简单起见,使用工具箱中的 System.Windows.Forms Timer)
Toolbox 中的这种替代方法也有效:
this.timer_Switch.Enabled = true;
this.timer_Switch.Interval = 400;
this.timer_Switch.Tick += new System.EventHandler(this.timer_Switch_Tick);
private void timer_Switch_Tick(object sender, EventArgs e)
{
motorSwitchControl.Init(); // globally available Switch status
SwitchRight = SwitchOK("right");
SwitchRightOK.Visible = SwitchRight;
SwitchLeft = SwitchOK("left"); // globally available Switch status
SwitchLeftOK.Visible = SwitchLeft;
SwitchAllOK = SwitchRight & SwitchLeft;
this.Refresh();
}
a) Sleep() 实际上发生在工作线程中是否正确? - 没有主线程
b) 如果我在 DoWork 中操作用户界面对象,会出现什么问题? (与 MSDN 注释相反) - 在主线程中工作?
c) 定期更新 PictureBox 的正确方法是什么? DoWork、ProgressChanged、RunWorkerCompleted...? - 来自 laszlokiss88 答案的三种可能性。
【问题讨论】:
-
如果你想在线程中操作控件,那么你需要
Dispatcher类来做到这一点。 -
不一定。 ProgressChanged 事件处理程序在创建 BackgroundWorker 的线程上执行(很可能是 UI 线程)。
-
我建议去代理。只需在 Google 上搜索“代表更新 ui”
-
Windows 窗体有它自己的线程安全计时器。你不能用它来检查你的交换机的状态,然后根据需要更新图片框吗?
-
有多个定时器,
System.Timers中的一个在单独的线程上运行,System.Windows.Forms中的一个与 UI 在同一个线程中运行,从而更容易更新 UI 元素.如果您的应用程序是 Windows 窗体,我将使用工具箱中的 Windows 窗体计时器,并检查和更新Timer_Tick事件中的图片框
标签: c# .net multithreading backgroundworker picturebox