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;
}
}