views:

1115

answers:

4

Hi!

I have a Windows Form with a label and a picture box. To retreive an image from a web service I use a thread.

The user click on a button and first the label must be displayed and then new thread is started to retrieve the image. This is the code:

private void menuItem1_Click(object sender, EventArgs e)

{

etiquetaCargando.Visible = true;

this.Invoke(new System.Threading.ThreadStart(RequestImage));

}

The problem is this: label etiquetaCargando doesn't appear.

I'm programming on Compact Framework.

What it's happening?

Thanks!

+3  A: 

The question is unclear, but by using Invoke, you are defeating the purpose of having the image request on a separate thread. (You might notice that your interface becomes unresponsive while the request is happening)

Instead, it is better to create a new thread object, start it, and then use the Invoke to set the image (after it has been retrieved), something along the lines of

private void menuItem1_Click(object sender, EventArgs e)
{    
    etiquetaCargando.Visible = true;
    Thread reqThread =
        new Thread(new ThreadStart(RequestImage));
    reqThread.Start();
}

private void RequestImage()
{
    /* Get the image
    ...
    */
    Invoke(SetTheImage, new object[] { theImage });
}

this assumes that you have a method SetTheImage which will actually do the job of displaying the image on your form.

Daniel LeCheminant
In method RequestImage I have to access this properties:this.Heightthis.WidthHow can I retrieve the values of these two properties?
VansFannel
@VansFannel: You should be able to access them directly, otherwise you can pass the size into a parameterized ThreadStart, and have RequestImage take the size as a parameter
Daniel LeCheminant
+3  A: 

You shouldn't be calling this:

this.Invoke(new System.Threading.ThreadStart(RequestImage));

I assume that you are deadlocking here, as Invoke method will route the delegate to the UI thread, but you are waiting for your operation to complete.

To get the code to execute on another thread, you would do this:

private void menuItem1_Click(object sender, EventArgs e)
{
    etiquetaCargando.Visible = true;

    Thread t = new Thread(RequestImage);

    t.Start();
}

Then, in your RequestImage method, you would call the Invoke method, passing a delegate to be executed on the UI thread, in this case, passing the image you downloaded.

casperOne
Don't forget to Start that new t. ;)
JMD
@JMD: Added code based on comment.
casperOne
If I do this the label makes visible but I can't asign the image retrieved from web service.
VansFannel
@VansFannel: You should make sure that whatever you are assigning to is available to the RequestImage method. Then, in that method, you would pass that to a delegate which you pass to the Invoke method.
casperOne
A: 

You could do the following:

private void menuItem1_Click(object sender, EventArgs e)
{
    etiquetaCargando.Visible = true;
    Thread t = new Thread(new ThreadStart(RequestImage));

    // NOTE THIS LINE
    // Without this, if your application is closed and the thread isn't,
    // it will leave your program in memory until it does
    t.IsBackground = true;

    this.Invoke(new System.Threading.ThreadStart(RequestImage));
}

private void RequestImage() 
{
    // Do the work here
    // let's assume img is the image you've got

    // Prepare a delegate to invoke
    MethodInvoker m = (MethodInvoker)delegate() {
         myImage.Image = img;
    };

    // Invoke it on the UI thread if needed, otherwise
    // do a straight invoke
    if (this.InvokeRequired)
        this.Invoke(m);
    else 
        m.Invoke();
}
Fritz H
Maybe this.IsBackground is unnecesary. Inside RequestImage I need to access two properties: this.Height and this.Width. How can I do this?
VansFannel
You can access those using `System.Drawing.Size` IIRC, by declaring your own method delegate (`delegate Size getSizeDelegate(Control c);` somewhere else in the code), replacing `MethodInvoker` with `getSizeDelegate`, adding `Control c` to `delegate()` and returning the size from within its body. :)
Fritz H
A: 

As others have said, you're using the invoke backwards.

My suggestion is something like this in your original handler:

ThreadPool.QueueUserWorkItem(RequestImage)

and then use a lamda to do the Invoke from RequestImage:

RequestImage()
{
  ...
  ...

  Invoke(() => { myControl.BackgroundImage = img; myControl.Width = img; etc, etc });
}

Or something like that.

Will Dean