Object passed to TimerHandler immutable?

B

Bill

This one has me stumped. I'm using sample code in the C# Programmer's
Cookbook as a guide. Specifially, Recipe 4.3, Execute a Method Using a
Timer.

I created a form with a ProgressBar on it (Min=0, Max=100). When I
created my Timer object I passed the ProgressBar as the second
parameter, the "state" object. This object is passed to the
TimerHandler.

Inside the TimerHandler I increment ProgressBar.Value by 1 unless its
at the Max value, in which case I reset it to the Min value.\

I expected to see the ProgressBar grow in the form, but nothing
happened.

I set a few breakpoints and discovered that indeed the ProgressBar
object is being passed through the Timer object to the TimerHandler
method. However, in the TimerHandler the ProgressBar object is always
in its initial state.

This leads me to believe I'm getting a COPY of the ProgressBar object
in my TimerHandler.

As an experiment I left the Timer object out of the loop and called
TimerHandler directly from a loop and it worked perfectly.

I'm guessing my "problem" is rooted in the fact that I'm trying to
modify an object across threads.

Does anyone have any ideas what I'm doing wrong or how I might
accomplish my goal?

Thanks,

Bill...
 
B

Bill

OK, so I did a bit more searching and discovered that this question
gets asked about once a week. Apparently it was my turn to ask it this
week. ;-)

I neglected to mention in my original post that this all all under .NET
Compact Framework.

Anyway... I figured it all out and now have a nice progress bar
crawling across my display while my worker thread is doing its thing.

For those who may be interested, here's a piece of sample code:

using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using System.Diagnostics;

namespace Thread_Timer_Callback_Test
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.ProgressBar pbProgress;
private System.Windows.Forms.Label lblMsg;
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.MenuItem menuItem1;
private TimerCallback handler;
private delegate void pbUpdateDelegate(Object sender, EventArgs e);
private System.Threading.Timer timer;
private System.Windows.Forms.Button btnClickMe;
private System.Windows.Forms.Button btnStop;
private System.Windows.Forms.Button btnRestart;
private int i = 0;

public Form1()
{
InitializeComponent();
handler = new TimerCallback(TimerHandler);
}
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.pbProgress = new System.Windows.Forms.ProgressBar();
this.lblMsg = new System.Windows.Forms.Label();
this.menuItem1 = new System.Windows.Forms.MenuItem();
this.btnClickMe = new System.Windows.Forms.Button();
this.btnStop = new System.Windows.Forms.Button();
this.btnRestart = new System.Windows.Forms.Button();
this.mainMenu1.MenuItems.Add(this.menuItem1);
this.pbProgress.Location = new System.Drawing.Point(8, 56);
this.pbProgress.Size = new System.Drawing.Size(224, 20);
this.lblMsg.Location = new System.Drawing.Point(8, 24);
this.lblMsg.Size = new System.Drawing.Size(224, 20);
this.lblMsg.Text = "Message appears here";
this.lblMsg.TextAlign = System.Drawing.ContentAlignment.TopCenter;
this.menuItem1.Text = "Exit";
this.menuItem1.Click += new
System.EventHandler(this.menuItem1_Click);
this.btnClickMe.Location = new System.Drawing.Point(84, 96);
this.btnClickMe.Text = "Click Me";
this.btnClickMe.Click += new
System.EventHandler(this.btnClickMe_Click);
this.btnStop.Location = new System.Drawing.Point(84, 136);
this.btnStop.Text = "Stop";
this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
this.btnRestart.Location = new System.Drawing.Point(84, 176);
this.btnRestart.Text = "Restart";
this.btnRestart.Click += new
System.EventHandler(this.btnRestart_Click);
this.Controls.Add(this.btnRestart);
this.Controls.Add(this.btnStop);
this.Controls.Add(this.btnClickMe);
this.Controls.Add(this.lblMsg);
this.Controls.Add(this.pbProgress);
this.Menu = this.mainMenu1;
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
}
static void Main()
{
Application.Run(new Form1());
}

private void Form1_Load(object sender, System.EventArgs e)
{
timer = new System.Threading.Timer(handler, this.pbProgress, 0,
100);
}

private void TimerHandler(object obj)
{
pbUpdateDelegate _pbUpdate = new pbUpdateDelegate(pbUpdate);
((ProgressBar)obj).Invoke(new EventHandler(_pbUpdate));
}

private void pbUpdate(Object sender, EventArgs e)
{
if (this.pbProgress.Value == this.pbProgress.Maximum)
this.pbProgress.Value = this.pbProgress.Minimum;
else
this.pbProgress.Value++;
}

private void menuItem1_Click(object sender, System.EventArgs e)
{
timer.Dispose();
Application.Exit();
}

private void btnClickMe_Click(object sender, System.EventArgs e)
{
i++;
this.lblMsg.Text = "That was click #" + i.ToString();
}

private void btnStop_Click(object sender, System.EventArgs e)
{
timer.Change(0,0);
}

private void btnRestart_Click(object sender, System.EventArgs e)
{
timer.Change(0,100);
}
}
}
 
M

Mehdi

OK, so I did a bit more searching and discovered that this question
gets asked about once a week. Apparently it was my turn to ask it this
week. ;-)

I neglected to mention in my original post that this all all under .NET
Compact Framework.

Anyway... I figured it all out and now have a nice progress bar
crawling across my display while my worker thread is doing its thing.

Something looks just strange in your code. Why are you using a
System.Threadind.Timer since at each timer tick you are marshalling the
call to your pbUpdate function to the UI thread? In this case, a simple
System.Window.Form.Timer timer would make more sense since its callback
method executes in the UI thread already and not in a worker thread, that
would make the code simpler.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top