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 {
.....
}
Related
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
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.
I'm trying to create a simple program to download few files. I've tried some ready-made solutions I found on the web but I can't manage to make it work the way I want it to. I'm using this:
private void startDownload(string toDownload, string saveLocation)
{
Thread thread = new Thread(() =>
{
WebClient client = new WebClient();
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
client.DownloadFileAsync(new Uri(toDownload), saveLocation);
});
thread.Start();
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
this.BeginInvoke((MethodInvoker)delegate
{
double bytesIn = double.Parse(e.BytesReceived.ToString());
double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
double percentage = bytesIn / totalBytes * 100;
labelPercentage.Text = "Downloading " + Convert.ToInt32(percentage) + "% - " + Convert.ToInt32(bytesIn / 1024) + " / " + Convert.ToInt32(totalBytes / 1024) + " kB";
progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
});
}
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
this.BeginInvoke((MethodInvoker)delegate
{
textBoxLog.AppendText("OK");
});
}
I'd like to make the program continue (download next file / show the "OK" message / do whatever is in the next line of code) AFTER the download has finished.
In the current form, if I'd put eg.
private void button1_Click(object sender, EventArgs e)
{
startDownload(url, localpath + #"\file.zip");
textBoxLog.AppendText("the cake is a lie");
}
it's showing me this text first and "OK" later.
I'm beginning with c#/.net and I've never learned object-oriented programming before so it's kind of double challenge for me and I can't figure it out by myself. I would be really grateful for relatively easy explanation.
You can have startDownload wait for the asynchronous file download through Application.DoEvents() like this:
private bool downloadComplete = false;
private void startDownload(Uri toDownload, string saveLocation)
{
string outputFile = Path.Combine(saveLocation, Path.GetFileName(toDownload.AbsolutePath));
WebClient client = new WebClient();
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
client.DownloadFileAsync(toDownload, outputFile);
while (!downloadComplete)
{
Application.DoEvents();
}
downloadComplete = false;
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
// No changes in this method...
}
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
this.BeginInvoke((MethodInvoker)delegate
{
textBoxLog.AppendText("OK");
downloadComplete = true;
});
}
And the download queue...
private void button1_Click(object sender, EventArgs e)
{
FireDownloadQueue(urls, localpath);
textBoxLog.AppendText("the cake is a lie");
}
private async void FireDownloadQueue(Uri[] toDownload, string saveLocation)
{
foreach (var url in urls)
{
await Task.Run(() => startDownload(url, localpath));
}
}
However, I think you're better off reading about HttpWebRequest and writing your own downloader class with proper checks and events...
Here's a pretty good example by Hemanshu Bhojak (Source) that you can expand upon:
public class Downloader
{
public async Task Download(string url, string saveAs)
{
var httpClient = new HttpClient();
var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));
var parallelDownloadSuported = response.Headers.AcceptRanges.Contains("bytes");
var contentLength = response.Content.Headers.ContentLength ?? 0;
if (parallelDownloadSuported)
{
const double numberOfParts = 5.0;
var tasks = new List<Task>();
var partSize = (long)Math.Ceiling(contentLength / numberOfParts);
File.Create(saveAs).Dispose();
for (var i = 0; i < numberOfParts; i++)
{
var start = i*partSize + Math.Min(1, i);
var end = Math.Min((i + 1)*partSize, contentLength);
tasks.Add(
Task.Run(() => DownloadPart(url, saveAs, start, end))
);
}
await Task.WhenAll(tasks);
}
}
private async void DownloadPart(string url, string saveAs, long start, long end)
{
using (var httpClient = new HttpClient())
using (var fileStream = new FileStream(saveAs, FileMode.Open, FileAccess.Write, FileShare.Write))
{
var message = new HttpRequestMessage(HttpMethod.Get, url);
message.Headers.Add("Range", string.Format("bytes={0}-{1}", start, end));
fileStream.Position = start;
await httpClient.SendAsync(message).Result.Content.CopyToAsync(fileStream);
}
}
}
Example usage:
Task.Run(() => new Downloader().Download(downloadString, saveToString)).Wait();
With something along the lines of:
public class Downloader
{
public event EventHandler DownloadProgress;
DownloaderEventArgs downloaderEventArgs;
public void DownloadStarted(DownloaderEventArgs e)
{
EventHandler downloadProgress = DownloadProgress;
if (downloadProgress != null)
downloadProgress(this, e);
}
// ...
}
class DownloaderEventArgs : EventArgs
{
public string Filename { get; private set; }
public int Progress { get; private set; }
public DownloaderEventArgs(int progress, string filename)
{
Progress = progress;
Filename = filename;
}
public DownloaderEventArgs(int progress) : this(progress, String.Empty)
{
Progress = progress;
}
}
startDownload initiates the download on a new thread, so when you invoke startDownload it starts the thread and the rest of the code after that continues immediately because it is on a separate thread. That is why you are seeing "the cake is a lie" before the "OK".
I have a problem with a simple updater program. It should to download archive and extract it. Well, this works fine. But i cannot overcome progressbar.
It shows at setProgressBar(progressBar1.Maximum/20) fine.
But all other changes of it's value doesn't shows at all. I can't define reason for this behavior. Could anyone help me?
UPD: It seems that e.ProgressPercentage returns always 0, and only once 100, right before download is complete.
Here is my code:
static EventWaitHandle _waitHandle = new AutoResetEvent(false);
void UpdaterForm_Shown(object sender, System.EventArgs e)
{
setProgressBar(progressBar1.Maximum/20);
UpdateProgram(address, login, password);
setProgressBar(progressBar1.Maximum);
ExecuteProgram(Path + "../" + programName + ".exe");
Application.Exit();
}
private void UpdateProgram(string pathToUpdateArchive, string login, string pass)
{
string downloadedArchive = null;
Thread downloadAsync = new Thread(() => DownloadFileFromFtp(pathToUpdateArchive, login, pass, out downloadedArchive));
downloadAsync.Name = "downloadAsync";
downloadAsync.Start();
_waitHandle.WaitOne();
ExtractFile(downloadedArchive, Path + "/../");
}
private void DownloadFileFromFtp(string address, string login, string password, out string archivePath)
{
fileName = address.Substring(address.LastIndexOf('/') + 1);
try
{
WebClient wc = new WebClient();
wc.Credentials = new NetworkCredential(login, password);
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
wc.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(wc_DownloadFileCompleted);
archivePath =Path + fileName;
wc.DownloadFileAsync(new Uri(address), archivePath);
}
catch
{
}
}
void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
setProgressBar(e.ProgressPercentage*7/10 + 10);
}
void wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
_waitHandle.Set();
}
private void setProgressBar(int value)
{
if (progressBar1.InvokeRequired)
this.BeginInvoke((MethodInvoker)delegate { progressBar1.Value = value; });
else
progressBar1.Value = value;
}
It has been surprisingly hard to find a code example of downloading multiple files using the webclient class asynchronous method, but downloading one at a time.
How can I initiate a async download, but wait until the first is finished until the second, etc. Basically a que.
(note I do not want to use the sync method, because of the increased functionality of the async method.)
The below code starts all my downloads at once. (the progress bar is all over the place)
private void downloadFile(string url)
{
WebClient client = new WebClient();
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
// Starts the download
btnGetDownload.Text = "Downloading...";
btnGetDownload.Enabled = false;
progressBar1.Visible = true;
lblFileName.Text = url;
lblFileName.Visible = true;
string FileName = url.Substring(url.LastIndexOf("/") + 1,
(url.Length - url.LastIndexOf("/") - 1));
client.DownloadFileAsync(new Uri(url), "C:\\Test4\\" + FileName);
}
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
double bytesIn = double.Parse(e.BytesReceived.ToString());
double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
double percentage = bytesIn / totalBytes * 100;
progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
What I have done is populate a Queue containing all my urls, then I download each item in the queue. When there are no items left, I can then process all the items. I've mocked some code up below. Keep in mind, the code below is for downloading strings and not files. It shouldn't be too difficult to modify the below code.
private Queue<string> _items = new Queue<string>();
private List<string> _results = new List<string>();
private void PopulateItemsQueue()
{
_items.Enqueue("some_url_here");
_items.Enqueue("perhaps_another_here");
_items.Enqueue("and_a_third_item_as_well");
DownloadItem();
}
private void DownloadItem()
{
if (_items.Any())
{
var nextItem = _items.Dequeue();
var webClient = new WebClient();
webClient.DownloadStringCompleted += OnGetDownloadedStringCompleted;
webClient.DownloadStringAsync(new Uri(nextItem));
return;
}
ProcessResults(_results);
}
private void OnGetDownloadedStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null && !string.IsNullOrEmpty(e.Result))
{
// do something with e.Result string.
_results.Add(e.Result);
}
DownloadItem();
}
Edit:
I've modified your code to use a Queue. Not entirely sure how you wanted progress to work. I'm sure if you wanted the progress to cater for all downloads, then you could store the item count in the 'PopulateItemsQueue()' method and use that field in the progress changed method.
private Queue<string> _downloadUrls = new Queue<string>();
private void downloadFile(IEnumerable<string> urls)
{
foreach (var url in urls)
{
_downloadUrls.Enqueue(url);
}
// Starts the download
btnGetDownload.Text = "Downloading...";
btnGetDownload.Enabled = false;
progressBar1.Visible = true;
lblFileName.Visible = true;
DownloadFile();
}
private void DownloadFile()
{
if (_downloadUrls.Any())
{
WebClient client = new WebClient();
client.DownloadProgressChanged += client_DownloadProgressChanged;
client.DownloadFileCompleted += client_DownloadFileCompleted;
var url = _downloadUrls.Dequeue();
string FileName = url.Substring(url.LastIndexOf("/") + 1,
(url.Length - url.LastIndexOf("/") - 1));
client.DownloadFileAsync(new Uri(url), "C:\\Test4\\" + FileName);
lblFileName.Text = url;
return;
}
// End of the download
btnGetDownload.Text = "Download Complete";
}
private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
// handle error scenario
throw e.Error;
}
if (e.Cancelled)
{
// handle cancelled scenario
}
DownloadFile();
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
double bytesIn = double.Parse(e.BytesReceived.ToString());
double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
double percentage = bytesIn / totalBytes * 100;
progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
I am struggling to understand where the issue is. If you only call the async method for the first file, won't it only download that file? Why not use the client_downlaodFileCompleted event to initiate the next file download based on some value passed by AsyncCompletedEvents, or maintain a list of downloaded files as a static variable and have client_DownloadFileCompleted iterate tht list to find the next file to download.
Hope that helps but please post more information if I have miunderstood your question.
I would make a new method, for example named getUrlFromQueue which returns me a new url from the queue (collection or array) and deletes it.. then it calls downloadFile(url) - and in client_DownloadFileCompleted I call getUrlFromQueue again.