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

孤者浪人 提交于 2019-11-30 18:57:00

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();
}

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();
    });
}

Use the dispatcher of the main window. Usually there is just one UI thread per application, otherwise you may get an "invalid cross-thread access" exception from WPF.

The file system watcher raises its events on a different thread and the dispatcher can be used to make that code run in the UI thread.

Use Dispatcher.BeginInvoke in that event handler and you'll be fine. :)

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!