我正在使用BackgroundWorker定期检查硬件开关。由于它通过慢速RS485网络连接,我必须延迟下一次状态更新。 在开关状态更改时,我想更新OK / nOK图片框。这是在nOK pictureBox上实现的绿色OK图片框。这里没有真正的工作。
为了扩展性,我决定使用Backgroundworker。最后我想要一个隐藏的工人,
问题描述 启动BackgroundWorker后,它将按预期工作。然而,GUI冻结了。
我尝试了什么? MSDN BackgroundWorker Class Note 1 {{3}} 说,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
计时器)
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 Note相反) - 在主线程中工作?
c)定期更新PictureBox的正确方法是什么? DoWork,ProgressChanged,RunWorkerCompleted ......? - 来自laszlokiss88的三种可能性。
答案 0 :(得分:5)
您可以通过Dispatcher或Control.Begininvoke(winforms)从DoWork事件更新UI,或者您可以通过BackgroundWorker的ProgressChanged事件来执行此操作:
public MainWindow()
{
InitializeComponent();
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerAsync();
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// You are in the main thread
// Update the UI here
string data = (string)e.UserState;
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// You are in a worker thread
(sender as BackgroundWorker).ReportProgress(0, "right");
}
答案 1 :(得分:1)
首先,您几乎不需要将活跃的背景放入睡眠状态。我也不确定你为什么用这种方式构建/定义委托,尝试像
这样的东西public delegate void UpdatePictureBox();
myDelegate = new UpdatePictureBox(UpdatePictureboxMethod);
然后你有一个方法UpdatePictureBoxMethod
private void UpdatePictureBoxMethod()
{
this.pictureBox1.Image = Properties.Resources.SomeImage;
}
或simalar,您传入图片以更新为。
或者,您可以使用(bgWorker as BackgroundWorker).ReportProgress(progress, object);
方法。所以从后台线程中调用
(bgWorker as BackgroundWorker).ReportProgress(progressBarValue, infoBall);
此处,课程IfoBall
将包含您所有重要信息
class InfoBall
{
public int nProgressBar { get; set; }
public int nMaxProgressBar { get; set; }
public Image image { get; set; }
}
然后您可以将此对象传递回UI线程并进行更新
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// On UI thread.
InfoBall someBall = (InfoBall)e.UserState;
this.pictureBox1.Image = someBall.image;
// etc...
}
我希望这会有所帮助。