10

首先,我已经阅读了网站上类似问题的几个答案,但老实说,我发现它们有点令人困惑(由于我缺乏经验而不是答案!)。我正在使用 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
        }
  }

希望您能提供帮助,非常感谢。

4

3 回答 3

22

使用多少线程并不重要。只有一条规则:您创建 UI 的任何线程都必须是 STA。

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

[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();
}
于 2012-10-03T19:38:11.053 回答
5

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

我写了一个扩展方法,我在我的唯一一个 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 Dispatcher,您可以在事件回调中执行此操作:

public void OnChanged(object source, FileSystemEventArgs e)
{
    this.Dispatcher.InvokeOrExecute(()=>{
       var imagePreview = new ImagePreview();
       imagePreview.Show();
    });
}
于 2012-10-03T19:59:51.873 回答
1

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

文件系统观察程序在不同的线程上引发其事件,并且调度程序可用于使该代码在 UI 线程中运行。

在该事件处理程序中使用 Dispatcher.BeginInvoke 就可以了。:)

于 2012-10-03T19:43:53.237 回答