简体   繁体   中英

FileSystemWatcher: aggregate multiple filter events into a single one?

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?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM