web-dev-qa-db-ja.com

多くのUIコンポーネントがWPFでこのエラーを必要とするため、呼び出しスレッドはSTAである必要があります。 form.show()

最初に、サイトで同様の質問に対するいくつかの回答を読みましたが、正直に言うと、少し混乱していることがわかります(回答ではなく経験が不足しているためです!)。 FileSystemWatcher()クラスを使用して、作成/変更されているファイルのフォルダーを監視しています。イベントが発生したら、プロジェクトに別のフォームをロードします。フォームをロードする代わりに、新しいフォームのコンストラクターが実行しようとするとエラーが発生します。私は1つのスレッドのみを使用しています-別のスレッドでフォームをロードしようとはしていません。私のコードは次のとおりです

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

よろしくお願いします。よろしくお願いします。

10
Chief Wiggum

使用しているスレッドの数は関係ありません。ルールがあります。UIを作成するスレッドはすべてSTAである必要があります。

スレッドが1つしかない場合、これはSTAである必要があります。 :-)メインスレッドをSTAにするには、STAThreadMain属性を使用する必要があります。

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

非UIスレッドからUIのものを呼び出しています(つまり、FileSystemWatcherは非UIスレッドからイベントを発生させています)。

唯一のWPFプロジェクトで使用した拡張メソッドを作成しました(ugh):

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

メインウィンドウのディスパッチャを使用します。通常、アプリケーションごとにUIスレッドは1つだけです。そうしないと、WPFから「無効なクロススレッドアクセス」例外が発生する可能性があります。

ファイルシステムウォッチャーは別のスレッドでイベントを発生させ、ディスパッチャーを使用してそのコードをUIスレッドで実行できます。

そのイベントハンドラーでDispatcher.BeginInvokeを使用すれば、問題はありません。 :)

1
Venemo