As my English is not well, I explain my question simple and paste a code snippet here to describe the problem.
The problem is a multiple threading issue in our winForm application. I simple the logic as following code sample.
In the test code, there are 1 mainForm Form1 and a button named "Start" in the mainForm. When user click the button, two forms form2 and form3 will be shown from 2 background threads. After form2 was closed, the Form1 will be triggered to close. But form3 is shown here, so I need user to close form3 by himself. So I handled form.Closing event and use Application.DoEvents() to let user close form3. It looks work in mind. But actually, the form3 can accept user's actions but form3 will not be closed as expected.
Please explain why form3 cannot be closed here and how to modify the code to make user's close operation work.
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace CloseFormFromMainThread
{
public partial class Form1 : Form
{
private Form2 _form2;
private Form2 _form3;
private SynchronizationContext _synchronizationContext;
public Form1()
{
InitializeComponent();
Closing += Form1Closing;
}
void Form1Closing(object sender, CancelEventArgs e)
{
while (_form3 != null)
{
Application.DoEvents();
Thread.Sleep(100);
}
}
private void ButtonStartClick(object sender, EventArgs e)
{
var thread = new Thread(StartForm3);
thread.Start();
var thread2 = new Thread(StartForm2);
thread2.Start();
}
private void StartForm3()
{
Thread.Sleep(200);
var action = new Action(() =>
{
_form3 = new Form2();
_form3.Text = "form 3";
_form3.ShowDialog();
_form3 = null;
});
ExecuteActionInUiThread(action);
}
private void Form1Load(object sender, EventArgs e)
{
_synchronizationContext = SynchronizationContext.Current;
}
private void StartForm2()
{
Thread.Sleep(500);
var action = new Action(() =>
{
_form2 = new Form2();
_form2.Text = "form 2";
_form2.ShowDialog();
Close();
});
ExecuteActionInUiThread(action);
}
private void ExecuteActionInUiThread(Action action)
{
var sendOrPostCallback = new SendOrPostCallback(o => action());
_synchronizationContext.Send(sendOrPostCallback, null);
}
}
}