Click here to Skip to main content
15,880,725 members
Articles / Programming Languages / C#

Enhanced FileSystemWatcher

Rate me:
Please Sign up or sign in to vote.
4.45/5 (20 votes)
20 Feb 2014CPOL2 min read 82K   2.6K   72   21
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.

C#
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.

C#
// 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.

C#
/// <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".

C#
// 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

Image 1

Figure 1 - Observing a file Test.txt using System.IO.FileSystemWatcher when editing file using Textpad.

Image 2

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)


Written By
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

 
NewsCreated a NuGet package Pin
Stef Heyenrath3-Feb-18 21:54
Stef Heyenrath3-Feb-18 21:54 
QuestionExcelent!!!! Pin
Member 1188507915-Dec-16 2:56
Member 1188507915-Dec-16 2:56 
GeneralMy vote of 4 Pin
Muhammad Shahid Farooq16-Jun-16 0:42
professionalMuhammad Shahid Farooq16-Jun-16 0:42 
QuestionUse another concept Pin
Tomaž Štih21-Feb-14 2:23
Tomaž Štih21-Feb-14 2:23 
Questionvery interesting article about Enhanced FileSystemWatcher class, which can be used to suppress duplicate events Pin
Volynsky Alex20-Feb-14 23:33
professionalVolynsky Alex20-Feb-14 23:33 
Thumbs Up | :thumbsup: Thumbs Up | :thumbsup: Thumbs Up | :thumbsup:
QuestionBahaviour seems to be same as FileSystemWatcher Pin
Mizan Rahman4-Jun-13 5:00
Mizan Rahman4-Jun-13 5:00 
AnswerRe: Bahaviour seems to be same as FileSystemWatcher Pin
Vipul Prashar4-Jun-13 11:37
Vipul Prashar4-Jun-13 11:37 
GeneralMy vote of 3 Pin
StCroixSkipper23-Aug-10 12:25
StCroixSkipper23-Aug-10 12:25 
GeneralRe: My vote of 3 Pin
Vipul Prashar25-Aug-10 10:34
Vipul Prashar25-Aug-10 10:34 
GeneralRe: My vote of 3 Pin
StCroixSkipper25-Aug-10 12:13
StCroixSkipper25-Aug-10 12:13 
GeneralGood Article Pin
Warpfield23-Aug-10 10:37
Warpfield23-Aug-10 10:37 
GeneralRe: Good Article Pin
Vipul Prashar25-Aug-10 10:27
Vipul Prashar25-Aug-10 10:27 
GeneralMy vote of 4 Pin
Pranay Rana17-Aug-10 19:07
professionalPranay Rana17-Aug-10 19:07 
GeneralRe: My vote of 4 Pin
Vipul Prashar19-Aug-10 10:45
Vipul Prashar19-Aug-10 10:45 
GeneralRe: My vote of 4 Pin
Pranay Rana19-Aug-10 19:40
professionalPranay Rana19-Aug-10 19:40 
GeneralMy vote of 3 Pin
karabax17-Aug-10 10:35
karabax17-Aug-10 10:35 
GeneralRe: My vote of 3 Pin
Vipul Prashar19-Aug-10 10:37
Vipul Prashar19-Aug-10 10:37 
GeneralUseful! Pin
StNickolay17-Aug-10 9:50
StNickolay17-Aug-10 9:50 
GeneralRe: Useful! Pin
Vipul Prashar19-Aug-10 10:35
Vipul Prashar19-Aug-10 10:35 
GeneralRe: Useful! Pin
SidharthaShenoy23-Aug-10 20:22
SidharthaShenoy23-Aug-10 20:22 
GeneralRe: Useful! Pin
Vipul Prashar25-Aug-10 10:36
Vipul Prashar25-Aug-10 10:36 

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

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