BackgroundWorker using C# - c#

I am totally lost. I just seem to get no response from it.
BackgroundWorker NewWorker;
public void StartBackgroundWorker()
{
BackgroundWorker NewWorker = new BackgroundWorker();
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
StartWorker();
}
void NewWorker_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
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select StatusCode from Win32_PingStatus where address = 'Metabox-PC'");
ManagementObjectCollection objCollection = searcher.Get();
foreach (ManagementObject Results in objCollection)
{
MessageBox.Show(Results.ToString());
}
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(1);
worker.ReportProgress((i * 10));
}
}
}
private void NewWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Finished");
}
None of the message boxes appear, indicating it's finished.
What have I missed?

You're never calling RunWorkerAsync on the BackgroundWorker that you have configured.
You think you are, because you're calling StartWorker which, (I presume) contains the code NewWorker.RunWorkerAsync(); somewhere in it, however NewWorker, inside of StartWorker, is referring to an entirely different BackgroundWorker that you have not configured to do anything.
You have an instance field, NewWorker, and a local variable NewWorker inside of StartBackgroundWorker that is shadowing the instance field.
If you really need the background worker to be an instance field, then don't shadow it in StartBackgroundWorker and use the instance field throughout. If you don't need it to be an instance field (which, for the record, is likely, and needlessly promoting variables to instance fields makes for messier programs) then you simply need to start the BackgroundWorkder you created in StartBackgroundWorker. If StartWorker really has enough code that it needs to be in another method, then it probably means it should accept a BackgroundWorker as a parameter, so you can pass in the worker you created in StartBackgroundWorker.

The problem is that you never start your NewWorker. That's because your "global" NewWorker is always null.
See the fixed code:
NewWorker = new BackgroundWorker(); // this line is now fixed.
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
StartWorker();

Why do you need to make the background worker a field? Just make it in the constructor and everything else like. In its events you can just cast the object as the BackgroundWorker.
public void StartBackgroundWorker()
{
BackgroundWorker NewWorker = new BackgroundWorker();
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
NewWorker.RunWorkerAsync();
}

Francis is right... Any attempt to call MessageBox in a thread other than the GUI thread will block. You'll have to invoke:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => {
MessageBox.Show("Hello World!");
}));

Thank you to all your answers. The description supplied makes more sense to me now.
Also to the previous debate: the message box does block the thread. It got stuck at 10% and then crashed my computer (VM box).
Now my only issue is that e.results is returning an array of items. But it is in object.
I am finding it difficult to get the data back.
public void StartBackgroundWorker()
{
BackgroundWorker NewWorker = new BackgroundWorker();
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
NewWorker.RunWorkerAsync();
}
void NewWorker_DoWork(object sender, DoWorkEventArgs e)
{
List<string> ReturnResults = new List<string>();
BackgroundWorker worker = sender as BackgroundWorker;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select StatusCode from Win32_PingStatus where address = 'Metabox-PC'");
ManagementObjectCollection objCollection = searcher.Get();
foreach (ManagementObject Results in objCollection)
{
ReturnResults.Add(Results["StatusCode"].ToString());
}
e.Result = ReturnResults;
// Perform a time-consuming operation and report progress.
System.Threading.Thread.Sleep(1);
}
private void NewWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
txtPingable.text = e.Result;
}

Related

C# WPF Cancel ProgressBar from a different Thread

I have a UserControl with a big table that is displaying values using a lot of converters. I am trying to display a ProgressBar in a new Window with Indeterminate State that is closing automatically when the UserControl Loaded event is fired.
This is the Thread creation in the backcode of my UserControl :
Thread progressBarThread = new Thread(new ThreadStart(delegate {
ProgressBarWindow progressBarWindow = new ProgressBarWindow();
progressBarWindow.IsIndeterminate = true;
progressBarWindow.LaunchProgressBarInBackground();
progressBarWindow.ShowDialog();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => { Dispatcher.FromThread(progressBarThread).InvokeShutdown(); };
This code is "working", it is opening progressBarWindow but when I shutdown the thread using InvokeShutdown (the ugliest way to do it, i agree). The problem is that the DoWork from my backgroundWorker.
Here is the DoWork function :
private void BackgroundWorker_WaitUntilShouldStop(object sender, DoWorkEventArgs e)
{
// Do not access the form's BackgroundWorker reference directly.
// Instead, use the reference provided by the sender parameter.
BackgroundWorker bw = sender as BackgroundWorker;
// Start the time-consuming operation.
while (!bw.CancellationPending)
{
Thread.Sleep(500);
}
}
I would like to call my function contained in ProgressBarWindow to stop the DoWork from runnning and close the ProgressBarWindow normaly using :
progressBar.StopProgressBarInBackground();
This method is calling backgroundWorker.CancelAsync();
This will result in backgroundWorker terminating and progressBarWindow closing automatically.
But I don't have access to progressBar that is inside the progressBarThread. I tried to pass my UserControl using :
progressBarThread.Start(this);
this being the main window.
When trying to pass a variable from the main thread, this error is thrown :
An exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll but was not handled in user code Additional information: The calling thread cannot access this object because a different thread owns it.
Does someone have a nice and correct way to do it without using myThread.InvokeShutdown() ?
Edit 1 :
I found a solution to my problem using a volatile variable :
volatile bool _isLoaded;
void CreateAndStopProgressBarWhenIsLoaded()
{
Thread progressBarThread= new Thread(new ThreadStart(
{
Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
progressBar.IsIndeterminate = true;
progressBar.LaunchProgressBarInBackground();
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += (sender, e) => {
if (_isLoaded)
progressBar.StopProgressBarInBackground();
};
// Try to stop `progressBar` every 500 ms
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(500);
dispatcherTimer.Start();
progressBar.ShowDialog();
// Will only be reached once progressBar.ShowDialog(); returns
dispatcherTimer.Stop();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => {
_isLoaded = this.IsLoaded;
progressBarThread.Join(); // Wait for progressBarThread to end
};
}
Now the question is do you have a better solution ?
Edit 2 :
Here is my final solution thanks to #AlexSeleznyov :
void CreateAndStopProgressBarWhenIsLoaded()
{
Controls.ProgressBar.ProgressBar pb = null;
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
Thread progressBarThread = new Thread(new ThreadStart(delegate
{
Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
pb = progressBar;
manualResetEvent.Set();
progressBar.IsIndeterminate = true;
progressBar.LaunchProgressBarInBackground();
progressBar.ShowDialog();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => {
pb.Dispatcher.Invoke(delegate {
manualResetEvent.WaitOne();
pb.StopProgressBarInBackground();
});
progressBarThread.Join();
};
}
You might try this approach, to cache ProgressBar instance and then use it from another thread. Dispatcher.Invoke eradicates need for CheckAccess I've mentioned in comments.
void CreateAndStopProgressBarWhenIsLoaded()
{
var pb = new Controls.ProgressBar.ProgressBar[1];
Thread progressBarThread= new Thread(new ThreadStart(
{
Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
pb[0] = progressBar;
progressBar.IsIndeterminate = true;
progressBar.LaunchProgressBarInBackground();
progressBar.ShowDialog();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => {
pb[0].Dispatcher.Invoke(()=>pb[0].Close());
};
}
I think that you can use BackgroundWorker.RunWorkerCompleted in ProgressBarWindow - it will be invoked when you cancel a backgroundWorker.
private void backgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
//close the window
}
}

BackgroundWorker WPF difficulties with Progress Bar

I am developing a WPF, C# application and I have to read a very big CSV file and write to a database. This process takes a long time so I want to at least show a progress bar that grows in size as it nears completition.
Now, I have the following code at the top:
private readonly BackgroundWorker worker = new BackgroundWorker();
Then inside the loop I have this:
worker.WorkerReportsProgress = true;
worker.ReportProgress((100 * i) / 10000);
and I have a private sub like this:
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
prgBar.Value = Math.Min(e.ProgressPercentage, 100);
}
Yet I don't see the progress bar updating or anything happening, program still hangs. Am I doing something wrong?
UPDATE: Tried the MSDN guide here http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx and I somehow failed to get it working with that example. I adjusted the looping part of course. Heh. It still stays idle and nothing updates. Heck, all I want to do is get a small counter that increases every time a line is read and added.
UPDATED CODE:
private BackgroundWorker bw = new BackgroundWorker();
public Process()
{
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 bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Perform a time consuming operation and report progress.
int d = 0;
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Set filter for file extension and default file extension
dlg.DefaultExt = ".csv";
dlg.Filter = "CSV File (*.csv)|*.csv";
dlg.Title = "file";
// Display OpenFileDialog by calling ShowDialog method
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
// Open document
string filename = dlg.FileName;
List<String[]> fileContent = new List<string[]>();
using (FileStream reader = File.OpenRead(#filename)) // mind the encoding - UTF8
using (TextFieldParser parser = new TextFieldParser(reader))
{
using (SqlConnection conn = new SqlConnection(Classes.PublicVariables.Connection))
{
parser.Delimiters = new[] { "," };
parser.HasFieldsEnclosedInQuotes = true;
while (!parser.EndOfData)
{
string[] line = parser.ReadFields();
fileContent.Add(line);
SqlCommand comm = QUERYANDPARAMETERS
d += 1;
comm.ExecuteNonQuery();
worker.ReportProgress((d * 10));
}
}
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblCount.Content = "Complete";
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.lblCount.Content = (e.ProgressPercentage.ToString() + "%");
}
I am getting an excerption when the worker runs.
An exception of type 'System.NullReferenceException' occurred inSolution.exe but was not handled in user code
Additional information: Object reference not set to an instance of an object.
UPDATE3
Got it working! All I needed was the same code as above but this:
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
else
{
bw_DoWork(null, null);
}
In the event handler for the button press.
This will not compile as-is, but should get you started in the right direction:
private readonly BackgroundWorker worker
= new BackgroundWorker { WorkerReportsProgress = true };
public MainWindow()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
}
private void worker_DoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
// Do some long process, break it up into a loop so you can periodically
// call worker.ReportProgress()
worker.ReportProgress(i); // Pass back some meaningful value
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
prgBar.Value = Math.Min(e.ProgressPercentage, 100);
}
is the varible i int? if it is, the value of (100 * i) / 10000 will return 0 while i < 100. and the param of ReportProgress method should be a precent value. you can change it to worker.ReportProgress(i);,try it.

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 doesnt get progress info from another class

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.

c# EventArrivedEventHandler stuck in infinite loop, can't reference watcher

I've googled this problem for the past week, it's killing my peace! Please help... EventArrivedEventHandler is stuck in a loop, and if I stop it, then it won't catch events. But when I use a handler method, the thread is still concentrating on the loop, and won't give attention to the new form I'm trying to make in the handler! Strange thing is, if I just use something small, like a MessageBox, it doesn't cause an issue, just trying to instantiate a form causes the buttons to NOT draw. Then shortly after the program stops responding. In case you're wondering where the form code is, it's just a standard form made by .NET, that works everywhere else in the code except for in the event handler.
Thanks!
class MainClass
{
public static void Main()
{
TaskIcon taskbarIcon;
EventWatch myWatcher;
taskbarIcon = new TaskIcon();
taskbarIcon.Show();
myWatcher = new EventWatch();
myWatcher.Start();
Application.Run();
}
}
public class TaskIcon
{
public void Show()
{
NotifyIcon notifyIcon1 = new NotifyIcon();
ContextMenu contextMenu1 = new ContextMenu();
MenuItem menuItem1 = new MenuItem();
MenuItem menuItem2 = new MenuItem();
contextMenu1.MenuItems.AddRange(new MenuItem[] { menuItem1, menuItem2 });
menuItem1.Index = 0;
menuItem1.Text = "Settings";
menuItem1.Click += new EventHandler(notifyIconClickSettings);
menuItem2.Index = 1;
menuItem2.Text = "Exit";
menuItem2.Click += new EventHandler(notifyIconClickExit);
notifyIcon1.Icon = new Icon("app.ico");
notifyIcon1.Text = "Print Andy";
notifyIcon1.ContextMenu = contextMenu1;
notifyIcon1.Visible = true;
}
private static void notifyIconClickSettings(object Sender, EventArgs e)
{
MessageBox.Show("Settings Here");
}
private static void notifyIconClickExit(object Sender, EventArgs e)
{
//taskbarIcon.Visible = false; // BONUS QUESTION: Why can't I hide the tray icon before exiting?
Application.Exit();
}
}
public class EventWatch
{
public void Start()
{
string thisUser = WindowsIdentity.GetCurrent().Name.Split('\\')[1];
WqlEventQuery query = new WqlEventQuery();
query.EventClassName = "__InstanceCreationEvent";
query.Condition = #"TargetInstance ISA 'Win32_PrintJob'";
query.WithinInterval = new TimeSpan(0, 0, 0, 0, 1);
ManagementScope scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true;
ManagementEventWatcher watcher = new ManagementEventWatcher(scope, query);
watcher.EventArrived += new EventArrivedEventHandler(showPrintingForm);
watcher.Start();
}
void showPrintingForm(object sender, EventArrivedEventArgs e)
{
// MessageBox.Show("This will draw just fine");
Form1 myForm;
myForm = new Form1();
myForm.Show(); // This causes a hangup
}
}
My guess would be that the ManagementEventWatcher calls the EventArrived handler from a different thread than the UI thread. Then your showPrintingForm is executed on that thread and accessing UI from a different thread than the UI thread is bad. You need to marshal your code back onto the UI thread.

Categories