backgroundworker doesnt get progress info from another class - c#

i call this after a button click:
FORM1-Code:
{
ProgressBar.Maximum = 500;
myArguments gifargs = new myArguments(); //class for passing arguments to the bgW
gifargs.InputFilePath = listBox1.SelectedItem.ToString(); // input filepath
gifargs.OutputFilePath = saveFileDialog1.FileName; //output filepath
backgroundWorker1.RunWorkerAsync(gifargs); // run bgW async with args
}
// here is bgW doWork
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
myArguments args = e.Argument as myArguments; //myArguments class
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
}
PictureHandler makeAnimatedGIf = new PictureHandler(); // creating new object
makeAnimatedGIf.imageGif(args.InputFilePath,args.OutputFilePath); //call method with args
makeAnimatedGIf.GifProgress += new PictureHandler.myprogressgetter(this.GifProgressF1);
//add the delegate
works perfect until here
this is my Callback function which should update bgW.ReportProgress
but it never gets there?!
private void GifProgressF1(int i)
{
System.Threading.Thread.Sleep(500);
backgroundWorker1.ReportProgress(i);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressBar.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
ProgressBar.Value = 0;
if (e.Cancelled)
{
MessageBox.Show("Process canceled!");
}
else
{
MessageBox.Show("Process complete!");
}
}
<----Picturehandler.cs-Code----->
//Delegate definition in my Picturehandler class
public delegate void myprogressgetter(int i);
public myprogressgetter GifProgress;
public void imageGif(string input, string output)
{
Process imagemagick = new Process();
imagemagick.StartInfo.FileName = "convert.exe";
imagemagick.StartInfo.Arguments = "-monitor -delay 1 " + input + " +map " + output;
imagemagick.EnableRaisingEvents = false;
imagemagick.StartInfo.UseShellExecute = false;
imagemagick.StartInfo.CreateNoWindow = true;
imagemagick.StartInfo.RedirectStandardOutput = true;
imagemagick.StartInfo.RedirectStandardError = true;
imagemagick.Start();
StreamReader ima = imagemagick.StandardError;
bool assign2 = false;
do
{
string consolausgabe = ima.ReadLine();
if (consolausgabe.Contains("Reduce") == true)
{
assign2 = true;
}
gifprocess(consolausgabe, assign2);
} while (!ima.EndOfStream);
imagemagick.WaitForExit();
imagemagick.Close();
}
private void gifprocess(string cline, bool zähl)
{
if (cline.Contains("Load"))
{
string a1 = cline;
string[] a11 = a1.Split(new char[] { ':', ',' });
string a12 = a11[3];
string[] a13 = a12.Split(new char[] { '%' });
int load1 = Convert.ToInt32(a13[0]);
GifProgress(load1;) //<<<<<------------- this will give me an exception
// Visual Studio says GifProgress = null in Autos
}
now if i call GifProgress(100) or any other integer, I get exception(Object reference not set to an instance of an object.), progressbar gets never updated.
The progress information from the picturehandler class wont get to the UI, I tried for 2 days now.
I use same code to get textbox.text from form2 and callback function works just fine.
WorkerReportProgress = TRUE.

Typically, your DoWork method would have a loop in it. Each iteration of the loop could finish with a call to ReportProgress, which would cause the stuff in ProgressChanged to run.
Since you're just running a few lines of code, use the RunWorkerCompleted to set the progress indicator and forget ReportProgress altogether.
Here's a tutorial I used to understand the BackgroundWorker better, if it helps...
Unless maybe you're doing what you're doing because your background worker thread exits before makeAnimatedGIf.imageGif is finished doing whatever it does...

Make sure your BackgroundWorker's WorkerReportsProgress property is set to true. Default is false

Make sure to set WorkerReportsProgress to true and use Invoke() to update the progressbar from another thread.

The function that you expect to be called, GifProgressF1 is only referenced as a call back for an instance of `PictureHandler' class. Where and how this call back is called in entirely up to that class then. However from your description it is not clear where this class comes from or what it does. I'd suggest referring either to the class documentation or source code, to find exactly when this callback is supposed to be called and going from there.

Related

Passing argument to Backgroundworker error handler

My program has to test several products in different slots simultaneously. When there's an error in a slot like accidental detachment from the computer, the program is suppose to log the error type and the serial number of the product that has been provided by the user when starting up the UI into a textfile.
I'm using Background Worker to handle the multi-threading. While I have managed to log the error type using e.Error, I can't seem to figure out how to pass the serial number from the DoWork function to the Background Worker error handler.
I tried googling for a solution but it seems like nobody has asked this before. I will really appreciate any help given.
PS: I'm quite new to C# so be gentle haha :)
Below is an example code:
private void startAsync_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
}
private void cancelAsync_Click(object sender, EventArgs e)
{
if (backgroundWorker1.WorkerSupportsCancellation == true)
{
// Cancel the asynchronous operation.
backgroundWorker1.CancelAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
int b = 0; //simulate error
for (int i = 1; i <= 10; i++)
{
if (worker.CancellationPending == true)
{
string[] array2 = { "1", "cancelled" };
e.Result = array2; //passing values when user cancel through e.Result object
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
worker.ReportProgress(i * 10, "Test a");
int a = 1 / b; //simulate error
System.Threading.Thread.Sleep(1000);
}
string[] array1 = {"1","done"};
e.Result = array1; //passing values when complete through e.Result object
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
resultLabel.Text = e.ProgressPercentage.ToString() + "%" + e.UserState.ToString();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
string[] someArray2 = e.Result as string[];
string sernum = someArray2[0];
string status = someArray2[1];
resultLabel.Text = sernum + " " + status;
}
else if (e.Error != null)
{
resultLabel.Text = "Error: " + e.Error.Message; //how to pass sernum here?
}
else
{
string[] someArray = e.Result as string[];
string sernum = someArray[0];
string status = someArray[1];
resultLabel.Text = sernum + " " + status;
}
}
There are lots of different ways to get data back to the RunWorkerCompleted event handler in the case of an exception.
IMHO, the most natural from a semantic point of view is to put the data in the exception itself. For example:
class BackgroundWorkerException : Exception
{
public string Sernum { get; }
public BackgroundWorkerException(string sernum, Exception inner)
: base("DoWork event handler threw an exception", inner)
{
Sernum = sernum;
}
}
Then in your DoWork handler:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
try
{
int b = 0; //simulate error
for (int i = 1; i <= 10; i++)
{
if (worker.CancellationPending == true)
{
string[] array2 = { "1", "cancelled" };
e.Result = array2; //passing values when user cancel through e.Result object
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
worker.ReportProgress(i * 10, "Test a");
int a = 1 / b; //simulate error
System.Threading.Thread.Sleep(1000);
}
string[] array1 = {"1","done"};
e.Result = array1; //passing values when complete through e.Result object
}
}
catch (Exception e)
{
throw new BackgroundWorkerException("1", e);
}
}
Finally, in the RunWorkerCompleted event handler:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
string[] someArray2 = e.Result as string[];
string sernum = someArray2[0];
string status = someArray2[1];
resultLabel.Text = sernum + " " + status;
}
else if (e.Error != null)
{
string sernum = ((BackgroundWorkerException)e.Error).Sernum;
resultLabel.Text = "Error: " + e.Error.Message;
}
else
{
string[] someArray = e.Result as string[];
string sernum = someArray[0];
string status = someArray[1];
resultLabel.Text = sernum + " " + status;
}
}
Your question isn't clear about what sernum actually represents, and in particular whether it's a single value for a given background task, or a single task could have more than one value for sernum. If it's the former, i.e. you know when you start the task what the value is, then you could pass it directly to the event handlers by capturing it in an anonymous method used for each actual event handler.
That approach won't work though in your specific scenario without some changes. You appear to have added a single BackgroundWorker object to your form as a component and are reusing it. Using an anonymous method works better/more easily if you are creating a new BackgroundWorker each time, so that you can subscribe your anonymous method delegate to DoWork and RunWorkerCompleted. (You have to subscribe it just before each invocation because, presumably, the sernum value is different each time.)
You could get it to work with the single component added to the form in the Designer as you're doing here, but it's a lot more complicated because you have to dynamically add a handler to the RunWorkerCompleted event which unsubscribes both itself and the delegates you subscribed to the DoWork and RunWorkerCompleted events (you wouldn't subscribe any methods directly to the component in the Designer, in this scheme).
Another alternative is to create a custom data structure passed as the argument for RunWorkerAsync(), which can contain a property for the sernum value. You can set this value in the method that starts the worker, or in the DoWork event handler.
This approach fits only a little better with the component-in-Designer scenario you have, because you still need a way to get the reference to that custom data structure back to the RunWorkerCompleted event handler, which you can do only by storing it in e.g. an instance field that can be shared between the Click event handler that starts the worker and the RunWorkerCompleted event (and frankly, if you do that, at that point it's debatable whether it's even worth it to pass that reference to the RunWorkerAsync() method, since the DoWork event handler could get at the same instance field just as well.)
Another alternative is to catch the exception as I've done in my code example above, but then instead of rethrowing the exception, treat it as if the work was cancelled (i.e. set the Result and Cancel properties).
Yet another approach is to abandon BackgroundWorker altogether and switch to the TPL Task-based idiom. That doesn't solve the problem implicitly, but it allows any of the above options, as well as the option of just defining your own mode for passing errors back.
If you need more specific help than that, you'll need to post a new question, with a good Minimal, Complete, and Verifiable code example that shows which of the above approaches you've attempted to try, or some other alternative not listed here, and what specifically you're unable to figure out.
See code below. You don't need a class. Can simple send a string or int using similar code.
public class Parameters
{
public string message = "";
}
private void startAsync_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
// Start the asynchronous operation.
Parameters parameters = new Parameters() { message = "The quick brown fox jumped over the lazy dog" };
backgroundWorker1.RunWorkerAsync(parameters);
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Parameters parameters = e.Argument as Parameters;
}

Invoke self to bypass different threads? C#

I have been looking around for about 3 hours and can not get this invoke to work. I need the invoke because whats calling it is in a different thread and says its unstable.
Here's what I'm calling (I call it like this textBox1_TextChanged(null, null);):
private void textBox1_TextChanged(object sender, EventArgs e)
{
if(this.InvokeRequired)
{
this.Invoke(this?WHAT GOES HERE, null); // I know it should be a delegate or something but I can't change this to that
}
else
{
string temp = "";
temp += TextToAdd;
textBox1.Text = "s";
}
}
You can use BeginInvoke to update the UI from other Thread.
if (this.InvokeRequired)
{
var action = new Action(() => textBox1.Text = "s");
this.BeginInvoke(action);
}

How to uniquely identify a control in a child form when multiple instances of the form exist

I am working on a MDI app where the user can create multiple instances of the same form (call it ListForm). Each instance of the ListForm has a flowlayoutpanel containing a unique set of user controls. The ListForm also contains a StatusStrip ProgressBar and a button called 'ReadAll'.
Each user control has a 'Read' button that will perform a read operation when clicked. This operation can take up to 3 seconds to complete.
What I am trying to do is when the user clicks the 'ReadAll' button, the childform spawns a background thread the iterates through the flowlayoutpanel.controls collection and invokes each user controls .PerformClick() method. This updates all the usercontrols in the form.
The problem is that it looks like the event handler for all instances of the form is being called resulting in all user controls in all instances of the ListForm are being updated. Additionally, when I ReportProgress from the backgroundworker, all the progressbars for all instances of the ListForm are updated. This functionality is not desired.
How can I ensure that only the ListForm that spawned the backgroundworker is updated? Is there a preferred way to uniquely identify the child form?
Thanks in advance for your help. Code is below...
public partial class ListForm: Form
{
// Background Worker Thread for Read / Write All tasks
private static BackgroundWorker bw = new BackgroundWorker();
public ListForm()
{
InitializeComponent();
// Configure the Background Worker that reads and writes all variable data
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
private void btnReadAll_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
{
// Start the ReadAll parameters thread
btnReadAll.Text = "Cancel Read";
btnWriteAll.Enabled = false;
bw.RunWorkerAsync("R");
}
else if (bw.WorkerSupportsCancellation == true)
{
// Cancel the ReadAll parameters thread
bw.CancelAsync();
}
}
// ****************************** Background Thread Methods ***************************
public delegate void DoUIWorkHandler();
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
DoUIWorkHandler DoReadClick;
DoUIWorkHandler DoWriteClick;
int CurrentControlCount = 1;
string StatusText = "";
int ProgressValue = 0;
string argument = e.Argument as string;
// *******************Perform a time consuming operation and report progress.
try
{
foreach (UserControl c in this.flowLayoutPanel1.Controls)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Update the status and return it to the UI
StatusText = "Updating: (" + (CurrentControlCount).ToString() + " of " + flowLayoutPanel1.Controls.Count.ToString() + ") " + c.ParamProperties.strDHIndexDescription;
ProgressValue = (int)(((float)CurrentControlCount / (float)flowLayoutPanel1.Controls.Count) * 100);
worker.ReportProgress(ProgressValue, StatusText);
System.Threading.Thread.Sleep(20);
CurrentControlCount++;
// Update the contorl
if (c.InvokeRequired)
{
if (argument == "R")
{
DoReadClick = c.btnRead.PerformClick;
c.Invoke(DoReadClick);
}
else
{
DoWriteClick = c.btnWrite.PerformClick;
c.Invoke(DoWriteClick);
}
}
}
}
}
catch(InvalidCastException ex)
{
// Catch any functions that are in the Layout panel
string ErrorStr = "Could not cast a Function control to a Parameter control. \n\r\r Exception: " + ex.Message;
srvcAppLogger.Logger.Log(new clsApplicationLogger.LoggerMessage(ErrorStr, "bw_DoWork", "frmVariableHandlerGUI"));
}
catch (Exception ex)
{
string ErrorStr = "An unecpected exception occured. Error: " + ex.Message.ToString();
srvcAppLogger.Logger.Log(new clsApplicationLogger.LoggerMessage(ErrorStr, "bw_DoWork", "frmVariableHandlerGUI"));
}
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.tsStatus.Text = e.UserState.ToString();
this.tsProgressBar.Value = e.ProgressPercentage;
}
You've one instance of BackgroundWorker and each ListForm you create, is registered to this worker. So you've to pass the instance of the Form to the worker.
Create a little Helper class with two Attributes. This is just an example. You could also pass an identifier or what ever you like:
public struct ReadAllArguments
{
public bool Read;
public ListForm CallingForm;
public ReadAllArguments(bool read, ListForm callingForm)
{
Read = read; CallingForm = callingForm;
}
}
You could pass it then like this:
...
if (bw.IsBusy != true)
{
// Start the ReadAll parameters thread
btnReadAll.Text = "Cancel Read";
btnWriteAll.Enabled = false;
bw.RunWorkerAsync(new ReadAllArguments(true, this));
}
...
An later read it like that:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
DoUIWorkHandler DoReadClick;
DoUIWorkHandler DoWriteClick;
int CurrentControlCount = 1;
string StatusText = "";
int ProgressValue = 0;
ReadAllArguments arguments = e.Argument as ReadAllArguments;
if (this != arguments.ListForm)
return;
...
if (arguments.Read)
{
DoReadClick = c.btnRead.PerformClick;
c.Invoke(DoReadClick);
}
else
{
DoWriteClick = c.btnWrite.PerformClick;
c.Invoke(DoWriteClick);
}
...
You'll realize that you can even move the Work-Method out of you Form because there are no direct dependencies and you don't need access to the "this"-Qualifier. You've passed everything in you argument. After replacing every "this" by that argument you could register exactly one Work-Method to the DoWork-Event of your Worker. This would be much cleaner and more elegant...
Here's an example how you could do this:
public partial class ListForm: Form
{
// Background Worker Thread for Read / Write All tasks
private static BackgroundWorker bw = new BackgroundWorker();
static ListForm()
{
//We move the do-work out of the instance constructor, because the work that has to be done, is not connected to our instances. So we've only one definition of our work that has to be done
bw.DoWork += new DoWorkEventHandler(TheWorkThatHasToBeDone);
}
public ListForm()
{
InitializeComponent();
// Configure the Background Worker that reads and writes all variable data
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
//no more registering on instance level
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
//Your new instance-independent doWork-Method - static here
private static void TheWorkThatHasToBeDone(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
ReadAllArguments arguments = e.Argument as ReadAllArguments;
//You call the instance-Method here for your specific instance you want the work to be done for
arguments.ListForm.bw_DoWork(worker, arguments);
}
//Your old bw_DoWork-Method with nicer arguments - you should change the method name...
private void bw_DoWork(BackgroundWorker worker, ReadAllArguments arguments)
{
DoUIWorkHandler DoReadClick;
DoUIWorkHandler DoWriteClick;
int CurrentControlCount = 1;
string StatusText = "";
int ProgressValue = 0;
// *******************Perform a time consuming operation and report progress.
try
{
...
}
}
It would again be more elegant to move the stuff out of the forms code and not doing this with static members, but I think the idea is clear.
To identify object you could use HashCode or create an Id property, and next use it in custom EventArgs.
private Guid _controlId;
public ListForm()
{
_controlId = Guid.NewGuid();
...
}
Try also to menage the event observators in this way:
private void btnReadAll_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
{
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged);
bw.RunWorkerCompleted +=bw_RunWorkerCompleted;
// Start the ReadAll parameters thread
btnReadAll.Text = "Cancel Read";
btnWriteAll.Enabled = false;
bw.RunWorkerAsync("R");
}
else if (bw.WorkerSupportsCancellation == true)
{
// Cancel the ReadAll parameters thread
bw.CancelAsync();
}
bw.DoWork -= bw_DoWork;
bw.ProgressChanged -= bw_ProgressChanged;
bw.RunWorkerCompleted -= bw_RunWorkerCompleted;
}

BackgroundWorker ShowDialog causes app to stop

I have been trying to implement a BackgroundWorker into my application, and so far, it has not gone well. On a new thread, I want to open up a new Form that will have a progressbar and a label to report progress, however, this is not working well. When I call ShowDialog, the application does not respond any more. Is this because my code is running from my Form1, and I am showing WorkingForm? Also, can this be implemented cleaner?
private void button14_Click(object sender, EventArgs e)
{
List<object> param = new List<object>();
object[] objectparams = new object[1];
objectparams[0] = null;
Opera opera = new Opera();
System.Reflection.MethodInfo clearOpera = opera.GetType().GetMethod("ClearOpera");
param.Add(clearOpera);
param.Add(opera);
param.Add(objectparams);
backgroundWorker1.RunWorkerAsync(param);
}
private void button2_Click_1(object sender, EventArgs e)
{
Browser.cancelPending = true;
}
private delegate void getnewform();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
mainForm main = new mainForm();
TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
var variab = (bool)form.Invoke(new getnewform(main.AskForConfirmation));
List<object> param = e.Argument as List<object>;
List<object> result = new List<object>();
var method = param[0] as MethodInfo;
object[] parameters = param[2] as object[];
if (parameters[0] == null)
{
result.Add(method.Invoke(param[1], null));
result.Add(false);
}
else
{
result.Add(method.Invoke(param[1], parameters));
if (parameters.Contains(true))
result.Add(true);
}
int progress = (100 * Browser.progressValue) / Browser.progressMax;
backgroundWorker1.ReportProgress(progress);
// If the BackgroundWorker.CancellationPending property is true, cancel
if (backgroundWorker1.CancellationPending)
{
Console.WriteLine("Cancelled");
Browser.cancelPending = true;
}
e.Result = result;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
form.progressBar1.Value = e.ProgressPercentage;
form.label1.Text = Browser.progressValue + "/" + Browser.progressMax;
Application.DoEvents();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
List<object> param = e.Result as List<object>;
if (e.Cancelled == false && param.Contains(true))
{
Display.DisplayURLs(param[0] as SortableBindingList<URL>);
TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
MessageBox.Show("Done");
}
else if (e.Cancelled == false && param.Contains(false))
{
TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
MessageBox.Show("Done");
}
}
public class mainForm
{
public void AskForConfirmation()
{
TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
var workingForm = new TestURLGUI4.WorkingForm();
workingForm.ShowDialog(form);
workingForm.DialogResult = DialogResult.None;
}
}
Edit:
Ok, I have updated my code according to the suggestions, and now, this produces a stackoverflowexception in System.Windows.Forms.dll:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
mainForm main = new mainForm();
TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
List<object> param = e.Argument as List<object>;
List<object> result = new List<object>();
var method = param[0] as MethodInfo;
object[] parameters = param[2] as object[];
if (parameters[0] == null)
{
result.Add(method.Invoke(param[1], null));
result.Add(false);
}
else
{
result.Add(method.Invoke(param[1], parameters));
if (parameters.Contains(true))
result.Add(true);
}
int progress = (100 * Browser.progressValue) / Browser.progressMax;
backgroundWorker1.ReportProgress(progress);
// If the BackgroundWorker.CancellationPending property is true, cancel
if (backgroundWorker1.CancellationPending)
{
Console.WriteLine("Cancelled");
Browser.cancelPending = true;
}
e.Result = result;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
TestURLGUI4.Form1 form1 = (TestURLGUI4.Form1)Application.OpenForms[0];
if (Application.OpenForms.Count >= 2)
{
TestURLGUI4.WorkingForm form2 = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
form2.progressBar1.Value = e.ProgressPercentage;
form2.label1.Text = Browser.progressValue + "/" + Browser.progressMax;
Application.DoEvents();
}
else if(Application.OpenForms.Count == 1)
{
var workingForm = new TestURLGUI4.WorkingForm();
workingForm.ShowDialog(form1);
}
}
The purpose of a BackgroundWorker is to invoke code on another thread (not the UI thread). By calling Invoke in the DoWork method, you're completely circumventing the purpose of BackgroundWorker. Do all your UI work before you start the worker. If you need to interact with the user while the worker is working, do it in the ProgressChanged handler--it runs on the UI thread and you don't need to use Invoke in ProgressChanged.
By invoking UI work in DoWork, you run the risk of a deadlock, which will hang your program
you cant run UI on other threads. Has to be on the main thread.
Instantiate the UI before you start the new thread. In the new thread use cross thread invoke methods on the controls you want to work with. Look here for example http://msdn.microsoft.com/en-us/library/ms171728.aspx

Update text in the label of main form while application is processing an another thread

I have created a thread to perform certain functionality in my application and while performing it I want to update the label in the main form of the application which is visible to user.
I tried to return the string data through the function which I am calling usinag seprate thread but it does not work.
Please let me know if there is any solution to update the label text while performing an activity using thread.
class e2ertaData : e2erta1
{
public void rsData()
{
network networkDetails = new network();
csv csvFile = new csv();
ftpFile ftpData = new ftpFile();
//Host Geo Data
string getIP = networkDetails.GetIP();
string[] hostData = getIP.Split('~');
GeoIP geoIPReq = new GeoIP();
GeoIpData geoIPReqData = new GeoIpData();
geoIPReqData = geoIPReq.GetMy();
if (geoIPReqData.KeyValue["Error"].ToString() == "NO")
{
//Reading server names from XML file
XmlDocument thisXmlDoc = new XmlDocument();
thisXmlDoc.LoadXml(ftpData.getConfigFile("server.xml"));
XmlNodeList xnList = thisXmlDoc.SelectNodes("/servers/server");
//updating label in e2erta1
this.l1.Text = "daaaaaaaaaaa";
this.l1.Visible = true;
this.l1.Refresh();
foreach (XmlNode xn in xnList)
{
string rtNote = "";
string requestedServer = xn["sname"].InnerText;
string rtGet = networkDetails.GetRT(requestedServer);
if (rtGet.Contains("Exception"))
{
rtNote = rtGet;
//MessageBox.Show(rtNote);
}
try
{
var row = new List<string> { rtGet, rtNote };
ftpData.addToCSVFile(row);
}
catch (Exception c)
{
MessageBox.Show(c.ToString());
}
}
}
else
{
MessageBox.Show("Geo data : " + geoIPReqData.KeyValue["Error"].ToString());
}
//return null;
}
}
Thanks,
Naveed
Also consider using BackgroundWorker component.
Drag BackgroundWorker from ToolBox to your form
Set backgroundworker's property WorkerReportsProgress to true
Subscribe to backgroundworker's event DoWork
Subscribe to backgroundworker's event ProgressChanged
Inside DoWork event handler run all what you do in your thread, and call ReportProgress method to pass progress to your form:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// reading server names from XML file
for (int i = 0; i < xnList.Count; i++)
{
XmlNode xn = xnList[i];
// process node
// report percentage to UI thread
int percentProgress = (i+1)*100/xnList.Count;
backgroundWorker1.ReportProgress(percentProgress);
}
}
Inside ReportProgress event handler simply assign value to label:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = e.ProgressPercentage.ToString();
}
To start background processing call backgroundWorker1.RunWorkerAsync();
UPDATE: Your code is not working, because controls could be updated only from thread which created them (UI thread). So you should use Invoke to execute update functionality on UI thread. Example and description you can find here.
Use this from your thread:
this.Invoke((MethodInvoker)delegate
{
label.Text = "...";
});
Edit:
You can also test the IsHandleCreated property before using Invoke:
private void UpdateLabel(string text)
{
if (this.IsHandleCreated)
{
this.Invoke((MethodInvoker)delegate
{
label.Text = text;
});
}
else
{
label.Text = text;
}
}

Categories