Позвольте мне попытаться объяснить вам это исключение.
Сначала я попытаюсь объяснить, что такое фоновый поток и поток пользовательского интерфейса и чем они отличаются, а затем два моих простых правила, которым нужно следовать при работе с этим исключением.
Мое объяснение STA и MTA, потоков пользовательского интерфейса и фоновых потоков
STA
означает Single Thread Apartment
, и у него есть аналог, называемый Multi Thread Apartment (MTA)
.И их различия обсуждаются в этом ответе , и вы можете использовать STA vs. MTA
в качестве ключевых слов для поиска в Google, если вы заинтересованы.Вы можете быть озадачены, когда увидите в поиске такие термины, как «STAThreadAttribute» / «COM» / «CoInitializeEx», не беспокойтесь, потому что вы не одиноки, это несколько сумасшедших старых технологий, с которыми мало кто работает.
И поток использует одну из этих моделей, STA
или MTA
.Вы могли заметить, что некоторый код, такой как
[STAThread]
static void Main()
Это означает, что поток использует STA
.В WPF основной поток использует STA
, элементы управления создаются в этом потоке, и в этом потоке также выполняются обработчики событий, такие как Button_Click
, назовем это потоком пользовательского интерфейса .
И фоновые потоки , которые вы начали использовать с классами вроде Thread/ThreadPool/Task
, по умолчанию используют MTA
, это также относится к обработчику событий System.Timers.Timer.Elapsed
, и, как вв этом случае обработчик событий FileSystemWatcher
events.
Теперь у вас есть идея, что ваш код работает либо на потоке пользовательского интерфейса (STA) , либо на одном из фоновых потоков(MTA) .
Мои простые правила
Обычно необходимо создавать элементы управления в потоке пользовательского интерфейса .(Я не обсуждаю альтернативное решение «создания в фоновом потоке и его замораживания» здесь, просто чтобы соблюдать простые правила. И, как указано в комментарии @slugster, вы можете создать элемент управления на любой поток , но вы можете получить к нему доступ только из того же потока, в котором он был создан.)
Из фонового потока , если вы хотите получить к нему доступэлементы управления, вам нужно перенаправить ваш код в поток пользовательского интерфейса с помощью Dispatcher.Invoke/BeginInvoke
- чтобы код выполнялся в потоке пользовательского интерфейса.В противном случае вы получите это исключение (The calling thread must be STA...
).