Update GUI controls from a background thread of another class - c#

I am creating an windows based application which download the data from server.
I am using background thread which is created on different class to perform these download operation.And I want to continuously show the download status on rich textbox i.e on main thread.But i am unable to do this,get an Cross-thread operation not valid.
Please help me to resolve this problem.
method on Form1.cs
public void UpdateRichText(string Text)
{
SetRichText(Text);
}
public delegate void SetRichTextTextDelegate(string text);
public void SetRichText(object number)
{
if (InvokeRequired)
{
this.BeginInvoke(new SetRichTextTextDelegate(SetRichText),text);
return;
}
richTextBox1.Text += number.ToString() + "\n";
}
private void button3_Click_1(object sender, EventArgs e)
{
demo d = new demo();
d.display();
}
methods on demo.cs
Form1 f = new Form1();
public void display()
{
Thread t = new Thread(new ThreadStart(call));
t.Start();
}
public void call()
{
//when i call this method every time if(InvokeRequired) is false.
f.UpdateRichText("Called from Thread");
}

Try changing your check to:
if (richTextBox1.InvokeRequired)
{
richTextBox1.BeginInvoke(new SetRichTextTextDelegate(SetRichText),text);

Try using the following
if (richTextBox1.InvokeRequired)
{
richTextBox1.Invoke(new Action(delegate { richTextBox1.Text += number.ToString() + "\n"; richTextBox1.ScrollToCaret(); }));
}
else
{
richTextBox1.Text += number.ToString() + "\n";
richTextBox1.ScrollToCaret();
}
richTextBox1.Text += number.ToString() + "\n"; Can be changed as follows,
rtbEvents.AppendText(Environment.NewLine + number.ToString() );

Related

Converting 2 txt files in 2 different texboxes from lowercase to uppercase

I am trying to convert two different .txt files from lower case to upper case and the main objective is to measure and display the execution time.
Everything goes well if the files are saved with upper cases in my predefined path and the program displays the execution time. In my GUI however, texts do not convert because of the following exception in text-boxes:
System.InvalidOperationException: Cross-thread operation not valid: Control "textBox2" accessed from a thread other than the thread it was created on.
namespace Threads
{
public partial class Form1 : Form
{ String prim= #"C:\Users\Wheelz\Desktop\Laborator09\fis1.txt";
String secund= #"C:\Users\Wheelz\Desktop\Laborator09\fis2.txt";
public Form1()'
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var read = File.ReadAllText(prim);
textBox1.Text = read;
}
private void button2_Click(object sender, EventArgs e)
{
var read = File.ReadAllText(secund);
textBox2.Text = read;
}
private void modifica1()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis1upper.txt", read);
textBox1.Text = textBox1.Text.ToUpper();
}
private void modifica2()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis2upper.txt", read);
textBox2.Text = textBox2.Text.ToUpper() ;
}
private void timp_Click(object sender, EventArgs e)
{
Thread firstThread = new Thread(new ThreadStart(modifica1));
Thread secondThread = new Thread(new ThreadStart(modifica2));
var ceas= new Stopwatch();
ceas.Start();
firstThread.Start();
secondThread.Start();
ceas.Stop();
if (ceas.ElapsedMilliseconds == 1)
{
cron.Text = ceas.ElapsedMilliseconds.ToString() + " milisecundă";
}
else
{
if ((ceas.ElapsedMilliseconds < 20))
cron.Text = ceas.ElapsedMilliseconds.ToString() + " milisecunde";
else
cron.Text = ceas.ElapsedMilliseconds.ToString() + " de milisecunde";
}
}
}
}
Yes, you can't share the form called in mainthread into a subthread.
You must use an Delegate to mainthread to update the textboxes.
READ:
Invoke(Delegate)
Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control's method from a different thread, you must use one of the control's invoke methods to marshal the call to the proper thread. This property can be used to determine if you must call an invoke method, which can be useful if you do not know what thread owns a control.
You can also work with backgroundworker or async
use "BeginInvoke" for update control value in Thread. like ...
private void modifica1()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis1upper.txt", read);
this.BeginInvoke(new MethodInvoker(() =>
{
textBox1.Text = textBox1.Text.ToUpper();
}));
}
private void modifica2()
{
var read = File.ReadAllText(prim);
read = read.ToUpper();
File.WriteAllText(#"C:\Users\Wheelz\Desktop\Laborator09\fis2upper.txt", read);
this.BeginInvoke(new MethodInvoker(() =>
{
textBox2.Text = textBox2.Text.ToUpper();
}));
}

Background Worker is called twice

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:
// constructor
public TeacherScheduleForm(Therapist therapist)
{
this.therapist = therapist;
InitializeComponent();
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
load = new LoadingForm();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
load.AppendProgress(e.ProgressPercentage);
// load.AppendText(e.ProgressPercentage.ToString() + "%");
Console.Write("Progress: " + e.ProgressPercentage);
// MessageBox.Show("Progress : " + e.ProgressPercentage);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
MessageBox.Show("Cancelled");
}
else if (!(e.Error == null))
{
MessageBox.Show("Error : " + e.Error);
}
else
{
updateUI();
load.Close();
Console.Write( "Done!");
}
}
// do work of background worker
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 2); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
Console.Write("Before Doing work");
setup(therapist.therapistID + "", schoolYear);// the time consuming operation
Console.Write("Doing work");
//System.Threading.Thread.Sleep(100);
worker.ReportProgress((i*5));
}
}
}
The background worker is called when the user selects the school year through a combo box which is in this code below:
private void comboBoxSchoolYear_SelectedIndexChanged(object sender, EventArgs e)
{
//load = new LoadingForm();
schoolYear = int.Parse(comboBoxSchoolYear.SelectedValue + "");
try{
if (!bw.IsBusy)
{
bw.RunWorkerAsync();
load.ShowDialog();
}
else
{
bw.CancelAsync();
}
}
catch(Exception ex)
{
Console.Write("Error : " + ex.Message);
}
}
You are loading the form after creating the event-handler. Thats the only point I can think off doing the trouble. Try to load the form first and then create the handler.
Reason: At InitializeComponent(); the IndexChanged normally will fire up because the control is set at this point with its index. I havnt noticed this behaviour on FormLoad till now. But as I cant see any other problem in here its worth a try.
IF this doesnt solves it, you should also take care if TeacherScheduleForm is being called twice.
Something handy for debugging-purposes:
MessageBox.Show((new StackTrace().GetFrame(0).GetMethod().Name));
Paste this into your event/method or whatever. It will popup a messagebox with the method-name which called your current method. In this case (from comments) it would've raised 2 messageBoxes saying TeacherScheduleForm for both.
I've saved this to my code-snippets.

What is the purpose of backgroundworker in c#?

I am kind of new to c# and I am required to create a client server chat. Our professor gave us the following as a small hint to get us going. But I do not understand what the backgroundworker does.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) // Receive data
{
while (client.Connected)
{
try
{
receive = streamreader.ReadLine();
this.textBox2.Invoke(new MethodInvoker(delegate() { textBox2.AppendText("You : " + receive + "\n"); }));
receive = "";
}
catch (Exception x)
{
MessageBox.Show(x.Message.ToString());
}
}
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e) // Send data
{
if (client.Connected)
{
streamwriter.WriteLine(text_to_send);
this.textBox2.Invoke(new MethodInvoker(delegate() { textBox2.AppendText("Me : " + text_to_send + "\n"); }));
}
else
{
MessageBox.Show("Send failed!");
}
backgroundWorker2.CancelAsync();
}
The BackgroundWorker class is designed to execute operations on a seperate thread, whilst reporting to the main thread through the ProgressChanged and RunWorkerCompleted events.
The example your professor provided is far from a typical implementation of the class, and a backgroundworker should probably not be used for something like that.

how do I convert wpf dispatcher to winforms

I was moving over a method to my winforms project from a wpf project.
Everything but this section was moved without issue:
private void ServerProcErrorDataReceived(object sender, DataReceivedEventArgs e)
{
// You have to do this through the Dispatcher because this method is called by a different Thread
Dispatcher.Invoke(new Action(() =>
{
richTextBox_Console.Text += e.Data + Environment.NewLine;
richTextBox_Console.SelectionStart = richTextBox_Console.Text.Length;
richTextBox_Console.ScrollToCaret();
ParseServerInput(e.Data);
}));
}
I have no idea how to convert over Dispatcher to winforms.
Can anyone help me out?
You should use Invoke to replace the Dispatcher.
private void ServerProcErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (richTextBox_Console.InvokeRequired)
{
richTextBox_Console.Invoke((MethodInvoker)delegate
{
ServerProcErrorDataReceived(sender, e);
});
}
else
{
richTextBox_Console.Text += e.Data + Environment.NewLine;
richTextBox_Console.SelectionStart = richTextBox_Console.Text.Length;
richTextBox_Console.ScrollToCaret();
ParseServerInput(e.Data);
}
}

invalidOperationException while using delegate in thread

I divided my programme in 3 layers; GUI, BL, IO and tried to grap files from my server to my pc. I made it multi threaded and zorks fine, but when i tried to add a delegate to it for sending messages from my IO to my GUI, it troubels me. It said something like:
It is not allowed to perform an operation through various threads: it
was from another thread had access to the control label download
progress than the thread on which the element is created.
What i have is this:
GUI
private void buttonDownload_Click(object sender, EventArgs e)
{
download = new BL_DataTransfer(Wat.FILM, titel, this.downloadDel);
t = new Thread(new ThreadStart(download.DownLoadFiles));
t.Start();
}
private void UpdateDownloadLabel(string File)
{
labelDownloadProgress.Text = "Downloading: " + File;
}
BL
public void DownLoadFiles()
{
//bestanden zoeken op server
string map = BASEDIR + this.wat.ToString() + #"\" + this.titel + #"\";
string[] files = IO_DataTransfer.GrapFiles(map);
//pad omvormen
string[] downloadFiles = this.VeranderNaarDownLoadPad(files,this.titel);
IO_DataTransfer.DownloadFiles(#".\" + this.titel + #"\", files, downloadFiles, this.obserdelegate);
}
IO
public static void DownloadFiles(string map, string[] bestanden, string[] uploadPlaats, ObserverDelegate observerDelegete)
{
try
{
Directory.CreateDirectory(map);
for (int i = 0; i < bestanden.Count(); i++)
{
observerDelegete(bestanden[i]);
File.Copy(bestanden[i], uploadPlaats[i]);
}
}
catch (UnauthorizedAccessException uoe) { }
catch (FileNotFoundException fnfe) { }
catch (Exception e) { }
}
Delgate
public delegate void ObserverDelegate(string fileName);
Assuming that it's the update of the label that's failing you need to marshal the event onto the UI thread. To do this change your update code to be:
private void UpdateDownloadLabel(string File)
{
if (labelDownloadProgress.InvokeRequired)
{
labelDownloadProgress.Invoke(new Action(() =>
{
labelDownloadProgress.Text = "Downloading: " + File;
});
}
else
{
labelDownloadProgress.Text = "Downloading: " + File;
}
}
I've ended up creating an extension method for this that I can call - thus reducing the amount of repeated code in my applications:
public static void InvokeIfRequired(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action();
}
}
Which is then called like this:
private void UpdateDownloadLabel(string File)
{
this.labelDownloadProgress.InvokeIfRequired(() =>
labelDownloadProgress.Text = "Downloading: " + File);
}
if UpdateDownloadLabel function is in some control code-file, use pattern like this:
private void UpdateDownloadLabel(string File)
{
this.Invoke(new Action(()=> {
labelDownloadProgress.Text = "Downloading: " + File;
})));
}
You need to invoke assignment on UI thread in order to be able to change something on label.

Categories

Resources