How to abort thread invoked ShowDialog - c#

Have C# Windows Forms application with secondary thread that receives requests from an external system to show or hide a form/dialog. I understand that secondary threads do not have a message loop mechanism. I understand that ShowDialog has its own message loop, thus a secondary thread can invoke it, but the secondary thread is then blocked until the form closes, and thus cannot respond to later requests to hide the form. The problem is, how to get the form displayed by the secondary thread to hide [or again become visible]. Tried invoking Interrupt on secondary thread, but that does not interrupt or abort the ShowDialog. Nothing appears to abort ShowDialog except a ShowDialog UI callback that calls Close.

Actually both forms share the same message loop.
Every code that handles gui must be ran on the same thread (which handles the gui part).
What you need is to run the commands on that thread using BeginInvoke.
I made a sample application which only has a simple button and when you press it a thread is started which sleeps 3 seconds and then opens the dialog and sleeps again and next time it closes it down and so forth.
Here is the code for the main window:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Thread t;
Form2 f2;
private void button1_Click(object sender, EventArgs e)
{
t = new Thread(ThreadMethod);
t.Start();
button1.Enabled = false;
}
private void ShowForm()
{
f2 = new Form2();
f2.ShowDialog();
}
private void ThreadMethod()
{
for (; ; )
{
Thread.Sleep(3000);
if(f2 == null)
{
BeginInvoke((Action)(() => { ShowForm(); }));
}
else
{
f2.CloseMe();
f2 = null;
}
}
}
}
And then the code for the form used as a dialog:
using System;
using System.Windows.Forms;
namespace QuestionTesting
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public void CloseMe()
{
BeginInvoke((Action)(() => { Close(); }));
}
}
}
This is just simple code and adapt it as you see fit. Instead of the one liners you can create a delegate instead and use that in the BeginInvoke call.

Related

How to invoke UI thread in Winform application without a form or control?

I have created a tray application for controlling some hardware components. How can I invoke the UI thread without a main form or control?
The tray app is started with Application.Run(new MyTrayApp()):
class MyTrayApp : ApplicationContext
{
private NotifyIcon trayIcon;
public MyTrayApp()
{
trayIcon = new NotifyIcon()
{
Icon = Resources.app_icon,
ContextMenu = new ContextMenu(new MenuItem[] {
new MenuItem("Exit", Exit)
}),
Visible = true
};
// context is still null here
var context = SynchronizationContext.Current;
// but I want to invoke UI thread in hardware events
MyHardWareController controller= new MyHardWareController(context);
}
void Exit(object sender, EventArgs e)
{
// context is accessible here because this is a UI event
// too late tho
var context = SynchronizationContext.Current;
trayIcon.Visible = false;
Application.Exit();
}
}
Control.Invoke() is not available as there are no controls
Searching suggests that SynchronizationContext.Current should be saved for later invoke but there is no ApplicationContext.Load() event...?
I've noticed that MainForm is null in the whole cycle. I wonder how does SynchronizationContext initialized in this case?
Edit:
Just to add some background info on why I would like to invoke UI thread. It is because System.Threading.ThreadStateException will be thrown when attempt to access Windows resources such as Clipboard or SendKeys in another thread:
HResult=0x80131520
Message=Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.
Source=System.Windows.Forms
StackTrace:
...
It's another can of worms but just for information:
[STAThreadAttribute] is already set for Main function (no effect)
Creating a new STA thread would result in anti-virus deleting my application upon compile
Thus Form.Invoke() or the equivalent to invoke main thread should be the easiest.
Edit 2:
Add a gist for reproducing the error:
https://gist.github.com/jki21/eb950df7b88c06cc5c6d46f105335bbf
Solved it with Application.Idle as mentioned by Loathing! Thanks everyone for your advice!
TrayApp:
class MyTrayApp: ApplicationContext {
private MyHardwareController controller = null;
public MyTrayApp() {
Application.Idle += new EventHandler(this.OnApplicationIdle);
// ...
}
private void OnApplicationIdle(object sender, EventArgs e) {
// prevent duplicate initialization on each Idle event
if (controller == null) {
var context = TaskScheduler.FromCurrentSynchronizationContext();
controller = new MyHardwareController((f) => {
Task.Factory.StartNew(
() => {
f();
},
CancellationToken.None,
TaskCreationOptions.None,
context);
});
}
}
// ...
}
MyHardwareController:
class MyHardwareController {
private Action < Action > UIInvoke;
public MyHardwareController(Action < Action > UIInvokeRef) {
UIInvoke = UIInvokeRef;
}
void hardware_Event(object sender, EventArgs e) {
// Invoke UI thread
UIInvoke(() => Clipboard.SetText("I am in UI thread!"));
}
}
An alternative solution would be to create a dummy form (which will never be shown, but should be stored somewhere. You just have to access the Handle property of the Form to be able to invoke it from now on.
public static DummyForm Form { get; private set; }
static void Main(string[] args)
{
Form = new DummyForm();
_ = Form.Handle;
Application.Run();
}
Now it is possible to invoke into the UI thread:
Form.Invoke((Action)(() => ...);

Thread updating GUI freezes in random moments

I have form with button and text box. Button is starting thread which is updating value of text box.
public Form1()
{
InitializeComponent();
myDelegate = new UpdateUi(updateUi);
}
private void button1_Click(object sender, EventArgs e)
{
myThread = new Thread(new ThreadStart(ThreadFunction));
myThread.Start();
}
private void ThreadFunction()
{
MyThreadClass myThreadClassObject = new MyThreadClass(this);
myThreadClassObject.Run();
}
private void updateUi(int i)
{
textBox1.Text = i.ToString();
Thread.Sleep(1000);
}
public Thread myThread;
public delegate void UpdateUi(int i);
public UpdateUi myDelegate;
and ThreadClass:
public class MyThreadClass
{
Form1 myFormControl1;
public MyThreadClass(Form1 myForm)
{
myFormControl1 = myForm;
}
public void Run()
{
// Execute the specified delegate on the thread that owns
// 'myFormControl1' control's underlying window handle.
for(int i=0;i<100;i++)
{
if(myFormControl1.InvokeRequired)
{
myFormControl1.Invoke(myFormControl1.myDelegate,i);
}
}
}
}
As You can see there is nothing special in my code but sometimes the code freeze.
eg it goes 1->2->3->freeze->16->17 and so on.
I took code from HERE with little modifications
The issue is you are delaying the UI thread not the the process itself so what happens is you issue all the update commands but since it all runs on the same thread it gets clogged because the Thread.Sleep stops the UI thread so it runs a bunch of textBox1.Text = i.ToString(); then it stops for all the time of all the Thread.Sleep(1000); probably the number of 1->2->3... you see is equal to the number of cores in your machine.
When you stop the run method what happens is you issue one update command that runs immediately and wait for one second until you issue the next command witch I think its what you are trying to accomplish.

Closing a form that is created in another thread

I have been searching for an answer to my particular problem for a while with no success.
I have a task in my program that takes a few seconds and I want to show a new form while that task is being done. The new form has a loadingbar and some text.
I need to show the new form parallel to the task otherwise the task will not start untill I close the new form.
This is the solution I have now:
private void loadingBar()
{
frmLoading frm = new frmLoading("Please wait while the database is being backed up", "This might take several days.");
frm.ShowDialog();
}
public void Backup()
{
Thread load = new Thread(new ThreadStart(loadingBar));
load.Start();
///Execute a task.
load.Abort();
}
So, this works OK but my question is: Wouldn't it be better to close the the form "frm" in the load-thread to make it stop?
You could do this a few ways...
1 - You could do as BendEg suggested and invoke you frmClose once you are ready
Something like;
Invoke(new Action(Close));
or
Invoke(new Action(() => frmMain.Close()));
2 - Or you could simply use a background worker;
The simplest way to demonstrate this would be to add a BackgroundWorker to your form, and use the events provided;
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
MessageBox.Show(#"Please wait while the database is being backed up", #"This might take several days.");
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Debug.WriteLine("Running"); //Execute a task
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Debug.WriteLine("Ended"); //Dispose of any objects you'd like (close yor form etc.)
}
I hope this helps.
You can declare the form on Class-Level and later close it with an invoke.
MSDN-Windows Forms Invoke
Like this:
public class Class1
{
private Form myForm;
public Class1()
{
myForm = new Form();
}
public void DoSomeWork()
{
// ===================================================
// Do Some Work...
// ===================================================
myForm.Invoke(new MethodInvoker(this.Hide));
}
public void Hide()
{
myForm.Hide();
}
public void Backup()
{
myForm.ShowDialog();
Thread load = new Thread(new ThreadStart(DoSomeWork));
load.Start();
}
}
I think this can work for you.
void YourMethod()
{
WaitForm wf = new WaitForm();
Invoke(new PleaseWaitDelegate(Launch),wf);
bool val = BoolMethodDoWork();
Invoke(new PleaseWaitDelegate(Close), wf);
if(val)
{
MessageBox.Show("Success!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
return;
}
MessageBox.Show("Damn!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
delegate void PleaseWaitDelegate(Form form);
void Launch(Form form)
{
new System.Threading.Thread(()=> form. ShowDialog()).Start();
}
void Close(Form form)
{
form.Close();
}
I think this will help you (if i understood you right):
Parallel.Invoke(() => somemethod(), () =>
{
someothertaskmethod();
});
I placed methods as example to demonstrate 2 tasks running.
You nee to use the proper using statement using System.Threading.Tasks;

TPL Equivalent of Thread Class 'Splash-Type' Screen

Given the class below, to launch a splash screen on an alternate thread:
public partial class SplashForm : Form
{
private static Thread _splashThread;
private static SplashForm _splashForm;
public SplashForm()
{
InitializeComponent();
}
// Show the Splash Screen (Loading...)
public static void ShowSplash()
{
if (_splashThread == null)
{
// Show the form in a new thread.
_splashThread = new Thread(new ThreadStart(DoShowSplash));
_splashThread.IsBackground = true;
_splashThread.Start();
}
}
// Called by the thread.
private static void DoShowSplash()
{
if (_splashForm == null)
_splashForm = new SplashForm();
// Create a new message pump on this thread (started from ShowSplash).
Application.Run(_splashForm);
}
// Close the splash (Loading...) screen.
public static void CloseSplash()
{
// Need to call on the thread that launched this splash.
if (_splashForm.InvokeRequired)
_splashForm.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
}
This is called and closed with the following respective commands
SplashForm.ShowSplash();
SplashForm.CloseSplash();
Fine.
I am not exactly new to the TPL, of course we can show the form on another thread using something as simple as:
Task task = Task.Factory.StartNew(() =>
{
SomeForm someForm = new SomeForm();
someForm.ShowDialog();
};
My issue is closing this SomeForm down when you are ready. There must be a better way than creating a public static method in the SomeForm class like
private static SomeForm _someForm;
public static void CloseSomeForm()
{
if (_someForm.InvokeRequired)
_someForm.Invoke(new MethodInvoker(CloseSomeForm));
}
My question is, what is the best way to do the same thing as shown using the SplashForm class above using the Task Parrallel Library (TPL)? Specifically, the best way to close the form invoked on another thread from the UI.
Your question does not seem to be so much about a difference between Thread and Task because what you want is to get rid of the "dirty" static state. I suggest you encapsulate it into a class:
class SplashController
{
public void Run() {
_someForm = new SomeForm();
someForm.ShowDialog();
}
private SomeForm _someForm;
public void CloseSomeForm()
{
if (_someForm.InvokeRequired)
_someForm.Invoke(new MethodInvoker(CloseSomeForm));
}
}
You can call Run using whatever threading mechanism you like. CloseSomeForm does not use threading so it is independent of this problem.
You can now store a reference to an instance of SplashController wherever you like. In local variables or indeed in a static variable. The latter makes sense because there is exactly one splash screen.
Because the static state is now well encapsulated I don't see any problem with it being statically held.
You probably shouldn't do something like this
Task task = Task.Factory.StartNew(() =>
{
SomeForm someForm = new SomeForm();
someForm.ShowDialog();
};
because it would require a message loop to be present on the exact thread that creates the Form, which is a ThreadPool thread. But I haven't tested this.
You could try this:
public static Task<SplashForm> ShowSplash()
{
var tcs = new TaskCompletionSource<SplashForm>();
// Show the form in a new thread.
_splashThread = new Thread(() =>
{
var splashForm = new SplashForm();
tcs.SetResult(_splashForm);
// Create a new message pump on this thread (started from ShowSplash).
Application.Run(splashForm);
});
_splashThread.IsBackground = true;
_splashThread.Start();
}
this would allow you to remove the static modifier from CloseSplash:
// Close the splash (Loading...) screen.
public void CloseSplash()
{
// Need to call on the thread that launched this splash.
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
May be used like this:
var form = await SplashForm.ShowSplash();
form.CloseSplash();

Show form in main thread from another thread

I developing multithreading application with main form and another form in which progress is shown.
At first: I create ProgressForm in MainForm
Progress p=new Progress();
Second: I create new instance of class Model (whith all data in my app).
Model m = new Model();
And subscribe for event:
m.OperationStarted += new EventHandler(OnCopyStarted);
private void OnCopyStarted(object sender, EventArgs e)
{
p.Show();
}
Third: I run some operation in another thread where I change property in another Model
private bool isStarted;
public bool IsStarted
{
get{return isStarted;}
set
{
isStarted = value;
if (isStarted && OperationStarted != null)
{
OperationStarted(this, EventArgs.Empty);
}
}
}
My questoin is: Why Progress form is show not in Main Thread? How can I run it without lockups?
All UI operations must run on the main UI thread.
The OnCopyStarted method is being called on another thread, so it must switch to the UI thread before before showing the dialog.
You can use your form's BeginInvoke to switch to the UI thread. Such as:
void OnCopyStarted(object sender, EventArgs e)
{
p.BeginInvoke((Action) (() => p.Show()));
}
Try it :
var t = new Thread(() => {
Application.Run(new Progress ());
});
t.Start();

Categories