FileSystemWatcher
file filtering
multiple file types
coding
programming tips

How to set filter for FileSystemWatcher for multiple file types?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

FileSystemWatcher.Filter only supports a single pattern (e.g., "*.txt"). To watch multiple file types, use FileSystemWatcher.Filters (plural, .NET 6+) which accepts multiple patterns, or create multiple FileSystemWatcher instances (one per extension), or use a single watcher with Filter = "*.*" and filter events manually in the event handler. The Filters collection is the cleanest approach on modern .NET.

Method 1: Filters Collection (.NET 6+)

.NET 6 added a Filters collection that accepts multiple patterns:

csharp
1using System.IO;
2
3var watcher = new FileSystemWatcher(@"C:\Documents")
4{
5    EnableRaisingEvents = true,
6    NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite
7};
8
9// Add multiple filters
10watcher.Filters.Add("*.txt");
11watcher.Filters.Add("*.csv");
12watcher.Filters.Add("*.xml");
13
14watcher.Created += (sender, e) =>
15    Console.WriteLine($"Created: {e.FullPath}");
16
17watcher.Changed += (sender, e) =>
18    Console.WriteLine($"Changed: {e.FullPath}");
19
20Console.ReadLine();

This is the recommended approach for .NET 6 and later.

Method 2: Multiple FileSystemWatcher Instances

Create one watcher per file type (works on all .NET versions):

csharp
1using System.IO;
2
3string[] extensions = { "*.txt", "*.csv", "*.xml", "*.json" };
4var watchers = new List<FileSystemWatcher>();
5
6foreach (var ext in extensions)
7{
8    var watcher = new FileSystemWatcher(@"C:\Documents")
9    {
10        Filter = ext,
11        EnableRaisingEvents = true,
12        NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite
13    };
14
15    watcher.Created += OnFileCreated;
16    watcher.Changed += OnFileChanged;
17    watcher.Deleted += OnFileDeleted;
18    watcher.Renamed += OnFileRenamed;
19
20    watchers.Add(watcher);
21}
22
23void OnFileCreated(object sender, FileSystemEventArgs e)
24    => Console.WriteLine($"Created: {e.FullPath}");
25
26void OnFileChanged(object sender, FileSystemEventArgs e)
27    => Console.WriteLine($"Changed: {e.FullPath}");
28
29void OnFileDeleted(object sender, FileSystemEventArgs e)
30    => Console.WriteLine($"Deleted: {e.FullPath}");
31
32void OnFileRenamed(object sender, RenamedEventArgs e)
33    => Console.WriteLine($"Renamed: {e.OldFullPath}{e.FullPath}");
34
35// Keep watchers alive
36Console.ReadLine();
37
38// Cleanup
39foreach (var w in watchers)
40    w.Dispose();

Method 3: Watch All Files and Filter in Handler

Use Filter = "*.*" or Filter = "" (empty string watches everything) and check the extension in the event handler:

csharp
1using System.IO;
2
3var allowedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
4{
5    ".txt", ".csv", ".xml", ".json"
6};
7
8var watcher = new FileSystemWatcher(@"C:\Documents")
9{
10    Filter = "*.*",  // Watch all files
11    EnableRaisingEvents = true,
12    IncludeSubdirectories = true,
13    NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite
14};
15
16watcher.Created += (sender, e) =>
17{
18    string ext = Path.GetExtension(e.FullPath);
19    if (allowedExtensions.Contains(ext))
20    {
21        Console.WriteLine($"Created: {e.FullPath}");
22    }
23};
24
25watcher.Changed += (sender, e) =>
26{
27    string ext = Path.GetExtension(e.FullPath);
28    if (allowedExtensions.Contains(ext))
29    {
30        Console.WriteLine($"Changed: {e.FullPath}");
31    }
32};

This is the simplest approach but raises more events (all file types) and filters them afterward.

Reusable MultiFilterWatcher Class

csharp
1using System.IO;
2
3public class MultiFilterWatcher : IDisposable
4{
5    private readonly FileSystemWatcher _watcher;
6    private readonly HashSet<string> _extensions;
7
8    public event FileSystemEventHandler Created;
9    public event FileSystemEventHandler Changed;
10    public event FileSystemEventHandler Deleted;
11    public event RenamedEventHandler Renamed;
12
13    public MultiFilterWatcher(string path, params string[] extensions)
14    {
15        _extensions = new HashSet<string>(
16            extensions.Select(e => e.StartsWith(".") ? e : "." + e),
17            StringComparer.OrdinalIgnoreCase
18        );
19
20        _watcher = new FileSystemWatcher(path)
21        {
22            Filter = "*.*",
23            EnableRaisingEvents = true,
24            NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite
25| NotifyFilters.CreationTime }; _watcher.Created += (s, e) => { if (IsMatch(e.FullPath)) Created?.Invoke(s, e); }; _watcher.Changed += (s, e) => { if (IsMatch(e.FullPath)) Changed?.Invoke(s, e); }; _watcher.Deleted += (s, e) => { if (IsMatch(e.FullPath)) Deleted?.Invoke(s, e); }; _watcher.Renamed += (s, e) => { if (IsMatch(e.FullPath)) Renamed?.Invoke(s, e); }; } private bool IsMatch(string path) => _extensions.Contains(Path.GetExtension(path)); public void Dispose() => _watcher.Dispose(); } // Usage using var watcher = new MultiFilterWatcher(@"C:\Documents", ".txt", ".csv", ".json"); watcher.Created += (s, e) => Console.WriteLine($"New file: {e.Name}"); watcher.Changed += (s, e) => Console.WriteLine($"Modified: {e.Name}"); ``` ## Handling Duplicate Events `FileSystemWatcher` often fires multiple events for a single file operation. Debounce with a timer: ```csharp using System.Collections.Concurrent; var recentEvents = new ConcurrentDictionary<string, DateTime>(); watcher.Changed += (sender, e) => { string ext = Path.GetExtension(e.FullPath); if (!allowedExtensions.Contains(ext)) return; var now = DateTime.UtcNow; if (recentEvents.TryGetValue(e.FullPath, out var lastTime) && (now - lastTime).TotalMilliseconds < 500) { return;  // Duplicate event within 500ms — skip } recentEvents[e.FullPath] = now; Console.WriteLine($"Changed: {e.FullPath}"); }; ``` ## Common Pitfalls * **Assuming `Filter` accepts multiple patterns separated by `|` or `;`**: Unlike some other APIs, `FileSystemWatcher.Filter` only accepts a single pattern. Passing `"*.txt|*.csv"` does not work — it matches nothing. Use `Filters` (plural, .NET 6+) or multiple watchers. * **Not handling duplicate `Changed` events**: Saving a file in many editors triggers multiple `Changed` events (write, flush, close). Without debouncing, your handler processes the same file change 2-3 times. Use a timestamp-based deduplication strategy. * **Setting `EnableRaisingEvents = false` by default**: `FileSystemWatcher` does not raise events until `EnableRaisingEvents` is set to `true`. Forgetting this property is a common reason why watchers appear to do nothing. * **Not disposing watchers when no longer needed**: `FileSystemWatcher` uses system resources (file handles, kernel objects). Failing to dispose watchers in long-running applications can exhaust OS file notification limits. * **Watching network drives or very large directories**: `FileSystemWatcher` may miss events on network shares due to network latency or OS buffering limits. Increase `InternalBufferSize` (default 8KB, max 64KB) for high-volume directories, and use polling as a fallback for network paths. ## Summary * .NET 6+: Use `watcher.Filters.Add("*.txt")` for multiple patterns (cleanest approach) * All .NET versions: Create multiple `FileSystemWatcher` instances, one per extension * Alternative: Watch `"*.*"` and filter by extension in the event handler * Debounce duplicate events with a timestamp dictionary * Set `EnableRaisingEvents = true` to start receiving notifications

Course illustration
Course illustration

All Rights Reserved.