Question

I have in my Form constructor, after the InitializeComponent the following code:

using (WebClient client = new WebClient())
{
    client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(client_DownloadDataCompleted);
    client.DownloadDataAsync("http://example.com/version.txt");
}

When I start my form, the UI doesn't appears till client_DownloadDataCompleted is raised. The client_DownloadDataCompleted method is empty, so there's no problem there.

What I'm doing wrong? How is supposed to do this without freezing the UI?

Thanks for your time.
Best regards.

FULL CODE:

Program.cs

using System;
using System.Windows.Forms;

namespace Lala
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

Form1.cs

using System;
using System.Net;
using System.Windows.Forms;

namespace Lala
{
    public partial class Form1 : Form
    {
        WebClient client = new WebClient();

        public Form1()
        {
            client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(client_DownloadDataCompleted);
            client.DownloadDataAsync(new Uri("http://www.google.com"));
            InitializeComponent();
        }

        void client_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
        {
            textBox1.Text += "A";
        }
    }

    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(12, 12);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(12, 41);
            this.textBox1.Multiline = true;
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(468, 213);
            this.textBox1.TabIndex = 1;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(492, 266);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.TextBox textBox1;
    }
}
Was it helpful?

Solution

Now that we've got full code, I can say I'm definitely not seeing the problem - not quite as described, anyway.

I've got a bit of logging to indicate just before and after the DownloadDataAsync calls, and when the completed handler is fired. If I download a large file over 3G, there is a pause between "before" and "after" but the UI comes up ages before the file completes downloading.

I have a suspicion that the connect is done synchronously, but the actual download is asynchronous. That's still unfortunate, of course - and possibly punting all of that into a different thread is the way to go - but if I'm right it's at least worth knowing about.

OTHER TIPS

Encountered the same problem, and found a solution. Quite complex discussion here: http://social.msdn.microsoft.com/Forums/en-US/a00dba00-5432-450b-9904-9d343c11888d/webclient-downloadstringasync-freeze-my-ui?forum=ncl

In short, the problem is web client is searching for proxy servers and hanging the app. The following solution helps:

WebClient webClient = new WebClient();
webClient.Proxy = null;
... Do whatever else ...

You want to run the download in a different thread, see this as a starting point.

I strongly suspect that it's to do with disposing of the WebClient while you're still using it for an asynchronous call.

Try removing the using statement, and call Dispose in an event handler instead. (Or just for testing, don't worry about disposing it at all.

If you could post a short but complete program which demonstrates the issue, that would be really handy.

As well as the disposing of something which is possibly still running the async call that's been mentioned by other people, I would STRONGLY recommend against doing heavyweight stuff like this in a form's constructor.

Do it in an OnLoad override instead, where you will also be able to check the DesignMode property which will help you avoid several levels of hell with the VS forms designer.

UNDELETED: As many think about the using block like I do, I've confirmed that it is not related.

Can you remove the using block, I think it is waiting to dispose the webclient instance.

DownloadDataAsync vs. DownloadData in a non-UI thread:

DownloadDataAsync is nice because it doesn't actually tie up a thread until handling the DownloadDataCompletedEvent, after the request has been made and the server responds.

I believe Jon Skeet is on the right track - I've read that DNS resolution must complete synchronously before the asynchronous HTTP request is queued up and the DownloadDataAsync call returns.

Could the DNS resolution be slow?

I've just tested the same thing in a WPF project under VS2010, .NET 4.

I'm downloading a file with a progress bar to show percentage completed using WebClient.DownloadDataCompleted etc.

And, to my amazement, I'm finding the same thing @Dan mentioned: Within the debugger it blocks the thread in a funny way. In debug, my progress meter gets updated at 1%, then does nothing for a while, then updates again suddenly at 100%. (Debug.WriteLn statements print smoothly throughout). And between these two times, the UI is frozen.

But outside the debugger, the progress bar moves smoothly from 0% to 100%, and the UI never freezes. Which is what you'd expect.

try this:

client.Proxy = GlobalProxySelection.GetEmptyProxy();

That looks a little weird to me.

Try keeping a member ref of the WebClient so you don't destroy it in the constructor, maybe it's blocking on the client.Dispose()

The using() statement is trying to call Dispose() of the WebClient while it is still downloading. The Dispose method probably waits for the download to finish before continuing.

Try not using a using() statement and dispose of the WebClient in your DownloadDataCompleted event.

I can run your code fine. And the form shows up and the download is completed AFTER the form showed up.

I do not have any freezes as you mentioned.

I think it has something to do with the environment you are running it inside.

What version of .NET/Visual Studio are you on?

I've tried your code and it works fine.

Could you post your Main(Args[]) method and the value of a and b when this is run:

    int a, b;
    ThreadPool.GetMaxThreads(out a, out b);

I tried it in .NET 3.5 and VS2008. I'm at a loss, but I am convinced it's to do with the setup on your machine. Not the code. Check these things:

  • Check the thread pool (above). I get a=250 b=1000
  • Disable all third party plugins
  • Load VS "Clean" (Have you rebooted)
  • Close as many programs/services as you can
  • Check your IE config. I think that class uses IE code/settings
  • Firewall? AntiVirus?
  • Try it on another computer

Ummm.... I am just curious

Do you have any Firewalls on?

any firewalls at all on your machine?

Maybe ZoneAlarm?

In my experience, it sort-of blocks the thread when running debugging the project (running it inside Visual Studio) and when accessing the server for the first time.

When running the compiled exe, the blocking is not perceivable.

This problem is still ongoing even in VS2015. I finally figured this out, there's nothing wrong with the code people are using, the problem is actually how fast you can write data to a label control and this is what hangs up the process and causes your UI to freeze. Try replacing your labels that you reference with textboxes in your progresschanged handlers. This solved all lags in the UI for me, I hope this helps others as I spent hours trying to figure out why the code worked sometimes and not others.

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