Question

Si vous ne vous embêtez pas à lire le texte entier, vous pouvez passer aux deux derniers points: P

Ce site m'a déjà aidé une douzaine de fois dans le passé, mais maintenant j'ai vraiment besoin d'aider moi-même.

Le problème est le suivant:

  • J'ai d'abord utilisé la fonction de téléchargement avec l'option d'interface utilisateur Show. Cela a très bien fonctionné, mais l'interface utilisateur est laide et peu d'options sont disponibles.

  • Je suis ensuite passé à DownloadFileasync avec l'événement de progression modifié afin d'avoir ma propre interface utilisateur. Le seul problème que j'ai eu est que je parcourt une liste de fichiers que le programme doit télécharger et appeler la fonction de téléchargement (qui appelle la fonction téléchargéeaSync). Comme ça:

    foreach (ListViewItem t in themeList.CheckedItems)
            {
                DownloadFile(file to be downloaded);
            }
    
  • Mais évidemment, cela n'a pas fonctionné, car la fonction DownloadFileSync ne prend pas en charge plusieurs appels en même temps car il n'y a pas de système de file d'attente comme le téléchargement, il ne téléchargera donc que le premier fichier appelé. Donc, ce que j'ai fait, c'est faire une fonction qui ajoute le fichier à télécharger dans un tableau et a une boucle de fond via la liste et attendez avec le téléchargement d'appel jusqu'à ce que le téléchargement précédent soit terminé. Cela a fonctionné un peu. Voici le 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
    
  • Donc, fondamentalement, mon prochain problème est que le programme doit attendre en exécutant plus de code jusqu'à ce que les téléchargements soient terminés. J'ai fait ça en faisant:

    while (_fd.Count != 0)
            Application.DoEvents();
    
  • Ce n'est évidemment pas la meilleure solution car ils peuvent cliquer sur d'autres choses tandis que les téléchargements sont occupés, mais oui, Thread.Sleep ferait tout en geler. Au lieu de cela, je ferais un formulaire d'attente (peut-être ici une barre de progression plutôt que sur le formulaire principal) avec l'accent sur le dessus du formulaire principal, afin qu'ils ne puissent pas cliquer sur le formulaire principal et mettre un fil. ?

  • Comment résoudre cela? Souhaitez-vous également un backgroundworker qui boucle via le tableau de fichiers ou existe-t-il un moyen plus facile et plus efficace. Peut-être ne pas utiliser le téléchargement de téléchargement, mais le téléchargement manuel du socket?

  • Ce que je veux fondamentalement, c'est télécharger des fichiers de manière synchrone, mais j'ai ma propre interface utilisateur (j'ai donc besoin d'utiliser des fonctions de téléchargement asynchrones). Haha

J'espère que je vous ai suffisamment informé. Merci d'avance.

Était-ce utile?

La solution

Utilisez une boîte de dialogue modale, ajoutant peut-être une barre de progression pour la gratification visuelle, informant l'utilisateur qu'un processus fonctionne.

De cette façon, vous pouvez effectuer votre téléchargement Async sans autoriser l'interaction avec les contrôles du formulaire principal.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top