Firstly I have read several answers to similar questions on the site but to be honest I find them a bit confusing (due to my lack of experience rather than the answers!). I am using a the FileSystemWatcher() class to monitor a folder for a file being created/changed. Once the event occurs I then want to load another form in the project. Instead of loading the form I get the error when the constructor on the new form is trying to execute. I am only using one thread - I'm not attempting to load the form under a different thread. My code is as follows
//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
}
}
Hope you can help, many thanks in advance.
It doesn't matter how many threads you are using. There is just a rule: any thread where you are creating UI must be STA.
In case you have only one thread, this very one has to be STA. :-) In order to make the main thread an STA, you need to use the STAThread
attribute on your Main
:
[STAThread]
static void Main(string[] args)
{
// ...
If you just create a standard WPF application, the main thread is already marked with the needed attribute, so there should be no need for change.
Beware that the events from FileSystemWatcher
may come from some other thread which is internally created by the framework. (You can check that by setting a breakpoint in OnChanged
.) In this case, you need to forward the window creation to an STA thread. In case your application is a WPF application, it's done this way:
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();
}
You're calling UI stuff from a non-UI thread (i.e. the FileSystemWatcher
is firing the event from a non-UI thread).
I wrote an extension method that I used in my one and only WPF project (ugh):
public static class DispatcherEx
{
public static void InvokeOrExecute(this Dispatcher dispatcher, Action action)
{
if (dispatcher.CheckAccess())
{
action();
}
else
{
dispatcher.BeginInvoke(DispatcherPriority.Normal,
action);
}
}
}
so now, after making the methods non-static so you have access to the MainWindow Dispatcher, you'd do this in your event callback:
public void OnChanged(object source, FileSystemEventArgs e)
{
this.Dispatcher.InvokeOrExecute(()=>{
var imagePreview = new ImagePreview();
imagePreview.Show();
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With