Do I need to use invoke in backgroundworker progresschanged event? - c#

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.

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

Related

Why when trying to download the files again when clicking the start button I'm getting exception on the progressBar? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
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>();
List<string> urls = new List<string>();
string radarImageWebAddressP1;
string radarImageWebAddressP2;
public Form1()
{
InitializeComponent();
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;
}
}
radarImageWebAddressP1 = "http://www.ims.gov.il/Ims/Pages/RadarImage.aspx?Row=";
radarImageWebAddressP2 = "&TotalImages=10&LangID=1&Location=";
for (int i = 0; i < 9; i++)
{
urls.Add(radarImageWebAddressP1 + i + radarImageWebAddressP2);
}
}
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;
}
}*/
parsedlinks = urls;
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 = "RadarImage" + i.ToString() + ".gif";//parsedlinks[i].Substring(71);
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();
}
}
}
}
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)
{
}
else
{
label3.Text = "Download Completed";
stopwatch.Reset();
stopwatch.Stop();
timer1.Stop();
}
if(e.Cancelled)
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);
}
}
}
First time it's downloading fine but next time when clicking again the start button(button1) it's showing exception: System.Reflection.TargetInvocationException: 'Exception has been thrown by the target of an invocation.'
I tried to reset the progressBar value to 0 in the start button(button1) click event but it didn't solve the problem.
ArgumentOutOfRangeException: Value of '111' is not valid for 'Value'. 'Value' should be between 'minimum' and 'maximum'.
Parameter name: Value
The ArgumentOutOfRange exception is because you never reset Counter:
You define Counter as class level variable and initialize to 0:
int Counter = 0;
Then in your loop you call:
Counter += 1;
percentage = Counter * 100 / total;
When you click the button to restart the download, Counter still holds the final value of the previous run.
In button1_Click, prior to calling RunWorkerAsync
You need to reset it:
Counter = 0;

UI unresponsive + progress bar not working while using backgroundworker c#

I'm working on a WPF application in Visual Studio, I need to download a large file and extract it in my code. Someone recommended that I use background workers, but now when I try to increase the value on my progress bar it doesn't work... Can anyone help?
public void InstallVersion(string version)
{
string location = File.ReadAllText(#"C:\Users\" + Environment.UserName + #"\AppData\Roaming\MidnightFallsLauncher\data\locator.txt");
location = location + #"\Versions\" + version;
if (File.Exists(location + ".zip"))
File.Delete(location + ".zip");
if (Directory.Exists(location))
{
DirectoryInfo di = new DirectoryInfo(location);
foreach (FileInfo file in di.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo dir in di.GetDirectories())
{
dir.Delete(true);
}
}
if (!myWorker.IsBusy)
{
myWorker.RunWorkerAsync();
}
}
And here is my worker code
public MainWindow()
{
InitializeComponent();
myWorker.DoWork += new DoWorkEventHandler(myWorker_DoWork);
myWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
myWorker.ProgressChanged += new ProgressChangedEventHandler(myWorker_ProgressChanged);
myWorker.WorkerReportsProgress = true;
myWorker.WorkerSupportsCancellation = true;
}
protected void myWorker_DoWork(object sender, DoWorkEventArgs e)
{
string location = File.ReadAllText(#"C:\Users\" + Environment.UserName + #"\AppData\Roaming\MidnightFallsLauncher\data\locator.txt");
location = location + #"\Versions\" + Version;
WebClient Client = new WebClient();
string url = "";
string content = "";
string downloadlink = "";
List<string> availibleVersions = new List<string>();
List<string> versionDownload = new List<string>();
url = "https://midnightfalls.glitch.me/versions.html";
content = Client.DownloadString(url);
foreach (string line in content.Split(new string[] { "<br>", "<br />" }, StringSplitOptions.None))
{
if (line.Contains("0"))
{
availibleVersions.Add(line);
}
}
url = "https://midnightfalls.glitch.me/versionslink.html";
content = Client.DownloadString(url);
foreach (string line in content.Split(new string[] { "<br>", "<br />" }, StringSplitOptions.None))
{
if (line.Contains("https"))
{
versionDownload.Add(line);
}
}
for (var i = 0; i < availibleVersions.Count; i++)
{
if (availibleVersions[i] == Version)
{
downloadlink = versionDownload[i];
}
}
Client.DownloadFile(downloadlink, location + ".zip");
ZipFile.ExtractToDirectory(location + ".zip", location);
File.Delete(location + ".zip");
RunGame(Version);
}
protected void myWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
protected void myWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Dispatcher.Invoke(() =>
{
progress.Value += 10;
});
}
Also, while my worker is running, the UI freezes... I'm pretty sure that's not meant to happen.
EDIT:
The UI is now updating but the progress bar still doesnt work...
It looks like the problem is that the main thread is constantly working while the background worker is doing its work:
while (this.myWorker.IsBusy)
{
this.Dispatcher.Invoke(() =>
{
progress.Value += 10;
});
}
means that your main thread is constantly doing stuff while the background job is working, which is why the UI doesn't update.
You need to move the progress update to the background worker (where you could also set a value that actually makes some sense, e.g. to indicate how many of the 'availableVersion's you have downloaded).
Hope that makes sense.
EDIT:
Suppose we'll put all the code directly in the view, so assume we have a progress bar named 'progressBar' and a button named 'btnStart' (which kicks of the backgroundworker).
Here's the codebehind:
private BackgroundWorker worker;
public MainWindow()
{
InitializeComponent();
this.worker = new BackgroundWorker();
this.worker.DoWork += new DoWorkEventHandler(myWorker_DoWork);
this.worker.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
this.worker.ProgressChanged += new
ProgressChangedEventHandler(myWorker_ProgressChanged);
this.worker.WorkerReportsProgress = true;
this.worker.WorkerSupportsCancellation = true;
}
private void myWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar.Value = e.ProgressPercentage;
}
private void myWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Whatever you need to do when finished here (alert, update a label, etc.)
}
private void myWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Just loop and report new progress. Sleep a little in between each
// progress update so that it isn't over before we have a chance to see it.
for(int i=0;i<100;i++)
{
Thread.Sleep(200);
this.worker.ReportProgress(i);
}
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
this.worker.RunWorkerAsync();
}
What happens is that the background worker fires an event notifying that progress has changed.
The main thread has a handle on that event, and updates the progress bar.
Since it's a background worker you don't need to use Dispatcher.Invoke - this is already taken care of.
Hope the example clarifies things for you.
I'm pretty sure you're blocking right here:
while (this.myWorker.IsBusy)
{
this.Dispatcher.Invoke(() =>
{
progress.Value += 10;
});
}
You should be calling ReportProgress on your BackgroundWorker instance within your myWorker_DoWork method.
Also, if you're using .NET 4.5 or later, you can dump the BackgroundWorker entirely and rewrite this code using the async/await pattern.

C# How to fill ProgressBar in a while loop?

I'm trying to fill my ProgressBar inside a while loop.
But somehow the Progressbar value wont grow.
I want the code to open a textfile and to post a request for each row in it.
My code for this:
public void registerAccounts()
{
const Int32 BufferSize = 128;
using (var fileStream = File.OpenRead(nsTextBox3.Text))
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize))
{
String line;
while ((line = streamReader.ReadLine()) != null)
{
var request = (HttpWebRequest)WebRequest.Create("http://registration.zwinky.com/registration/register.jhtml");
var postData = "form=reg&success=http://profile.zwinky.com/zwinkyprofile/main.jhtml?username=&tp=&user.partnerId=ZZzer000&source=zwinky&design=classic&ad=/&user.userName=" + line + "&user.password=" + nsTextBox1.Text + "&user.passwordConfirm=" + nsTextBox1.Text + "&user.secretQId=1&user.secretQAnswer=" + line + "&user.birthdayMonth=1&user.birthdayDay=1&user.birthdayYear=1996&user.gender=M&user.emailAddr=" + nsTextBox2.Text + "&emailAddrConfirm=" + nsTextBox2.Text + "&x=42&y=51&partnerCode=/";
var data = Encoding.ASCII.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
}
}
}
How would I let the ProgressBar in my GUI grow now?
I thought about counting the lines inside the text document like this:
var lineCount = File.ReadLines(nsTextBox3.Text).Count();
and then run a for loop in it. But it wont grow sadly.
Would appreciate any kind of help.
You appear to be running this method on the UI thread of your application. This thread is responsible for updating the UI, so while it's busy with the loop it can't do that. What likely happens is that once your loop completes the progress bar is updated straight to 100% and you don't see it.
Look at using a Background Worker.
It takes care of the above concerns by doing all of the threading and callbacks for you in a very simple manner.
Example taken straight from the MSDN article:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace SL_BackgroundWorker_CS
{
public partial class Page : UserControl
{
private BackgroundWorker bw = new BackgroundWorker();
public Page()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
private void buttonCancel_Click(object sender, RoutedEventArgs e)
{
if (bw.WorkerSupportsCancellation == true)
{
bw.CancelAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10));
}
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
this.tbProgress.Text = "Canceled!";
}
else if (!(e.Error == null))
{
this.tbProgress.Text = ("Error: " + e.Error.Message);
}
else
{
this.tbProgress.Text = "Done!";
}
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
}
}
Put your loop in DoWork and your progress bar in ProgressChanged. Simples!
you can try this,this is not perfect but worked
int linecount=0;
while ((line = streamReader.ReadLine()) != null)
{
linecount=linecount+1;
}
int number=0;
while ((line = streamReader.ReadLine()) != null)
{
number=number+1;
// what is yor work
progressBar1.Value = i * progressBar1.Maximum / linecount; //show process bar counts
LabelTotal.Text = i.ToString() + " of " + linecount; //show number of count in lable
int presentage = (i * 100) / linecount;
LabelPresentage.Text = presentage.ToString() + " %"; //show precentage in lable
Application.DoEvents(); //keep form active in every loop
}

C# Backgroundworker download progress in label, get bytes in label

I've created an application that patches my game servers files.
However, I've got 3 problems which I can't solve:
When downloading a new patch, it doesn't update the progressbar instantly, but refreshes it after around 30-40 seconds
I want a label to show how much mega bytes they are downloading, and how much they have so far (for example: 122Mb/750Mb
When downloading, I want a label to show ~% of how much it has downloaded so far
I am not sure how to add number 2 and 3, and the number 1 problem just seems ridiculous, because there's nothing that indicates it should refresh after 30-40 seconds in my coding (at least as far as I know)
My backgroundWorker1_DoWork:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//Defines the server's update directory
string Server = "http://localhost/dl/game-updates/";
//Defines application root
string Root = AppDomain.CurrentDomain.BaseDirectory;
//Make sure version file exists
FileStream fs = null;
if (!File.Exists("version"))
{
using (fs = File.Create("version"))
{
}
using (StreamWriter sw = new StreamWriter("version"))
{
sw.Write("1.0");
}
}
//checks client version
string lclVersion;
using (StreamReader reader = new StreamReader("version"))
{
lclVersion = reader.ReadLine();
}
decimal localVersion = decimal.Parse(lclVersion);
//server's list of updates
XDocument serverXml = XDocument.Load(#Server + "Updates.xml");
//The Update Process
foreach (XElement update in serverXml.Descendants("update"))
{
string version = update.Element("version").Value;
string file = update.Element("file").Value;
decimal serverVersion = decimal.Parse(version);
string sUrlToReadFileFrom = Server + file;
string sFilePathToWriteFileTo = Root + file;
if (serverVersion > localVersion)
{
Uri url = new Uri(sUrlToReadFileFrom);
System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse();
response.Close();
Int64 iSize = response.ContentLength;
Int64 iRunningByteTotal = 0;
using (System.Net.WebClient client = new System.Net.WebClient())
{
using (System.IO.Stream streamRemote = client.OpenRead(new Uri(sUrlToReadFileFrom)))
{
using (Stream streamLocal = new FileStream(sFilePathToWriteFileTo, FileMode.Create, FileAccess.Write, FileShare.None))
{
int iByteSize = 0;
byte[] byteBuffer = new byte[iSize];
while ((iByteSize = streamRemote.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
{
streamLocal.Write(byteBuffer, 0, iByteSize);
iRunningByteTotal += iByteSize;
double dIndex = (double)(iRunningByteTotal);
double dTotal = (double)byteBuffer.Length;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
backgroundWorker1.ReportProgress(iProgressPercentage);
}
streamLocal.Close();
}
streamRemote.Close();
}
}
//unzip
using (ZipFile zip = ZipFile.Read(file))
{
foreach (ZipEntry zipFiles in zip)
{
zipFiles.Extract(Root + "\\", true);
}
}
//download new version file
WebClient webClient = new WebClient();
webClient.DownloadFile(Server + "version.txt", #Root + "version");
//Delete Zip File
deleteFile(file);
}
}
}
My backgroundWorker1_ProgressChanged:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label1.Text = "Downloading updates...";
}
And my backgroundWorker1_RunWorkerCompleted:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
settings_btn.Enabled = true;
start_btn_disabled.Enabled = false;
start_btn_disabled.Visible = false;
start_btn.Visible = true;
start_btn.Enabled = true;
progressBar1.Value = 100;
label1.Text = "Client is up to date!";
}
Also, a side note: I'm also having a bit problems of updating labels in backgroundWorker2_DoWork?
Any ideas?
Here's some working code which updates a label on Form1 using the BackgroundWorker.
Create a new Windows Form project and drop it in your code and it'll work.
It's super ugly, but it works.
After that, just plug your code into the DoWork method and calculate your value and send to ReportProgress.
Keep in mind that the work done in DoWork method is the actual Background Thread.
That means that in that method (DoWork) you cannot access UI (form) elements because they are on the UI thread.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Enabled = true;
}
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = e.ProgressPercentage.ToString();
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
FakeCountingWork();
}
private void FakeCountingWork()
{
int totalNumber = 100;
int progressCounter = 0;
while (progressCounter < totalNumber)
{
int fakecounter = 0;
for (int x = 0; x < 100000000; x++)
{
fakecounter++;
}
progressCounter++;
backgroundWorker1.ReportProgress(progressCounter);
}
}
}
################################## EDITED TO ADD OTHER FUNCTIONALITY
Okay, here's how you can implement a label which displays the number of bytes downloaded so far.
Add a second label named label2 to your form.
Next alter the following methods from my previous example.
Here we are going to use the UserState to pass an extra value to the ProgressChanged Event. It's very simple. You can see that I'm generating a random number and it will now appear in Label2. This is where you could show your number of bytes.
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = e.ProgressPercentage.ToString();
label2.Text = e.UserState.ToString();
}
private void FakeCountingWork()
{
int totalNumber = 100;
int progressCounter = 0;
Random rnd = new Random();
while (progressCounter < totalNumber)
{
int fakecounter = 0;
for (int x = 0; x < 100000000; x++)
{
fakecounter++;
}
progressCounter++;
updateValue = rnd.Next();
backgroundWorker1.ReportProgress(progressCounter,updateValue);
}
}
I would imagine this is because you are trying to update UI objects on a different thread. HAve you tried using the Dispatcher if using wpf? https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.invoke(v=vs.110).aspx
or Invoke if using Winforms? https://msdn.microsoft.com/fr-ca/library/zyzhdc6b(v=vs.85).aspx
Edit:
As #daylight pointed out to be, the UI was being updated in the progresschanged event, which executes on the thread which created the background worker, therefore there shouldn't be an issue regarding threading. See https://msdn.microsoft.com/en-us/library/ka89zff4(v=vs.110).aspx for more info

Creating a multi-thread webdownload via WebClient / BeginInvoke Method

I have actually the following code:
private Stopwatch _sw;
public void DownloadFile(string url, string fileName)
{
string path = #"C:\DL\";
Thread bgThread = new Thread(() =>
{
_sw = new Stopwatch();
_sw.Start();
labelDownloadAudioStatusText.Visibility = Visibility.Visible;
using (WebClient webClient = new WebClient())
{
webClient.DownloadFileCompleted +=
new AsyncCompletedEventHandler(DownloadCompleted);
webClient.DownloadProgressChanged +=
new DownloadProgressChangedEventHandler(DownloadStatusChanged);
webClient.DownloadFileAsync(new Uri(url), path + fileName);
}
});
bgThread.Start();
}
void DownloadStatusChanged(object sender, DownloadProgressChangedEventArgs e)
{
this.BeginInvoke((MethodInvoker) delegate
{
int percent = 0;
if (e.ProgressPercentage != percent)
{
percent = e.ProgressPercentage;
progressBarDownloadAudio.Value = percent;
labelDownloadAudioProgress.Content = percent + "%";
labelDownloadAudioDlRate.Content =
(Convert.ToDouble(e.BytesReceived)/1024/
_sw.Elapsed.TotalSeconds).ToString("0.00") + " kb/s";
Thread.Sleep(50);
}
});
}
private void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
{
this.BeginInvoke((MethodInvoker) delegate
{
labelDownloadAudioDlRate.Content = "0 kb/s";
labelDownloadAudioStatusText.Visibility = Visibility.Hidden;
});
}
My problem is that in a previous version without the outer thread, the whole GUI freezes sporadically and the GUI is liquid when the download is finished. So I googled around and found this: https://stackoverflow.com/a/9459441/2288470
An answer was to pack everything into a separate thread which performs the interaction with DownloadFileAsync, but I got the fault, that the BeginInvoke method can not be found.
When using WPF, the BeginInvoke method is not exposed by the Window class, like it is for Form in WinForms. Instead you should use Dispatcher.BeginInvoke.
Working code:
private Stopwatch _sw;
public void DownloadFile(string url, string fileName)
{
string path = #"C:\DL\";
Thread bgThread = new Thread(() =>
{
_sw = new Stopwatch();
_sw.Start();
labelDownloadAudioStatusText.Visibility = Visibility.Visible;
using (WebClient webClient = new WebClient())
{
webClient.DownloadFileCompleted +=
new AsyncCompletedEventHandler(DownloadCompleted);
webClient.DownloadProgressChanged +=
new DownloadProgressChangedEventHandler(DownloadStatusChanged);
webClient.DownloadFileAsync(new Uri(url), path + fileName);
}
});
bgThread.Start();
}
void DownloadStatusChanged(object sender, DownloadProgressChangedEventArgs e)
{
Dispatcher.BeginInvoke((MethodInvoker) delegate
{
int percent = 0;
if (e.ProgressPercentage != percent)
{
percent = e.ProgressPercentage;
progressBarDownloadAudio.Value = percent;
labelDownloadAudioProgress.Content = percent + "%";
labelDownloadAudioDlRate.Content =
(Convert.ToDouble(e.BytesReceived)/1024/
_sw.Elapsed.TotalSeconds).ToString("0.00") + " kb/s";
Thread.Sleep(50);
}
});
}
private void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
{
Dispatcher.BeginInvoke((MethodInvoker) delegate
{
labelDownloadAudioDlRate.Content = "0 kb/s";
labelDownloadAudioStatusText.Visibility = Visibility.Hidden;
});
}
Invoke method and BeginInvoke method is implemented on System.Windows.Forms.Control Class. If you are not writing code in such as Form Class, you cannot use this method. To resolve this problem, inherit your Job Class from System.Windows.Forms.Control Class, then you could use BeginInvoke method. Please note that you have to create instance on main thread.
public class JobClass : System.Windows.Forms.Control {
.....
}

Categories