My form does not display properly when it starts from another thread

Here's the situation: I am developing a simple application with the following structure:

  • FormMain (launch point)
  • FormNotification
  • CompleFunctions

Right?

Well, in FormMain I have the following function:

private void DoItInNewThread(ParameterizedThreadStart pParameterizedThreadStart, object pParameters, ThreadPriority pThreadPriority) { Thread oThread = new Thread(pParameterizedThreadStart); oThread.CurrentUICulture = Settings.Instance.Language; oThread.IsBackground = true; oThread.Priority = pThreadPriority; oThread.Name = "μRemote: Background operation"; oThread.Start(pParameters); } 

So, every time I need to call a time method located on ComplexFunctions , I do the following:

 // This is FormMain.cs string strSomeParameter = "lala"; DoItInNewThread(new ParameterizedThreadStart(ComplexFunctions.DoSomething), strSomeParameter, ThreadPriority.Normal); 

Another class, FormNotification, is its Form, which displays some process information for the user. This FormNotification can be called from FormMain or ComplexFunctions. Example:

 // This is ComplexFunctions.cs public void DoSomething(string pSomeParameter) { // Imagine some time consuming task FormNotification formNotif = new FormNotification(); formNotif.Notify(); } 

FormNotify has a timer, so after 10 seconds it closes the form. I do not use formNotif.ShowDialog because I do not want to focus on this form. You can check this link to find out what I'm doing in Notify.

Ok, here is the problem: When I call FormNotify from ComplexFunction , which is called from another thread in FormMain ... this FormNotify disappears after a few milliseconds. This is the same as when you do something like this:

 using(FormSomething formSomething = new FormSomething) { formSomething.Show(); } 

How to avoid this?

These are possible solutions that I do not want to use:

  • Using Thread.Sleep (10000) in FormNotify
  • Using FormNotif.ShowDialog ()

This is a simplified scenario (FormNotify does some other fancy stuff that just stays on for 10 seconds, but they are not related to the problem).

Thank you for your time!!! And please excuse my English.

+4
source share
5 answers

Almost every GUI library is only for permissions that change the graphical user interface that must be created in a single thread dedicated to this purpose (called a user interface thread). If you are in another thread, you need to organize a call to change the graphical interface that will be created in the user interface thread. In .NET, the way to do this is to invoke Invoke (synchronous) or BeginInvoke (asynchronous). An equivalent Java Swing call is invokeLater () - almost every GUI library has similar functions.

There is something called thread bonding. There are two threads in a WinForm application: one for rendering and one for managing the user interface. You only deal with the user interface thread. The rendering stream remains hidden - runs in the background. The only objects created in the user interface thread can manipulate the user interface — that is, the objects are similar in streams to the user interface thread.

Since you are trying to update the interface (show notification) from a thread other than the UI thread. Therefore, in the workflow, define a delegate and ask FormMain to listen for this event. In the event handler (define in FormMain) write code to show FormNotify.

Fire events from the workflow when you want to show a notification.

When a thread, other than the control creating the thread, tries to access one of these methods or control properties, it often leads to unpredictable results. A common invalid thread is to call the wrong thread that accesses the Handle control. Set CheckForIllegalCrossThreadCalls to true to make it easier to find and diagnose this thread activity during debugging. Note that illegal cross-thread calls will always throw an exception when an application starts outside the debugger.

Note. Installing CheckForIllegalCrossThreadCalls should only be done in DEBUGGIN POSITIONS. Unpredictable results will occur, and you will complete the attempt to pursue errors that you will have difficulty detecting.

+5
source

You cannot make WinForms calls from other threads. Look at BeginInvoke in the form - you can call a delegate to show the form from the user interface stream.

Edit: from comments (do not set CheckForIllegalCrossThreadCalls to false).

Additional Information Almost every GUI library is designed to allow calls that change the graphical user interface that must be created in a single thread dedicated to this purpose (called a user interface thread). If you are in another thread, you need to organize a call to change the graphical interface that will be created in the user interface thread. In .NET, the way to do this is to invoke Invoke (synchronous) or BeginInvoke (asynchronous). An equivalent Java Swing call is invokeLater () - almost every GUI library has similar functions.

+5
source

There is something called thread bonding. There are two threads in a WinForm application: one for rendering and one for managing the user interface. You only deal with the user interface thread. The rendering stream remains hidden - runs in the background. The only objects created in the user interface thread can manipulate the user interface — that is, the objects are similar in streams to the user interface thread.

Since you are trying to update the interface (show notification) from a thread other than the UI thread. Therefore, in the workflow, define a delegate and ask FormMain to listen for this event. In the event handler (define in FormMain) write code to show FormNotify.

Fire events from the workflow when you want to show a notification.

+1
source

Use the SetWindowPos API to make sure your notification form is the topmost window. This post explains how to:

http://www.pinvoke.net/default.aspx/user32/SetWindowPos.html

0
source

Assuming you have a button in the form and want to open another Form1 when the user clicks this button

 private void button1_Click(object sender, EventArgs e) { Thread t = new Thread(new ThreadStart(this.ShowForm1)); t.Start(); } 

All you have to do is check the InvokeRequired property and, if so, call the Invoke method of your form, passing in the ShowForm1 delegate, which ultimately leads to a recursive call where InvokeRequired will be false

 delegate void Func(); private void ShowForm1() { if (this.InvokeRequired) { Func f = new Func(ShowForm1); this.Invoke(f); } else { Form1 form1 = new Form1(); form1.Show(); } } 
0
source

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


All Articles