-2

Take the following code, the events raised by the watcher all call the same method:

[NonSerialized]
private FileSystemWatcher Watcher;

private void WatcherInit()
{
    Watcher ??= new FileSystemWatcher();

    Watcher.Path                  = Application.dataPath;
    Watcher.Filter                = "*.mat";
    Watcher.IncludeSubdirectories = true;
    Watcher.NotifyFilter          = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.CreationTime;

    Watcher.EnableRaisingEvents = true;

    Watcher.Changed += OnWatcherChanged;
    Watcher.Created += OnWatcherCreated;
    Watcher.Deleted += OnWatcherDeleted;
    Watcher.Renamed += OnWatcherRenamed;
}

private void OnWatcherRenamed(object sender, RenamedEventArgs args)
{
    DoViewReload();
}

private void OnWatcherDeleted(object sender, FileSystemEventArgs args)
{
    DoViewReload();
}

private void OnWatcherCreated(object sender, FileSystemEventArgs args)
{
    DoViewReload();
}

private void OnWatcherChanged(object sender, FileSystemEventArgs args)
{
    DoViewReload();
}

This results in the same method called multiple times while I'd like it to be only once.

In my case, I refresh the UI and it happens 3 times when I create a new file.

I suppose there should be some timer that would wait for successive events and concat them, then only emit a single call; that's just a guess and maybe there's a better solution around that I'm not aware of.

Any ideas?

aybe
  • 15,516
  • 9
  • 57
  • 105
  • 1
    `Watcher.Path = Application.dataPath;` Put a breakpoint on this line of code. Run the code. How many times is the breakpoint hit? – mjwills Aug 15 '21 at 08:16
  • 3
    You probably want to coalesce the events into a delay anyway because FSW often raises events while a file is still in use by the program creating it – Caius Jard Aug 15 '21 at 08:18
  • In addition to what @CaiusJard wrote, you can also get a changed event raised by accessing the file from your code (last access time changes). – Steeeve Aug 15 '21 at 08:28
  • Is this relevant? [FileSystemWatcher Changed event is raised twice](https://stackoverflow.com/questions/1764809/filesystemwatcher-changed-event-is-raised-twice) – Theodor Zoulias Aug 15 '21 at 09:10
  • @mjwills Why would you want this to be hit multiple times ? Initialization is only done once. – aybe Aug 16 '21 at 02:25
  • @CaiusJard Yep, that's what I was thinking. – aybe Aug 16 '21 at 02:25
  • @Steeeve Yep, got you, I have written a small chart about the events occurring. – aybe Aug 16 '21 at 02:26
  • @TheodorZoulias That's helpful somewhat. – aybe Aug 16 '21 at 02:26
  • 1
    @aybe I think you forgot to answer my specific question. A very common cause of this issue is subscribing twice. I was merely trying to establish whether you may have done that. – mjwills Aug 16 '21 at 02:47
  • @mjwills You made a point, I can confirm that events are only subscribed once! – aybe Aug 16 '21 at 03:25

1 Answers1

1

The following pattern ended up working well:

private void OnWatcherRenamed(object sender, RenamedEventArgs args)
{
    WatcherTimerRestart();
}

private void OnWatcherDeleted(object sender, FileSystemEventArgs args)
{
    WatcherTimerRestart();
}

private void OnWatcherCreated(object sender, FileSystemEventArgs args)
{
    WatcherTimerRestart();
}

private void OnWatcherChanged(object sender, FileSystemEventArgs args)
{
    WatcherTimerRestart();
}

private void WatcherTimerRestart()
{
    WatcherTimer.Enabled = false;
    WatcherTimer.Enabled = true;
}

private void WatcherTimerCallback(object sender, ElapsedEventArgs e)
{
    // do the funky stuff you wanted to execute only once
}

Notes:

I decided to switch the UI to a specific state like:

Waiting for I/O to complete...

And in this case, setting the interval to a not too low value such as 3000 ms is better:

  • user has time to read the message
  • a lower value in practice turns out to not be enough, even on small files and/or an SSD
  • FileSystemWatcher is slow at raising events

And here's a mini-table about events raised according a user's action:

User action Callbacks raised
Create Created
Delete Changed, Deleted
Rename Changed, Created, Deleted
Move Changed, Created, Deleted

You may want to note that, ironically, Renamed isn't raised at all even for a rename operation; this probably has to do with how an application renames a file, there are multiple ways to do it.

aybe
  • 15,516
  • 9
  • 57
  • 105