Changing ObservableCollection according to fileSystemWatcher change notification

I am trying to update my ObservableCollection as FileSystemWatcher notifies me of changes. I know this is not possible due to cross-flow operations.
Therefore, I would like to get the name of the file created / deleted / renamed when the event is triggered, and update it in the user interface stream after the event is completed, as in BackgroundWorker. Can someone tell me how to do this?

Also tell me where I should define and run this FileSystemWatcher. I have currently defined it in MainViewModel.

PS: I saw similar questions in SO, but didn't get a clear image

Thanks in advance,
Veer

+4
source share
3 answers

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) { // Code to handle Changed event } 

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.

+2
source
 public void SomeActionToBeInvokedOnTheMainThread() { if (someControl.Dispatcher.CheckAccess()) { // you can modify the control } else { someControl.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Normal, new Action(SomeActionToBeInvokedOnTheMainThread) ); } } 
+2
source

I used Ray B.'s approach, but I had to make a little difference and thought that I would post an update here to possibly keep others for some time.

My VS2010 / .NET 4.0 WPF project threw an error:

 Cannot assign lambda expression to an implicitly-typed local variable 

After some tweaking, I came up with the following. Note the additional variable defined to handle the Renamed event:

 var switchThreadForFsEvent = (Func<FileSystemEventHandler, FileSystemEventHandler>)( (FileSystemEventHandler handler) => (object obj, FileSystemEventArgs e) => Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() => handler(obj, e)))); var switchThreadForFsRenameEvent = (Func<RenamedEventHandler, RenamedEventHandler>)( (RenamedEventHandler handler) => (object obj, RenamedEventArgs e) => Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() => handler(obj, e)))); _fileSystemWatcher = new FileSystemWatcher(documentCollectionPath); _fileSystemWatcher.Created += switchThreadForFsEvent(OnFileCreated); _fileSystemWatcher.Deleted += switchThreadForFsEvent(OnFileDeleted); _fileSystemWatcher.Renamed += switchThreadForFsRenameEvent(OnFileRenamed); _fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName; _fileSystemWatcher.IncludeSubdirectories = true; _fileSystemWatcher.EnableRaisingEvents = true; 
+1
source

Source: https://habr.com/ru/post/1302960/


All Articles