2

ハードウェア スイッチを定期的にチェックするために BackgroundWorker を使用しています。低速の RS485 ネットワークで接続されているため、次のステータス更新を遅らせなければなりません。On switch Status change OK/nOK Picture Box を更新したい。これは、nOK pictureBox 上の緑色の OK pictureBox として実現されます。ここでは実際の作業は行われません。

拡張性のために、Backgroundworker を使用することにしました。最後に、非表示のワーカーが必要です。

  1. 3 つのスイッチのステータスをグローバルに提供し、
  2. StatusChange の更新 PictureBoxes。

問題 の説明 BackgroundWorker が開始されると、期待どおりに動作します。ただし、GUI はフリーズします。

私は何を試しましたか?MSDN BackgroundWorker Class Note 1には 、 ProgressChanged を介して GUI を更新する必要があると書かれています。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...? - lazlokiss88 の回答からの 3 つの可能性。

4

2 に答える 2

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");
    }
于 2012-09-25T11:10:15.137 に答える
1

まず、アクティブなバックグラウンドシードをスリープ状態にする必要はほとんどありません。また、なぜデリゲートをこのように構築/定義しているのかわかりません。次のようなことを試してください。

public delegate void UpdatePictureBox();
myDelegate = new UpdatePictureBox(UpdatePictureboxMethod);

その後、あなたは方法を持っていますUpdatePictureBoxMethod

private void UpdatePictureBoxMethod()
{
    this.pictureBox1.Image = Properties.Resources.SomeImage;
}

または、更新する画像を渡す、類似したもの。

または、この方法を使用することもできます(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...
}

これがお役に立てば幸いです。

于 2012-09-25T11:19:08.620 に答える