Updating progressbar from another class in c# - c#

I'm having an issue getting a progressbar to show download progress. The files are downloading without issue, but something is causing my progressbar to not update and I can't figure out why.
I've tried setting the progressBar value manually in the download and wc_DownloadProgressChanged method, but the only place it actually changes is the Form1_Load method.
using System;
using System.Windows.Forms;
using System.Threading;
namespace Launch
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Downloader downloader = new Downloader();
ThreadStart job = new ThreadStart(downloader.download);
Thread thread = new Thread(job);
thread.Start();
}
private void ProgressBar_Click(object sender, EventArgs e)
{
}
public void SetProgress(int val)
{
progressBar.Value = val;
}
public void SetVisible(bool val)
{
progressBar.Visible = val;
}
}
}
using System;
using System.Data;
using System.Net;
using Newtonsoft.Json;
namespace Launch
{
class Downloader
{
public void download()
{
WebClient client = new WebClient();
string url = "https://someurl.com/manifest.json";
string json = client.DownloadString(url);
DataSet dataSet = JsonConvert.DeserializeObject<DataSet>(json);
DataTable dataTable = dataSet.Tables["Required"];
foreach (DataRow row in dataTable.Rows)
{
string remoteUri = row["url"].ToString();
string fileName = row["name"].ToString();
client.DownloadProgressChanged += client_DownloadProgressChanged;
client.DownloadFile(remoteUri, fileName);
Console.WriteLine("Did something with " + remoteUri);
}
}
private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
var form = new Form1();
form.SetProgress(e.ProgressPercentage);
}
}
}
Would anyone be able to shed some light on what I'm doing wrong here?
EDIT:
I was able to get this working for the most part using DownloadFileAsync, but the progress bar was bouncing back and forth, I'm assuming because it's trying to calculate the progress for each individual file as bytes are received, so I'd like to get this working with DownloadFile.
The issue I'm now having with DownloadFile is that I have it running as a task but it's skipping all of the files (not downloading any of them, just prints them all out to console super fast).
Here's the code I'm using currently:
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
WebClient client = new WebClient();
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
string url = "https://someurl.com/manifest.json";
string json = client.DownloadString(url);
DataSet dataSet = JsonConvert.DeserializeObject<DataSet>(json);
DataTable dataTable = dataSet.Tables["Required"];
foreach (DataRow row in dataTable.Rows)
{
string remoteUri = row["url"].ToString();
string fileName = row["name"].ToString();
Task.Run(() => {
client.DownloadFile(remoteUri, fileName);
});
Console.WriteLine("Did something with " + remoteUri);
}
}
private 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;
label1.Text = "Downloaded ";
label2.Text = e.BytesReceived.ToString();
label3.Text = e.TotalBytesToReceive.ToString();
progressBar.Value = int.Parse(Math.Truncate(percentage).ToString());
});
}
Any ideas?

Use this code below :
private void Form1_Load(object sender, EventArgs e)
{
WebClient client = new WebClient();
client.DownloadProgressChanged += client_DownloadProgressChanged;
client.DownloadStringCompleted += client_DownloadStringCompleted;
Uri url = new Uri("url");
client.DownloadStringAsync(url);
}
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
SetVisible(false);
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
SetProgress(e.ProgressPercentage);
}
public void SetProgress(int val)
{
progressBar.Value = val;
}
public void SetVisible(bool val)
{
progressBar.Visible = val;
}

This likely should belong to https://codereview.stackexchange.com/
First of all, do not add a handler in a loop
client.DownloadProgressChanged += client_DownloadProgressChanged;
First time it will be fine, on 2nd item it will be called 2x times, on 3rd time it will be called 3 times, etc. You want to set handler once. Move it just after line:
WebClient client = new WebClient();
Second of all, each time a progress update is fired you are creating a new instance of the form. Create a private variable or property once.
private Form1 form = new Form1();
edit:
In case of UI, usually you can modify it only in the dispatcher thread that created it or use marshaling. I would just remove it as our already downloading the string async.
Also, I would not use e.ProgressPercentage, but something like:
Math.Truncate(e.BytesReceived / (double)e.TotalBytesToReceive * 100)

Related

C# Download from url and Custom progress bar

So I made a software that can download strings from url. Here is my code:
private void button1_Click(object sender, EventArgs e)
{
if (radioButton1.Checked)
{
var accounts = new WebClient().DownloadString("https://pastebin.pl/view/raw/sample").Split('\n');
textBox1.Text = accounts[new Random().Next(0, accounts.Length)];
}
How can I make custom progress bar made by text, such that only text will show for progressing the download? example:
When the download is 10% I want to put on text progressbar (Status: requesting database)
when it comes up to 50% I want to put on text progressbar (Status: getting information)
when it comes 100% (Status: completed)
My Full Code
My Ui
you should use async method to download
private void button1_Click(object sender, EventArgs e)
{
if (radioButton1.Checked)
{
Download(new Uri("https://pastebin.pl/view/raw/sample"));
}
else if (radioButton2.Checked)
{
Download(new Uri("https://pastebin.pl/view/raw/sample2"));
}
else if (radioButton3.Checked)
{
Download(new Uri("https://pastebin.pl/view/raw/sample4"));
}
else
{
MessageBox.Show(this, "select radio btn");
}
}
private void Download(Uri uri)
{
Thread thread = new Thread(() =>
{
WebClient client = new WebClient();
client.DownloadProgressChanged += Client_DownloadProgressChanged1;
client.DownloadStringCompleted += Client_DownloadStringCompleted;
client.DownloadStringAsync(uri);
});
thread.Start();
}
private void Client_DownloadProgressChanged1(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;
if(percentage >=10 && percentage <50)
{
textBox1.Text ="message for 10%";
}
else if if(percentage >=50 && percentage <100)
{
textBox1.Text ="message for 50%";
}
else
{
textBox1.Text ="completed";
}
// you can use to show to calculated % of download
});
}
private void Client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
BeginInvoke((MethodInvoker)delegate
{
var accounts = e.Result.Split('\n');
textBox1.Text = accounts[new Random().Next(0,accounts.Length)];
});
}
WebClient has been deprecated in favor of HttpClient, but here goes.
You should use Progress<T> to report back progress.
You can define an extension method like:
public static class WebClientExtensions
{
public static async Task<string> DownloadStringTaskAsync(
this WebClient webClient,
string address,
IProgress<int> progress)
{
try
{
webClient.DownloadProgressChanged += downloadProgressChanged;
return await webClient.DownloadStringTaskAsync(address);
}
finally
{
webClient.DownloadProgressChanged -= downloadProgressChanged;
}
void downloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progress.Report(e.ProgressPercentage);
}
}
}
and use it like this:
private async void button1_Click(object sender, EventArgs e)
{
if (radioButton1.Checked)
{
var text = await new WebClient()
.DownloadStringTaskAsync(
"https://pastebin.pl/view/raw/sample",
new Progress<int>(p => /* report progress */));
var accounts = text.Split('\n');
textBox1.Text = accounts[new Random().Next(0, accounts.Length)];
}
}

How to update the text in a listView with the DownloadFileAsync progress percentage?

I am currently making a podcast client to download episodes. I have got a listView filled with the episodes for a feed and then when you double click on one it places it into a separate 'downloads' lisview which has a 'name' and a 'progress' column.
The problem I am having is trying to individually update each progress while downloading asynchronously. As I am not sure of how to keep track of the progress for each ListViewItem and how to reference it in the downloadProgressChanged function.
private void lvPodDownloads_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lvPodEpisodes.SelectedItems.Count == 1) // Check if an item is selected just to be safe
{
ListViewItem item = (ListViewItem)lvPodEpisodes.SelectedItem;
string[] epInfo = (string[])item.Tag;
txtTitle.Text = epInfo[0];
txtDesc.Text = epInfo[1];
try
{
imgFeedImage.Source = new BitmapImage(new Uri((Environment.CurrentDirectory + "\\..\\..\\feedImages\\" + epInfo[3])));
}
catch (Exception) // If it fails to set the image (Eg. It's non-existent) It will leave it blank
{
imgFeedImage.Source = null;
}
}
}
private void lvPodEpisodes_MouseDoubleClick(object sender, MouseButtonEventArgs e) // Downloading the episode in here
{
if (e.ChangedButton == MouseButton.Left) // Left button was double clicked
{
ListViewItem selected = (ListViewItem)lvPodEpisodes.SelectedItem;
string[] epInfo = (string[])selected.Tag;
Uri downloadUrl = new Uri(epInfo[2]);
List<Episode> downloading = new List<Episode>();
downloading.Add(new Episode() { Title = epInfo[0], Progress = "0%" });
lvPodDownloads.Items.Add((new Episode() { Title = epInfo[0], Progress = "0%" }));
using (WebClient client = new WebClient())
{
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
}
}
}
static int intDownloadProgress = new int();
private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
intDownloadProgress = e.ProgressPercentage;
}
private void Completed(object sender, AsyncCompletedEventArgs e)
{
MessageBox.Show("Download completed!");
}
This is a code sample of the downloading section of the program.
Here is an image of what I have so far:
https://s33.postimg.cc/gthzioxlr/image.png
You should add an extra argument to your ProgressChanged method.
private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e, Episode curEpisode)
{
curEpisode.Progress = $"{e.ProgressPercentage} %";
}
And to modify the handler setting like that:
List<Episode> downloading = new List<Episode>();
var newEpisode = new Episode() { Title = epInfo[0], Progress = "0%" };
downloading.Add(newEpisode);
lvPodDownloads.Items.Add(newEpisode);
using (WebClient client = new WebClient())
{
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler((sender, e) => ProgressChanged(sender, e, newEpisode));
}
The static property intDownloadProgress is then useless.
You should also think about using an observable collection for the episode list and using it for the binding via the XAML code.

Timer event is not triggering button click

I'm trying transfer Image file for each 3 seconds from source to destination directory, when I click on the "Start" button. And "Stop" button is for stopping the file transfer.
If I use the Start button for the third time, the event is not firing; so, the files are not transferring.
The progress I've done is in the below:
Start --- File Transferring; Pressed STOP Button // It Works as well.
Start --- File Transferring; Pressed STOP Button // It works as well.
Start ----- File is not transferring, Event is not firing // It not works!
What I've done for solving the problem:
I put a break-point in the Tick event and the tick event is not firing.
I Checked that SourceFiles.Count is greater than the TransferCount (SourceFiles.Count > TransferCount).
I noticed that during the file transfer, if I click Stop button during a file transfer that particular file remains un-transferred.
How can i fix this? Thank you in advance.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Threading;
namespace GridTest
{
/// <summary>
/// Interaction logic for Window3.xaml
/// </summary>
public partial class Window3 : Window
{
DispatcherTimer dispatcherTimer = new DispatcherTimer();
private static List<string> SourceFiles = new List<string>();
private static readonly string SourceDir = #"C:\TestFiles\Images\";
private static readonly string DestinationDir = #"C:\Files\Images\3_5x5\";
private static int TransferCount = 0;
public Window3()
{
InitializeComponent();
this.Loaded += Window3_Loaded;
}
void Window3_Loaded(object sender, RoutedEventArgs e)
{
dispatcherTimer.Interval = new TimeSpan(0, 0, 3);
dispatcherTimer.Tick += dt_Tick;
}
void dt_Tick(object sender, EventArgs e)
{
if (TransferCount < SourceFiles.Count)
{
var fileName = System.IO.Path.GetFileName(SourceFiles[TransferCount]);
var destFile = System.IO.Path.Combine(DestinationDir, fileName);
System.IO.File.Copy(SourceFiles[TransferCount], destFile,true);
System.IO.File.Delete(SourceFiles[TransferCount]);
Console.WriteLine(string.Format("Total Files: {0} Number of files transferred: {1}", SourceFiles.Count, TransferCount + 1));
TransferCount += 1;
}
else
{
Console.WriteLine(string.Format("Total number of files transferred: {0}. Transfer Completed", TransferCount + 1));
(sender as DispatcherTimer).Stop();
(sender as DispatcherTimer).Tick -= dt_Tick;
}
}
private void Start_Click(object sender, RoutedEventArgs e)
{
if (!System.IO.Directory.Exists(DestinationDir))
{
System.IO.Directory.CreateDirectory(DestinationDir);
}
if (System.IO.Directory.Exists(SourceDir))
{
SourceFiles = Directory.GetFiles(SourceDir).ToList();
}
}
private void Stop_Click(object sender, RoutedEventArgs e)
{
dispatcherTimer.Stop();
}
}
}
If you call Stop, your timer event should not fire until you set IsEnabled property to true.
So I suggest to do a bit of refactoring
void Window3_Loaded(object sender, RoutedEventArgs e)
{
LoadSourceFiles();
StartTimer();
}
void StartTimer()
{
dispatcherTimer.Interval = new TimeSpan(0, 0, 3);
dispatcherTimer.Tick += dt_Tick;
dispatcherTimer.IsEnabled = true;
}
void LoadSourceFiles()
{
if (!System.IO.Directory.Exists(DestinationDir))
{
System.IO.Directory.CreateDirectory(DestinationDir);
}
if (System.IO.Directory.Exists(SourceDir))
{
SourceFiles = Directory.GetFiles(SourceDir).ToList();
}
}
and call this method every time you press the Start button
private void Start_Click(object sender, RoutedEventArgs e)
{
StartTimer();
}
private void Stop_Click(object sender, RoutedEventArgs e)
{
dispatcherTimer.IsEnabled = false;
}
I suggest also to reset the global variable that keeps track of the files transfered to zero when inside the Tick event you discover that every file has been transfered (or disable the Start button)
....
else
{
Console.WriteLine(string.Format("Total number of files transferred: {0}. Transfer Completed", TransferCount + 1));
(sender as DispatcherTimer).Stop();
(sender as DispatcherTimer).Tick -= dt_Tick;
TransferCount = 0;
}
Otherwise if you restart the timer in this situation it will stop immediately
Not really an answer, but here's a re-factored version which doesn't use timer. Instead, it uses TaskEx.Delay and async/await, untested (targeting .NET 4.0 with vs2012+ and Microsoft.Bcl.Async):
using System;
using System.Collections.Generic;
using System.Windows;
using System.Threading;
using System.Threading.Tasks;
namespace GridTest
{
/// <summary>
/// Interaction logic for Window3.xaml
/// </summary>
public partial class Window3 : Window
{
private static List<string> SourceFiles = new List<string>();
private static readonly string SourceDir = #"C:\TestFiles\Images\";
private static readonly string DestinationDir = #"C:\Files\Images\3_5x5\";
public Window3()
{
InitializeComponent();
this.Loaded += Window3_Loaded;
}
void Window3_Loaded(object sender, RoutedEventArgs e)
{
}
async Task DoWorkAsync(CancellationToken token)
{
int transferCount;
for (transferCount = 0; transferCount < SourceFiles.Count; transferCount++)
{
token.ThrowIfCancellationRequested();
var fileName = System.IO.Path.GetFileName(SourceFiles[transferCount]);
var destFile = System.IO.Path.Combine(DestinationDir, fileName);
System.IO.File.Copy(SourceFiles[transferCount], destFile, true);
System.IO.File.Delete(SourceFiles[transferCount]);
Console.WriteLine(string.Format("Total Files: {0} Number of files transferred: {1}", SourceFiles.Count, transferCount + 1));
transferCount += 1;
await TaskEx.Delay(3000, token); // 3s delay
}
Console.WriteLine(string.Format("Total number of files transferred: {0}. Transfer Completed", transferCount + 1));
}
CancellationTokenSource _cts;
Task _task;
private void Start_Click(object sender, RoutedEventArgs e)
{
if (_cts != null)
_cts.Cancel();
_cts = new CancellationTokenSource();
_task = TaskEx.Run(() => DoWorkAsync(_cts.Token), _cts.Token);
}
private void Stop_Click(object sender, RoutedEventArgs e)
{
if (_cts != null)
_cts.Cancel();
}
}
}
You never call Start on the DispatchTimer.
Add this to the Window3_Loaded method:
dispatcherTimer.Start();

Stopping application from not displaying because of form_load code

How can I stop my application from not displaying until the form_load code is completed?
public partial class updater : Form
{
public updater()
{
InitializeComponent();
timer1.Interval = (10000) * (1);
progressBar1.Value = 0;
progressBar1.Maximum = 100;
progressBar1.Update();
timer1.Start();
}
private void updater_Load(object sender, EventArgs e)
{
WebClient webClient = new WebClient();
webClient.DownloadProgressChanged += webClient_DownloadProgressChanged;
webClient.DownloadFile("http://download827.mediafire.com/jl9c098fnedg/ncqun56uddq0y1d/Stephen+Swartz+-+Survivor+%28Feat+Chloe+Angelides%29.wav", Application.StartupPath + "\\Stephen Swartz - Survivor (Feat Chloe Angelides).wav");
// System.Diagnostics.Process.Start("\\Test.exe");
this.Close();
}
void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
progressBar1.Update();
}
}
If you use DownloadFileAsync it wont block the UI thread and will allow the Form to load and show the progress in the Progressbar, then you can use the DownloadFileCompleted event to close the Form
Example:
public Form1()
{
InitializeComponent();
progressBar1.Value = 0;
progressBar1.Maximum = 100;
progressBar1.Update();
}
private void updater_Load(object sender, EventArgs e)
{
WebClient webClient = new WebClient();
webClient.DownloadProgressChanged += webClient_DownloadProgressChanged;
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(webClient_DownloadFileCompleted);
webClient.DownloadFileAsync(new Uri("http://download827.mediafire.com/jl9c098fnedg/ncqun56uddq0y1d/Stephen+Swartz+-+Survivor+%28Feat+Chloe+Angelides%29.wav"), Application.StartupPath + "\\Stephen Swartz - Survivor (Feat Chloe Angelides).wav");
}
private void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
Close();
}
private void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
progressBar1.Update();
}
One way is to move your code from Load Shown Event. So the code will start runing after the form is shown.
The other is create a thread, where you will download a file.
For this purpose you can use BackgroundWorker
private void updater_Load(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, eArgs) =>
{
WebClient webClient = new WebClient();
webClient.DownloadFile("someUrl", "somePath");
};
worker.RunWorkerAsync();
}
Also there exists webClient.DownloadFileAsync method witch suits better in this situation. You can find description in sa_ddam213 answer.

Updating Progress Bar in a Form from Downloading in a Class

I know there have been a lot of questions here but I have went through a ton of them and have had little luck. I'm new to events and background workers and I just don't know the best way to implement this.
I have a a basic Windows form in C#. It contains a progress bar. I am calling a class that is to download a file. I want the progress bar to update based on that download. I have it working fine if everything is all in the same class, but I can't get it to work in this situation. What is the best way to handle this and how might I go about it? Currently I'm doing this:
WebClient downloader = new WebClient();
downloader.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
downloader.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
And then for progress changed I do this:
public void ProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
pbDownload.Value = e.ProgressPercentage;
}
But when I put all of this except for the progress bar in a separate class, it gets all messed up. Ideas? Thanks!
I have this witch is about the same as what you are trying to do and works fine
FStatus ... form reference
FrmStatus FStatus = null;
public void ProgressInit(String caption, string opis, int Max)
{
FStatus = new SZOKZZ.FrmStatus();
FStatus.InitProc(Max);
FStatus.SetCaption(caption, opis);
FStatus.Show();
}
public void DLProgress(string curl, string cdl)
{
WebClient webClient = new WebClient();
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(DLDone);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DLSetProc);
webClient.DownloadFileAsync(new Uri(curl), cdl);
}
private void DLSetProc(object sender, DownloadProgressChangedEventArgs e)
{
this._FileProcentDownloaded = e.ProgressPercentage;
FStatus.SetProcDL(this._FileProcentDownloaded);
}
private void DLDone(object sender, AsyncCompletedEventArgs e)
{
_DlError = e.Error;
FStatus.Dispose();
FStatus = null;
}
You should call Application.DoEvents(); to force your form to update controls based on their new values :
public void ProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
pbDownload.Value = e.ProgressPercentage;
Application.DoEvents();
}
Regards

Categories

Resources