Fixing thread safe calls from backgroundWorker - c#

using System;
using System.ComponentModel;
using System.Net;
using System.Windows.Forms;
using Ionic.Zip;
namespace downloader
{
public partial class GUI : Form
{
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
public GUI()
{
InitializeComponent();
}
private void Download_Click(object sender, EventArgs e)
{
label1.Text = ("Downloading...");
WebClient x = new WebClient();
x.DownloadProgressChanged += new DownloadProgressChangedEventHandler(x_DownloadProgressChanged);
x.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(x_DownloadFileCompleted);
x.DownloadFileAsync(new Uri("http://google.com/"), desktop + "\\index.html");
download.Enabled = false;
}
void x_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
label2.Text = null;
label1.Text = "Download Complete.";
MessageBox.Show("Download Done.", "Done!");
}
public void x_DownloadProgressChanged(Object sender, DownloadProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
this.Text = ":: Kyle :: " + e.ProgressPercentage + "%";
label2.Text = e.BytesReceived + " bytes saved.";
}
public void unzip(String zFile)
{
Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(zFile);
zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(zip_ExtractProgress);
zip.ExtractAll(desktop, ExtractExistingFileAction.OverwriteSilently);
zip.Dispose();
zip = null;
}
public void zip_ExtractProgress(object sender, ExtractProgressEventArgs e)
{
if (e.EventType == ZipProgressEventType.Extracting_EntryBytesWritten)
{
this.label2.Text = e.BytesTransferred.ToString(); //unsafe also?
}
else if (e.EventType == ZipProgressEventType.Extracting_BeforeExtractEntry)
{
this.label3.Text = e.CurrentEntry.FileName; //unsafe
}
}
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
unzip(desktop + "\\Client.zip");
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Enabled = true;
MessageBox.Show("Done Unzipping.");
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
}
}
How do I fix my text labels? I'm using a backgroundWorker and it works without the labels but when I have em it keeps saying Cross-thread operation not valid: Control 'label3' accessed from a thread other than the thread it was created on.

You should report the progress by calling the BackgroundWorker's ReportProgress method.
Alternatively, you can run on the UI thread by calling BeginInvoke.

Related

WPF Outputting a response from the command line,GUI microcontroller

No response from the command line is displayed when JLink.exe starts, nothing happens when the "ReadMemory" button is pressed.
But if after that you enter the command "exit" all the operations performed on the command line are displayed.
How can you fix this and why is it happening?
public partial class MainWindow : Window
{
Process jLinkProcess = new Process();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
jLinkProcess.StartInfo = new ProcessStartInfo(#"C:\Program Files\SEGGER\JLink\JLink.exe");
jLinkProcess.StartInfo.UseShellExecute = false;
jLinkProcess.StartInfo.RedirectStandardInput = true;
jLinkProcess.StartInfo.RedirectStandardOutput = true;
jLinkProcess.StartInfo.RedirectStandardError = true;
jLinkProcess.StartInfo.CreateNoWindow = true;
jLinkProcess.OutputDataReceived += CmdProcess_OutputDataReceived;
jLinkProcess.ErrorDataReceived += CmdProcess_ErrorDataReceived;
jLinkProcess.Start();
jLinkProcess.BeginOutputReadLine();
jLinkProcess.BeginErrorReadLine();
jLinkProcess.StandardInput.WriteLine("connect");
jLinkProcess.StandardInput.WriteLine("NRF52811_XXAA");
jLinkProcess.StandardInput.WriteLine("SWD");
jLinkProcess.StandardInput.WriteLine("4000");
}
private void CmdProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Dispatcher.Invoke(() =>
{
TextBox2.Text += e.Data + "\r\n";
TextBox2.ScrollToEnd();
});
}
private void CmdProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Dispatcher.Invoke(() =>
{
TextBox2.Text += e.Data + "\r\n";
TextBox2.ScrollToEnd();
});
}
private void TextBox1_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (sender is TextBox textBox && e.Key == Key.Enter)
{
jLinkProcess.StandardInput.WriteLine(textBox.Text);
textBox.Text = string.Empty;
e.Handled = true;
}
}
private void ReadMemory(object sender, RoutedEventArgs e)
{
string mac = "mem 10000060,8";
jLinkProcess.StandardInput.WriteLine(mac);
}
}

While Loop Locks UI Thread

I have a button in my WinForms application with the following Click event:
private void Button_Click(object sender, EventArgs e)
{
treasureFound = false;
refreshNumber = 0;
Label_StartDateTime.Text = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss");
while (!treasureFound)
{
Label_StatusData.Text = "Refreshed " + refreshNumber + " times.";
refreshNumber++;
using (WebClient client = new WebClient())
{
string htmlCode = client.DownloadString(webUrl);
if (htmlCode.Contains("Treasure"))
{
treasureFound = true;
Label_StatusData.Text = "Found.";
// etc etc
}
}
}
}
When the button is clicked, the UI thread locks up (not responding, labels don't update) until the while loop ends.
What can I do to keep the UI responsive? There should only be one WebClient instance at any one time.
you should execute the time-consuming tasks in a separate thread, so it will not block the main thread (aka your UI thread). One way is to use the BackgroundWorker.
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
Taken from: How to use a BackgroundWorker?

How can I show correctly a progressbar and a label (that show the percentage) for filling multiple datasets?

I need to update a DataGridView that has master detail part, I'm trying to use this control for the grid (I use 4 datasets for filling the grid).
Question: how can I add correctly a thread or more that will add to the grid these datasets and show the correct percentage and the same time the UI remain usable(responsive, not to become "not responding" for user)?
In every table I have about 10000 rows.
Please provide some code if possible, because I'm stuck.
Thanks in advance,
Steve
I tried like this, but I'm stuck at showing the user the correct percentage and correctly using the thread:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace MasterDetail
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
MasterControl masterDetail;
private void frmMain_Load(object sender, EventArgs e)
{
//label1.Text = DateTime.Now.ToString();
clearFields();
loadData();
//label2.Text = DateTime.Now.ToString();
}
void clearFields()
{
panelView.Controls.Clear();
masterDetail = null;
Refresh();
masterDetail = new MasterControl(nwindDataSet);
panelView.Controls.Add(masterDetail);
}
internal delegate void SetDataSourceDelegate();
private void setDataSource()
{
if (this.masterDetail.InvokeRequired)
{
this.masterDetail.BeginInvoke(new SetDataSourceDelegate(setDataSource));
}
else
{
createMasterDetailView();
}
}
void loadData()
{
System.Threading.Thread thread =
new System.Threading.Thread(new System.Threading.ThreadStart(delegate() { loadDataThread(); }));
thread.Start();
}
void loadDataThread()
{
orderReportsTableAdapter.Fill(nwindDataSet.OrderReports);
invoicesTableAdapter.Fill(nwindDataSet.Invoices);
employeesTableAdapter.Fill(nwindDataSet.Employees);
customersTableAdapter.Fill(nwindDataSet.Customers);
setDataSource();
}
void createMasterDetailView()
{
masterDetail.setParentSource(nwindDataSet.Customers.TableName, "CustomerID");
masterDetail.childView.Add(nwindDataSet.OrderReports.TableName, "Orders");
masterDetail.childView.Add(nwindDataSet.Invoices.TableName, "Invoices");
masterDetail.childView.Add(nwindDataSet.Employees.TableName, "Employees");
typeof(DataGridView).InvokeMember("DoubleBuffered", System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.SetProperty, null,
masterDetail, new object[] { true });
foreach (DataGridView dvg in masterDetail.childView.childGrid)
{
typeof(DataGridView).InvokeMember("DoubleBuffered", System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.SetProperty, null,
dvg, new object[] { true });
}
masterDetail.childView.childGrid[2].RowTemplate.Height = 100;
}
private void btnLoad_Click_1(object sender, EventArgs e)
{
Application.Exit();
}
}
}
I think it would be better for you to use a backgroundworker
It has an event called WorkerReportsProgress that can help you create the progressbar you need.
Here is the sample code from msdn:
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
}
private void startAsyncButton_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
}
private void cancelAsyncButton_Click(object sender, EventArgs e)
{
if (backgroundWorker1.WorkerSupportsCancellation == true)
{
// Cancel the asynchronous operation.
backgroundWorker1.CancelAsync();
}
}
// This event handler is where the time-consuming work is done.
private void backgroundWorker1_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);
}
}
}
// This event handler updates the progress.
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
resultLabel.Text = (e.ProgressPercentage.ToString() + "%");
}
// This event handler deals with the results of the background operation.
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
resultLabel.Text = "Canceled!";
}
else if (e.Error != null)
{
resultLabel.Text = "Error: " + e.Error.Message;
}
else
{
resultLabel.Text = "Done!";
}
}
you just need to add the proper computation of the percentage and you're done

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.

Communicating with another thread

This is how I create a thread that does domething reps times:
protected virtual void RedButtonClicked(object sender, System.EventArgs e)
{
Nuker n = new Nuker(target, reps);
bombThread = new Thread(new ThreadStart(n.nuke));
bombThread.Start();
}
The thread class:
public class Nuker
{
private string target;
private int reps;
//...
public void nuke()
{
for(int i=0; i<reps; ++i)
{
ICBM.nuke(target);
Thread.Sleep(5500);
}
System.Console.WriteLine("Done.");
}
}
(I create a new class to store some variables since I can't pass these in ThreadStart().)
Now I would like to have a simple visualisation of the process, let's say printing the current repetition in a text field on a form. How would I use the i from the loop to do that?
In the simplest form you proved a callback in the Nuker class
public Nuker(string target, int reps, Action reportCallback){..}
In the loop you just call reportCallback(i);
Nuker n = new Nuker(target, reps, ReportMethod);
with
private void ReportMethod(int currentIdx)
{
if (InvokeRequired) // Invoke if UI update
...
}
But, probably you want to use the BackgroundWorker that has build in methods for reporting progress on the UI thread. Just check the examples on MSDN.
you can do it with a backgroundworker, it´s one of the easiest threads :-)
below i have post you a sample that i have createt to teach some friends the use of the backgroundworker ;-)
private BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
public void buttonStart_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
bw.RunWorkerAsync(12); //Start
}
public int Pils(int i)
{
Thread.Sleep(2000);
bw.ReportProgress(70, "In the middle of the work..");
Thread.Sleep(2000);
bw.ReportProgress(90, "Returning the result..");
Thread.Sleep(2000);
return (2 * i);
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
bw.ReportProgress(20, "Waiting for cancel..");
Thread.Sleep(2000);
if ((bw.CancellationPending == true))
e.Cancel = true;
else
{
bw.ReportProgress(50, "Starting process..");
e.Result = Pils((int)e.Argument);
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bw.ReportProgress(100, "Work done..");
if ((e.Cancelled == true))
textBox1.Text = "Canceled!";
else if (e.Error != null)
textBox1.Text = ("Error: " + e.Error.Message);
else textBox1.Text = e.Result.ToString();
}
private void buttonCancel_Click(object sender, EventArgs e)
{
if (bw.WorkerSupportsCancellation == true)
bw.CancelAsync();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listBox1.Items.Add((e.ProgressPercentage.ToString() + "%") + " - " + e.UserState as String);
}
url to the blogpost: link

Categories

Resources