Search code examples
uwpstoragefile

StorageFileQueryResult.GetFilesAsync() proper usage with IndexerOption in UWP


I am wondering if the following is a bug or at best a documentation inaccuracy of the Windows Search API for a StorageFolder.

This is a simple UWP app which allows a user to pick a folder and scan this folder deeply to simply count the number of files.

The returned count is consistent with Windows 10's File Explorer if the folder is sitting here for a while, but inconsistent if the folder has been just copied to the C drive from another partition on my laptop. To make the issue clear I copied a folder containing 1768 video and image files into the desktop and run the app on the spot such that Windows 10 did not have the time to index the content.

The C# code (XAML is simple and can be guessed from the C# code)

    private async void AppBarButton_Click(object sender, RoutedEventArgs e)
    {
        var folderPicker = new FolderPicker
        {
            SuggestedStartLocation = PickerLocationId.Desktop,
            ViewMode = PickerViewMode.Thumbnail

        };

        folderPicker.FileTypeFilter.Add("*");

        _folder = await folderPicker.PickSingleFolderAsync();
        if (_folder != null)
        {
            resultStackPanel.Children.Clear();
            var indexState = await _folder.GetIndexedStateAsync();
            bottomInfoTextBlock.Text = indexState.ToString();
            var queryOptions = new QueryOptions { FolderDepth = FolderDepth.Deep };
            foreach (IndexerOption indexerOption in Enum.GetValues(typeof(IndexerOption)))
            {
                queryOptions.IndexerOption = indexerOption;
                await CountFilesAsync(queryOptions);
            }
        }
    }

    private async Task CountFilesAsync(QueryOptions queryOptions)
    {
        var textBlock = new TextBlock();
        resultStackPanel.Children.Add(textBlock);

        if (!_folder.AreQueryOptionsSupported(queryOptions))
        {
            textBlock.Text = $"Query unsupported for {queryOptions.IndexerOption}";
            return;
        }
        var query = _folder.CreateFileQueryWithOptions(queryOptions);

        textBlock.Text = $"{queryOptions.IndexerOption}: Wait, getting files count ...";
        var numOfFiles = (await query.GetFilesAsync()).Count;
        textBlock.Text = $"{queryOptions.IndexerOption} {numOfFiles}";
    }
}

I obtain the following result as I loop through the IndexerOption Enum

Scan folder result

What I expected is that the IndexerOption.UseIndexerWhenAvailable would return the exact count of 1768, as it is documented as

Use the system index for content that has been indexed and use the file system directly for content that has not been indexed.

What I worry about is that I would like to avoid using DoNotUseIndexer Enum value because of performance as I plan to retrieve thumbnails and extra properties from each the storage file, but this option is the only one that gives the correct count.

I checked out that the Photos app from Microsoft loads only a portion of the content, i.e. as if it uses UseIndexerWhenAvailable, but it has the capability to update itself as I wait for 1-2 minutes.

Hence I wonder if:

1) My only solution to provide the user with an accurate result is to use the DoNotUseIndexer Enum or is it possible to UseIndexerWhenAvailable or ideally OnlyUseIndexerAndOptimizeForIndexedProperties but track by some event when Windows 10 eventually indexed all files to notify my app?

2) What is UseIndexerWhenAvailable actually doing? It seems inconsistent with its description

Thank you


Solution

  • The following allows to inform the app as the file system changes. In the calling method fire the event

                query.ContentsChanged += Query_ContentsChanged;
    

    with

       private async void Query_ContentsChanged(IStorageQueryResultBase sender, object args)
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
            {
                var count = await sender.GetItemCountAsync();
                var textBlock = new TextBlock();
                resultStackPanel.Children.Add(textBlock);
                textBlock.Text = $"Changed {count} {sender.Folder.Name}";
            });
        }
    

    The local variable count can be set as a property in the Page and, e.g. OneWay bound to a XAML TextBlock.Text with INotifyPropertyChanged. In this way the value will be updated as Windows indexes all the files and after a minute or so the count reaches the final value of 1768. In addition, a removal or addition of files in the folder will update the UI count. The only caution is if you rename the folder at some point. In this case the app will start counting the default SuggestedStartLocation, which is the Desktop in my case. Hence the utility of checking sender.Folder.Name against the picked _folder (code not shown).