I have a problem with background worker, it gets called twice thus, increasing the time of execution for my long routine, I created background worker manually so, there is no chance for the DoWork to be initialized within the InitializeComponent() method, any help is appreciated.
Here is my code:
namespace formStudent2
{
public partial class formStudent2 : Form
{
private BackgroundWorker backgroundWorker1 = new BackgroundWorker();
private ProgressBar progressBar1 = new ProgressBar();
private Label label1 = new Label();
public formStudent2()
{
InitializeComponent();
}
...
private void btnExportCsv_Click(object sender, EventArgs e)
{
// Path output file CSV
string pathFile = #"D:\DataTest\ListStudent.csv";
int filesCount = 3;
// Check if the backgroundWorker is already busy running the asynchronous operation
if (!backgroundWorker1.IsBusy)
{
btnExportCsv.Enabled = false;
// This method will start the execution asynchronously in the background
backgroundWorker1.RunWorkerAsync();
}
else
{
label1.Text = "Busy processing, please wait...";
}
/**
* Display dialog Progress Bar : Exporting data...
*/
Form form = new Form();
form.Text = "Exporting data...";
form.ClientSize = new Size(376, 100);
form.FormBorderStyle = FormBorderStyle.FixedSingle;
form.ShowIcon = false;
form.ControlBox = false;
form.MaximizeBox = false;
form.MinimizeBox = false;
form.StartPosition = FormStartPosition.CenterScreen;
label1.AutoSize = true;
label1.Location = new System.Drawing.Point(17, 25);
label1.Name = "lblPercent";
label1.Size = new System.Drawing.Size(102, 15);
label1.TabIndex = 1;
label1.Text = "0% Completed";
form.Controls.Add(label1);
Button btnCancel = new Button();
btnCancel.Location = new System.Drawing.Point(268, 19);
btnCancel.Name = "btnCancel";
btnCancel.Size = new System.Drawing.Size(99, 27);
btnCancel.TabIndex = 3;
btnCancel.Text = "Cancel";
btnCancel.UseVisualStyleBackColor = true;
btnCancel.Click += (senderq, eq) =>
{
if (backgroundWorker1.IsBusy)
{
// Cancel the asynchronous operation id still in progress
backgroundWorker1.CancelAsync();
}
else
{
form.Close();
}
};
form.Controls.Add(btnCancel);
progressBar1.Location = new System.Drawing.Point(17, 61);
progressBar1.Name = "progressBar1";
progressBar1.Size = new System.Drawing.Size(350, 27);
form.Controls.Add(progressBar1);
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += (senderq, eq) =>
{
for (int i = 0; i < filesCount; i++)
{
/**
* Export to file CSV
*/
ExportFileCsv(pathFile, listStudent);
int percentage = (i + 1) * 100 / filesCount;
backgroundWorker1.ReportProgress(percentage);
// Set cancellation to pending
if (backgroundWorker1.CancellationPending)
{
// Execute cancellation
eq.Cancel = true;
backgroundWorker1.ReportProgress(0);
return;
}
}
};
backgroundWorker1.ProgressChanged += (senderq, eq) =>
{
// This is updated from doWork. Its where GUI components are update,
// receives updates after 100ms
progressBar1.Value = eq.ProgressPercentage;
label1.Text = eq.ProgressPercentage.ToString() + "% Completed";
};
backgroundWorker1.RunWorkerCompleted += (senderq, eq) =>
{
// Called when the heavy operation in background is over.
// Can also accept GUI components
if (eq.Cancelled)
{
label1.Text = "Processing cancelled.";
}
else if (eq.Error != null)
{
label1.Text = eq.Error.Message;
}
else
{
btnExportCsv.Enabled = true;
MessageBox.Show("Export Finished!");
form.Close();
}
};
form.ShowDialog();
}
}
}
The running status of the worker is not static. It is dependent an instance of a new background worker. Try to make it shared.
Below is some sort of a pseudo-code (I am a java guy, never programmed with C# before) to do it:
public partial class formStudent {
boolean workerRunningStatus = false;
private void btnExportCsv_Click(object sender, EventArgs e)
{
// Path output file CSV
string pathFile = #"D:\DataTest\ListStudent.csv";
int filesCount = 3;
// Check if the backgroundWorker is already busy running the asynchronous operation
if (!workerRunning)
{
btnExportCsv.Enabled = false;
// This method will start the execution asynchronously in the background
backgroundWorker1.RunWorkerAsync();
}
else
{
workerRunningStatus = true;
//then exit
return;
}
//Continue with the remaining logic
}
}
It's just all about having a single instance of the worker running. Or see how to use a singleton pattern in C#.
Related
I have been working to write a SQL installer for my application and haven't been given a lot of time to work on it therefore its a bit dirty. My UI thread collects values from the form, plugs them into a series of SQL scritps and then one by one kicks them of "asynchronously" using SMO. Here is a sample of the calling method on the UI thread:
private static bool BeginScriptExecution(Dictionary<string, string[]> scripts)
{
try
{
foreach (var script in scripts)
{
if (script.Key.Length > 0)
{
SqlConnection conn = new SqlConnection();
conn = ReplaceDatabaseName(GetConnectionString(), script.Value[0]);
if (TestSqlConnection())
{
if (ConfigurationManager.AppSettings["useSMO"] == "1")
{
if (!RunDatabaseCommandsSmo(conn, script.Key, script.Value[1]).Result)
{
throw new Exception("Script failed to run. Error in SMO functions.");
}
}
else
{
if (!RunDatabaseCommandsNonSmo(conn, script.Key, script.Value[1]))
{
throw new Exception("Script failed to run. Non-SMO related failure.");
}
}
}
else
{
throw new Exception("Connection not available. Script failed to run");
}
}
}
return true;
}
catch (Exception ex)
{
log.Error(ex.ToString());
return false;
}
}
Here is my async method to run the command using SMO:
public static async Task<bool> RunDatabaseCommandsSmo(SqlConnection connectionString, string scriptText, string scriptName)
{
Helper.UpdateProgressLabel(scriptName);
bool isSuccess = false;
try
{
ServerConnection srvCon = new ServerConnection(connectionString);
Server server = new Server(srvCon);
//script = scriptText;
if (scriptName.Contains("Creating Database")||scriptName.Contains("Building objects"))
{
try
{
isSuccess = await Task.Run(() => RunCommand(scriptText, server))
.ConfigureAwait(continueOnCapturedContext:false);
return isSuccess;
}
catch(Exception ex)
{
log.Error("Cannot create database");
log.Error(ex.StackTrace.ToString());
return false;
}
}
else
{
try
{
//server.ConnectionContext.ExecuteNonQuery(scriptText);
isSuccess = await Task.Run(() => RunTransaction(scriptText, server, srvCon))
.ConfigureAwait(continueOnCapturedContext: false);
return isSuccess;
}
catch (Exception ex)
{
log.Error(string.Format("Error writing transaction from script {0}. Installation halted - check inner exception.", scriptName));
log.Error(ex.ToString());
log.Error(ex.StackTrace.ToString());
return false;
}
}
}
catch (Exception ex)
{
log.Error(string.Format("Error writing transaction from script {0}. Installation halted - check inner exception.", scriptName));
log.Error(ex.StackTrace.ToString());
return false;
}
}
Here is the code running the transactions:
static bool RunCommand(string script, Server server)
{
try
{
server.ConnectionContext.ExecuteNonQuery(script);
return true;
}
catch(Exception ex)
{
log.Error(ex.ToString());
return false;
}
}
static bool RunTransaction(string script, Server server, ServerConnection srvCon)
{
try
{
srvCon.BeginTransaction();
server.ConnectionContext.ExecuteNonQuery(script);
srvCon.CommitTransaction();
return true;
}
catch (Exception ex)
{
srvCon.RollBackTransaction();
log.Error(ex.ToString());
return false;
}
}
Before I started moving towards async I was running all on the UI thread and the form would give me "Not Responding" until the script returned completed. Once I moved to this style of async the app is a bit more responsive and I no longer get not responding, but I am not sure of this method and whether or not it is correct use of async. Can someone let me know how I can alter my code to make this work correctly? There are 4 scripts that run and must run in specific order meaning script 2 cannot run until script 1 returns.
A good way to use asynchronous tasks is using a background worker,
take a look at the code example here:
Background Worker
If B.G. is the way you go, here is an example: How to: Implement a Form That Uses a Background Operation
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
namespace BackgroundWorkerExample
{
public class FibonacciForm : System.Windows.Forms.Form
{
private int numberToCompute = 0;
private int highestPercentageReached = 0;
private System.Windows.Forms.NumericUpDown numericUpDown1;
private System.Windows.Forms.Button startAsyncButton;
private System.Windows.Forms.Button cancelAsyncButton;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Label resultLabel;
private System.ComponentModel.BackgroundWorker backgroundWorker1;
public FibonacciForm()
{
InitializeComponent();
InitializeBackgroundWorker();
}
// Set up the BackgroundWorker object by
// attaching event handlers.
private void InitializeBackgroundWorker()
{
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(
backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged +=
new ProgressChangedEventHandler(
backgroundWorker1_ProgressChanged);
}
private void startAsyncButton_Click(System.Object sender,
System.EventArgs e)
{
// Reset the text in the result label.
resultLabel.Text = String.Empty;
// Disable the UpDown control until
// the asynchronous operation is done.
this.numericUpDown1.Enabled = false;
// Disable the Start button until
// the asynchronous operation is done.
this.startAsyncButton.Enabled = false;
// Enable the Cancel button while
// the asynchronous operation runs.
this.cancelAsyncButton.Enabled = true;
// Get the value from the UpDown control.
numberToCompute = (int)numericUpDown1.Value;
// Reset the variable for percentage tracking.
highestPercentageReached = 0;
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync(numberToCompute);
}
private void cancelAsyncButton_Click(System.Object sender,
System.EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundWorker1.CancelAsync();
// Disable the Cancel button.
cancelAsyncButton.Enabled = false;
}
// This event handler is where the actual,
// potentially time-consuming work is done.
private void backgroundWorker1_DoWork(object sender,
DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
// Assign the result of the computation
// to the Result property of the DoWorkEventArgs
// object. This is will be available to the
// RunWorkerCompleted eventhandler.
e.Result = ComputeFibonacci((int)e.Argument, worker, e);
}
// This event handler deals with the results of the
// background operation.
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
resultLabel.Text = "Canceled";
}
else
{
// Finally, handle the case where the operation
// succeeded.
resultLabel.Text = e.Result.ToString();
}
// Enable the UpDown control.
this.numericUpDown1.Enabled = true;
// Enable the Start button.
startAsyncButton.Enabled = true;
// Disable the Cancel button.
cancelAsyncButton.Enabled = false;
}
// This event handler updates the progress bar.
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
// This is the method that does the actual work. For this
// example, it computes a Fibonacci number and
// reports progress as it does its work.
long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)
{
// The parameter n must be >= 0 and <= 91.
// Fib(n), with n > 91, overflows a long.
if ((n < 0) || (n > 91))
{
throw new ArgumentException(
"value must be >= 0 and <= 91", "n");
}
long result = 0;
// Abort the operation if the user has canceled.
// Note that a call to CancelAsync may have set
// CancellationPending to true just after the
// last invocation of this method exits, so this
// code will not have the opportunity to set the
// DoWorkEventArgs.Cancel flag to true. This means
// that RunWorkerCompletedEventArgs.Cancelled will
// not be set to true in your RunWorkerCompleted
// event handler. This is a race condition.
if (worker.CancellationPending)
{
e.Cancel = true;
}
else
{
if (n < 2)
{
result = 1;
}
else
{
result = ComputeFibonacci(n - 1, worker, e) +
ComputeFibonacci(n - 2, worker, e);
}
// Report progress as a percentage of the total task.
int percentComplete =
(int)((float)n / (float)numberToCompute * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
worker.ReportProgress(percentComplete);
}
}
return result;
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();
this.startAsyncButton = new System.Windows.Forms.Button();
this.cancelAsyncButton = new System.Windows.Forms.Button();
this.resultLabel = new System.Windows.Forms.Label();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
this.SuspendLayout();
//
// numericUpDown1
//
this.numericUpDown1.Location = new System.Drawing.Point(16, 16);
this.numericUpDown1.Maximum = new System.Decimal(new int[] {
91,
0,
0,
0});
this.numericUpDown1.Minimum = new System.Decimal(new int[] {
1,
0,
0,
0});
this.numericUpDown1.Name = "numericUpDown1";
this.numericUpDown1.Size = new System.Drawing.Size(80, 20);
this.numericUpDown1.TabIndex = 0;
this.numericUpDown1.Value = new System.Decimal(new int[] {
1,
0,
0,
0});
//
// startAsyncButton
//
this.startAsyncButton.Location = new System.Drawing.Point(16, 72);
this.startAsyncButton.Name = "startAsyncButton";
this.startAsyncButton.Size = new System.Drawing.Size(120, 23);
this.startAsyncButton.TabIndex = 1;
this.startAsyncButton.Text = "Start Async";
this.startAsyncButton.Click += new System.EventHandler(this.startAsyncButton_Click);
//
// cancelAsyncButton
//
this.cancelAsyncButton.Enabled = false;
this.cancelAsyncButton.Location = new System.Drawing.Point(153, 72);
this.cancelAsyncButton.Name = "cancelAsyncButton";
this.cancelAsyncButton.Size = new System.Drawing.Size(119, 23);
this.cancelAsyncButton.TabIndex = 2;
this.cancelAsyncButton.Text = "Cancel Async";
this.cancelAsyncButton.Click += new System.EventHandler(this.cancelAsyncButton_Click);
//
// resultLabel
//
this.resultLabel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.resultLabel.Location = new System.Drawing.Point(112, 16);
this.resultLabel.Name = "resultLabel";
this.resultLabel.Size = new System.Drawing.Size(160, 23);
this.resultLabel.TabIndex = 3;
this.resultLabel.Text = "(no result)";
this.resultLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// progressBar1
//
this.progressBar1.Location = new System.Drawing.Point(18, 48);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(256, 8);
this.progressBar1.Step = 2;
this.progressBar1.TabIndex = 4;
//
// backgroundWorker1
//
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.WorkerSupportsCancellation = true;
//
// FibonacciForm
//
this.ClientSize = new System.Drawing.Size(292, 118);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.resultLabel);
this.Controls.Add(this.cancelAsyncButton);
this.Controls.Add(this.startAsyncButton);
this.Controls.Add(this.numericUpDown1);
this.Name = "FibonacciForm";
this.Text = "Fibonacci Calculator";
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
this.ResumeLayout(false);
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new FibonacciForm());
}
}
}
I have background worker where I am getting some callback in a thread and I am updating the UI and the progress bar as the states change. Currently there are 3 states 1) Cartridgr Drawer Closed 2) Processing 3) Processed.
First Time when the application starts everything works fine. The Run Worker Completed fires after the Processed state and I am launching an other success window through an event.
But for the second when I rerun the workflow without closing the application, I get the success Window when the worker still says Processing. Why is that also the remaining time that I am updating behaves incorrectly. Please help. (Please see the DoWork event Thread th I guess thats the issue).
//This method is a RelayCommand thats called on some button click
private void StartCurrentRun(bool obj)
{
this.worker = new BackgroundWorker();
this.worker.WorkerReportsProgress = true;
this.worker.WorkerSupportsCancellation = true;
StartTimer();
PropertyCallBackChangedInstance.PropertyChanged -= PropertyCallBackChangedInstance_PropertyChanged;
WhenCancelledBlurVolumesGrid = false;
this.worker.DoWork += this.DoWork;
this.worker.ProgressChanged += this.ProgressChanged;
this.worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
IsLiveProgress = true;
this.worker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
int OriginalTimeRemaining = SelectedVolumeEstimatedTime();
int TotalPrecentagebasedOnNumberOfStates = 50;
int PercentProgress = 100 / TotalPrecentagebasedOnNumberOfStates;
CurrentCartridgeStatus status = CurrentCartridgeStatus.NotProcessed;
var instance = ConnectToInstrument.InstrumentConnectionInstance;
instance.InitalizeRun();
Thread th = new Thread(() =>
{
PropertyCallBackChangedInstance.PropertyChanged += PropertyCallBackChangedInstance_PropertyChanged;
});
th.Start();
Thread th2 = new Thread(() =>
{
while (PropertyCallBackChangedInstance.CurrentCartridgeStatusChanged != CurrentCartridgeStatus.Processed)
{
lock (_objectForThread2)
{
if (OriginalTimeRemaining > 0)
{
OriginalTimeRemaining -= 2;
}
var time = TimeSpan.FromSeconds(OriginalTimeRemaining);
EstimatedTimeRemaining = string.Format("{0:00}:{1:00}:{2:00}",
time.Hours,
time.Minutes,
time.Seconds);
OnPropertyChanged("EstimatedTimeRemaining");
}
Thread.Sleep(2000);
}
});
th2.Start();
int counter = 0;
for (int i = 0; i < PercentProgress; i++)
{
//Keep checking to see if the Run is cancelled
if (WhenCancelledBlurVolumesGrid) //That means the run is cancelled
{
if (worker.CancellationPending)
{
e.Cancel = true;
worker.CancelAsync();
}
}
for (int j = counter; j <= TotalPrecentagebasedOnNumberOfStates; j++)
{
worker.ReportProgress(Math.Min(j, 100));
if (status != CurrentCartridgeStatus.Processed)
{
Thread.Sleep(55);
}
}
counter = 50;
TotalPrecentagebasedOnNumberOfStates += 50;
}
th.Join();
th2.Join();
}
private void PropertyCallBackChangedInstance_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "CurrentCartridgeStatusChanged")
{
var value = sender as InstrumentCallBackProperties;
CurrentStatus = EnumExtensions.GetDescription(value.CurrentCartridgeStatusChanged);
}
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.ProgressValue = e.ProgressPercentage;
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
EstimatedTimeRemaining = "00:00:00";
stopWatch.Stop();
timer.Stop();
Messenger.Default.Send(new NotificationMessage("RunComplete"));
if (e.Cancelled)
{
SelectedVolume = string.Empty;
}
else
{
IsLiveProgress = false;
if (IsRunSuccessfullyComplete != null && !WhenCancelledBlurVolumesGrid) //This indicates that Success will only open when the run is complete
{
IsRunSuccessfullyComplete(NoErrors);//This event opens the success window from a different page
}
WhenCancelledBlurVolumesGrid = true;
}
//I have some Dispose method that i am trying to do after the first run.
public void Dispose()
{
if (this.worker != null)
{
this.ProgressValue = 0;
this.worker.CancelAsync();
this.worker.Dispose();
this.worker = null;
timer = null;
stopWatch = null;
TimeElapsed = string.Empty;
}
}
Here is some code that I use to download a file and then calculate the time remaining time and kbps. It will then post those results on the form by updating a text box and it has a progress bar. The problem I am having is the UI is freezing and I am thinking it might be how I am using the stopwatch but not sure. Anyone have any input?
/// Downloads the file.
private void Download_Begin()
{
web_client = new System.Net.WebClient();
web_client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(Download_Progress);
web_client.DownloadFileCompleted += new AsyncCompletedEventHandler(Download_Complete);
stop_watch = new System.Diagnostics.Stopwatch();
stop_watch.Start();
try
{
if (Program.Current_Download == "Install_Client.exe")
{
web_client.DownloadFileAsync(new Uri("http://www.website.com/Client/Install_Client.exe"), #"C:\Downloads\Install_Client.exe");
}
else
{
web_client.DownloadFileAsync(new Uri((string.Format("http://www.website.com/{0}", Program.Current_Download))), (string.Format(#"C:\Downloads\{0}", Program.Current_Download)));
}
}
catch(Exception)
{
stop_watch.Stop();
}
Program.Downloading = true;
Download_Success = false;
}
/// -------------------
/// Tracks download progress.
private void Download_Progress(object sender, DownloadProgressChangedEventArgs e)
{
double bs = e.BytesReceived / stop_watch.Elapsed.TotalSeconds;
this.label_rate.Text = string.Format("{0} kb/s", (bs / 1024d).ToString("0.00"));
long bytes_remaining = e.TotalBytesToReceive - e.BytesReceived;
double time_remaining_in_seconds = bytes_remaining / bs;
var remaining_time = TimeSpan.FromSeconds(time_remaining_in_seconds);
string hours = remaining_time.Hours.ToString("00");
if (remaining_time.Hours > 99)
{
hours = remaining_time.Hours.ToString("000");
}
this.time_remaining.Text = string.Format("{0}::{1}::{2} Remaining", hours, remaining_time.Minutes.ToString("00"), remaining_time.Seconds.ToString("00"));
progressBar1.Maximum = (int)e.TotalBytesToReceive / 100;
progressBar1.Value = (int)e.BytesReceived / 100;
if (e.ProgressPercentage == 100)
{
Download_Success = true;
}
}
/// -------------------------
The UI thread could be freezing for various reasons, despite the fact that you are calling the asynchronous download function. One way of preventing the UI to freeze would be to invoke the downloading of the file from different thread than the UI. For instance you can accomplish this with BackgroundWorker and safely modify form's controls via thread safe calls or in short wrapping the code executed in the non-UI thread with BeginInvoke() calls.
private void button1_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
worker.RunWorkerAsync();
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
Download_Begin();
}
/// Downloads the file.
private void Download_Begin()
{
web_client = new System.Net.WebClient();
web_client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(Download_Progress);
web_client.DownloadFileCompleted += new AsyncCompletedEventHandler(Download_Complete);
stop_watch = new System.Diagnostics.Stopwatch();
stop_watch.Start();
try
{
if (Program.Current_Download == "Install_Client.exe")
{
web_client.DownloadFileAsync(new Uri("http://www.website.com/Client/Install_Client.exe"), #"C:\Downloads\Install_Client.exe");
}
else
{
web_client.DownloadFileAsync(new Uri((string.Format("http://www.website.com/{0}", Program.Current_Download))), (string.Format(#"C:\Downloads\{0}", Program.Current_Download)));
}
}
catch (Exception)
{
stop_watch.Stop();
}
Program.Downloading = true;
Download_Success = false;
}
/// -------------------
/// Tracks download progress.
private void Download_Progress(object sender, DownloadProgressChangedEventArgs e)
{
this.BeginInvoke(new Action(() =>
{
double bs = e.BytesReceived / stop_watch.Elapsed.TotalSeconds;
this.label_rate.Text = string.Format("{0} kb/s", (bs / 1024d).ToString("0.00"));
long bytes_remaining = e.TotalBytesToReceive - e.BytesReceived;
double time_remaining_in_seconds = bytes_remaining / bs;
var remaining_time = TimeSpan.FromSeconds(time_remaining_in_seconds);
string hours = remaining_time.Hours.ToString("00");
if (remaining_time.Hours > 99)
{
hours = remaining_time.Hours.ToString("000");
}
this.time_remaining.Text = string.Format("{0}::{1}::{2} Remaining", hours, remaining_time.Minutes.ToString("00"), remaining_time.Seconds.ToString("00"));
progressBar1.Maximum = (int)e.TotalBytesToReceive / 100;
progressBar1.Value = (int)e.BytesReceived / 100;
if (e.ProgressPercentage == 100)
{
Download_Success = true;
}
}));
}
Some thoughts about your code:
No need to close stopwatch when exceptions happen, stopwatch does not do any work inside, it simply remember current time when you start stopwatch and calculate difference when you access elapsed time.
When you catch all exceptions, there is no need to provide Exception class (i.e. catch instead of catch(Exception)).
Mark download as completed only in DownloadFileCompleted event, not in DownloadProgressChanged, because ProgressPercentage can be 100 even when download is not completed yet.
When working with async code it is always better to initialize status variables (in your case Download_Success and Program.Downloading) before calling async method, not after.
Now about freezes. DownloadProgreesChanged can be fired very often by WebClient, so UI thread can be flooded by update messages. You need to split report progress and update UI code. UI should be updated in timed manner, for example, twice per second. Very rough code sample below:
// Put timer on your form, equivalent to:
// Update_Timer = new System.Windows.Forms.Timer();
// Update_Timer.Interval = 500;
// Update_Timer.Tick += Timer_Tick;
private void Download_Begin()
{
web_client = new System.Net.WebClient();
web_client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(Download_Progress);
web_client.DownloadFileCompleted += new AsyncCompletedEventHandler(Download_Complete);
Program.Downloading = true;
Download_Success = false;
stop_watch = System.Diagnostics.Stopwatch.StartNew();
Update_Timer.Start();
web_client.DownloadFileAsync(new Uri("uri"), "path");
}
private int _Progress;
private void Download_Progress(object sender, DownloadProgressChangedEventArgs e)
{
_Progress = e.ProgressPercentage;
}
private void Download_Complete(object sender, AsyncCompletedEventArgs e)
{
Update_Timer.Stop();
Program.Downloading = false;
Download_Success = true;
}
private void Timer_Tick(object sender, EventArgs e)
{
// Your code to update remaining time and speed
// this.label_rate.Text = ...
// this.time_remaining.Text = ...
progressBar1.Value = _Progress;
}
i have a code which is like this :
private void testToolStripMenuItem_Click(object sender, EventArgs e)
{
toolStripStatusLabel1.Text = " Device Testing...";
positive = false;
clearsensors_gui();
datarec = false;
cmd = 04;
datarec = serialport_FT(0, 1);
if (datarec)
{
char ab = Convert.ToChar(rec_data[1]);
//MessageBox.Show("\n" + ab + "\n");
int cab = Convert.ToInt16(ab);
int cabc1 = cab & 1;
int cabc2 = cab & 2;
int cabc3 = cab & 4;
int cabc4 = cab & 8;
int cabc5 = cab & 16;
int cabc6 = cab & 32;
if (cabc1 == 1)
ovalShape1.FillColor = Color.Green;
else
ovalShape1.FillColor = Color.Red;
if (cabc2 == 2)
ovalShape2.FillColor = Color.Green;
else
ovalShape2.FillColor = Color.Red;
if (cabc3 == 4)
ovalShape3.FillColor = Color.Green;
else
ovalShape3.FillColor = Color.Red;
if (cabc4 == 8)
ovalShape4.FillColor = Color.Green;
else
ovalShape4.FillColor = Color.Red;
if (cabc5 == 16)
ovalShape5.FillColor = Color.Green;
else
ovalShape5.FillColor = Color.Red;
if (cabc6 == 32)
ovalShape6.FillColor = Color.Green;
else
ovalShape6.FillColor = Color.Red;
toolStripStatusLabel1.Text = " Device Tested";
}
else
{
toolStripStatusLabel1.Text = "Try Again or Communication With Device Failure....";
}
}
the above code is to read a the sensors i.e datarec = serialport_FT(0, 1); function provides me a sensor output at the GUI side which'll be later depicted with red\green ovalShapeX(1-6)
Question: datarec = serialport_FT(0, 1); this function takes liltime and so the GUI freezes till that time how to avoid this?
i tried using background worker but didn't get where to put this whole process
also encountered with cross-threaded operation error when it goes to ovalShape and changing its properties.
I'm not getting what part of the function to be used in the background and where and when to get back to the 1st thread
please help me to use backgroundworker or use invoke if i have to use Threading
You could do something like this:
toolStripStatusLabel1.Text = " Device Testing...";
positive = false;
clearsensors_gui();
datarec = false;
cmd = 04;
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
// Will be run on background thread
args.Result = serialport_FT(0, 1);
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
bool result = (bool)args.Result;
if (result)
{
// Do your UI updates here
}
};
worker.RunWorkerAsync();
One improvement could be to combine datarec and rec_data as a Tuple in args.Result.
In the background worker you use the DoWork event.
worker.DoWork += new DoWorkEventHandler(yourEventHandler);
void yourEventHandler(object sender, DoWorkEventArgs e)
{
//your work here
}
As you are using WinForms, here is a great MSDN article to get you started with using multiple threads in an application: Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads
The article is 'a few days old', but the principles remain absolutely valid today.
If you are working in a .NET 4.x version, you can also use the Task Parallel Library to make working with multiple threads easier.
The upcoming .NET 4.5 also offers the even more comfortable await and asyc keywords: Asynchronous Programming with Async and Await.
Put this in a background thread as you already tried (or better yet, a Task), but be careful to call GUI-related operations only through Control.Invoke (for WinForms) or Dispatcher.Invoke (for WPF).
Used a Label which updates in real time as the task progresses. You can try this code [using BackGroundWorker]. Look at DoWork where you put your business logic [See BusinessClass usage in the code], then see the ProgressChanged where the background task signals the UI in real time as the task progresses & finally see the RunWorkerCompleted where you handle code after task completion, error or cancellation.
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form3 : Form
{
private BackgroundWorker _worker;
BusinessClass _biz = new BusinessClass();
public Form3()
{
InitializeComponent();
InitWorker();
}
private void InitWorker()
{
if (_worker != null)
{
_worker.Dispose();
}
_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.ProgressChanged += ProgressChanged;
_worker.RunWorkerAsync();
}
void DoWork(object sender, DoWorkEventArgs e)
{
int highestPercentageReached = 0;
if (_worker.CancellationPending)
{
e.Cancel = true;
}
else
{
double i = 0.0d;
int junk = 0;
for (i = 0; i <= 199990000; i++)
{
int result = _biz.MyFunction(junk);
junk++;
// Report progress as a percentage of the total task.
var percentComplete = (int)(i / 199990000 * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
// note I can pass the business class result also and display the same in the LABEL
_worker.ReportProgress(percentComplete, result);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// Display some message to the user that task has been
// cancelled
}
else if (e.Error != null)
{
// Do something with the error
}
}
void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
}
}
public class BusinessClass
{
public int MyFunction(int input)
{
return input+10;
}
}
}
I want to create a timer in a separate thread, but I'm not sure how to do it.
The timer should stop after clicking button a button.
Below I have an example that mostly works but it stops sometimes for 1-2 seconds when the loop is executing. So I guess I have to put it in a separate thread? This is what I've tried:
private void buttonStop_Click(object sender, EventArgs e)
{
timer1.Stop();
}
public void TimeThread()
{
th = new Thread(new ThreadStart(Timer));
th.Start();
}
public void Timer()
{
var delta = DateTime.Now - startTime;
textBoxSeconds.Text = delta.Seconds.ToString("n0");
textBoxMinutes.Text = Math.Floor(delta.TotalMinutes).ToString("n0");
}
EDIT:
So here is all the code that I have, still not exactly sure how to put the timer in separate thread.
namespace Imgur
{
public partial class Form1 : Form
{
bool flag = true;
int downloadedNumber = 0;
private DateTime startTime;
public Form1()
{
InitializeComponent();
}
public void buttonStart_Click(object sender, EventArgs e)
{
buttonStart.Enabled = false;
buttonStop.Enabled = true;
if (!flag)
{
flag = true;
}
startTime = DateTime.Now;
timer1.Start();
for (int i=0;i<100000 && flag;i++)
{
WebClient webClient = new WebClient();
string pic1 = rnd_str(5);
string pic2 = ".jpg";
string picture = pic1 + pic2;
//********** GETTING SIZE OF IMAGE ***********
Size sz = GetSize("http://i.imgur.com/" + picture);
string imageSize = (sz.Width.ToString() + " " + sz.Height.ToString()); ;
//********************************************
if(imageSize != "161 81")
{
webClient.DownloadFile("http://i.imgur.com/" + picture, #"e:\test\" + picture);
richTextBox1.Text += String.Format("Downloaded picture: {0}\r\n", picture);
downloadedNumber++;
textBoxDownloadedNumber.Text = string.Format("{0}", downloadedNumber);
}
webClient.Dispose();
Application.DoEvents();
if (i == 999995)
{
flag = false;
}
}
richTextBox1.Text += "End Dowloaded Session \n";
buttonStart.Enabled = true;
buttonStop.Enabled = false;
timer1.Stop();
}
public static Size GetSize(string url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Accept = "image/gif";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream s = response.GetResponseStream();
Bitmap bmp = new Bitmap(s);
Size sz = new Size(bmp.Width, bmp.Height);
return sz;
}
public static string rnd_str(int liczba_liter)
{
Random r = new Random();
int char_type;
string return_string = "";
int i =0;
for (i = 0; i < liczba_liter; i++)
{
if (r.Next(1, 3) == 1)
{
char_type = r.Next(1, 4);
switch (char_type)
{
case 1:
return_string += (char)r.Next(48, 58); // convertion int -> ASCII character; 48-57 are ASCII digits
break;
case 2:
return_string += (char)r.Next(97, 123); // convertion int -> ASCII character; as above but small letters
break;
case 3:
return_string += (char)r.Next(65, 91); // as above; large letters
break;
default:
i -= 1;
break;//do not add any letter if no type is allowed
}
}
else
{
i -= 1;
return_string += "";
}
}
return return_string;
}
private void buttonStop_Click(object sender, EventArgs e)
{
flag = false;
buttonStart.Enabled = true;
timer1.Stop();
}
public void timer1_Tick(object sender, EventArgs e)
{
var delta = DateTime.Now - startTime;
textBoxSeconds.Text = delta.Seconds.ToString("n0");
textBoxMinutes.Text = Math.Floor(delta.TotalMinutes).ToString("n0");
}
}
}
In your code it seems there is no connection between the timer and the print of the time passed.
You need to pay attention because it isn't possible to change GUI elements from another thread that isn't the main one.
To do this you need to use Invoke in WinForms
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyThreadSafeDelegate(SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue });
}
else
{
control.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { propertyValue });
}
or dispatcher in WPF/SL - http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher
In order to use another thread and you have some options:
Threadpool (On .Net 4) - http://msdn.microsoft.com/en-us/library/3dasc8as(v=vs.80).aspx
Thread class - http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx
BackgroundWorker Class - http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
The 3rd option is the easiest if you don't know how to work with theards
There're tree Timers in .Net take a look at the following article and select correct timer looks like you need timer from System.Threading, not from System.Windows.Forms.