1. Some events will be fired multiple times.
When you rename a file, you could get several events fired. This is a known issue for file watchers. If you process the changes in the event handler, you could handle multiple times for one change. A good choice is to group the changes together, and then only process them once. So a Timer may be good in this situation. I will discuss Timer in another paragraph.
2. The monitored folder name change event is not fired.
When I debugged and found there was no events fired when the monitored folder was renamed, I was really frustrated. Actually, FileSystemWatcher does catch the event and furthermore changes the monitored folder to the new folder and starts monitoring the new folder. But you just cannot catch it. So this is the design behaviour, but apparent not what I wanted, because I needed to display the new folder name immediately. So to monitor the renaming event is a must.
The original thought came from this thread. The idea is to create another watcher to monitor the folder's parent folder, and only watch the directory name renaming event. Meanwhile you can specify a filter to only watch the sub folder you are interested in. The following is code to create the parent watcher.
_parentWatcher = new FileSystemWatcher();
_parentWatcher.Path = (Directory.GetParent(_watchedFolder)).FullName;
string filter = _watchedFolder.Substring(_watchedFolder.LastIndexOf('\\') + 1);
_parentWatcher.Filter = filter;
_parentWatcher.IncludeSubdirectories = false;
_parentWatcher.Error += Watcher_Error;
_parentWatcher.NotifyFilter = NotifyFilters.DirectoryName;
_parentWatcher.EnableRaisingEvents = true;
3. You cannot rename or delete the monitored folder's parent folders.
Let's say you are watching folder A. You are OK to change any files under A, rename folder A, and maybe delete A. But you can not rename A's parent B, B's parent C, and so on. Check this thread. When you try to rename it from Windows Explorer, you will get the following infamous message:
The action can't be completed because the folder or a file in it is open in another program
This is ridiculous since no file or folder is opened, just the folder is monitored. Again this is a design behaviour.
A workaround here could be you go ahead to watch your entire drive, let's say C:\ or D:\. As long as this guy doesn't have a parent, you don't worry about renaming a parent folder. But this probably brings performance issue, because you watch many unnecessary changes, especially for network drives.
4. Use a Timer to group multiple events
To be honest, I am not a fan of Timers. I always feel Timers are low classes in the system and not reliable. Maybe I am wrong, but I just have that feeling. But in some cases where missing an event is not that critical, Timers still can do the work. We should notice there are at least 3 timers: System.Timers.Timer, System.Threading.Timer, and System.Windows.Threading.DispatcherTimer. This thread discussing Timers may be useful. I used System.Timers.Timer in this case. Somebody mentioned we should use DispatcherTimer, but it turns out DispatcherTimer behaves differently with Timer.
When you respond to every event, you stop and restart the timer to wait for the same period, so this probably can group all changes together and fire the final change request.
Another thing needs to notice is in the Timer elapsed event, we should call
_uiDispatcher.BeginInvoke(new Action(() => {
Messenger.Default.Send("StartRefreshing",
"StartRefreshing");
}));
not just simply send the message.
Messenger.Default.Send
}));
Messenger.Default.Send("StartRefreshing",
"StartRefreshing");
The
difference is the
later will send the message to a background thread but the former will
send the event to the main ui thread. When you want to change
UI
stuff in the responding function, in the later case you will get
The calling thread cannot access this object because a different thread
owns it
Because
in WPF only the main GUI thread can change the GUI elements.
We
can use Dispatcher.Invoke or BeginInvoke to execute a delegate in the
dispatcher thread. In the middle of this work, I wanted to
use
DispatcherTimer, but it turned out the timer is not fired.
Check this
thread and this
thread
to see the possible reason why this timer is not fired. The
dispatcher timer is created in one thread and will only fire events in
that thread and only the dispatcher of that thread can access these
events. 5. Do we need explicit multiple threads?
Here comes another concern users normally have, do we need explicitly to put the file monitor to another thread? Check this discussion. The answer is no, because .NET framework will handle it. The class will create a thread if it needs that. So unless necessary, you don't have to create a thread to put the file monitor in it. This is different with the old-time Win32 way and of course a nice improvement.
No comments:
Post a Comment