Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The calling thread must be STA, because many UI components require this error In WPF. On form.show()

Tags:

c#

wpf

sta

Firstly I have read several answers to similar questions on the site but to be honest I find them a bit confusing (due to my lack of experience rather than the answers!). I am using a the FileSystemWatcher() class to monitor a folder for a file being created/changed. Once the event occurs I then want to load another form in the project. Instead of loading the form I get the error when the constructor on the new form is trying to execute. I am only using one thread - I'm not attempting to load the form under a different thread. My code is as follows

 //MainWindow
 public static void FolderWatcher()
  {
        FileSystemWatcher fsWatcher = new FileSystemWatcher();
        fsWatcher.Path = "C:\\dump";
        fsWatcher.Filter = "*";
        fsWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
        | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        fsWatcher.Created += new FileSystemEventHandler(OnChanged);
        fsWatcher.EnableRaisingEvents = true;    
  }

  public static void OnChanged(object source, FileSystemEventArgs e)
  {
       var imagePreview = new ImagePreview();
       imagePreview.Show();
  }


  //SecondForm
  public partial class ImagePreview : Window
  {
        public ImagePreview()
        {
              InitializeComponent(); //error occurs here
        }
  }

Hope you can help, many thanks in advance.

like image 673
Chief Wiggum Avatar asked Oct 03 '12 19:10

Chief Wiggum


2 Answers

It doesn't matter how many threads you are using. There is just a rule: any thread where you are creating UI must be STA.

In case you have only one thread, this very one has to be STA. :-) In order to make the main thread an STA, you need to use the STAThread attribute on your Main:

[STAThread]
static void Main(string[] args)
{
    // ...

If you just create a standard WPF application, the main thread is already marked with the needed attribute, so there should be no need for change.

Beware that the events from FileSystemWatcher may come from some other thread which is internally created by the framework. (You can check that by setting a breakpoint in OnChanged.) In this case, you need to forward the window creation to an STA thread. In case your application is a WPF application, it's done this way:

public static void OnChanged(object source, FileSystemEventArgs e)
{
    var d = Application.Current.Dispatcher;
    if (d.CheckAccess())
        OnChangedInMainThread();
    else
        d.BeginInvoke((Action)OnChangedInMainThread);
}

void OnChangedInMainThread()
{
    var imagePreview = new ImagePreview();
    imagePreview.Show();
}
like image 191
Vlad Avatar answered Sep 26 '22 07:09

Vlad


You're calling UI stuff from a non-UI thread (i.e. the FileSystemWatcher is firing the event from a non-UI thread).

I wrote an extension method that I used in my one and only WPF project (ugh):

public static class DispatcherEx
{
    public static void InvokeOrExecute(this Dispatcher dispatcher, Action action)
    {
        if (dispatcher.CheckAccess())
        {
            action();
        }
        else
        {
            dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                   action);
        }
    }
}

so now, after making the methods non-static so you have access to the MainWindow Dispatcher, you'd do this in your event callback:

public void OnChanged(object source, FileSystemEventArgs e)
{
    this.Dispatcher.InvokeOrExecute(()=>{
       var imagePreview = new ImagePreview();
       imagePreview.Show();
    });
}
like image 36
spender Avatar answered Sep 23 '22 07:09

spender