Question

I am trying to load an image from a webserver into a pictureBox. To not block the Form untill the picture is loaded i start a new Thread which works on the LoadPicture()-function till all work is done.

Everything will be started on a MouseHover-Event so it can be triggered multiple times in a short period as the default WindowsHoverTime is 180ms and can't be changed (i searched a lot on that). This is the function:

public void LoadPicture(string url)
{
    try
    {
        WebRequest req = HttpWebRequest.Create(url);
        req.Timeout = 3500;
        req.Method = "HEAD";
        using (WebResponse resp = req.GetResponse())
        {
            int ContentLength;
            if (int.TryParse(resp.Headers.Get("Content-Length"), out ContentLength))
            {
                string ContentType = resp.Headers.Get("Content-Type");
                if (ContentLength < 1048576 && ContentType == "image/png")
                {
                    this.pictureBox1.Load(url);
                }
                else
                {
                    this.pictureBox1.Image = mainprogram.Properties.Resources.sample;
                }
            }
        }
    }
    catch { this.pictureBox1.Image = mainprogram.Properties.Resources.sample; }
}

If the url contains a png-file which is smaller than 1MB it should be loaded into pictureBox1, if not, then a deafult image from resources is loaded.

Thread and calling:

namespace mainprogram
{
    public partial class Form1 : Form
    {
        Thread threadworker;
        .
        .
        .
        private void button1_MouseHover(object sender, EventArgs e)
        {
            if (threadworker.IsAlive == false)
            {
                threadworker = new Thread(() => LoadPicture(url));
                threadworker.Start();
            }
        }

Now the problem: The thread will fail almost all the time. Catch{} will be executed 9/10 times. Mostly crash at WebResponse resp = req.GetResponse(). If i use the function within the same thread (without starting a new one) it will load just fine, but the GUI will stop responding for like 3seconds untill the picture is loaded.

Edit: It DOES work sometimes, so i am not sure what i am doing wrong.

Was it helpful?

Solution 2

Ok after a lot of research and trouble i got it working. First i have to thank SLaks for his comment. I used async/await + GetResponseAsync and it was working flawless:

public async void LoadPicture(string url)
{
    bool worker = await GetURLContentsAsync(url);
}

private async Task<bool> GetURLContentsAsync(string url)
{
    var webReq = (HttpWebRequest)WebRequest.Create(url);             
    using (WebResponse response = await webReq.GetResponseAsync())
    {
        .
        .
        .

Unfortunately GetResponseAsync is only aviable in .NET Framework 4.5 so i tried a different approach with BeginGetResponse from here: https://www.informit.com/guides/content.aspx?g=dotnet&seqNum=583 But it was either blocking the UI-thread (not asynchronous) or failed to load the picture 3/4 times (same image).

So i ended up using threads again:

private void LoadPicture(string url)
{
    threadworker = new Thread(() => pictureBox1.Image = getImgFromUrl(url));
    threadworker.Start();
} 

public Bitmap getImgFromUrl(string url)
{
    Bitmap bmp = null;
    try
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        try
        {
            bmp = new Bitmap(response.GetResponseStream());
            .
            .
            .

As you can see it is almost the same code as in the question-post, i figured out that threadworker.IsAlive was the culprit and pictureBox1.Load() takes a lot longer than writing the stream into a temporary BitMap-file and loading it afterwards.

OTHER TIPS

Using async

To exemplify SLaks excellent comment, here is a walktrough on MSDN how to use async/await for the web http://msdn.microsoft.com/en-us/library/hh300224.aspx#BKMK_ConvertGtBtArr

Also lots of explanation on the workings and benefits of async/await on this answer to a similar question.

Using .NET 4.0

There is also an asyncronous version of GetResponse available for .NET 4.0 (and lower) called BeginGetResponse. It's documented on MSDN http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetresponse(v=vs.100).aspx but the example on that page is horrible because it blocks using events. What you want instead is having your callback called in the UI thread. async/await do so automatically. With callbacks you might have to defer the callback to the UI syncContext manually.

Remarks

Anyway, all of this is only helpful if your actual problem is an access violation because you try to draw your requested image in a non UI thread and/or blocking the UI. If instead GetResponse throws an exception because of DNS problems, error 403/404, timeouts or similar you should instead invest in finding out why.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top