문제

If you don't bother reading the whole text, you can skip to the two last dots :p

This site has helped me a dozen of times already in the past, but now I really need help myself.

The problem is as follows:

  • first I used the DownloadFile function with the show UI option. This worked great, but the UI is ugly and not many options are available.

  • I then switched to DownloadFileAsync with the changed progress event in order to basicly have my own UI. The only problem I've had is that I loop through a list of files the program has to download and call the download function (which calls the DownloadAsync function). Like this:

    foreach (ListViewItem t in themeList.CheckedItems)
            {
                DownloadFile(file to be downloaded);
            }
    
  • But obviously this didn't work, as the DownloadFileAsync function doesn't support multiple calls at the same time because there is no queue system like DownloadFile has, so it will only download the first called file. So what I did is make a function which adds the file to be downloaded to an array and have a backgroundworker loop through the list and wait with calling DownloadAsync until the previous download is complete. This kinda worked out. Here is the code:

    #region "Download functions"
    //Function that converts download speed to a nice user friendly format
    private static string BpsToString(double bps)
    {
        var m = new string[] { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
        var i = 0;
        while (bps >= 0.9 * 1024)
        {
            bps /= 1024;
            i++;
        }
    
        return String.Format("{0:0.00} {1}/sec", bps, m[i]);
    }
    
    private bool _complete = false;
    private string _speed;
    private int _secondsRemaining = -1;
    private long _transferred = 0;
    private Stopwatch _sw = new Stopwatch();
    private List<string[]> _fd = new List<string[]>();
    private void DownloadFile(string url, string des, bool overwrite = false)
    {
        if (overwrite) //if the file needs to be overwritten or not
        {
            if (File.Exists(des)) File.Delete(des);
        }
        else
        {
            if (File.Exists(des)) return;
        }
    
        if (!Directory.Exists(Path.GetDirectoryName(des))) //create the directory if it doesn't exist
            Directory.CreateDirectory(Path.GetDirectoryName(des));
    
        string[] file = {url, des};
        _fd.Add(file); //add file to queue list
    
        if(!backgroundDownloader.IsBusy) //if downloader isn't doing anything, start it again
            backgroundDownloader.RunWorkerAsync();
    }
    
    //function called by the backgroundworker to actually download the file
    private void ContinueDownloadFile(string url, string des)
    {
        var webClient = new WebClient();
        webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
        webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
        webClient.DownloadFileAsync(new Uri(_fd[0][0]), _fd[0][1]);
    }
    
    //when download completed, set progress bar to 0% and remove the first (0) download from the queue
    private void Completed(object sender, AsyncCompletedEventArgs e)
    {
        SetProgressText("Idle");
        SetProgressValue(0);
    
        if(_fd.Count != 0)
            _fd.RemoveAt(0);
    
        _complete = true; //if it's complete, set to true so the backgroundworker knows it can start the next download
    }
    
    //progress bar value change and status change for download speed etc...
    private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        if(progressLabel.Text == "Idle")
            SetProgressText("Downloading...");
    
        if (_sw.Elapsed >= TimeSpan.FromSeconds(1))
        {
            _sw.Stop();
    
            var bytes = e.BytesReceived - _transferred;
            var bps = bytes * 1000.0 / _sw.Elapsed.TotalMilliseconds;
            _speed = BpsToString(bps);
    
            _secondsRemaining = (int)((e.TotalBytesToReceive - e.BytesReceived) / bps);
    
            _transferred = e.BytesReceived;
            _sw.Reset();
            _sw.Start();
    
            SetProgressText("Downloading: " + e.ProgressPercentage + "% | Seconds remaining: " +
            _secondsRemaining + " | Files remaining: " + _fd.Count + " | Speed: " + _speed);
        }
    
        SetProgressValue(e.ProgressPercentage);
    }
    
    //the backgroundworker who starts the downloads from the list one by one
    private void BackgroundDownloaderDoWork(object sender, DoWorkEventArgs e)
    {
        while (_fd.Count != 0)
        {
            _sw.Start();
            _complete = false; //let the backgroundworker wait till the download is complete
            ContinueDownloadFile(_fd[0][0], _fd[0][1]);
    
            while(!_complete) //let it wait here
                Thread.Sleep(100);
    
            _sw.Stop();
            _sw.Reset();
        }
    }
    
    #endregion
    
  • So basicly my next problem is that the program has to wait with executing any more code until the downloads are done. I did this by doing:

    while (_fd.Count != 0)
            Application.DoEvents();
    
  • This is obviously not the best solution as they can click other things while the downloads are busy, but yeah, Thread.Sleep would just freeze everything. Instead I would make a wait form (maybe here a progress bar instead of on the main form) with focus on it on top of the main form, so they can't click the main form and put a Thread.Sleep on the main form?

  • How would you solve this? Would you also use a backgroundworker which loops through the file array or is there an easier, more efficient way. Maybe not using DownloadFileAsync, but manual socket downloading?

  • What I basicly want is to download files synchronously, but have my own UI (so I need to use asynchronous download functions). Haha

I hope I informed you enough. Thanks in advance.

도움이 되었습니까?

해결책

Use a Modal dialog, maybe adding a progress bar for visual gratification, informing the user that a process is working.

This way you can perform your download async without allowing interaction with the main form's controls.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top