I would think that the main view model is the right place to define FileSystemWatcher. Regarding thread issues, this is an easy way:
_watcher = new FileSystemWatcher(path); _watcher.Created += (obj, e) => Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() => { // Code to handle Created event }; _watcher.Changed += (obj, e) => Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() => { // Code to handle Changed event }; _watcher.Renamed += (obj, e) => Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() => { // Code to handle Renamed event }; _watcher.Deleted += (obj, e) => Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() => { // Code to handle Deleted event }; // ... _watcher.EnableRaisingEvents = true;
Each of the "Code for Processing" will be executed in the user interface thread so that it can update the ObservableCollection . Note that FileSystemEventArgs "e" is available in this code.
If you prefer to use separate event handler methods, you can call them from the above code or use this convenient shortcut:
var switchThread = (FileSystemEventHandler handler) => (object obj, FileSystemEventArgs e) => Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() => handler(obj, e)) _watcher = new FileSystemWatcher(path); _watcher.Created += switchThread(OnCreated); _watcher.Changed += switchThread(OnChanged); _watcher.Deleted += switchThread(OnDeleted); _watcher.Renamed += switchThread(OnRenamed); _watcher.EnableRaisingEvents = true;
where OnCreated , OnChanged , OnDeleted and OnRenamed are normal event handlers with a normal signature, for example:
void OnChanged(object sender, FileSystemEventArgs e) {
Personally, I prefer the first way to do this, because I don't like creating four additional single-line methods.
Please note that your view model should know which dispatcher should call back. The easiest way to do this is to derive your view model from DispatcherObject, as suggested above. Another way for a view model constructor or method is to log FileSystemWatcher events to store a copy of Dispatcher.Current in a local field or local variable, and then use this for .BeginInvoke calls.
Also note that you can use exactly the same code in your code code, and not in your view model if you want.