In my program I am using a separate thread to read from an external file and convey data to my program. I am in the process of trying to run a ProgressBar on the UI thread while that thread is running, in order to provide feedback to the user. As of now, I have a ProgressBar that opens, but does not close because I do not know how to tell it that the child thread has ended. I have read this tutorial on ProgressBars, and should make a note that I am not using a BackgroundWorker for my thread. Instead of this, I am just using a System.Threading.Thread. Please let me know if this is a problem.
The OpenFile() method that opens the Window containing the ProgressBar, and also launches the thread that will do the open file work:
public void OpenFile()
{
//Open Progress Bar Window
LoadingWindow = new LoadingScreen(App.MainWindowViewModel.LoadScreen);
LoadingWindow.Show();
App.MainWindowViewModel.LoadScreen.IsIndeterminate = true;
FILE_INPUT = true; //bool that Signifies that the background thread is running
//Create Thread -- run routines in thread
var openFileThread = new Thread(() => openDefault(txtFile)); //Passes the file
openFileThread.Start(); //Start File open thread
}
//This thread reads in the file and transfers the data to the program
private void openDefault(StreamReader txtFile)
{
//Gathers info from file and changes program accordingly
FILE_INPUT = false; //background thread is no longer running
}
As of now, I do not have an increment method for my ProgressBar, as I would like to make sure that I can get it working correctly before working on a technical detail like that. Anyway, my question is... how and where in my program do I make it known that the background thread is done running and the LoadingWindow (ProgressBar) can be closed?
Some more code to help give you an idea of my program's flow:
Main Window Code Behind:
private void OpenFile_Click(object sender, RoutedEventArgs e)
{
OpenFile(); //calls to the method that launches the new thread and the window with the Progress Bar
//Wait until background Thread is complete
while (ViewModel.FILE_INPUT == true) { }
//After thread has stopped running, close the window (DOES NOT WORK)
ViewModel.LoadingWindow.Close(); // -- Close Prog Bar
}
Eventual Solution:
//In OpenFile() method
LoadingWindow = new LoadingScreen(App.MainWindowViewModel.LoadScreen); //Open Progress Bar Window
LoadingWindow.Show();
App.MainWindowViewModel.LoadScreen.IsIndeterminate = true;
FILE_INPUT = true; //A file is being inputted*
Dispatcher UIDispatcher = Dispatcher.CurrentDispatcher;
Task.Factory.StartNew(() =>
{
openDefault(txtFile); //Passes the file
//After thread has stopped running, close the Loading Window
UIDispatcher.Invoke((Action)(() => LoadingWindow.Close())); //Close progress bar
});
Without a complete code example, it's hard to provide a complete answer. Depending on how you're actually doing the file I/O and managing the ProgressBar, there might be other changes you could make to also improve the code. But to solve the immediate problem, I recommend taking advantage of the async/await pattern:
public async Task OpenFile()
{
//Open Progress Bar Window
LoadingWindow = new LoadingScreen(App.MainWindowViewModel.LoadScreen);
LoadingWindow.Show();
App.MainWindowViewModel.LoadScreen.IsIndeterminate = true;
FILE_INPUT = true; //bool that Signifies that the background thread is running
//Create Thread -- run routines in thread
await Task.Run(() => openDefault(txtFile)); //Passes the file
}
private async void OpenFile_Click(object sender, RoutedEventArgs e)
{
await OpenFile(); //calls to the method that launches the new thread and the window with the Progress Bar
//After thread has stopped running, close the window
ViewModel.LoadingWindow.Close(); // -- Close Prog Bar
}
This will avoid causing your UI to freeze while the operation is working, while allowing the code to be written in a simple, straight-through manner.
It's possible that with the above changes, you can get rid of your FILE_INPUT variable altogether (it's not clear for what else you might be using it, but it might be nothing else).
Note that the OpenFile() method is changed to return Task. This is the norm for async methods without an actual return value. The same change would be made to the OpenFile_Click() method, except that as an event handler, these are generally required to return void. Either syntax is legal, but the former is preferred when possible so that access to the Task object is possible (for exception handling, completion, etc.).
EDIT: for those without .NET 4.5 and who cannot install the necessary components to support async/await (which is just a compile-time change), here's an example that will work in .NET 4.0:
public void OpenFile()
{
//Open Progress Bar Window
LoadingWindow = new LoadingScreen(App.MainWindowViewModel.LoadScreen);
LoadingWindow.Show();
App.MainWindowViewModel.LoadScreen.IsIndeterminate = true;
FILE_INPUT = true; //bool that Signifies that the background thread is running
//Create Thread -- run routines in thread
Task.Factory.StartNew(() =>
{
openDefault(txtFile); //Passes the file
//After thread has stopped running, close the window
Dispatcher.Invoke((Action)(() => ViewModel.LoadingWindow.Close())); // -- Close Prog Bar
});
}
private void OpenFile_Click(object sender, RoutedEventArgs e)
{
//calls to the method that launches the new thread and the window with the Progress Bar
OpenFile();
}
(This is actually semantically a little different from the async/await implementation, in that rather than creating a continuation for the first task, I just put the window-close operation in that task. But it should have the same net result).
Related
I've got the following program flow in my Windows Forms application (WPF is not a viable option unfortunately):
The GUI Thread creates a splash screen and a pretty empty main window, both inheriting Form.
The splash screen is shown and given to Application.Run().
The splash screen will send an event which triggers an async Event Handler which performs initialization, using the IProgress interface to report progress back to the GUI. (This works flawlessly.)
At some point during the initialization, I need to dynamically create GUI components based on information provided by certain plugins and add them to the Main Window.
At this point I'm stuck: I know I need to ask the GUI thread to create those components for me, but there is no Control I could call InvokeRequired on. Doing MainWindow.InvokeRequired works neither.
The only idea I could come up with was to fire an event which is connected to a factory in the GUI Thread, and then wait for that factory to fire another event which provides the created controls. However I am pretty sure there is a more robust solution. Does anyone know how to achieve this?
Using the comments on my question, especially the note about the continuation method which made me find this very useful question, I achieved the following:
The first part of initialization is performed asynchronously (no change).
The second part of the initialization (which creates the UI elements) is performed afterwards as a Continuation Task, in the context of the UI thread.
Apart from the rather short GUI initialization part, the Splash Screen is responsive (i.e. the mouse cursor does not change to "Waiting" once it hovers the Splash Screen).
Neither of the initialization routines knows the splash screen at all (i.e. I could easily exchange it).
The core controller only knows the SplashScreen interface and does not even know it is a Control.
There currently is no exception handling. This is my next task but doesn't affect this question.
TL;DR: The code looks somewhat like this:
public void Start(ISplashScreen splashScreen, ...)
{
InitializationResult initializationResult = null;
var progress = new Progress<int>((steps) => splashScreen.IncrementProgress(steps));
splashScreen.Started += async (sender, args) => await Task.Factory.StartNew(
// Perform non-GUI initialization - The GUI thread will be responsive in the meantime.
() => Initialize(..., progress, out initializationResult)
).ContinueWith(
// Perform GUI initialization afterwards in the UI context
(task) =>
{
InitializeGUI(initializationResult, progress);
splashScreen.CloseSplash();
},
TaskScheduler.FromCurrentSynchronizationContext()
);
splashScreen.Finished += (sender, args) => RunApplication(initializationResult);
splashScreen.SetProgressRange(0, initializationSteps);
splashScreen.ShowSplash();
Application.Run();
}
It is much easier to manage multiple forms and display one while the other is working or being constructed.
I suggest you try the following:
When application is started you create splash screen form so your Program.cs is like this
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SplashForm());
}
Inside the splash form constructor, create a new thread (I will use BackgroundWorker but there are other options like tasks) to build your main form.
public SplashForm()
{
InitializeComponent();
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerAsync();
}
Now we need to write the SplashForm member functions to tell background worker what to do
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Perform non-GUI initialization - The GUI thread will be responsive in the meantime
// My time consuming operation is just this loop.
//make sure you use worker.ReportProgress() here
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10));
}
}
SetVisible(false);
MainForm mainForm = new MainForm();
mainForm.ShowDialog();
//instead of
//this.Visible = false;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
You might have noticed by now, I am using another member function to hide the splash screen. It is because you are now in another thread and you can't just use this.visible = false;. Here is a link on the matter.
delegate void SetTextCallback(bool visible);
private void SetVisible(bool visible)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetVisible);
this.Invoke(d, new object[] { visible });
}
else
{
this.Visible = visible;
}
}
When I run this sample project it shows the progress bar and then loads the MainForm windows form after hiding the SplashForm.
This way you can put any controls that you might need inside the MainForm constructor. The part you shortened as // Perform GUI initialization afterwards in the UI context should go into MainForm constructor.
Hope this helps.
I have an issue that relates to threading, cleaning up unmanaged resources and shutting down my app.
In the main UI thread I have a method that creates a new instance of class Worker. In Worker's constructor I start a new thread that has a while(Scanning) loop that updates some controls in my UI using Invoke() continuously (until Scanning bool is set to false). In the UI thread I raise the event FormClosing() whenever the application is closing down (through X button or Application.Exit() etc.). In FormClosing() I set Scanning to false and do some cleanup of unmanaged resources (that can only be done after the worker thread is done, because it uses those resources. The problem is that when I close the app down the MainForm apparently gets instantly disposed, so the app crashes at the Invoke (because it is trying to make a delegate run from UI thread, but that thread is disposed).
In an attempt to make the worker finish before the UI closes I tried to create a method StopWorker() in the worker class where I put Scanning = false, and then Thread.Join. As you can imagine the Join caused a deadlock as it makes the UI thread sleep but the Invoke needs the UI thread to move on.
In summary I need to cleanup unmanaged resources in FormClosing. I need the worker thread to be done before I do that though, as it uses these resources. The worker thread cannot finish (it uses Invoke) if the MainForm is disposed, therefore creating a tricky situation.
Based on Hans Passant's answer here, I created the below solution. It seems to be working very well.
In UI class/thread:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
var button = sender as Button;
if (button != null && string.Equals(button.Name, #"CloseButton"))
{
//FormClosing event raised by a user-created button action
}
else
{
//FormClosing event raised by program or the X in top right corner
//Do cleanup work (stop threads and clean up unmanaged resources)
if (_bw.Scanning)
{
_bw.Scanning = false;
ClosePending = true;
e.Cancel = true;
return;
}
//Code to clean up unmanaged resources goes here (dummy code below)
ApplicationLogger.Get.Log("Doing final cleanup work and exiting application...");
MemoryHandler.Get.Dispose();
ApplicationLogger.Get.Dispose();
}
}
My worker thread is in another class that has a public bool property called Scanning. It also has this while loop (notice the line at the bottom):
private void Worker()
{
while (Scanning)
{
Thread.Sleep(50);
_sendBackValue[0] = "lbOne";
_sendBackValue[1] = "blaBla";
_synch.Invoke(_valueDelegate, _sendBackValue);
_sendBackValue[0] = "lbTwo";
_sendBackValue[1] = "blaBla";
_synch.Invoke(_valueDelegate, _sendBackValue);
_sendBackValue[0] = "lbThree";
_sendBackValue[1] = "blaBla";
_synch.Invoke(_valueDelegate, _sendBackValue);
}
MainForm.Get.Invoke((Action)(() => MainForm.Get.StopScanning()));
}
Finally, back in the UI class/thread I have this method:
public void StopScanning()
{
if (!ClosePending) return;
ApplicationLogger.Get.Log("Worker thread is closing the application...");
Close();
}
Could you not better use the BackgroundWorker class/control? It is much easier to use because it has already a lot of synchronization stuff in it.
But if you have a separate thread, in your FormClosing event, use:
yourThread.Abort();
yourThread.Join(); // or yourThread.Join(1000); where 1000 is some kind of time out value
in your thread use try-excpet-finally construct
try
{
// do your stuff
}
catch (ThreadAbortException)
{
// do someting when your thread is aborted
}
finally
{
// do the clean up. Don't let it take too long.
}
Note that the Join command will block further execution until the thread has stopped. Therefore, I would recommend a not too high value for the time out parameter, otherwise the user interface will be blocked and will irritate users.
Disclaimer: I do not advocate the use of Thread, ManualResetEvent and, above all, volatile in the .NET 4.5+ era, but since the .NET version was not specified I've done my best to address the problem while keeping things as backwards-compatible as possible.
Here's a solution which uses a polling variable and a ManualResetEvent to block the execution of the FormClosing handler until the loop has completed - without any deadlocks. In your scenario if you have a class-level reference to the Thread which runs the loop, you can use Thread.Join instead of ManualResetEvent.WaitOne in the FormClosing handler - the semantics will be the same.
using System;
using System.Threading;
using System.Windows.Forms;
namespace FormClosingExample
{
public partial class Form1 : Form
{
private volatile bool Scanning = true;
private readonly ManualResetEvent LoopFinishedMre = new ManualResetEvent(false);
private readonly SynchronizationContext UiContext;
public Form1()
{
this.InitializeComponent();
// Capture UI context.
this.UiContext = SynchronizationContext.Current;
// Spin up the worker thread.
new Thread(this.Loop).Start();
}
private void Loop()
{
int i = 0;
while (this.Scanning)
{
// Some operation on unmanaged resource.
i++;
// Asynchronous UI-bound action (progress reporting).
// We can't use Send here because it will deadlock if
// the call to WaitOne sneaks in between the Scanning
// check and sync context dispatch.
this.UiContext.Post(_ =>
{
// Note that it is possible that this will
// execute *after* Scanning is set to false
// (read: when the form has already closed),
// in which case the control *might* have
// already been disposed.
if (this.Scanning)
{
this.Text = i.ToString();
}
}, null);
// Artifical delay.
Thread.Sleep(1000);
}
// Tell the FormClosing handler that the
// loop has finished and it is safe to
// dispose of the unmanaged resource.
this.LoopFinishedMre.Set();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Tell the worker that it needs
// to break out of the loop.
this.Scanning = false;
// Block UI thread until Loop() has finished.
this.LoopFinishedMre.WaitOne();
// The loop has finished. It is safe to do cleanup.
MessageBox.Show("It is now safe to dispose of the unmanaged resource.");
}
}
}
Now, while this solution is (somewhat) tailored to your description of the problem (which I interpreted to the best of my ability), I had to make a large number of assumptions. If you want a better answer, you'll need to post a concise repro of the problem - not necessarily your production code, but at least a trimmed down working version which still has all the main nuts and bolts in place and exhibits the problem you've described.
I would like to implement a simple popup window in Windows Forms, which will show a simple timer to the user while some slow-running process is executing. The premise is simple; show to the user that something is indeed going on and the application is not frozen. Note that this slow-running process is not a loop, nor is it something that I can tap into.
What I want is a simple popup window, showing some message along the lines "Elapsed time: x seconds", where x is incremented every second.
The basic concept is the following:
public void test()
{
//Some code which does stuff
//Popup window with counter
//Perform long running process
//Close popup window with counter
//Some other code which does other stuff
}
I tried to do it using various ways, including background workers, threads, and of course timers. But I did not manage to make it work as I wanted. And I would prefer not to post any of my code so as not to "lead" the responses to a specific way of doing this.
So what would be the best way to do this work?
Thanks.
UPDATE:
In reply to some comments, since I cannot paste any code in the replies section, I'm editing my original question to accomodate this. One of the implementations that I tried is to spawn the popup window in a separate thread. Although I got no runtime errors, the popup window did not refresh correctly. It indeed poped-up, but no text would show within it, and the counter would not refresh. Here's the code:
private void test()
{
frmProgressTimer ofrmProgressTimer = new frmProgressTimer(); //Instance of popup Form
System.Threading.Tasks.Task loadTask = new System.Threading.Tasks.Task(() => ProgressTimer(ofrmProgressTimer));
loadTask.Start();
//Perform long running process
System.Threading.Tasks.Task cwt = loadTask.ContinueWith(task => EndProgressTimer(ofrmProgressTimer));
}
private void ProgressTimer(frmProgressTimer ofrmProgressTimer)
{
ofrmProgressTimer.Show();
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.startTimer();
}));
}
private void EndProgressTimer(frmProgressTimer ofrmProgressTimer)
{
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.stopTimer();
ofrmProgressTimer.Close();
}));
}
And here's my popup form code:
public partial class frmProgressTimer : Form
{
private int counter = 0;
private Timer timer1;
public frmProgressTimer()
{
InitializeComponent();
timer1 = new Timer();
timer1.Interval = 1000;
timer1.Tick += new EventHandler(timer1_Tick);
}
public void startTimer()
{
timer1.Start();
}
public void stopTimer()
{
timer1.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
counter += 1;
labelText.Text = counter.ToString();
}
}
This is actually quite easy to do. Create your dialog, define your long running operation to take place in a non-UI thread when it is shown, add a continuation to that operation which closes the dialog when the task finishes, and then show the dialog.
MyDialog dialog = new MyDialog();
dialog.Shown += async (sender, args) =>
{
await Task.Run(() => DoLongRunningWork());
dialog.Close();
};
dialog.ShowDialog();
The code to have the ticking over time should be entirely contained within the dialog, and based on the question it seems you already have that well under control with a simple Timer.
Make a new form, which will pop up, and show a timer. That way it won't be interrupted with all the work on your main form, and the timer will work continuously.
Remember when showing a new from to use newForm.ShowDialog() not newForm.Show(). Your can google the differences
I would simply start your work on a separate thread. Launch a modal form with your timer output. To display the timer use an actual timer instance set to update every second. When the timer event fire update your dialog.
Finally once you're thread completes close the dialog so your main form is active again.
First of all you need to make it not closeable by the user (as if modal dialogs weren't annoying enough) but closeable by your code. You could accomplish this by subscribing to the FormClosing event of the form. Let's say your popup form's name is Form2:
private bool mayClose = false;
public void PerformClose()
{
this.mayClose = true;
this.Close();
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
if (!this.mayClose)
e.Cancel = true;
}
Create a Timer, provide a Tick event handler, enable it and set its Interval to 500 milliseconds:
Create a label to host your desired text. Let's call it label1.
Within and surrounding your Tick event handler do something like this:
private DateTime appearedAt = DateTime.UtcNow;
private void timer1_Tick(object sender, EventArgs e)
{
int seconds = (int)(DateTime.UtcNow - this.appearedAt).TotalSeconds;
this.label1.Text = string.Format(#"Ellapsed seconds: {0}", seconds);
}
Make sure your long running process is happening on a background thread, not on the GUI thread.
Say your long running process can be thought of as the execution of a method called MyProcess.
If that is the case, then you need to call that method from a secondary thread.
// PLACE 1: GUI thread right here
Thread thread = new Thread(() =>
{
// PLACE 2: this place will be reached by the secondary thread almost instantly
MyProcess();
// PLACE 3: this place will be reached by the secondary thread
// after the long running process has finished
});
thread.Start();
// PLACE 4: this place will be reached by the GUI thread almost instantly
Show the form right before the long running process starts. This can be done in any of the 2 places (marked in the previous section of code) called PLACE1 or PLACE2. If you do it in PLACE2 you will have to marshal a call back to the GUI thread in order to be able to interact with the WinForms framework safely. Why am I bringing this up ? It's because maybe the long running process is not started from within the GUI thread at all and you absolutely need to do this.
Close the form right after the long running process finishes. This can be done only in PLACE3 and you absolutely need to marshal a call.
To wrap the earlier 2 bullets and the answer, you could do this:
private void DoIt()
{
Form2 form2 = new Form2();
Action showIt = () => form2.Show();
Action closeIt = () => form2.PerformClose();
// PLACE 1: GUI thread right here
Thread thread = new Thread(() =>
{
form2.BeginInvoke(showIt);
// PLACE 2: this place will be reached by the secondary thread almost instantly
MyProcess();
form2.BeginInvoke(closeIt);
// PLACE 3: this place will be reached by the secondary thread
// after the long running process has finished
});
thread.Start();
// PLACE 4: this place will be reached by the GUI thread almost instantly
}
Finally I've managed to resolve this in the most simplistic manner. And it works like a charm. Here's how to do it:
//Create an instance of the popup window
frmProgressTimer ofrmProgressTimer = new frmProgressTimer();
Thread thread = new Thread(() =>
{
ofrmProgressTimer.startTimer();
ofrmProgressTimer.ShowDialog();
});
thread.Start();
//Perform long running process
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.stopTimer();
ofrmProgressTimer.Close();
}));
You can see the code for the popup window in the original post/question, with the only difference that the tick function changes the label text as:
labelText.Text = string.Format("Elapsed Time: {0} seconds.", counter.ToString());
Thank you to everybody for trying to help me out.
I have a WinForm load method that takes a long time to gather some data to display to the user.
I display a form with a large font with the word "Loading" while this method is executing.
However sometimes this error comes up and the "Loading" progress form does not close and then eventually my whole application will just exit:
Error creating window handle. at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
Is there a better way to display my progress/loading form while I am executing code in the load method?
This is my code:
//I launch a thread here so that way the Progress_form will display to the user
//while the Load method is still executing code. I can not use .ShowDialog here
//or it will block.
//Progress_form displays the "Loading" form
Thread t = new Thread(new ThreadStart(Progress_form));
t.SetApartmentState(System.Threading.ApartmentState.STA);
t.IsBackground = true;
t.Start();
//This is where all the code is that gets the data from the database. This could
//take upwards of 20+ seconds.
//Now I want to close the form because I am at the end of the Load Method
try
{
//abort the Progress_form thread (close the form)
t.Abort();
//t.Interrupt();
}
catch (Exception)
{
}
A BackgroundWorker is a great way to perform a long running operation without locking the UI thread.
Use the following code to start a BackgroundWorker and display a loading form.
// Configure a BackgroundWorker to perform your long running operation.
BackgroundWorker bg = new BackgroundWorker()
bg.DoWork += new DoWorkEventHandler(bg_DoWork);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
// Start the worker.
bg.RunWorkerAsync();
// Display the loading form.
loadingForm = new loadingForm();
loadingForm.ShowDialog();
This will cause the following method to be executed on a background thread. Note that you cannot manipulate the UI from this thread. Attempting to do so will result in an exception.
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
// Perform your long running operation here.
// If you need to pass results on to the next
// stage you can do so by assigning a value
// to e.Result.
}
When the long running operation completes, this method will be called on the UI thread. You can now safely update any UI controls. In your example, you would want to close the loading form.
private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Retrieve the result pass from bg_DoWork() if any.
// Note, you may need to cast it to the desired data type.
object result = e.Result;
// Close the loading form.
loadingForm.Close();
// Update any other UI controls that may need to be updated.
}
Ive successfully tested this on .NET 4.0. (WinForms) I'm reasonably certain that this will work on .NET 4.0+ and should be a useful code snippet to reuse in most of your projects that require closing forms at the end of a process.
private void SomeFormObject_Click(object sender, EventArgs e)
{
myWait = new YourProgressForm();//YourProgressForm is a WinForm Object
myProcess = new Thread(doStuffOnThread);
myProcess.Start();
myWait.ShowDialog(this);
}
private void doStuffOnThread()
{
try
{
//....
//What ever process you want to do here ....
//....
if (myWait.InvokeRequired) {
myWait.BeginInvoke( (MethodInvoker) delegate() { closeWaitForm(); } );
}
else
{
myWait.Close();//Fault tolerance this code should never be executed
}
}
catch(Exception ex) {
string exc = ex.Message;//Fault tolerance this code should never be executed
}
}
private void closeWaitForm() {
myWait.Close();
MessageBox.Show("Your Process Is Complete");
}
I would take the code that you have in your load method and place that into a thread. Setup a progress bar somewhere on your form and increment it at key stages in the code that's gathering the data - be careful not to do this in the thread itself though, i.e. don't tamper with ui elements in a separate thread, you'll need to invoke them using a delegate.
When a user clicks on Run, the application runs through a lot of code to generate a model and display it in a Chart. The Run takes about 1-2 minutes to run. I also have a Cancel button that gets enabled after the Run button is clicked. I am working with DotSpatial, so my buttons are on a plugin panel in a ribbon UI. The click event on the Run and Cancel start in the plugin, which calls the back-end class's code Run and Click.
When the user hits cancel after the run starts, there is a delay, but the cancel method is invokes and executes, but the run never stops and we eventually see the chart display. So, I'm thinking I need a separate thread for the Run. I'm fairly new to programming, and never worked with Threading. I've looked into it and added the below code, but my thread method isn't running. Here's my code:
The Run button is clicked:
This is at the top:
//check to see if RunModel thread needs to stop or continue
private volatile bool stopRun = false;
private Thread runThread;
Then this is the method that's called from the click event:
public void btnRun_testingThread(object sender, EventArgs e)
{
//create a new thread to run the RunModel
if (runThread == null)
{
//we don't want to stop this thread
stopRun = false;
runThread = new Thread(RunModel);
runThread.Start(); <--this isn't doing anything
}
So, I would think that when the code gets to the runThread.Start(), it would jump into my RunModel method and start running through the code. But it doesn't. Additionally, I'll want to cancel out of this thread (once I have it working correctly), so I have this, which gets called from the cancel click method:
private void StopRunThread()
{
if (runThread != null)
{
//we want to stop the thread
stopRun = true;
//gracefully pause until the thread exits
runThread.Join();
runThread = null;
}
}
Then the this is the RunModel() where I'm checking occasionally to see if the stopRun bool has changed.
public void RunModel()
{
...some code.....
//check to see if cancel was clicked
if (stopRun)
{
....clean up code....
return;
}
....some more code....
//check to see if cancel was clicked
if (stopRun)
{
....clean up code....
return;
}
}
And the cancel button click method:
public void btnCancel_Click(Object sender, EventArgs e)
{
stopRun = true;
StopRunThread();
//the model run has been canceled
....some code.....
}
Any help on getting the thread.start to actually run the Run method? Then do I need to constantly check the volatile bool in the run in order to clean everything up if it's being stopped? Thanks!
I think you'd be best looking at the BackgroundWorker - this essentially runs separately but can watch out for cancellation commands. Make sure you add 'WorkerSupportCancellation' when you initialise it:
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); // This does the job ...
backgroundWorker1.WorkerSupportsCancellation = true; // This allows cancellation.
Then on click you can start your process:
public void btnRun_testingThread(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
Your cancel button can issue a cancellation request:
public void btnCancel_Click(Object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
Then your worker can monitor for this as it's doing it's work ...
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
if (backgroundWorker1.CancellationPending)
{
break;
}
else
{
// Do whatever you're doing.
}
}
e.Result = backgroundWorker1.CancellationPending ? null : orders;
}
You can enhance this further by adding progress bars etc., but that gets a bit more complicated so I won't go into it here.
Considering new info provided in commend I believe you just missed a start of the RunModel() method in debugger because of wrong assumption regarding thread.Start() method behaviour.
Please see a note from MSDN, Thread.Start Method
Once a thread is in the ThreadState.Running state, the operating
system can schedule it for execution. The thread begins executing
at the first line of the method represented by the ThreadStart or
ParameterizedThreadStart delegate supplied to the thread constructor.
Small demonstration that thread start takes some time bits, for me it starts in 38-40 milliseconds:
Stopwatch watch = new Stopwatch();
Thread thread = new Thread((ThreadStart)watch.Stop);
thread.Start();
watch.Start();
Thread.Sleep(5000);
double startedAfter = watch.ElapsedMilliseconds;
Since .NET Framework 4.0 consider using TPL Tasks rather than threads explicitly, some pros:
You can easily synchronize with UI thread by passing in a Task UI Thread synchronization context
You can easily stop a Taks using CancellationToken