0

I've created a wrapper class for the FileSystemWatcher, but I cannot get the events to fire. They fire as expected when using the FileSystemWatcher class, but not my wrapper.

Any ideas why?

public class FolderWatcher
{
    public event FileSystemEventHandler Created;
    public event FileSystemEventHandler Changed;
    private FileSystemWatcher _watcher;

    public FolderWatcher(string watchedFolderPath)
    {
        _watcher = new FileSystemWatcher(watchedFolderPath);
        _watcher.Created += Created;
        _watcher.Changed += Changed;
        _watcher.EnableRaisingEvents = true;
    }
}

class Program
{
    static async Task Main(string[] args)
    {
        string watchedFolder = "/home/user";

        // 1. Custom wrapper for FileSystemWatcher. No events get fired.
        FolderWatcher watcher = new FolderWatcher(watchedFolder);

        // 2. The built in FileSystemWatcher works as expected. Uncomment to test.
        // FileSystemWatcher watcher = new FileSystemWatcher(watchedFolder);
        // watcher.EnableRaisingEvents = true;

        bool createdTriggered = false;
        bool changedTriggered = false;
        bool wait = (changedTriggered == false && createdTriggered == false);

        watcher.Created += (sender, args) => 
        {
            createdTriggered = true;
            Console.WriteLine($"{args.Name} created!");
        };

        watcher.Changed += (sender, args) => 
        {
            changedTriggered = true;
            Console.WriteLine($"{args.Name} changed!");
        };

        Console.WriteLine($"Watching folder {watchedFolder}");

        while (wait)
        {
            await Task.Delay(1);
        }

        Console.WriteLine("Closing application...");
    }
}

To test this, I've just been firing it up and creating a new file/editing an existing file in /home/user. Scenario 1 (annotated in the code) doesnt fire the events, but scenario 2 (annotated in the code), does fire the events. I'm struggling to work out why.

Worth noting that I'm loosely following this answer.

devklick
  • 2,000
  • 3
  • 30
  • 47

2 Answers2

2

Instead of trying to hook the File Watcher's events to yours, simply fire your events when you capture the File Watcher's events.

watcher.Changed += (sender, args) => 
{
    if (Changed != null)
        Changed(sender, args);
}

Your events are there for someone using your class to hook.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
  • Works like a charm, thanks! Have you any idea if my current approach *should* work? Just confused as it's the accepted answer on the post linked in my post above. – devklick Dec 12 '19 at 22:28
  • 1
    Think of it this way: An event is a socket; you hook it to a method which is a plug. You put electricity through the circuit by calling the method. `_watcher.Created += Created;` is akin to trying to plug a socket into another socket. – Robert Harvey Dec 12 '19 at 22:31
  • 1
    Or using the [null-conditional operator](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-): `watcher.Created += (sender, args) => Created?.Invoke(sender, args);` – Theodor Zoulias Dec 12 '19 at 22:32
  • 1
    The reason `_watcher.Created += Created` is not working is because every time you subscribe to an event, the `EventHandler` field is replaced with a new combined `EventHandler` that stores the new subscription along with the previous subscriptions. And the `Created` field holds no subscriptions at the time you assign it to `_watcher.Created`. In depth: [Delegates and Events](https://csharpindepth.com/Articles/Events) – Theodor Zoulias Dec 12 '19 at 22:40
0

To simplify Robert Harvey answer, just update your constructor to:

    public FolderWatcher(string watchedFolderPath)
    {
        _watcher = new FileSystemWatcher(watchedFolderPath);

        _watcher.Created += (sender, args) =>
        {
            if (this.Created != null)
            {
                this.Created(sender, args);
            }
        };

        _watcher.Changed += (sender, args) =>
        {
            if (this.Changed != null)
            {
                this.Changed(sender, args);
            }
        };

        _watcher.EnableRaisingEvents = true;
    }
sspaniel
  • 647
  • 3
  • 10
  • This is cleaner, but you loose the valuable check for `null`. Theador Soulias provided a comment with a example using a null-conditional operator, which is cleaner and retains the `null` check. – devklick Dec 12 '19 at 22:43