C#ProgressBar的交叉线程问题

时间:2011-10-30 21:49:49

标签: c# winforms multithreading progress-bar

我正在创建一个倒数计时器,允许用户选择有效时间,一旦倒计时到达零,它就会播放声音。

我已经从System.Timers命名空间中实例化了一个计时器,并将其Interval设置为1秒。我将用户指定的时间转换为秒,并在每次1函数命中时将该值减去Timer.Elapsed。一旦它达到零,这意味着倒计时已经达到零,这意味着它是播放声音的时间。

但是,只要它没有达到零,我就会将时间值减去1,我还希望使用ProgressBar函数增加progressbar.Increment

不幸的是,每当我这样做时,它都会给我一个与多线程问题有关的例外。我知道出了什么问题,但我不确定如何解决它。我是否需要在新线程上启动timer.Elapsed函数?

错误是:

  

跨线程操作无效:访问控制'CountdownProgress'   来自其创建的线程以外的线程。

此外,欢迎任何有关更好编程习惯的提示。 非常感谢!

以下是代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Timers;
using System.Media;
using System.Diagnostics;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form_CDA : Form
    {
        public Form_CDA()
        {
            InitializeComponent();
        }

        private bool m_CountdownActive; // declare global variables and instantiate timer.
        private bool m_Cancelled;
        public decimal seconds;
        public decimal minutes;
        public decimal hours;
        public decimal time;
        public System.Timers.Timer timer = new System.Timers.Timer();

        private void Form_CDA_Load(object sender, EventArgs e)
        {
            m_Cancelled = false;
            timer.AutoReset = false;
            timer.Interval = 0;
            m_CountdownActive = false;
            richTextBox1.Text = "Hello, please select an hour between 0 and 100, a minute between 0 and 59, and a second between 0 and 59. The countdown timer will play a sound when finished.";
            btn_Cancel.Enabled = false;
            seconds = 0;
            minutes = 0;
            hours = 0;
            time = 0;
            m_StatusBar.Text = "Program properly loaded, waiting for user input"; // initialize variables.
        }

        private void btn_SetCountdown_Click(object sender, EventArgs e)
        {
            seconds = numUpDown_Seconds.Value;
            minutes = numUpDown_Minutes.Value;
            hours = numUpDown_Hours.Value;
            time = (hours * 3600) + (minutes * 60) + seconds; // calculate the total time in seconds.
            if (time != 0) // if time is not zero, start timer and set up event handler timer.elapsed.
            {
                timer.Interval = 1000;
                timer.AutoReset = true;
                timer.Start();
                timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
                CountdownProgress.Maximum = (int)time;
                CountdownProgress.Minimum = 0;
                CountdownProgress.Value = 0;
            }
            else
            {
                m_StatusBar.Text = "Invalid selection of times. Try again.";
                return;
            }
            DateTime dt = DateTime.Now;
            dt.AddSeconds((double)time);
            Label_Countdown.Text = "Finishing time: " + dt.ToString(); // display end time to user.
            m_CountdownActive = true;
            btn_Cancel.Enabled = true;
            btn_SetCountdown.Enabled = false;
            numUpDown_Hours.Enabled = false;
            numUpDown_Minutes.Enabled = false;
            numUpDown_Seconds.Enabled = false; // disable controls.
            m_Cancelled = true;
            m_StatusBar.Text = "Timer set to " + numUpDown_Hours.Value.ToString() + " hours, " + numUpDown_Minutes.Value.ToString() + " minutes, " + numUpDown_Seconds.Value.ToString() + " seconds.";
        }

        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (time == 0)
            {
                m_StatusBar.Text = "Countdown Finished";
                SoundPlayer soundPlayer = new SoundPlayer(@"C:\Users\marupakuuu\Desktop\New stuff\doorbell.wav");
                soundPlayer.Play(); // play sound.
                timer.Stop();
                return;
            }
            else
            {
                time = time - 1;
                CountdownProgress.Increment(1); // exception occurs here; multithreading issues.
            }
        }

        private void btn_Cancel_Click(object sender, EventArgs e)
        {
            // if user wishes to stop the countdown to start a new one.
            m_Cancelled = true;
            m_CountdownActive = false;
            btn_SetCountdown.Enabled = true;
            numUpDown_Seconds.Value = 0;
            numUpDown_Minutes.Value = 0;
            numUpDown_Hours.Value = 0;
            numUpDown_Hours.Enabled = true;
            numUpDown_Minutes.Enabled = true;
            numUpDown_Seconds.Enabled = true;
            btn_Cancel.Enabled = false;
            m_StatusBar.Text = "Countdown cancelled";
        }
    }
}

1 个答案:

答案 0 :(得分:8)

只能在创建的线程中访问控件。因此,使用Invoke执行给定代码作为主线程上的委托,其中创建了控件(进度条)。

void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    if (time == 0)
    {
        m_StatusBar.Text = "Countdown Finished";
        SoundPlayer soundPlayer = new SoundPlayer(@"C:\Users\marupakuuu\Desktop\New stuff\doorbell.wav");
        soundPlayer.Play(); // play sound.
        timer.Stop();
        return;
    }
    else
    {
        time = time - 1;
        Invoke(new Action(() => CountdownProgress.Increment(1)));
    }
}

您可能需要阅读http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx