C # GUI thread error

I am developing an application that needs to receive commands through a socket interface and then execute them in a GUI. This application is being developed in C # .NET 4.0 and uses WPF for its GUI.

The socket interface has a worker thread that continues to listen to the socket and process its commands, so if, for example, a Show Popup command is received, the worker thread calls the manager class that is responsible for creating the popup and shows it on the main screen.

The manager method that creates the popup and then calls the main screen is as follows:

public void ProcessPopup(PopupModel model)
{
    switch (model.ScreenType)
    {
        case Screens.Type1:
            popup = new PopupType1();
            break;
        case Screens.Type2:
            popup = new PopupType2();
            break;
        case Screens.Type3:
            popup = new PopupType3();
            break;
        case Screens.Type4:
            popup = new PopupType4();
            break;
    }

    viewModel.SetModel(model);

    if (!Dispatcher.CurrentDispatcher.Equals(App.Current.Dispatcher))
    {
        App.Current.Dispatcher.Invoke((ThreadStart)delegate { mainScreen.ShowPopup(popup); });
    }
    else
    {
        mainScreen.ShowPopup(popup);
    }
}

      

PopupType1 class:

public partial class PopupType1 : UserControl
{
    public PopupType1 ()
    {
        InitializeComponent();
    }
}

      

The problem is that when I break the new PopupType1 object, I get the following exception:

System.InvalidOperationException: The calling thread must be STA, because many UI components require this.
   at System.Windows.Input.InputManager..ctor()
   at System.Windows.Input.InputManager.GetCurrentInputManagerImpl()
   at System.Windows.Input.InputManager.get_Current()
   at System.Windows.Input.KeyboardNavigation..ctor()
   at System.Windows.FrameworkElement.FrameworkServices..ctor()
   at System.Windows.FrameworkElement.EnsureFrameworkServices()
   at System.Windows.FrameworkElement..ctor()
   at System.Windows.Controls.Control..ctor()
   at System.Windows.Controls.UserControl..ctor()
   at MyApp.Views.PopupType1..ctor()
   at MyApp.Manager.ProcessPopup(PopupModel model)
   at MyApp.CommunicationController.ProcessAsync(XDocument messageXml)

      

I've tried a few things like converting my workflow to an STA thread or creating a new STA thread just to handle the creation of the Popup, but they caused more problems than they solved.

Finally, it is important to note that I am doing this because my application experiences several freezes while it is running, and I believe they are due to the WPF GUI thread being too overloaded with tasks, so I am trying to separate the non-GUI processing from GUI thread.

+3


source to share


5 answers


You also need to create a UI control on the UI thread. So basically everything ProcessPopup

should be done on the UI thread in your case, not just mainScreen.ShowPopup

()



+3


source


You are creating a popup on a background thread.
It won't work; you can create control over the UI thread.



You have to decouple expensive (slow) logic from the UI classes and only do it on a background thread.

+2


source


If the PopupType contains any controls (and looks like it does), it should be created on the main GUI thread.

+1


source


By design, only the thread that created the UI object (main) can access the UI object. From your description "socket interface has a worker thread" so the interface does not have access to the user interface. The callback from the background worker is where you access the user interface. Since you want the listener hot I think "progress" might work better for UI access.

0


source


You have to create a UI element on the UI thread. You can do the following, for example:

 private static UserControl CreatePopup(PopupModel model){
    switch (model.ScreenType)
    {
        case Screens.Type1:
            popup = new PopupType1();
            break;
        case Screens.Type2:
            popup = new PopupType2();
            break;
        case Screens.Type3:
            popup = new PopupType3();
            break;
        case Screens.Type4:
            popup = new PopupType4();
            break;
    }
 }

      

And change ProcessPopup

to:

public void ProcessPopup(PopupModel model)
{
    viewModel.SetModel(model);
    App.Current.Dispatcher.BeginInvoke(
              new Action(()=>{
                             mainScreen.ShowPopup(CreatePopup(model)); 
                             }
              ));
}

      

Also notice the use of BeginInvoke instead of Invoke. This is required to update your UIs asynchronously so you don't have to block the worker thread on some UI stuff.

0


source







All Articles