Daniel Lip
Daniel Lip

Reputation: 11319

Do I need to use invoke in backgroundworker progresschanged event?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Xml.Linq;
using System.Diagnostics;
using System.Management;
using System.Runtime.InteropServices;

namespace DownloadFiles
{
    public partial class Form1 : Form
    {
        Stopwatch sw = new Stopwatch();
        Stopwatch stopwatch = new Stopwatch();
        string filesdirectory = "Downloaded_Files";
        string mainurl = "http://www.usgodae.org/ftp/outgoing/fnmoc/models/navgem_0.5/latest_data/";
        List<string> parsedlinks = new List<string>();
        string path_exe = Path.GetDirectoryName(Application.LocalUserAppDataPath);

        List<string> results = new List<string>();
        //StreamWriter w = new StreamWriter(@"e:\monitordetector.txt");

        public Form1()
        {
            InitializeComponent();

            //DetectScreenName();

            label3.Text = "";
            label4.Text = "";
            label5.Text = "";
            label7.Text = "";
            button2.Enabled = false;
            button3.Enabled = false;
            filesdirectory = Path.Combine(path_exe, filesdirectory);
            if (!Directory.Exists(filesdirectory))
            {
                Directory.CreateDirectory(filesdirectory);
            }
            else
            {
                if (IsDirectoryEmpty(filesdirectory) == false)
                {
                    button3.Enabled = true;
                }
            }
        }

        public bool IsDirectoryEmpty(string path)
        {
            return !Directory.EnumerateFileSystemEntries(path).Any();
        }

        private string downloadhtml(string url)
        {
            backgroundWorker1.ReportProgress(0, "Downloading Main Url");
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            request.Proxy = null;
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader sr = new StreamReader(response.GetResponseStream());
            string html = sr.ReadToEnd();
            sr.Close();
            response.Close();
            StreamWriter w = new StreamWriter(path_exe + "\\page.html");
            w.Write(html);
            w.Close();
            return html;
        }

        int Counter = 0;
        int percentage = 0;
        int total = 0;
        int countfiletodownload = 0;
        bool processStatus = false;
        private void Parseanddownloadfiles()
        {
            downloadhtml(mainurl);
            if (bgw.CancellationPending == false)
            {
                backgroundWorker1.ReportProgress(0, "Parsing Links");
                HtmlAgilityPack.HtmlWeb hw = new HtmlAgilityPack.HtmlWeb();
                HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
                doc = hw.Load(path_exe + "\\page.html");
                foreach (HtmlAgilityPack.HtmlNode link in doc.DocumentNode.SelectNodes("//a[@href]"))
                {
                    string hrefValue = link.GetAttributeValue("href", string.Empty);
                    if (hrefValue.Contains("US"))
                    {
                        string url = "http://www.usgodae.org/ftp/outgoing/fnmoc/models/navgem_0.5/latest_data/" + hrefValue;
                        parsedlinks.Add(url);
                        if (bgw.CancellationPending == true)
                            return;
                    }
                }
                countfiletodownload = parsedlinks.Count;
                total = parsedlinks.Count;
                backgroundWorker1.ReportProgress(0, "Downloading Files");
                processStatus = true;
                for (int i = 0; i < parsedlinks.Count && bgw.CancellationPending == false; i++)
                {
                    try
                    {
                        using (WebClient client = new WebClient())
                        {
                            sw.Start();
                            Uri uri = new Uri(parsedlinks[i]);
                            string filename = parsedlinks[i].Substring(71);
                            //client.DownloadFile(parsedlinks[i], filesdirectory + "\\" + filename);
                            client.DownloadFileAsync(uri, filesdirectory + "\\" + filename);
                            Counter += 1;
                            percentage = Counter * 100 / total;
                            string filenametoreport = filename.Substring(1);
                            countfiletodownload--;
                            backgroundWorker1.ReportProgress(percentage, filenametoreport);//countfiletodownload, filenametoreport);
                        }
                    }
                    catch (Exception err)
                    {
                        string error = err.ToString();
                    }
                }
            }
        }

        /*void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            // Calculate download speed and output it to labelSpeed.
            if (label12.InvokeRequired)
            {
                label12.Invoke(new MethodInvoker(delegate
                {
                    label12.Text = string.Format("{0} kb/s", (e.BytesReceived / 1024d / sw.Elapsed.TotalSeconds).ToString("0.00"));
                }));
            }


            // Update the progressbar percentage only when the value is not the same.
            if (progressBar1.InvokeRequired)
            {
                progressBar1.Invoke(new MethodInvoker(delegate
                {
                    progressBar1.Value = e.ProgressPercentage;
                }));
            }

            // Show the percentage on our label.
            if (label13.InvokeRequired)
            {
                label13.Invoke(new MethodInvoker(delegate
                {
                    label13.Text = e.ProgressPercentage.ToString() + "%";
                }));
            }
            // Update the label with how much data have been downloaded so far and the total size of the file we are currently downloading
            if (label14.InvokeRequired)
            {
                label14.Invoke(new MethodInvoker(delegate
                {
                    label14.Text = string.Format("{0} MB's / {1} MB's",
                        (e.BytesReceived / 1024d / 1024d).ToString("0.00"),
                        (e.TotalBytesToReceive / 1024d / 1024d).ToString("0.00"));
                }));
            }
        }*/

        /*void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            // Reset the stopwatch.
            sw.Reset();

            if (e.Cancelled == true)
            {
                MessageBox.Show("Download has been canceled.");
            }
            else
            {
                //MessageBox.Show("Download completed!");
            }
        }*/

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        BackgroundWorker bgw;
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            bgw = (BackgroundWorker)sender;
            if (bgw.CancellationPending == true)
            {
                return;
            }
            else
            {
                Parseanddownloadfiles();
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.UserState.ToString() == "Downloading Main Url")
            {
                label3.Text = e.UserState.ToString();
            }
            if (e.UserState.ToString() == "Parsing Links")
            {
                label3.Text = e.UserState.ToString();
            }
            if (e.UserState.ToString() == "Downloading Files")
            {
                label7.Text = countfiletodownload.ToString();//parsedlinks.Count.ToString();
                label3.Text = e.UserState.ToString();
            }
            if (processStatus == true)
            {
                if (e.UserState.ToString() != "Downloading Files")
                {
                    label4.Text = e.UserState.ToString();
                    label7.Text = countfiletodownload.ToString();
                    progressBar1.Value = e.ProgressPercentage;
                    /*using (var bitmap = new Bitmap(this.Width, this.Height))
                    {
                        this.DrawToBitmap(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));
                        bitmap.Save(@"e:\screens\ss.gif" + countscreenshots, System.Drawing.Imaging.ImageFormat.Gif);
                        countscreenshots += 1;
                    }*/
                }
            }
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                string fff = null;
            }
            label3.Text = "Operation Cancelled";
            button1.Enabled = true;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            label3.Text = "Cancelling Operation";
            backgroundWorker1.CancelAsync();
            button2.Enabled = false;
            timer1.Stop();
            stopwatch.Stop();
            stopwatch.Reset();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            label3.Text = "";
            label4.Text = "";
            label7.Text = "";
            backgroundWorker1.RunWorkerAsync();
            timer1.Start();
            stopwatch.Start();
            button1.Enabled = false;
            button2.Enabled = true;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Process.Start(filesdirectory);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            label5.Text = string.Format("{0:hh\\:mm\\:ss}", stopwatch.Elapsed);
        }
    }
}

for some reason the code inside the event make the whole application to not to be async. If I comment the code not to use it then it's async working fine.

I also want to use the client_DownloadProgressChanged that now is not in use to show and display the downloading infos.

Like speed time using progressBar info per current file download and info for overall progress download.

Upvotes: 0

Views: 149

Answers (1)

Caius Jard
Caius Jard

Reputation: 74605

No*, you do not need to Invoke in a BackgroundWorker's ProgressChanged event. DoWork is run on a background thread but the thread that executes the code inside ProgressChanged (and the other event handlers on a backgroundworker) is done by the thread that created the backgroundworker, which should be the same as the thread that created the other ui controls and hence no Invokation required

*Having said this, pay close attention to the part where I said that BGW will run the ProgressChanged event using the thread that created the BGW. In most cases this will be the UI thread.. if you've used a thread other than the UI thread to create the BGW then YES, invokation would be required. Create the BGW on the UI thread along with all your other controls, if you want a simple life. For the rest of my advice I'll assume this is what you've done.

I wasn't able to understand exactly your problem, but remember that it's the UI thread that runs the event handler. If you send that thread off doing some long task or blocking operation as part of your efforts to update the labels in the UI then it will make the application seem hung. You must let the UI thread complete the code in the event handler as soon as possible. If it will need to access data that is from a long or blocking operation, either have the DoWork calculate the data before it raises its progress, or use another method to avoid blocking the UI thread, like async task pattern

Note that Invoke and blocking the ui are completely different things. Windows controls may only be accessed by the thread they were created with. If another thread wants to access the control, it should use Invoke to cause the ui thread to do the work instead. This is a very different thing to the notion of not jamming up your ui by using the UI thread to read 50 gigabytes from a slow server and not using something that lets it quickly get back to its job of processing window messages and keeping the app responsive

Upvotes: 2

Related Questions