Introduction
This article discusses an enhanced FileSystemWatcher
class which can be used to suppress duplicate events that fire on a single change to the file.
Background
System.IO.FileSystemWatcher
class helps the user to monitor a directory and multiple or single file within a directory. Whenever a change (Creation, Modification, Deletion or Renaming) is detected, an appropriate event is raised. However, duplicate events fire depending on the software that is being used to modify the file.
Observation
Using Notepad, modifying the contents of a file results in 2 Changed events being fired. Doing the same using Textpad results in 4 Changed events being fired.
Using Textpad, creating a file in a directory that was being watched resulted in 1 Created and 3 Changed events being fired. In case of Notepad, Created followed by Deleted!!, followed by Created and 3 Changed Events were observed.
My guess is that multiple events are fired depending on number of times the file system is accessed by particular software while performing any given operation (Save, Delete, Save As, etc.).
Proposed Solution
We need to keep track of the event as they get fired and suppress subsequent events that occur within pre-determined interval.
We create a class "MyFileSystemWatcher
" that inherits from System.IO.FileSystemWatcher
. We also create a dictionary to keep track of when the last event occurred for a file, along with some other private
members.
public class MyFileSystemWatcher : FileSystemWatcher, IDisposable
{
#region Private Members
private Dictionary<string,> _lastFileEvent;
private int _interval;
private TimeSpan _recentTimeSpan;
#endregion
........
}</string,>
We delegate the construction of the object to FileSystemWatcher
Base class and initialize the private
members in the InitializeMembers private
method.
public MyFileSystemWatcher() : base()
{
InitializeMembers();
}
public MyFileSystemWatcher(string Path) : base(Path)
{
InitializeMembers();
}
public MyFileSystemWatcher(string Path, string Filter) : base(Path, Filter)
{
InitializeMembers();
}
private void InitializeMembers()
{
Interval = 100;
FilterRecentEvents = true;
_lastFileEvent = new Dictionary<string,>();
base.Created += new FileSystemEventHandler(OnCreated);
base.Changed += new FileSystemEventHandler(OnChanged);
base.Deleted += new FileSystemEventHandler(OnDeleted);
base.Renamed += new RenamedEventHandler(OnRenamed);
}</string,>
We create a method to determine if a recent event has occurred for a particular file.
private bool HasAnotherFileEventOccuredRecently(string FileName)
{
bool retVal = false;
if (FilterRecentEvents)
{
if (_lastFileEvent.ContainsKey(FileName))
{
DateTime lastEventTime = _lastFileEvent[FileName];
DateTime currentTime = DateTime.Now;
TimeSpan timeSinceLastEvent = currentTime - lastEventTime;
retVal = timeSinceLastEvent < _recentTimeSpan;
_lastFileEvent[FileName] = currentTime;
_lastFileEvent.Add(FileName, DateTime.Now);
retVal = false;
}
}
return retVal;
}
We create Event Handler for base class event; check if a recent event has occurred and if not we raise the event. The code snippet shows this approach for "Changed
" event only, see attached code for other events like "Created
", "Renamed
" and "Deleted
".
public new event FileSystemEventHandler Changed;
private void OnChanged(object sender, FileSystemEventArgs e)
{
if (!HasAnotherFileEventOccuredRecently(e.FullPath))
this.OnChanged(e);
}
protected new virtual void OnChanged(FileSystemEventArgs e)
{
if (Changed != null) Changed(this, e);
}
Output Comparison
Figure 1 - Observing a file Test.txt using System.IO.FileSystemWatcher when editing file using Textpad.

Figure 2 - Observing a file Test.txt using Temp.IO.MyFileSystemWatcher when editing file using Textpad.
Conclusion
The public
interface of this class matches that of a file system watcher class so it can be replaced easily across code, especially if you are using a Factory
to create a FileSystemWatcher
object.
Other situations might arise that this code does not handle (based on what software is being used to edit the file). Constructive criticism is welcome. Hope this is useful for some readers.
History
- 16th August, 2010: Initial post