Problem with Timer and BackgroundWorker

Hi, After starting below code, the output I got is:
-------------
day
minute
day
minute,minute
day
minute,minute,minute
--------------
It should be:
--------------
day
minute,
day
minute
day
minute
--------------
I don`t knowo why the backgroud worker starts more then 1 time on each steps, is Background worker cancelletion working right
Please help.

CODE

private void Form1_Load(object sender, EventArgs e)
{


timer1.Interval = 3000;
timer1.Tick += new EventHandler(DayTicker);
timer1.Start();
}

public void DayTicker(object sender, EventArgs e)
{
timer1.Stop();
if (backgroundWorker1.IsBusy) backgroundWorker1.CancelAsync();
if (backgroundWorker1.CancellationPending) return;

richTextBox1.Text += "\n day \n";

backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(bw_DoWork_day);
backgroundWorker1.RunWorkerAsync("test");
backgroundWorker1.RunWorkerCompleted += bw_workerday_coplete;

}

public void bw_DoWork_day(object sender, DoWorkEventArgs e)
{
string w = "minute";
e.Result = 1;

e.Cancel = true;

if (InvokeRequired)
Invoke(new Change(OnChange), w);
}

public void bw_workerday_coplete(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
MessageBox.Show("Worker exception: " + e.Error.ToString());
else if (e.Cancelled)
{
timer1.Tick += new EventHandler(DayTicker);
timer1.Start();
}
}

private void OnChange(string w)
{
richTextBox1.Text += w + ",";
Application.DoEvents();
}

private delegate void Change(string w);
}
}


Answer this question

Problem with Timer and BackgroundWorker

  • Nickeay

    Thank you very much.

    You are the MAN!!!!


  • Steve Graber

    The problem is that each time you start your timer you add another event handler DayTicker to the event list. That means that each time you run yet another message will be generated so the pattern will continue. Remove the timer1.Tick assignment from the completion handler and it'll work the way you want.

    However I have to recommend that you don't write your code this way anyway. As it stands now you are really gaining nothing by using BWC since you are ultimately using the Invoke method. You should instead use the progress event handler to notify your UI of changes. Here is the updated/fixed code:

    public Form1 ( )
    {
    InitializeComponent();

    //Initialize
    timer1.Interval = 3000;
    timer1.Tick +=
    new EventHandler(DayTicker);
    backgroundWorker1.WorkerReportsProgress =
    true;
    backgroundWorker1.WorkerSupportsCancellation =
    true;
    backgroundWorker1.DoWork +=
    new DoWorkEventHandler(bw_DoWork_day);
    backgroundWorker1.RunWorkerCompleted += bw_workerday_coplete;
    backgroundWorker1.ProgressChanged += OnChange;
    }

    protected override void OnLoad(EventArgs e)
    {
    base.OnLoad(e);
    timer1.Start();
    }

    public void DayTicker(object sender, EventArgs e)
    {
    timer1.Stop();
    if (backgroundWorker1.IsBusy) backgroundWorker1.CancelAsync();
    if (backgroundWorker1.CancellationPending) return;

    richTextBox1.Text +=
    "\n day \n";
    backgroundWorker1.RunWorkerAsync(
    "test");
    }

    public void bw_DoWork_day(object sender, DoWorkEventArgs e)
    {
    string w = "minute";
    e.Result =
    1;
    e.Cancel =
    true;

    BackgroundWorker bwc = sender as BackgroundWorker;
    bwc.ReportProgress(
    100, w);
    }

    public void bw_workerday_coplete(object sender, RunWorkerCompletedEventArgs e)
    {
    if (e.Error != null)
    MessageBox.Show("Worker exception: " + e.Error.ToString());
    else if (e.Cancelled)
    {
    timer1.Start();
    }
    }

    private void OnChange(object sender, ProgressChangedEventArgs e )
    {
    string w = e.UserState as string;
    richTextBox1.Text += w +
    ",";
    }

    Here are the changes I made.

    1. Switched to the OnLoad overload rather than handling the Load event as it is the preferred that you override virtual methods rather than handle events when creating a derived class.
    2. Initialized the timer and BWC in the constructor rather than just before its usage. Keeps things clean. Ideally it would reside in InitializeComponent (because you'd set it through the designer) but I left it outside that method so I didn't have to post the code directly.
    3. Moved the assignment of the completion event handler before the async call. Otherwise the code might complete before the handler is even hooked up.
    4. Removed the Invoke call and simply raised a progress change event. Added progress change event handler to update the UI. Passed the string in user state.
    5. Removed delegate.

    Michael Taylor - 2/1/07
    http://p3net.mvps.org


  • Problem with Timer and BackgroundWorker