调用线程必须是STA,因为在WPF中许多UI组件需要此错误。在form.show()中。

11

首先,我已经阅读了网站上类似问题的几个答案,但说实话,我觉得它们有点混淆(这是由于我的经验不足而不是答案!)我正在使用FileSystemWatcher()类监视一个文件夹以检测文件是否被创建/更改。一旦事件发生,我想要在项目中加载另一个表单。但当新表单的构造函数尝试执行时,我得到了错误。我只使用了一个线程 - 我没有尝试在不同的线程下加载表单。我的代码如下:

 //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
        }
  }

希望您能帮忙,非常感谢。


1
通常应该发布您的异常和堆栈跟踪。 - spender
3个回答

22

使用多少个线程并不重要,唯一的规则是:任何创建UI的线程都必须是STA线程。

如果您只有一个线程,那么这个线程也必须是STA线程。 :-) 为了使主线程成为STA线程,您需要在Main上使用 STAThread 属性:

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

如果您只是创建了一个标准的WPF应用程序,主线程已经标记了所需的属性,因此无需更改。

请注意,FileSystemWatcher 的事件可能来自于框架内部创建的其他线程。 (您可以通过在 OnChanged 中设置断点来检查。)在这种情况下,您需要将窗口创建“转发”到STA线程。 如果您的应用程序是WPF应用程序,则可以按照以下方式完成:

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

非常感谢Vlad,你和Spender的解决方案都起作用了。我现在明白为什么会出现错误了。 - Chief Wiggum

5

您正在从非UI线程(即 FileSystemWatcher 正在从非UI线程触发事件)调用UI内容。

我编写了一个扩展方法,我在我的唯一的WPF项目中使用它(呃):

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

现在,将方法更改为非静态,这样您就可以访问MainWindow调度程序,在事件回调中执行以下操作:

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

谢谢Spender,你和Vlad的解决方案都有效。解释也很好 - 我现在明白为什么会出现错误了。 - Chief Wiggum

1

使用主窗口的调度程序。通常每个应用程序只有一个UI线程,否则您可能会从WPF获得“无效的跨线程访问”异常。

文件系统监视器在不同的线程上引发其事件,可以使用调度程序使该代码在UI线程中运行。

在该事件处理程序中使用Dispatcher.BeginInvoke,您就可以顺利完成。 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接