Event or other?

S

Steve

Hi All,

I'm a newbee in C# and have a Windows form application. The main form
has a progress bar. In one of the methods of the main form, I call a
class derived from Object which processes a file. During the
processing, I'd like the custom class to be able to update the
progress bar.

What's the best way to make this happen? I've been reading up on
events/delegates and am beginning to get a vague understanding, but I
wanted to make sure I'm not going down the wrong path.

Thanks for any suggestions/insight,
Steve
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

Steve said:
Hi All,

I'm a newbee in C# and have a Windows form application. The main form
has a progress bar. In one of the methods of the main form, I call a
class derived from Object which processes a file. During the
processing, I'd like the custom class to be able to update the
progress bar.

What's the best way to make this happen? I've been reading up on
events/delegates and am beginning to get a vague understanding, but I
wanted to make sure I'm not going down the wrong path.

You have to use a thread to perform the operation and in the meantime in the
UI thread the progress bar is running.
Take a look at Control.Invoke as the way to communication between the worker
thread and the UI
 
S

Steve

Hi,



You have to use a thread to perform the operation and in the meantime in the
UI thread the progress bar is running.
Take a look at Control.Invoke as the way to communication between the worker
thread and the UI
I'd like to avoid multi-threading if I can. I have no experience in
managing threads and it doesn't seem like it should be that
complicated to just reference a component on a form.
 
M

Morten Wennevik [C# MVP]

Hi All,

I'm a newbee in C# and have a Windows form application. The main form
has a progress bar. In one of the methods of the main form, I call a
class derived from Object which processes a file. During the
processing, I'd like the custom class to be able to update the
progress bar.

What's the best way to make this happen? I've been reading up on
events/delegates and am beginning to get a vague understanding, but I
wanted to make sure I'm not going down the wrong path.

Thanks for any suggestions/insight,
Steve

Hi Steve

An event should work fine here, but you also need to start your processing in a BackgroundWorker or another thread or the main thread won't get time to process the event. And since the event will get caught in a thread other than the main thread, you need to use Invoke as well. The sample below might give you some ideas. If you don't mind having the classcall the form directly (which isn't really good OO practice, you can skip throwing the event and call the form directly)

public class MyForm : Form
{

<...>

delegate void ProgressBarDelegate(int value);
BackgroundWorker worker = new BackgroundWorker();
MyClass myClass = new MyClass();

private void Method()
{
myClass.MyEvent += new MyClass.MyEventHandler(myClass_MyEvent);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}

void myClass_MyEvent(int value)
{
if (progressBar1.InvokeRequired)
progressBar1.Invoke(new ProgressBarDelegate(myClass_MyEvent), new object[] { value });
else
progressBar1.Value = value / 2;
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
myClass.Method();
}

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done");
}
}

public class MyClass
{
public event MyEventHandler MyEvent;
public delegate void MyEventHandler(int value);

public void Method()
{
for (int i = 0; i < 200; i++)
{
System.Threading.Thread.Sleep(200);
MyEvent(i);
}
}
}
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

Steve said:
I'd like to avoid multi-threading if I can. I have no experience in
managing threads and it doesn't seem like it should be that
complicated to just reference a component on a form.


It's not complicated, besides if you never start you will never learn :)

See the other post in this thread as well as in the archives, a solution to
your problem is posted or discussed at least once a week
 
G

Guest

Morten Wennevik said:
Hi All,

I'm a newbee in C# and have a Windows form application. The main form
has a progress bar. In one of the methods of the main form, I call a
class derived from Object which processes a file. During the
processing, I'd like the custom class to be able to update the
progress bar.

What's the best way to make this happen? I've been reading up on
events/delegates and am beginning to get a vague understanding, but I
wanted to make sure I'm not going down the wrong path.

Thanks for any suggestions/insight,
Steve

Hi Steve

An event should work fine here, but you also need to start your processing in a BackgroundWorker or another thread or the main thread won't get time to process the event. And since the event will get caught in a thread other than the main thread, you need to use Invoke as well. The sample below might give you some ideas. If you don't mind having the class call the form directly (which isn't really good OO practice, you can skip throwing the event and call the form directly)

public class MyForm : Form
{

<...>

delegate void ProgressBarDelegate(int value);
BackgroundWorker worker = new BackgroundWorker();
MyClass myClass = new MyClass();

private void Method()
{
myClass.MyEvent += new MyClass.MyEventHandler(myClass_MyEvent);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}

void myClass_MyEvent(int value)
{
if (progressBar1.InvokeRequired)
progressBar1.Invoke(new ProgressBarDelegate(myClass_MyEvent), new object[] { value });
else
progressBar1.Value = value / 2;
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
myClass.Method();
}

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done");
}
}

public class MyClass
{
public event MyEventHandler MyEvent;
public delegate void MyEventHandler(int value);

public void Method()
{
for (int i = 0; i < 200; i++)
{
System.Threading.Thread.Sleep(200);
MyEvent(i);
}
}
}

Hi Morten,

Thanks for the reply very useful. The only problem I get whilst running that
code is when I get to the MyEvent(i); call in public void Method() I get a
NullReferenceException with "Object reference not set to an instance of an
object". Any idea what I'm doing wrong?

Cheers,

Andy
 
M

Marc Gravell

That means that nobody is listening to your event; a minor nuicance,
but you need to check events for null; this is usually done in an "On"
method, i.e. (given your unusual event signature):

protected virtual void OnMyEvent(int value) {
MyEventHandler handler = MyEvent;
if(handler!=null) handler(value);
}

and then call OnMyEvent(i); instead of MyEvent(i);

For a lazy implementation, you could just use:
if(MyEvent!=null) {MyEvent(i);}
but there are a few subtle issues that make the OnMyEvent the
recommended approach.

Marc
 
G

Guest

Marc Gravell said:
That means that nobody is listening to your event; a minor nuicance,
but you need to check events for null; this is usually done in an "On"
method, i.e. (given your unusual event signature):

protected virtual void OnMyEvent(int value) {
MyEventHandler handler = MyEvent;
if(handler!=null) handler(value);
}

and then call OnMyEvent(i); instead of MyEvent(i);

For a lazy implementation, you could just use:
if(MyEvent!=null) {MyEvent(i);}
but there are a few subtle issues that make the OnMyEvent the
recommended approach.

Marc

Hi Marc,

Thanks for the reply - I'm obviously not doing something right;

In my form I have:-

delegate void ProgressBarDelegate(int value);
BackgroundWorker worker = new BackgroundWorker();

TryClass myClass = new TryClass();
private void Method()
{
myClass.MyEvent += new TryClass.MyEventHandler(myClass_MyEvent);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}

void myClass_MyEvent(int value)
{
if (prgStatus.InvokeRequired)
prgStatus.Invoke(new ProgressBarDelegate(myClass_MyEvent),
new object[] { value });
else
prgStatus.Value = value / 2;
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
myClass.Method();
}

void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done");
}

In the class I have :-

public event MyEventHandler MyEvent;
public delegate void MyEventHandler(int value);

public void Method()
{
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(200);

if (MyEvent != null)
{ MyEvent(i); }

}
}

I am then calling the method from another class in the same project;

TryClass x = new TryClass ();
x.Method();

All I seem to get are null values in the MyEvent(i) - any ideas what I am
doing wrong? It's like the MyEvent doesn't connect back to the form.

Thanks,

Andy
 
M

Marc Gravell

All I seem to get are null values in the MyEvent(i)
Sorry - can you clarify what you mean? is MyEvent null? Or is
something throwing an exception?

Note that with BackgroundWorker, a better way to handle the updates is
to set WorkerReportsProgress = true, and then from your loop call
ReportProgress(value). You can then catch the ProgressChanged event
(which is fired on the UI thread, so no InvokeRequired / Invoke) and
get the value from the event-arg.

Marc
 
P

Peter Duniho

Andy said:
Thanks for the reply - I'm obviously not doing something right;

Correct. :)
In my form I have:-

delegate void ProgressBarDelegate(int value);
BackgroundWorker worker = new BackgroundWorker();

TryClass myClass = new TryClass();

So, in your form, you've created an instance of TryClass() and subscribe
your event handler to the event in that instance.
[...]
I am then calling the method from another class in the same project;

TryClass x = new TryClass ();

And then within your background worker thread, you create a new instance
of TryClass().
x.Method();

And then call the method that raises the event using that new instance,
rather than the instance in which you subscribed your handler.
All I seem to get are null values in the MyEvent(i) - any ideas what I am
doing wrong? It's like the MyEvent doesn't connect back to the form.

You are creating a new instance of TryClass. That instance is not the
same one to which you subscribed your event handler, so of course the
event in that instance isn't subscribed.

You need to create just one instance of the class, and then use that
instance when executing the method of interest. Alternatively (and this
comes up more often than one might think) if you can genuinely get away
with creating a new instance each time you want to call the method, then
that method shouldn't be an instance method anyway as you're obviously
not using anything that is unique per-instance.

In this alternative case, just make the method and the event static.
And of course, if you have no other instance members in the class, then
the class itself could be static.

Pete
 
G

Guest

Peter Duniho said:
Andy said:
Thanks for the reply - I'm obviously not doing something right;

Correct. :)
In my form I have:-

delegate void ProgressBarDelegate(int value);
BackgroundWorker worker = new BackgroundWorker();

TryClass myClass = new TryClass();

So, in your form, you've created an instance of TryClass() and subscribe
your event handler to the event in that instance.
[...]
I am then calling the method from another class in the same project;

TryClass x = new TryClass ();

And then within your background worker thread, you create a new instance
of TryClass().
x.Method();

And then call the method that raises the event using that new instance,
rather than the instance in which you subscribed your handler.
All I seem to get are null values in the MyEvent(i) - any ideas what I am
doing wrong? It's like the MyEvent doesn't connect back to the form.

You are creating a new instance of TryClass. That instance is not the
same one to which you subscribed your event handler, so of course the
event in that instance isn't subscribed.

You need to create just one instance of the class, and then use that
instance when executing the method of interest. Alternatively (and this
comes up more often than one might think) if you can genuinely get away
with creating a new instance each time you want to call the method, then
that method shouldn't be an instance method anyway as you're obviously
not using anything that is unique per-instance.

In this alternative case, just make the method and the event static.
And of course, if you have no other instance members in the class, then
the class itself could be static.

Pete
Hi Peter,

Thanks for the reply - I think I'm nearly there now. I made the class static
but I'm still getting nothing but null values.

In the class I have;

public static class TryClass
{
public static event MyEventHandler MyEvent;

public delegate void MyEventHandler(int value);

public static void Method()
{
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(200);

if (MyEvent != null)
{ MyEvent(i); }

}
}

In the form I have;

delegate void ProgressBarDelegate(int value);
BackgroundWorker worker = new BackgroundWorker();

private void Method()
{
TryClass.MyEvent += new TryClass.MyEventHandler(TryClass_MyEvent);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}

void TryClass_MyEvent(int value)
{
if (prgStatus.InvokeRequired)
prgStatus.Invoke(new ProgressBarDelegate(TryClass_MyEvent),
new object[] { value });
else
prgStatus.Value = value / 2;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
TryClass.Method();
}

void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done");
}

I'm now calling TryClass.Method(); and I'm still getting nothing but null
values.

Thanks,

Andrew
 
P

Peter Duniho

Andy said:
[...]
I'm now calling TryClass.Method(); and I'm still getting nothing but null
values.

Well, maybe I'm just overlooking something, but I didn't see anything
obviously wrong in the code you posted (other than the fact that you
don't unsubscribe from the event after you're done with it, but that
won't affect whether you can be called when the event is raised).

If no one else catches the error, you should post a concise-but-complete
example of code that demonstrates the problem. Assuming I didn't miss
anything, the fact that it's still not working means that there's
something in the code you didn't post that's causing a problem.

Having a concise-but-complete code example would allow anyone to just
compile the thing and test it themselves. Since there's nothing about
what you're trying to do that requires a form, the code you post should
actually be a console application. Yes, this means you need to
basically write a new code sample, copying the important bits from the
code that already exists.

Of course, that assumes the code you posted is exactly the code you're
using. If it's not strictly a copy-and-paste from your program, that's
obviously going to prevent anyone from saying what's wrong with the code
you're actually using. :)

Pete
 
G

Guest

Many thanks - I got it to work. It helps if you actually call the event in
the first place. Doh!
 

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