nawfal
nawfal

Reputation: 73183

How to set filter for FileSystemWatcher for multiple file types?

Everywhere I find these two lines of code used to set filter for file system watcher in samples provided..

FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Filter = "*.txt";
//or
watcher.Filter = "*.*";

But I want my watcher to monitor more file types, but not all. How can I achieve this:

//watcher.Filter = "*.txt" | "*.doc" | "*.docx" | "*.xls" | "*.xlsx";

I tried these:

 watcher.Filter = "*.txt|*.doc|*.docx|*.xls|*.xlsx"; 
 // and
 watcher.Filter = "*.txt;*.doc;*.docx;*.xls;*.xlsx*";

Both did not work. This is just basics but I miss it. Thanks..

Upvotes: 77

Views: 66788

Answers (6)

Mrchief
Mrchief

Reputation: 76218

There is a workaround.

The idea is to watch for all extensions and then in the OnChange event, filter out to desired extensions:

FileSystemWatcher objWatcher = new FileSystemWatcher(); 
objWatcher.Filter = ""; // This matches all filenames
objWatcher.Changed += new FileSystemEventHandler(OnChanged); 

private static void OnChanged(object source, FileSystemEventArgs e) 
{ 
    // get the file's extension 
    string strFileExt = getFileExt(e.FullPath); 

    // filter file types 
    if (Regex.IsMatch(strFileExt, @"\.txt)|\.doc", RegexOptions.IgnoreCase)) 
    { 
        Console.WriteLine("watched file type changed."); 
    } 
} 

Upvotes: 60

xandermonkey
xandermonkey

Reputation: 4412

Since .Net Core 3.x and .Net 5 Preview you can simply add multiple filters to the Filters collection.

var watcher = new FileSystemWatcher();
watcher.Path = "/your/path";
watcher.Filters.Add("*.yml");
watcher.Filters.Add("*.yaml");
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
watcher.EnableRaisingEvents = true;

Alternatively if you like object initializers,

var watcher = new FileSystemWatcher
    {
        Path = "/your/path",
        Filters = {"*.yml", "*.yaml"},
        NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName,
        EnableRaisingEvents = true,
    };

Upvotes: 32

jdhurst
jdhurst

Reputation: 4395

You could also filter by using FileInfo by comparing to the string of the extension you're looking for.

For example the handler for a file changed event could look like:

void File_Changed(object sender, FileSystemEventArgs e)
{
    FileInfo f = new FileInfo(e.FullPath);

    if (f.Extension.Equals(".jpg") || f.Extension.Equals(".png"))
    {
       //Logic to do whatever it is you're trying to do goes here               
    }
}

Upvotes: 3

Anders Abel
Anders Abel

Reputation: 69260

You can't do that. The Filter property only supports one filter at a time. From the documentation:

Use of multiple filters such as *.txt|*.doc is not supported.

You need to create a FileSystemWatcher for each file type. You can then bind them all to the same set of FileSystemEventHandler:

string[] filters = { "*.txt", "*.doc", "*.docx", "*.xls", "*.xlsx" };
List<FileSystemWatcher> watchers = new List<FileSystemWatcher>();

foreach(string f in filters)
{
    FileSystemWatcher w = new FileSystemWatcher();
    w.Filter = f;
    w.Changed += MyChangedHandler;
    watchers.Add(w);
}

Upvotes: 94

Joe the Coder
Joe the Coder

Reputation: 1825

To expand on Mrchief's and jdhurst's solution:

private string[] extensions = { ".css", ".less", ".cshtml", ".js" };
private void WatcherOnChanged(object sender, FileSystemEventArgs fileSystemEventArgs)
{
    var ext = (Path.GetExtension(fileSystemEventArgs.FullPath) ?? string.Empty).ToLower();

    if (extensions.Any(ext.Equals))
    {
        // Do your magic here
    }
}

This eliminates the regex checker (which in my mind is too much overhead), and utilizes Linq to our advantage. :)

Edited - Added null check to avoid possible NullReferenceException.

Upvotes: 24

pbls624
pbls624

Reputation: 341

A quick look in the reflector shows that the filtering is done in .Net code after the windows api has reported the file system change.

I'd therefore suggest that the approach of registering multiple watchers is inefficient as you're putting more load on the API causing multiple callbacks and only one of the filters will match. Much better to just register a single watcher and filter the results yourself.

Upvotes: 34

Related Questions