Frage

Wenn Sie nicht den gesamten Text lesen möchten, können Sie zu den beiden letzten Punkten springen: p

Diese Seite hat mir schon in der Vergangenheit ein Dutzend Mal geholfen, aber jetzt brauche ich wirklich Hilfe.

Das Problem ist wie folgt:

  • Zuerst habe ich die DownloadFile-Funktion mit der Option show UI verwendet. Dies hat gut funktioniert, aber die Benutzeroberfläche ist hässlich und es stehen nicht viele Optionen zur Verfügung.

  • Ich habe dann mit dem geänderten Fortschrittsereignis zu DownloadFileAsync gewechselt, um grundsätzlich meine eigene Benutzeroberfläche zu haben. Das einzige Problem, das ich hatte, ist, dass ich eine Liste der Dateien durchlaufe, die das Programm herunterladen muss, und die Download-Funktion aufrufe (die die DownloadAsync-Funktion aufruft). So:

    foreach (ListViewItem t in themeList.CheckedItems)
            {
                DownloadFile(file to be downloaded);
            }
    

  • Dies hat jedoch offensichtlich nicht funktioniert, da die DownloadFileAsync-Funktion nicht mehrere Aufrufe gleichzeitig unterstützt, da es kein Warteschlangensystem wie DownloadFile gibt und nur die zuerst aufgerufene Datei heruntergeladen wird. Ich habe also eine Funktion erstellt, die die herunterzuladende Datei zu einem Array hinzufügt und eine Hintergrund-Worker-Schleife durch die Liste führt. Warten Sie mit dem Aufruf von DownloadAsync, bis der vorherige Download abgeschlossen ist. Das hat irgendwie geklappt. Hier ist der 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
    

  • Grundsätzlich besteht mein nächstes Problem darin, dass das Programm mit der Ausführung weiteren Codes warten muss, bis die Downloads abgeschlossen sind. Ich habe dies getan, indem ich:

    while (_fd.Count != 0)
            Application.DoEvents();
    

  • Dies ist offensichtlich nicht die beste Lösung, da sie auf andere Dinge klicken können, während die Downloads beschäftigt sind, aber ja, Thread.Sleep würde einfach alles einfrieren. Stattdessen würde ich ein Warteformular erstellen (vielleicht hier einen Fortschrittsbalken anstelle des Hauptformulars), wobei der Fokus oben auf dem Hauptformular liegt, sodass sie nicht auf das Hauptformular klicken und einen Thread.Sleep in das Hauptformular einfügen können ?

  • Wie würden Sie das lösen? Würden Sie auch einen Hintergrundarbeiter verwenden, der das Dateiarray durchläuft, oder gibt es eine einfachere und effizientere Möglichkeit? Verwenden Sie möglicherweise nicht DownloadFileAsync, sondern das manuelle Herunterladen von Sockets?

  • Grundsätzlich möchte ich Dateien synchron herunterladen, habe aber eine eigene Benutzeroberfläche (daher muss ich asynchrone Downloadfunktionen verwenden). Haha

    Ich hoffe, ich habe Sie ausreichend informiert. Vielen Dank im Voraus.

War es hilfreich?

Lösung

Verwenden Sie ein modales Dialogfeld und fügen Sie möglicherweise einen Fortschrittsbalken zur visuellen Befriedigung hinzu, um den Benutzer darüber zu informieren, dass ein Prozess funktioniert.

Auf diese Weise können Sie Ihren Download asynchron ausführen, ohne die Interaktion mit den Steuerelementen des Hauptformulars zuzulassen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top