c# newbie needs help with backgroundworker

C

c-sharp newbie!

Dear all,

I have a form which temporarily creates another form and has a
progressbar on it. I got trouble with updating the screen, if I didn't
use a background-worker. Therefore: My code looks something like the
following:

public partial class newForm : Form
{
public newForm()
{
InitializeComponent();

//this.Show();
//this.Activate();
this.progressBar1.Step = 1;
this.progressBar1.Maximum = 100;

BackgroundWorker bw0 = new BackgroundWorker();
bw0.WorkerReportsProgress = true;

bw0.DoWork += delegate(object sender0, DoWorkEventArgs e0)
{
for (int i = 0; i < 15; i++)
{
System.Threading.Thread.Sleep(50);
bw0.ReportProgress(i);
}
};
bw0.ProgressChanged += delegate(object sender0,
ProgressChangedEventArgs e0)
{
this.progress_indicator.Text = string.Format("{0}%",
e0.ProgressPercentage);
this.progressBar1.PerformStep();
};
bw0.RunWorkerCompleted += delegate(object sender0,
RunWorkerCompletedEventArgs e0)
{
// Here I insert 6 extra background-workers after
the previous completed - this is wrong!
}


Okay: My problem is that when I put code inside
bwXXXX.RunWorkerCompleted, everything works - except that I cannot
continue to update by using my bwXXXXX.ReportProgress(i). Therefore I
have some really really ugly code with many layers of background-
workers...

In addition, I need to get something like: (int)this.Handle

I cannot seem to be able to get: (int)this.Handle inside any of my
bwXXXXX.DoWork'ers.... Instead of having all my background-workers, I
only need a single one and put my code inside the bw0.DoWork (my code
is 100% sequential).

Any hints on why this doesn't work - do I need to post more code? I
hope this is sufficient for a qualified answer...
 
I

Ignacio Machin ( .NET/ C# MVP )

Dear all,

I have a form which temporarily creates another form and has a
progressbar on it. I got trouble with updating the screen, if I didn't
use a background-worker. Therefore: My code looks something like the
following:

    public partial class newForm : Form
    {
        public newForm()
        {
            InitializeComponent();

            //this.Show();
            //this.Activate();
            this.progressBar1.Step = 1;
            this.progressBar1.Maximum = 100;

            BackgroundWorker bw0 = new BackgroundWorker();
            bw0.WorkerReportsProgress = true;

            bw0.DoWork += delegate(object sender0, DoWorkEventArgs e0)
            {
                for (int i = 0; i < 15; i++)
                {
                    System.Threading.Thread.Sleep(50);
                    bw0.ReportProgress(i);
                }
            };
            bw0.ProgressChanged += delegate(object sender0,
ProgressChangedEventArgs e0)
            {
                this.progress_indicator.Text = string.Format("{0}%",
e0.ProgressPercentage);
                this.progressBar1.PerformStep();
            };
            bw0.RunWorkerCompleted += delegate(object sender0,
RunWorkerCompletedEventArgs e0)
            {
                   // Here I insert 6 extra background-workers after
the previous completed - this is wrong!
            }

Okay: My problem is that when I put code inside
bwXXXX.RunWorkerCompleted, everything works - except that I cannot
continue to update by using my bwXXXXX.ReportProgress(i). Therefore I
have some really really ugly code with many layers of background-
workers...

In addition, I need to get something like: (int)this.Handle

I cannot seem to be able to get: (int)this.Handle inside any of my
bwXXXXX.DoWork'ers.... Instead of having all my background-workers, I
only need a single one and put my code inside the bw0.DoWork (my code
is 100% sequential).

Any hints on why this doesn't work - do I need to post more code? I
hope this is sufficient for a qualified answer...

You have to use Control.Invoke to update the UI from a worker thread.
It would be something like:

bw0.ProgressChanged += delegate(object sender0,
ProgressChangedEventArgs e0)
{

progressBar1.Invoke( delegate(){

this.progress_indicator.Text = string.Format("{0}%",
e0.ProgressPercentage);

this.progressBar1.PerformStep();
}
);
};
 
C

c++ newbie!

You have to use Control.Invoke to update the UI from a worker thread.
It would be something like:

             bw0.ProgressChanged += delegate(object sender0,
ProgressChangedEventArgs e0)
             {

                progressBar1.Invoke( delegate(){

this.progress_indicator.Text = string.Format("{0}%",
e0.ProgressPercentage);

this.progressBar1.PerformStep();
                                                              }
                                               );
             };- Skjul tekst i anførselstegn -

Sorry for asking, but why exactly and what difference does it make?

BTW: I found out that your code doesn't work unless I made a cast
like:

progressBar1.Invoke((MethodInvoker) delegate() ... etc.
 
C

c++ newbie!

Sorry for asking, but why exactly and what difference does it make?


Okay, now I've read about it and I think you misunderstood the
question (or I'm to much a noob to understand the answer). I now see
that I have a UI thread and a background thread and my problem is that
everything works inside the bwXXXX.RunWorkerCompleted, because that is
my UI thread.

However, my problem is that from the background-thread (i.e.bw0.DoWork
( ... ) ), I need to get the value of this command:

int hParentWnd = (int)this.Handle; (as seen from the UI-thread).


Newbie question: Please help me, how to get around this... I'm
completely new to threading and need the integer value of hParentWnd
(from the UI-thread) inside the background thread....

Thank you (and I hope I made my problem more clear now)...
 
I

Ignacio Machin ( .NET/ C# MVP )

That doesn't really make sense.  The ProgressChanged event is, by design  
and documentation, raised on the main GUI thread.  There should be no need  
to use the Control.Invoke() method from within an event handler for the  
ProgressChanged event.

Unfortunately, the original question is fairly vague.  One hopes the OP 
can provide some clarification.

Pete

I do not see it in MSDN (the fact that ProgressChanged is raised on
the main event [even as it makes a lot of sense])

I do agree with you that the OP text is vague, I had originally though
he was having the common issue of trying to update the UI from another
thread, now I'm not so sure.
 
C

c++ newbie!

[...]
            bw0.RunWorkerCompleted += delegate(object sender0,
RunWorkerCompletedEventArgs e0)
            {
                   // Here I insert 6 extra background-workers after
the previous completed - this is wrong!
            }
Okay: My problem is that when I put code inside
bwXXXX.RunWorkerCompleted, everything works - except that I cannot
continue to update by using my bwXXXXX.ReportProgress(i). Therefore I
have some really really ugly code with many layers of background-
workers...

What does "I cannot continue to update by using my  
byXXXXX.ReportProgress(i)" mean?

Ok, it means: I cannot continue to update my GUI as I could use in the
bw0.DoWork-function. If I try to update my GUI in the
bw0.RunWorkerCompleted-function, it fails. That's what it means... I
then found the explanation, that there are separate threads.

I need to pass information from the GUI-thread to the worker-thread -
that's my problem. I don't know how to do this.
Why, when the problem you're having is  
apparently related to the code you put into the RunWorkerCompleted event  
handler, is that the one event handler that you failed to actually show  
the code for?

I'm using an API which I don't think any of you have. The API requires
that I pass (int)this.Handle, to the API-function I want to use.
Therefore I only need how to pass the value of (int) this.Handle (from
the GUI-thread) to the worker thread. At least, that is what I
think...
Suggestion: instead of casting the Handle value, use the ToInt32() method 
on it.

Okay, but still. I guess I don't complety understand the concept of
this.handle... Any help or comments on "this.handle" in relation to
the two threads in background-worker (GUI- vs. worker-thread) would be
appreciated...
As for the specific concern, there's nothing in the code you posted that  
demonstrates how you're trying to "get: (int) this.Handle", never mind  
what mind be going wrong with such code.  Assuming your DoWork event  
handlers are methods within your Form sub-class, "this.Handle" should be a  
valid expression, and it should evaluate to some specific value at  
run-time.

Here's what I try to do:

------------ CODE THAT DOESN'T WORK:------------
bw0.DoWork += delegate(object sender0, DoWorkEventArgs e0)
{
for (int i = 0; i < 5; i++)
{
System.Threading.Thread.Sleep(50);
bw0.ReportProgress(i);
}
int test = (int)this.Handle; //// DOESN'T WORK: "NOT
IN THE GUI-THREAD"
MessageBox.Show("integer = " + Convert.ToString
(test));

for (int i = 5; i < 15; i++)
{
System.Threading.Thread.Sleep(50);
bw0.ReportProgress(i);
}
// do something more....
};

------------ CODE THAT WORKS:------------

bw0.RunWorkerCompleted += delegate(object sender0,
RunWorkerCompletedEventArgs e0)
{
int test = (int)this.Handle; //// WORKS BECAUSE WE'RE
IN THE GUI-THREAD
MessageBox.Show("integer = " + Convert.ToString
(test));

// however: This doesn't work:
for (int i = 0; i < 5; i++)
{
System.Threading.Thread.Sleep(50);
bw0.ReportProgress(i); // New non-gui "worker-
thread", therefore this fails
}

Unfortunately, it's not.  See above for specific things that are missing  
 from the code, and keep in mind that a _complete_ code example is almost  
always the best.  There's no way for anyone to actually compile and run 

I cannot post complete code, because I'm using some API which I guess
you don't have. And my question should be so general, that it suffices
(I hope).
the code you posted without extra work, including some guesswork.  In any  
case, if you don't post any of the code that is actually giving you  
problems, it's impossible for anyone to provide any useful comments about 
that code.

I think I described my problem, however in a newbie-way and that might
not be good enough. Now I believe I've showed it.
 
C

c++ newbie!

[...]
I need to pass information from the GUI-thread to the worker-thread -
that's my problem. I don't know how to do this.

The most straightforward way is to use the RunWorkerAsync() overload that 
allows you to pass an argument to your DoWork event handler (the argument 
value can be retrieved from the DoWorkEventArgs object passed to the  
handler).

Aah, thank you very much! This seem to work on a minimal example here
at home :)

However, I have to check it for real on monday/tuesday on my real code
and cross my fingers that everything works out :)
Alternative methods include:

     -- Copying the value of the Handle property to a private classmember  
field, and then reading it from there.  The problem isn't with the value,  
it's with the Handle property itself.

What you're telling me, is a true eye-opener. I value that, however
what do you mean with the last sentence?

I think I understand the alternative method, but not the problem...
     -- Accessing the property using Control.Invoke() on an anonymous  
method that gets the property value and returns it.  Invoked methods can  
return values to the caller, even when using Control.Invoke().

Thanks: Finally, I got something to work and began understanding Invoke
() a bit. This page helped a lot: http://msdn.microsoft.com/en-us/library/ms171728.aspx

I now have something like this:

delegate int getHandleCallback(); // I haven't really
understood this yet

private int getHandle()
{
return (int)this.Handle;
}

And in my doworker-method:

int val;
getHandleCallback GH = new getHandleCallback(getHandle);

object valO = this.Invoke(GH);
val = (int)valO;
//val = (int)this.Handle; // produces some thread error/
casts exception
MessageBox.Show("Value is (from this.Invoke): " +
val.ToString());

One thing that still bugs me about your code example is that the "CODE  
THAT DOESN"T WORK" and the "CODE THAT WORKS" looks basically the same.

Yes, because I don't want my code in the "worker-completed UI"-thread,
but in the worker-thread and I got exceptions when I tried to move my
code into the worker-thread. So I don't understand exactly, what's
bugging you other than I couldn't make it work before (I haven't
tested it on my whole code, I just ran a minimal example here at home
which I believe has shown that your suggestions really have helped
me)?
The only significant difference is that in one case you are handling the  
DoWork event, and in the other case you are handling the  
RunWorkerCompleted event.  But, those events are for completely different  
purposes.  It doesn't make sense that you could treat the events  
interchangeably.

When you're a noob like me, you just put the code whereever it works
out. When you're trying to learn to improve your code like me, I ask
for help because I want all "work-stuff" in the worker-thread instead
of the completed-thread. My real problem is that I have multiple
embedded background-workers in the run-completed method, instead of
putting all inside one single background-worker "do-work"-method.

By following your suggestions I improve my code a lot, make it easier
to look at and doesn't need multiple background-workers due to the
requirement for knowing things from the main (UI)-thread.

I'm not sure if I removed your concerns?
In particular, while you may be able to resolve the business of retrieving  
the Handle property value in the worker thread, whether you can safely use  
that value anywhere else is still open to question.  Likewise, putting  

Yes, I agree: Whether I can use it anywhere else is still a question I
have to check tomorrow... I hope - or else I guess I must learn more
about invoke... I guess by using invoke, is the way to do it properly,
if I get problems...
code in the RunWorkerCompleted event handler that does the same thing as  
the code in your DoWork event handler begs the question: what's the point 
of the background worker thread at all, if you are satisfied executing the  
exact same code on the main GUI thread?

The point is that if you don't use a BW, the form-window is "locked"
while you're running your code. You don't see any progress indication
and my labels don't get updated, so the user can't see what's
happening. Therefore you need a background-worker. And so far most of
my code was ran in the "completed"-event because that was the only
place I could make it work. This introduced the need for multiple
background-workers, because I have multiple tasks I want to solve
while the user can see the program progress.
Anyway, hopefully the information above helps you solve your problem.

Yes, it was very helpful and thank you. I still need to check it
tomorrow/tuesday I hope on my full code. My test code here at home
shows me that your suggestions was very valuable and I learned
something from them.
 

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