Подтвердить что ты не робот

Вызывающий поток должен быть STA, потому что многие компоненты пользовательского интерфейса требуют этой ошибки. В WPF. На форме .show()

Во-первых, я прочитал несколько ответов на похожие вопросы на сайте, но, честно говоря, я нахожу их немного запутанными (из-за моего отсутствия опыта, а не ответов!). Я использую класс 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
        }
  }

Надеюсь, вы можете помочь, большое спасибо заранее.

4b9b3361

Ответ 1

Неважно, сколько потоков вы используете. Существует только правило: любой поток, в котором вы создаете пользовательский интерфейс, должен быть 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();
}

Ответ 2

Вы вызываете материал пользовательского интерфейса из потока, отличного от 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, вы сделаете это в своем обратном вызове:

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

Ответ 3

Используйте диспетчер главного окна. Обычно для каждого приложения существует только один поток пользовательского интерфейса, иначе вы можете получить исключение "недопустимого доступа к перекрестному потоку" из WPF.

Наблюдатель файловой системы повышает свои события в другом потоке, и диспетчер может использоваться для выполнения этого кода в потоке пользовательского интерфейса.

Используйте Dispatcher.BeginInvoke в этом обработчике событий, и все будет в порядке.:)