Click here to Skip to main content
Click here to Skip to main content

Enhanced FileSystemWatcher

, 20 Feb 2014
Rate this:
Please Sign up or sign in to vote.
Enhanced FileSystemWatcher class, which can be used to suppress duplicate events that fire on a single change to the file

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
    // This Dictionary keeps the track of when an event occurred 
    // last for a particular file
    private Dictionary<string,> _lastFileEvent;
    // Interval in Millisecond
    private int _interval;
    //Timespan created when interval is set
    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.

// Constructors delegate to the base class constructors and 
// call private InitializeMember method
public MyFileSystemWatcher() : base()
{
    InitializeMembers();
}

public MyFileSystemWatcher(string Path) : base(Path)
{
    InitializeMembers();
}

public MyFileSystemWatcher(string Path, string Filter) : base(Path, Filter)
{
    InitializeMembers();
}

/// <summary>
/// This Method Initializes the private members.
/// Interval is set to its default value of 100 millisecond
/// FilterRecentEvents is set to true, _lastFileEvent dictionary is initialized
/// We subscribe to the base class events.
/// </summary>
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.

/// <summary>
/// This method searches the dictionary to find out when the last event occurred
/// for a particular file. If that event occurred within the specified timespan
/// it returns true, else false
/// </summary>
/// <param name="FileName">The filename to be checked</param>
/// <returns>True if an event has occurred within the specified interval, 
/// False otherwise</returns>
private bool HasAnotherFileEventOccuredRecently(string FileName)
{
    bool retVal = false;

    // Check dictionary only if user wants to filter recent events 
    // otherwise return Value stays False
if (FilterRecentEvents)
{
    if (_lastFileEvent.ContainsKey(FileName))
    {
        // If dictionary contains the filename, check how much time has elapsed
        // since the last event occurred. If the timespan is less that the
        // specified interval, set return value to true
        // and store current datetime in dictionary for this file
        DateTime lastEventTime = _lastFileEvent[FileName];
        DateTime currentTime = DateTime.Now;
        TimeSpan timeSinceLastEvent = currentTime - lastEventTime;
        retVal = timeSinceLastEvent < _recentTimeSpan;
        _lastFileEvent[FileName] = currentTime;

        // If dictionary does not contain the filename,
        // no event has occurred in past for this file, so set return value to false
        // and filename alongwith current datetime to the dictionary
        _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".

// These events hide the events from the base class.
// We want to raise these events appropriately and we do not want the
// users of this class subscribing to these events of the base class accidentally
public new event FileSystemEventHandler Changed;

// Base class Event Handlers. Check if an event has occurred recently and call method
// to raise appropriate event only if no recent event is detected
private void OnChanged(object sender, FileSystemEventArgs e)
{
    if (!HasAnotherFileEventOccuredRecently(e.FullPath))
        this.OnChanged(e);
}
// Protected Methods to raise the Events for this class
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Vipul Prashar
Software Developer (Senior)
Canada Canada
I am currently based in Edmonton Alberta, and have been working in Software Industry since 2001.
 
Since 2003, I have been deeply involved in execution multiple Projects in .Net, and have worked on both Web Based and Windows based applications
 
In past, I have also played role of Team/Tech Lead while keeping myself hands on in terms of coding.

Comments and Discussions

 
QuestionUse another concept PinmemberTomaž Štih21-Feb-14 2:23 
Questionvery interesting article about Enhanced FileSystemWatcher class, which can be used to suppress duplicate events PinprofessionalVolynsky Alex20-Feb-14 23:33 
QuestionBahaviour seems to be same as FileSystemWatcher PinmemberMizan Rahman4-Jun-13 5:00 
AnswerRe: Bahaviour seems to be same as FileSystemWatcher Pinmemberviprash4-Jun-13 11:37 
GeneralMy vote of 3 PinmemberStCroixSkipper23-Aug-10 12:25 
GeneralRe: My vote of 3 Pinmemberviprash25-Aug-10 10:34 
GeneralRe: My vote of 3 PinmemberStCroixSkipper25-Aug-10 12:13 
GeneralGood Article PinmemberWarpfield23-Aug-10 10:37 
GeneralRe: Good Article Pinmemberviprash25-Aug-10 10:27 
GeneralMy vote of 4 PinmemberPranay Rana17-Aug-10 19:07 
GeneralRe: My vote of 4 Pinmemberviprash19-Aug-10 10:45 
GeneralRe: My vote of 4 PinmemberPranay Rana19-Aug-10 19:40 
GeneralMy vote of 3 Pinmemberkarabax17-Aug-10 10:35 
GeneralRe: My vote of 3 Pinmemberviprash19-Aug-10 10:37 
GeneralUseful! PinmemberStNickolay17-Aug-10 9:50 
GeneralRe: Useful! Pinmemberviprash19-Aug-10 10:35 
GeneralRe: Useful! PinmemberSidharthaShenoy23-Aug-10 20:22 
GeneralRe: Useful! Pinmemberviprash25-Aug-10 10:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 21 Feb 2014
Article Copyright 2010 by Vipul Prashar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid