I'm trying to update my ObservableCollection as the FileSystemWatcher notifies changes. I know this is not possible because of cr开发者_开发百科oss thread operations.
So i would like to get the name of the file created/deleted/renamed when the event is fired and update it in the UI thread once the event is completed, as we do in BackgroundWorker. Can anyone tell me how to do this?Also tell me where i should define and start this FileSystemWatcher. Currently i've defined it in the MainViewModel.
P.S.: I've seen similar questions in SO, but didn't get a clear picture
Thanks In Advance,
VeerI would think the main view model is the right place to define the FileSystemWatcher. As for the threading issues, this is the 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 to handle" will execute within the UI thread so it can update the ObservableCollection
. Note that the FileSystemEventArgs "e" is available within 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 handler methods with the normal signature, for example:
void OnChanged(object sender, FileSystemEventArgs e)
{
// Code to handle Changed event
}
Personally I prefer the first way of doing it because I don't like creating four extra 1-line methods.
Note that your view model will need to know which Dispatcher to call back on. The easiest way to do this is to derive your view model from DispatcherObject, as assumed above. Another way is for the view model's constructor or the method that registers the FileSystemWatcher events to store a copy of Dispatcher.Current in a local field or local variable, then use that for the .BeginInvoke calls.
Also note that you can use exactly the same code in your view code-behind instead of in your view model if you prefer.
public void SomeActionToBeInvokedOnTheMainThread()
{
if (someControl.Dispatcher.CheckAccess())
{
// you can modify the control
}
else
{
someControl.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(SomeActionToBeInvokedOnTheMainThread)
);
}
}
I used Ray B.'s approach but had to modify things slightly and thought I'd post an update here to maybe save others some time.
My VS2010/.NET 4.0 WPF project was throwing the error:
Cannot assign lambda expression to an implicitly-typed local variable
After some tweaking I came up with the following. Note the additional var 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;
精彩评论