It's been a long time since last post ...but now it's time to get back to more frequent and regular blogging. Today I'm going to say a couple words about files querying in UWP.
What's wrong with GetFilesAsync()?
In last post about files I have mentioned about StorageFolder method - GetFileAsync that is used to fetch a single file from a folder. To obtain all files at once from a folder we have at our disposal the GetFilesAsync method.
IReadOnlyList<StorageFile> files1 = await storageFolder.GetFilesAsync();
IReadOnlyList<StorageFile> files2 = await storageFolder.GetFilesAsync(CommonFileQuery.OrderByTitle);
IReadOnlyList<StorageFile> files3 = await storageFolder.GetFilesAsync(CommonFileQuery.OrderByTitle,index,numberOfFiles);
As you can see GetFilesAsync is a little bit limited. We can fetch all files, eventually order them with help of CommonFileQuery enum or, paginate the results (with the last overload). Everything is OK until we don't have to filter only couple of files from a folder that contains thousands of them. Fortunately UWP provides solution for these scenarios.
File Queries
To our aid comes method CreateFileQuery, there are also CreateFolderQuery for folders and CreateItemQuery for files and folders but I will focus on the first one.
StorageFileQueryResult query = storageFolder.CreateFileQuery();
IReadOnlyList<StorageFile> files = await query.GetFilesAsync();
CreateFileQuery method returns StorageFileQueryResult instance. StorageFileQueryResult provides us with two most important methods GetFilesAsync - that simply materialize our query and returns StorageFiles and ApplyNewQueryOptions that allows us to update QueryOptions that defines our query. You have to be aware of one thing - default CreateFileQuery, until we apply new QueryOptions on it, doesn't give us anything more than standard storageFolder.GetFilesAsync(). So maybe the faster way is to simply create query with predefined options in single method - CreateFileQueryWithOptions.
var options = new QueryOptions();
options.FolderDepth = FolderDepth.Deep;
StorageFileQueryResult query = storageFolder.CreateFileQueryWithOptions(options);
By QueryOptions we are able to set couple significant factors. Above I added two file extensions to FileTypeFilter and set FolderDepth as deep (that means that query will also look for files in all subfolders, by default CreateFileQuery and GetFilesAsync make only a shallow search). In this way, when I run GetFilesAsync on my query i get only .avi and .wmv files from all subfolders.
Regarding other useful properties of QueryOptions you can use UserSearchFilter that can be used to perform text search. Text search is performed by default on file name but it can be changed with ApplicationSearchFilter property. ApplicationSearchFilter is combined with UserSearchFilter into AQS - Advanced Query Syntax. By IndexerOptions you can force usage either of file system or indexed content (by default system index is used only when it is available for the content). For most common use cases you can also take a look at SortOrder collection that can be populated with custom SortEntry items.
Watching Files
Using file queries and StorageFileQueryResult has one more advantage. We can use it to monitor file changes in queried folder.
StorageFileQueryResult query = storageFolder.CreateFileQueryWithOptions(options);
query.ContentsChanged += OnContentsChanged;
Only action you have to take is registering of handler for ContentsChanged event. ContentsChanged is fired every time when any file is added/deleted/modified in queried folder.
private void OnContentsChanged(IStorageQueryResultBase sender, object args)
Unfortunately we don't get any information about the sort of change or changed file - args doesn't provide such info, because they are always null. Also unfortunately FileTypeFilters are not assembled with watcher. What does it mean? For our above query example, we get same notification when .avi file is added as well as .txt file. Sad conclusion at the end :(

Stay Tunned