Kai
Kai

Reputation: 5910

Prevent form from freezing

I'd like to download a picture and afterwards show it in a picturebox.

at first I did like this:

WebClient client = new WebClient();
client.DownloadFile(url, localFile);
pictureBox2.Picture = localFile;

But that wasn't perfect because for the time while the download is performed the app is kinda freezing.

Then I changed to this:

public class ParamForDownload
{
    public string Url { get; set; }
    public string LocalFile { get; set; }
}
ParamForDownload param = new ParamForDownload()
        {
            Url = url,
            LocalFile = localFile
        };

      ThreadStart starter = delegate { DownloadMap (param); };
        new Thread(starter).Start();

        pictureBox2.Picture = localFile;



    private static void DownloadMap(ParamForDownload p) 
    {
        WebClient client = new WebClient();
       client.DownloadFile(p.Url, p.LocalFile);
    }

But now I have to do something like a "wait for thread ending" because the file is accessed in the thread and to same time there's downloaded something to the file by the DownloadMap method.

What would be the best wait to solve that problem?

Upvotes: 3

Views: 5410

Answers (4)

Henk Holterman
Henk Holterman

Reputation: 273169

The simplest solution would be to just use the Picturebox.LoadAsync() method and let the Picturebox worry about loading it in the background. If you need to check for errors, use the LoadCompleted event (of the picturebox).

A line like:

pictureBox1.LoadAsync(@"http://imgs.xkcd.com/comics/tech_support_cheat_sheet.png");

is all you need.

Upvotes: 2

Lloyd Powell
Lloyd Powell

Reputation: 18760

Basically, What was happening originally, was the UI Thread was completing the download, and because it was working away on that, it couldn't be refreshed or painted (working synchronously). Now what is happening is that you're starting the thread then the UI thread is continuing, then trying to assign the local file (which hasn't finished downloading) to the picture box. You should try either of the following:

You should use a background worker to complete your download task.

It has events that will be very handy. DoWork, where you can start the download.

There is also a RunWorkerCompleted event that is fired when the Work has completed. Where you can set the image there (pictureBox2.Picture = localFile;).

It's definitely worth checking out, I think it's the most appropriate way to complete what you are trying to achieve.

Or

If you want to stick with using a Thread. You could take out the Image assignment after you've done the Thread.Start(), and put this in to your Worker Thread function:

private delegate void MyFunctionCaller();

private static void DownloadMap(ParamForDownload p) 
{
    WebClient client = new WebClient();
   client.DownloadFile(p.Url, p.LocalFile);
    DownloadMapComplete(p);
}

private void DownloadMapComplete(ParamForDownload p)
{
if (InvokeRequired == true)
  {
  MyFunctionCaller InvokeCall = delegate { DownloadMapComplete(p); };
  Invoke(InvokeCall);
  }
else
  {
  pictureBox2.Picture = p.LocalFile;
  }
}

Upvotes: 13

Jason Miesionczek
Jason Miesionczek

Reputation: 14448

Not related to the threading issue, but if you don't have any other requirements for saving the photo to the disk you can do this:

WebClient client = new WebClient();
byte[] data = client.DownloadData(item.Url);
MemoryStream ms = new MemoryStream(data);
Bitmap img = new Bitmap(ms);
pictureBox.Image = img;

Upvotes: 1

Leather
Leather

Reputation: 227

When the user initiates the process what you need to do is:

1) Update the UI to indicate something is happening. IE: Disable all the fields and put up a message saying "I am downloading the file, please wait...". Preferentially with some kind of progress indicator (sorry I am not sure if the WebClient supports progress etc but you need to update the UI as the download make take a while).

2) Attach an event handler to the WebClient's 'DownloadFileCompleted'.

3) Use the WebClient's DownloadFileAsync method to start the download on another thread. You don't need to spin threads up yourself.

4) When the 'DownloadFileCompleted' event is fired route the call back to the UI thread by using the form's invoke method. THIS IS VERY IMPORTANT.

See: http://weblogs.asp.net/justin_rogers/pages/126345.aspx

and: http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

5) Once the event has been routed back onto the UI thread open the file and update the UI as required (IE: Re-enable fields etc).

Leather.

Upvotes: 1

Related Questions