Home > Mobile >  Filter an ObservableCollection of DirectoryInfo with LINQ
Filter an ObservableCollection of DirectoryInfo with LINQ

Time:05-18

I'm using MVVM to populate a TreeView with a DirectoryInfo and all its sub-directories and files.

I then want to search through the treeview with LINQ .where() but I'm only searching the top directories and not the files.

I'm creating the treeview/DirectoryInfo with a recursive method:

  public class ItemProvider
    {
        public ObservableCollection<Item> GetItems(string path)
        {
            var items = new ObservableCollection<Item>();

            if (System.IO.Directory.Exists(path))
            {
                

                var dirInfo = new DirectoryInfo(path);

                foreach (var directory in dirInfo.EnumerateDirectories())
                {
                    var item = new DirectoryItem
                    {
                        Name = directory.Name,
                        Path = directory.FullName,
                        Items = GetItems(directory.FullName)
                    };

                    items.Add(item);
                }

                foreach (var file in dirInfo.EnumerateFiles())
                {
                    InteropBitmap thumbImg = ShellFile.FromFilePath(file.FullName).Thumbnail.BitmapSource as InteropBitmap;
                    thumbImg.Freeze();

                    var item = new FamilyItem
                    {
                        Name = file.Name,
                        Path = file.FullName,
                        FamImage = thumbImg
                    };

                    if (item.Name.Contains(".rfa"))
                    {

                    items.Add(item);

                    }

                }

                return items;
            }
            else
            {

                return items;
            }
        }
            
       

DirectoryItem and FamilyItem both inherits a model called Item:

Item model:

public class Item
    {
        public string Name { get; set; }
        public string Path { get; set; }
        public InteropBitmap FamImage { get; set; }
    }

DirectoryItem model:

public class DirectoryItem : Item

    {
        public ObservableCollection<Item> Items { get; set; }
    
        public DirectoryItem()
        {
        Items = new ObservableCollection<Item>();
        }
    }

FamilyItem:

 public class FamilyItem : Item
    {

    }

I'm filtering the collection with:

Items = new ObservableCollection<Item>(ItemsCache.Where(x => x.Name.ToLower().Contains(SearchText.ToLower())));

I only get the top directories that match - I want to get all the "end" files that match - how would I go about doing that?

CodePudding user response:

OK, modified the answer. You have to flatten your collection berforehand (or collect it flattened).

public ObservableCollection<Item> GetItemsFlat(string path)
        {
            var items = new ObservableCollection<Item>();

            if (!System.IO.Directory.Exists(path))
                return items;
            
            var dirInfo = new DirectoryInfo(path);

            foreach (var directory in dirInfo.EnumerateDirectories())
            {
                var item = new DirectoryItem
                {
                    Name = directory.Name,
                    Path = directory.FullName,
                    Items = GetItems(directory.FullName)
                };

                items.Add(item);
                foreach (var item in GetItemsFlat(directory.FullName))
                    items.Add(item);
            }

            foreach (var file in dirInfo.EnumerateFiles("*.rfa"))
            {
                InteropBitmap thumbImg = ShellFile.FromFilePath(file.FullName).Thumbnail.BitmapSource as InteropBitmap;
                thumbImg.Freeze();

                var item = new FamilyItem
                {
                    Name = file.Name,
                    Path = file.FullName,
                    FamImage = thumbImg
                };
                items.Add(item)
            }
        }

Use the Path (ie. FullName) property for filtering, to include files in matched subdirectories, ie.

ItemsCache.Where(x => x.Path.ToLower().Contains(SearchText.ToLower()))

also refactor Your code to use "return early" strategy. (https://dev.to/jpswade/return-early-12o5)

Ie. instead of if (System.IO.Directory.Exists(path)) { ...lots of code } do

if (!System.IO.Directory.Exists(path)) return EmptyCollection;`

...lots of code

You could also improve this code to discard 'empty' directory subtrees from collection.

Also check GetFileSystemInfo https://docs.microsoft.com/en-us/dotnet/api/system.io.directoryinfo.getfilesysteminfos?view=net-6.0 which can be used to search files including subdirectories

CodePudding user response:

You can override .Where(..) by a recursive version:

   public static class ExtenstioMethods
   {
      public static IEnumerable<Item> Where<Item>(this IEnumerable<Item> source, Func<Item, bool> predicate)
      {
         var result = new List<Item>();
         foreach (var item in source)
         {
            if ((item is FamilyItem) && predicate(item))
            {
               result.Add(item);
            }
            else if (item is DirectoryItem)
            {
               var newSourc = ((item as DirectoryItem).Items) as IEnumerable<Item>;
               result = result.Union(newSourc.Where(predicate)).ToList();
            }
         }
         return result;
      }
   }

  • Related