Threading with WinForms?

In my application, I run a hidden dummyForm that is created only to track the main UI thread. So, if a new form is created, then InvokeRequired used in a fictitious form to make sure that we are in the main stream of the user interface when creating a new form.

Immediately after creating an instance of the frmStart form frmStart I check frmStart.InvokeRequired and it is set to false, so there is no need to reference it here (the same for dummyForm.InvokeRequired ).

Then I got frmMyDialog , which will use frmStart as the parent / owner like this:

 using(Create frmMyDialog on main UI thread) { frmMyDialog.Show(frmStart); } 

This will throw a cross-thread exception, and it is strange that here:

 frmMyDialog.InvokeRequired = false dummyForm.InvokeRequired = false frmStart.InvokeRequired = true 

And is this even when I verify that dummyForm.InvokeRequired is false when creating frmStart ?

frmMyDialog.InvokeRequired always match dummyForm.InvokeRequired ? What's going on here?

I checked that frmStart and dummyForm not created at all after creating the first instance.

Edit1:

So the application begins:

 public static void Main(string[] args) { _instance = new MyClientMain(parameters); Application.Run(_instance); } 

The constructor of the MyClientMain class will run the installer in the static MainControl class. MainControler will be in the instanciate dummyform installation method as follows:

 if (_dummyForm == null) _dummyForm = new Form(); 

After that, the login form will process the login, and this form will be multithreaded. When the login is complete, the MainController will be called again to initiate and open the main MDI windmo that contains frmStart. To make sure that we are in the same thread, do the following:

 public static StartApplication() { if (_dummyForm.InvokeRequired) _dummyForm.Invoke(new MethodInvoker(delegate { OpenMainOrbitWindow(); })); //Instanciate mainform and frmStart then open mainForm with frmStart as a MDI child } 

There is no multithreading.

Then, when the service goes to the autonomous system, an event will be triggered, and I will need to pop up frmMyDialog, but when using .ShowDialog () the dialog will be placed behind the forms, so the main or main object will be found and installed as follows:

 public static Form GetActiveForm() { Form activeForm = Form.ActiveForm; if (activeForm != null) return activeForm; if (MainOrbitForm.TopMost) return MainOrbitForm; else { FormCollection openForms = Application.OpenForms; for (int i = 0; i < openForms.Count && activeForm == null; ++i) { Form openForm = openForms[i]; if (openForm.IsMdiContainer) return openForm.ActiveMdiChild; } } if (_patientForm != null) { if (_patientForm.TopMost) return _patientForm; } return null; } public static string ShowOrbitDialogReName() { frmMyDialog myDialog; Form testForm; //Makes sures that the frmOrbitDialog is created with the same thread as the dummyForm //InvokeRequired is used for this using (myDialog = MainController.CreateForm<frmOrbitDialog>()) { //Settings... testForm = GetActiveForm(); myDialog.ShowDialog(GetActiveForm(testForm)); } } 

The problem is

 myDialog.InvokeRequired = false testForm.InvokeRequired = true; MainController.DummyForm.InvokeRequired = false; 

Edit2: Run and create a dummy form:

 dummyForm.InvokeRequired = false Thread.CurrentThread.ManagedThreadId = 9 

After successful login, we create the main form

 _mainForm.InvokeRequired = false MainControl.DummyForm.InvokeRequired = false Thread.CurrentThread.ManagedThreadId = 9 

Everything looks good. Then a callback (WCF) is received, and the event creates frmMyDialog in the same thread (Invoke is used in dummyForm), and then ShowDialog is used:

 frmMyCustomDialog.ShowDialog(_mainForm) 

This raises a CrossThreadException, and here is what it looks like at the moment:

 _mainForm.InvokeRequired = true frmMyCustomDialog.InvokeRequired = false MainControl.DummyForm.InvokeRequired = false Thread.CurrentThread.ManagedThreadId = 12 

Why is MainControl.DummyForm not true? ManageThreadId is not 9, but 12?

+4
source share
3 answers

You should use System.Threading.SynchronizationContext.Current . It was created directly for such purposes. You can access it anywhere after the first form of your application has been created. Judging by your example below, this should not be a problem since you are creating a form to the right of the start of the application.

 public static void Main(string[] args) { _instance = new MyClientMain(parameters); Application.Run(_instance); } 

Then anywhere, you need code to execute in the user interface thread, you just use

 System.Threading.SynchronizationContext.Current.Send() // To execute your code synchronously System.Threading.SynchronizationContext.Current.Post() // To execute your code synchronously 

Remember that the SynchronizationContext is smart enough to see that you are already calling it from the user interface thread, and then it will just execute your delegate directly. Also, remember that you need to create a form or WinForms element before using the SynchronizationContext for the first time, because when you do this, the context will be initialized for the appropriate implementation.

There are 3 implementations: by default, it does nothing - just always run the code in synchronization, it remains in Current until you create a WinForms or WPF control. Then Current will be populated with either a context for Winforms or a context for WPF Manager.

+2
source

It’s just in my head, but, as Vladimir Perevalov said in another discussion, you must make your form visible.

If your frmDummy is never displayed, then it never creates or assigns a window handle, and therefore it will always reply False to "InvokeRequired". This would mean that all the code that you want to synchronize through frmDummy is never sent to the original UI thread, but always runs in the current thread. (which becomes the native UI thread for the control that he just created).

It is important to note that InvokeRequired is trying to determine if the control window handle is in a different thread. This has nothing to do with the constructor.

If you do not want to show frmDummy, you can call CreateControl immediately after creating it to make sure that it has a handle.

+1
source

I did not quite understand your question, because your examples do not really show anything about multithreading - however, if you want to create a form where the parent is a different form from another thread, you can use this code:

 public void CreateShowDialogForm() { if (this.InvokeRequired) { this.Invoke(new Action(CreateShowDialogForm)); } else { Form frmMyDialog = new Form(); frmMyDialog.Show(this); } } private void Form4_Load(object sender, EventArgs e) { Task t = new Task(() => CreateShowDialogForm()); t.Start(); t.ContinueWith(task => true); } 
0
source

Source: https://habr.com/ru/post/1383037/


All Articles