异步读取大型 txt 文件并在进度条 WPF C# 中报告进度

Reading large txt file async and reporting progress in progressbar WPF C#

我正在尝试异步读取一个大的 txt 文件(>50MB),在读取时,在 UI 进度条上报告进度,并可以选择取消该过程。到目前为止,我已经按照我的意愿读取并处理了文件异步,但我无法解决进度条部分。

public static async Task<string> ReadTxtAsync(string filePath)
    {
        try
        {
            using (var reader = File.OpenText(filePath))
            {
                var content = await reader.ReadToEndAsync();
                return content;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
            return null;
        }
    }

        
 public static async Task<Dictionary<string, int>> OpenTxtAsync()
    {
        Dictionary<string, int> uniqueWords = new Dictionary<string, int>();
        OpenFileDialog openFileDialog = new OpenFileDialog();
        openFileDialog.Filter = "Text Documents (*.txt)|*.txt";
        string content = null;
        try
        {
            if (openFileDialog.ShowDialog() == true)
            {
                string filePath = openFileDialog.FileName.ToString();

                if (openFileDialog.CheckFileExists && new[] { ".txt" }.Contains(Path.GetExtension(filePath).ToLower()) && filePath != null)
                {
                    Task<string> readText = ReadTxtAsync(filePath);
                    content = await readText;
                    uniqueWords = WordExtractor.CountWords(ref content);
                }
                else MessageBox.Show("Please use .txt format extension!");
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
        return uniqueWords;
    }

private async void LoadFileButtonClick(object sender, RoutedEventArgs e)
    {
        Task<Dictionary<string, int>> dictionaryContent = TextFileLoader.OpenTxtAsync();
        uniqueWords = await dictionaryContent;
        UpdateListView();
    }

如何查看 ReadToEndAsync() 当前所在的位置?怎么让它不断更新进度条,怎么取消?

编辑: 感谢@emoacht,我设法让进度条正确更新并显示其百分比。唯一剩下的就是取消任务,我根据 Tim Corey 的视频尝试过,但它在我的代码上不起作用。

        public static async Task<string> ReadTextAsync(string filePath, IProgress<(double current, double total)> progress, CancellationToken cancellationToken)
    {
        using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        using var reader = new StreamReader(stream);
        var readTask = reader.ReadToEndAsync();
        cancellationToken.ThrowIfCancellationRequested();

        var progressTask = Task.Run(async () =>
        {
            while (stream.Position < stream.Length)
            {
                await Task.Delay(TimeSpan.FromMilliseconds(100));
                progress.Report((stream.Position, stream.Length));
            }
        });

        await Task.WhenAll(readTask, progressTask);
        return readTask.Result;
    }

                        try
                    {
                        Task<string> readText = TextFileLoader.ReadTextAsync(filePath, progress, cts.Token);
                        content = await readText;
                        LabelProgress.Content = "Done Reading! Now creating wordlist...";
                    }
                    catch (OperationCanceledException)
                    {

                        LabelProgress.Content = "File laden wurde abgebrochen";
                    }

我有一个用于取消的 buttonClick 事件 cts.Cancel(); 但它唯一起作用的部分是字典创建。如果我将 cancellationToken.ThrowIfCancellationRequested(); 放入进度条更新部分,它只会停止更新,流读取仍在继续。如果我放置在 var readTask = reader.ReadToEndAsync(); 正下方,它什么都不做。

您可以通过定期检查Stream.Position 属性 来获取阅读时的当前位置。以下方法将每 100 毫秒检查一次当前位置,并通过 progress 参数的 current 值报告。要使用此方法,请实例化 Progess<(double current, double total)> 并订阅其 ProgressChanged 事件。

public static async Task<string> ReadTextAsync(string filePath, IProgress<(double current, double total)> progress)
{
    using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    using var reader = new StreamReader(stream);

    var readTask = reader.ReadToEndAsync();

    var progressTask = Task.Run(async () =>
    {
        while (stream.Position < stream.Length)
        {
            await Task.Delay(TimeSpan.FromMilliseconds(100));
            progress.Report((stream.Position, stream.Length));
        }
    });

    await Task.WhenAll(readTask, progressTask);

    return readTask.Result;
}