Stop thread from any form or thread - c#

I have a application that adds items queried from SQL into a list box on my main WPF form. The query takes a long time so I added it to its own thread. I have another thread that shows a new form with a please wait gif animation on it.
So in total I have 3 threads.
Main GUI thread
Thread running SQL query - thread named sql_thread
Thread opens/shows new instance of please wait form with Gif animation on it. - thread name please_wait_thread
I want the ability to cancel the thread running the SQL query from a cancel button on the please_wait_thread that shows a form with a please wait Gif animation.
I am having a real hard time understanding inheritance and how to reference objects between forms. I know how to create new instances of them but not make them communicate.
If I put the below code in the please_wait form and type sql_thread it cannot see the sql thread.
public void Cancel_btn_Click(object sender, EventArgs e)
{
sql_thread.abort();
}
How can I stop the sql_thread in it's tracks when I close the please_wait form?
The code to create the sql_thread on the main form is below.
private void btn_search_Click(object sender, EventArgs e)
{
Thread sql_thread = new Thread(sql_search_method);
}
P.S. I already tried a backgroundworker but it did not work and once it hits the long query there is no way to stop it until its done with a backgroundworker.

Related

How can I stop a long-running subroutine in my Form with a Button?

I have a Console app that displays a WinForms Form.
In the Form, the user clicks button 1 and it runs a long subroutine. I want to have a button 2 that can kill the subroutine at any point. However, the UI freezes when I click button 1 until the subroutine has finished. How can I get the UI to not freeze?
Your long-running code is blocking the UI thread, so you can no longer click the second button, nor interact with the UI in any way until the code is finished executing.
You'll need to move your long-running code to a separate thread. There are various (and newer) ways of doing this, but one way is the BackgroundWorker. It's pretty easy to learn, and wraps some nice functionality, like cancelling the thread.
Here's a short WinForms app to demonstrate. You have to explicitly enable the ability for the thread to be cancelled. In this example, the while loop continues indefinitely, but checks every 100ms to see if there's a request for it to be cancelled. When you click the second button, the cancellation request is sent, and the thread ends.
public partial class Form1 : Form
{
private BackgroundWorker bg;
public Form1()
{
InitializeComponent();
bg = new BackgroundWorker
{
WorkerSupportsCancellation = true
};
bg.DoWork += (sender, args) =>
{
while (true)
{
Thread.Sleep(100);
if (bg.CancellationPending)
break;
}
MessageBox.Show("Done!");
};
}
private void button1_Click(object sender, EventArgs e)
{
bg.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
bg.CancelAsync();
}
}
Following up on chouaib's comment, another nice thing about using the BackgroundWorker in a WinForms environment is that you can drop and drop it onto your designer, similar to a Menu, Timer, etc. You can then access its members in the "properties" panel, setting "WorkerSupportsCancellation" to true, subscribing to events, etc.
From your comment:
"is there a way to run this background process and be able to update the main userform? I keep getting the "cross-thread operation not valid control accessed from a thread other than the..." I want to run the long-running background operation, and have it update the main UI with text in a label (like a percentage of its progress)"
If you want to update the UI while the thread is running, you should do that from the ProgressChanged event. First, enable that option and subscribe to the event:
bg.WorkerReportsProgress = true;
bg.ProgressChanged += bg_ProgressChanged;
Then call ReportProgress() when you want to update the UI. You could pass back a percentage complete and some text, for instance:
bg.ReportProgress(50, "Almost there...");
Finally, update the UI from inside the event:
void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var message = e.UserState.ToString();
var percent = e.ProgressPercentage;
lblStatus.Text = message + " " + percent;
}
You need to make it multithreaded as suggested in the comments. The older way of doing this was manage your own thread. Then along came the background worker (cheap and easy). Now a days you have other options such as the Task Library.
Remember - anything the runs on the UI thread prevents the UI from sending and receiving events until that operation is finished.
Look into the BackgroundWorker component

Interthread communication - Manual Reset Event

I am trying to establish a pause and play communication between threads.I am spawning two threads for two forms at the startup, one is the mainthread which is for mainwindow and second thread of form2,
var thread = new Thread(ThreadStart);
thread.TrySetApartmentState(ApartmentState.STA);
thread.Start();
private static void ThreadStart()
{
Application.Run(new SecondForm()); // other form started on its own UI thread
}
Mainwindow has a button on clicking which multiple threads would be spawn for different operations - it executes without pause until the intended jobs are complete.
In the second form I have two buttons stop, and start clicking on which I should be able to stop and start the mainwindow(All the child threads).
For this I am declaring
public static ManualResetEvent mre = new ManualResetEvent(false);
in program.cs where two UI threads are started
and trying to invoke it from the second form on start and stop
private void btnStop_Click(object sender, EventArgs e)
{
Invoke((MethodInvoker) delegate
{
Program.mre.WaitOne();
});
}
private void btnStart_Click(object sender, EventArgs e)
{
Invoke((MethodInvoker) delegate
{
Program.mre.Set();
});
}
Once I click the button on mainwindow which continues with series of operations I try to click on STOP button in secondform, this disables the second form, and the operation from the mainwindow continues, please let me know where I am going wrong.
Other than the fact that this is a completely silly threading design, there's two issues:
You Invoke on the second form, so you're blocking its messaging loop. Instead, you would want to do mainWindow.Invoke( ... ).
Even that will not help you when main window spawns worker threads. It will only stop the form's message loop, not the threads it has already spawned.
I found a solution to the problem. When I click on Pause button I am setting a bool say continue to false, and using this variable in a loop where actual calls to function is done.
In the same class where I have my loop I have the manual reset event initialized, when continue is false I am calling mre.WaitOne(), which would pause the current thread from execution without causing any harm to other threads.
When Play is clicked I am Setting the Manual Reset event object which would restart the thread.

Allow user to click on a Button of the form when Application is in Pause/Sleep Mode in C# winform Application

I am working on a C# winform application which continuously navigates some links in a webbrowser control and process the data of that webpage. I need to provide two buttons on the form for Pause and Resume.
On click of button the whole application should get pause processing and after that on click of Resume button it should start again.
So to pause the application, on click of Pause button I made thread to sleep for infinite time by following code.
private void Pause_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
}
But this piece of code unable the user to click on Resume Button on form to resume the application. Also I am not getting a perfect piece of code to resume the application on click of Resume Button.
Can Anyone get me the correct solution for this issue ?
Thread.Sleep method yields execution of code to process scheduler and doesn't get it back until specified time passes. Therefore you can't make sleeping thread wake itself up. You can't even make a working thread wake another sleeping thread (to my knowledge).
You need to accomplish your goal differently.
Separate data processing code to separate, worker, method
Run worker method in separate thread
Share a state variable between UI and worker thread. This variable should pass signals on whether execution should be paused.
From UI thread, set pause signals in shared variable as needed
In processing loop, write handler code for stopping processing
I'll post some pseudo code below on how you should do this:
private bool _Paused = false;
private void OnPauseClick()
{
_Paused = true;
}
private void OnResumeClick()
{
_Paused = false;
}
private void OnRunClick()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(WorkerMethod));
}
private void WorkerMethod(object state)
{
...
while (needToDoMoreWork)
{
// do some work here
...
// if pause requested, wait to unpause
while (_Paused)
{
// Sleep for a short time and check again if paused
Thread.Sleep(100);
}
}
}
You'll need to fill in the blanks according to your business needs.
If you put your UI thread to indefinite sleep, you're going to kill your UI indefinitely. Don't do that.
Your application has some mechanism to know it's time to do its navigation and processing, probably a timer of some sort. If it's a timer, just stop it when you pause the application, and start it again when you resume. If it's some other mechanism, you need to stop it,too, but it's hard to tell you how without knowing what that mechanism is.

I can only close a form once, InvalidOperation Exception Invoke or BeginInvoke cannot be called on a control until the window handle has been created

Hi
I'm opening a form like this from my main form when the user makes a selection of a menu item.
private void commToolStripMenuItem_Click(object sender, EventArgs e)
{
Command_Form Command_Form1 = new Command_Form();
Command_Form1.ShowDialog();
// Command_Form1.Dispose(); this didn't help
}
Inside the form "Command_Form1"
I close it like this when the user clicks on the close button
private void Close_button_Click(object sender, EventArgs e)
{
this.Close(); //I get the exception here
}
This process works fine once but on the second closing of the form
(Which I hope is a completely different/new instance of the form) I get the error in the title of this post.
This is the output in the debug window.
A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
All the topics that list this error go on about not trying to do anything to a form that has not been displayed but this happens when I click on a button in the form.
It would seem to me that pretty much ensures the form has been displayed if I'm able to click its button.
The other posts I've found that list this type of error go on about making thread safe calls so I tried this as an experiment but it didn't make any difference.
private void Close_button_Click(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
CloseCallback d = new CloseCallback(Close_button_Click);
Invoke(d, new object[] { sender, e });
}
else
{
this.Close();
I have multiple threads in my application but they are created by the controls I'm using not by me explicitly.
I am passing data from a serial port to/from the form by Marshling the received/sent data via a delegate[s].
It makes sense that the serial port would run on a different thread than the form but why would a button click on a form be in a diffrent thread than the form????
The whole thread thing is very confuzing
How do I figure out what threads originated where and what is going on in the threads that I didn't create explicitly?
Why would I need to invoke the form's close method via a delegate?
Heck is there anything I can do in this multi threading environment that is thread safe How in do I know if what I'm doing is unsafe/safe if I don't know what/where/why/who/when is creating threads?
My guess is your close() call is not throwing that exception, but something that happens after close(). Have you stepped into the code with the debugger to see when it is fired?
As to when you need to invoke...There is only one thread allowed to make changes and access dynamic properties on the GUI, call it the GUI thread. The GUI thread is responsible for updating layout, firing events like buttons, etc. If you ever access the GUI from another thread (like a timer event) you need to use invoke() to queue your function to be run on the GUI thread. BeginInvoke will also queue the function but is asynchronous (will only queue the function to be run on GUI thread, but will not wait for it to finish).
Close_button_click will only be called by your gui thread when the button click event fires(unless you explicitly call it somewhere else in your code behind, not recommended!), so invokeRequired=false in your code above, and the invoke code is never executed.

How to improve UI in .Net Compact Framework, C#?

I have some WinForms in my application.
On some form , I am loading other forms which have lot of data to show.
So when I click a button to load these forms they take, 9-10 seconds to load,
I wana improve this. I have heard about Background thread but don't know how to use this.
Means on the button click what should i do so that I can see my form imidiately and the processing is done by background thread ?
Please guide me so that I can improve my UI.
Getting the form to show immediately, should be fairly simple and you don't need a background thread to do it.
My guess is that you are retrieving all that data prior to loading the form or you are retrieving the data on the same thread that loads the form. You should be able to call the form with the data it needs to retrieve the rest of the data (such as ID's).
Then you just need to find a way for the data processing to happen on a thread other than the thread that creates the form.
This may not be the most elegant solution, but it gets the job done. It uses a System.Windows.Forms.Timer
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
//fill controls with data
}
If you have several sets of data that you are retrieving, you may want to consider setting up a queue of tasks to retrieve each set.
Any way that you do it. Make sure to disable any buttons(or other ui) that could cause errors if the data is not loaded.
Edit: It should be noted that winform controls don't always play nice with threads. The nice thing about the System.Windows.Forms.Timer (as opposed to System.Timers.Timer)is that it designed to play nice with controls.
BackgroundWorker are easy to use. Mainly you subscribe to DoWork event, this is where you'd put your work that will be done in a seperate thread. And you also subscribe to RunWorkerCompleted event. This will get raised when your DoWork has completed.
In this method you'd simply bind the data fetched by the DoWork method.
You can use the DoWork eventarg to pass data between DoWork and RunWorkerCompleted.
You can also cancel the work and also the you can use the RunWorkerCompleted eventArg to get the data from the DoWork and also inspect the Error property for exception handling.
IF you have lots of data, you can use a batching process, to get data per batch and display them into your UI. You can show how many total records will be fetch and the progress of it 100 out of 1000 have been loaded, 200 out of 1000 have been loaded etc, to keep the user informed. You could also provide a Cancel option so the user can stop the fetch.
In your button click you'd simply call myBackgroundWorker.RunWorkerAsync().
this will call your DoWork Handler which will be done in a seperate thread not the main UI Thread, so your UI will not FREEZE during the background worker DoWork.
The backgroundworker also support report progress.
If your problem is the main form Freezing, what you need is to collect the data on a separated Thread than the Main form is running.
To achieve that, you can do something like this:
BackgroundWorker worker;
public Form1()
{
InitializeComponent();
worker = new BackgroundWorker();
}
private void button1_Click(object sender, EventArgs e)
{
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//Collect data and open your second form here;
}

Categories

Resources