C#Backgroundworker:定期更新PictureBox的方法是什么?

时间:2012-09-25 10:56:40

标签: c# .net multithreading backgroundworker picturebox

我正在使用BackgroundWorker定期检查硬件开关。由于它通过慢速RS485网络连接,我必须延迟下一次状态更新。 在开关状态更改时,我想更新OK / nOK图片框。这是在nOK pictureBox上实现的绿色OK图片框。这里没有真正的工作。

为了扩展性,我决定使用Backgroundworker。最后我想要一个隐藏的工人,

  1. 全局提供三个交换机的状态和
  2. 更新StatusChange PictureBoxes。
  3. 问题描述 启动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的三种可能性。

2 个答案:

答案 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...
}

我希望这会有所帮助。