3

I have a class, which should show some messages to the user, when the status of some operation is changed, like this:

public static class AutoUpdater
    {
        public static async void AutoUpdateCheck()
        {
            UpdaterStatus.CurrentUpdateStatus = await UpdaterLogic.CheckForUpdateAsync();
        }

        public static void OnStatusChanged()
        {
                switch (UpdaterStatus.CurrentStatus)
                {
                    case UpdateStatus.UpdateFound:
                        {
                            Message ToAdd = new Message("some params"); //Exception here
                            MessagesManager.AddNewMessage(ToAdd);
                            break;
                        }
                    //some other cases
                }
        }

When app starts, i subscribe AutoUpdater to an event like this:

UpdaterStatus.EventStatusChanged += (sender, args) => { AutoUpdater.OnStatusChanged(); };

The exception I get is: "The calling thread must be STA, because many UI components require this".

However, I can't create the STA thread by myself, and then add newly created message to its parent control, because this way I get an exception, saying that "that object belongs to another thread".

Is there any workaround?

Ilya Bezus
  • 140
  • 10
  • 3
    Do not create UI elements on a background thread. Create a view model for your messages and use Data Templates to visualize them, e.g. in an ItemsControl. – Clemens Sep 07 '16 at 11:27
  • Controls should only be accessed from the thread on which they are created. Also they should be created and accessed on UI thread. If you want to update them from another thread, then you should use Dispatcher. Refer answer provided by user 'tym32167'. – Kamalesh Wankhede Sep 07 '16 at 11:31

2 Answers2

4

You should to create UI controls from UI thread. You can try to use dispatcher here

Application.Current.Dispatcher.Invoke(/* your action here*/ () => {/* creating UI controls */});
tym32167
  • 4,741
  • 2
  • 28
  • 32
  • The thing i'm trying to do is inside WinForms project, while also using WPF control (Message class). There is no such thing as `Application.Current`, however, what i tried to do is: `Dispatcher.CurrentDispatcher.Invoke(new Action(() => MessagesManager.AddNewMessage(new Message("some params"))));` And the exception is still the same, am I doing something wrong? – Ilya Bezus Sep 07 '16 at 11:38
  • You need to find Dispatcher of UI thread. When you try to call ```Dispatcher.CurrentDispatcher``` - you get Dispatcher for current thread, but current thread is not UI, thats why this doesnt solve you problem. – tym32167 Sep 07 '16 at 11:41
  • about Application.Current, you can try this http://stackoverflow.com/questions/35902815/why-does-application-current-null-in-a-winforms-application Hope it helps. – tym32167 Sep 07 '16 at 11:44
  • Do avoid depending on the specific GUI class library, SynchronizationContext.Current is the appropriate solution. Copy its value in code that is going to run on the UI thread, the constructor is best. – Hans Passant Sep 07 '16 at 11:52
  • Man, it really helped, I just needed to use what you provided (`System.Windows.Application.Current.Dispatcher.Invoke`) instead of `Dispatcher.CurrentDispatcher.Invoke`. Works as intended now. Thank you! – Ilya Bezus Sep 07 '16 at 11:58
0

Update your function as below

public void OnStatusChanged()
    {
        if (this.Dispatcher.CheckAccess())
        {
            switch (UpdaterStatus.CurrentStatus)
            {
                case UpdateStatus.UpdateFound:
                    {
                        Message ToAdd = new Message("some params"); //Exception here
                        MessagesManager.AddNewMessage(ToAdd);
                        break;
                    }
                //some other cases
            }
        }
        else
            this.Dispatcher.Invoke(new Action(OnStatusChanged));
    }   
Pratik Parikh
  • 168
  • 1
  • 8