Вывод прогресса асинхронной операции во вторичное окно

Есть метод, загружающий данные из файла. О своём прогрессе он сообщает через вызов события OnFileLoad

    public Task UploadDataAsync()
    {
        return Task.Run(() =>
        {
            ...
            if (string.IsNullOrEmpty(FilePath))
            {
                OnFileLoading?.Invoke($"Error while loading: Path is empty");
                ...
                throw new Exception("Path is emprty");
            }

            if (!File.Exists(FilePath))
            {
                OnFileLoading?.Invoke($"Error while loading: File not found");
                ...
                throw new Exception("File not found");
            }

            OnFileLoading?.Invoke($"Start loading from '{FilePath}'");
            ...
            OnFileLoading?.Invoke("Loaded first line");
            OnFileLoading?.Invoke("Loaded second line");
            ...
            OnFileLoading?.Invoke("Loaded n line");
            ...
            OnFileLoading?.Invoke("Loading completed");
        });            
    }

В классе главного окна есть обработчик события нажатия на кнопку "Открыть". В нём создаётся новое окно, в котором показывается прогресс загрузки данных из файла.

    public async void OpenFileClick(object sender, RoutedEventArgs e)
    {
        try
        {
            var dataProvider = IocContainer.GetService<IDataProvider>();
            LoadingDataStatusWindow loadingDataStatusWindow = new LoadingDataStatusWindow();
            dataProvider.OnFileLoad = loadingDataStatusWindow.UpdateLoadingStatus;
            loadingDataStatusWindow.Show();
            dataProvider.FilePath = AppGlobalSettings.DataFilePath;
            await dataProvider.UploadDataAsync();
            items = dataProvider.CircuitItems;
            CircuitItemDataGrid.ItemsSource = dataProvider.CircuitItems;
        }
        catch(Exception ex)
        {
            LogUtility.LogError(ex);
        }
    }

Метод отображения прогресса окна LoadingDataStatusWindow:

    public void UpdateLoadingStatus(string currentRow)
    {            
        StatusTextBox.Text += currentRow + Environment.NewLine;          
    }

При попытке загрузить данные возникает ошибка, говорящая о том, что поток пытается обратиться к объекту, владельцем которого является другой поток. Я пробовал починить это, сделав добавление текста в StatusTextBox через Dispatcher. Но это не помогло.

Как в данном случае можно организовать вывод прогресса во второе окно с асинхронной загрузкой данных, чтобы она не подвешивала GUI?


Ответы (1 шт):

Автор решения: VladD

Как совершенно верно заметил @aepot в комментариях, для сообщения о прогрессе операции используйте Progress<T>, он именно для этого и предназначен, и сам маршаллирует в нужный контекст.

Попробуйте как-то так:

public async void OpenFileClick(object sender, RoutedEventArgs e)
{
    // ...
    Progress<string> p = new(s => StatusTextBox.Text += s + Environment.NewLine);
    await UploadDataAsync(p);
    // ...
}

и соответственно

public async Task UploadDataAsync(IProgress<string> progress)
{
    // ...
    progress.Report($"Start loading from '{filePath}'");
    // ...
    progress.Report("Loaded first line");
    // ...
    progress.Report("Loaded second line");
    // ...
    progress.Report("Loaded n line");
    // ...
    progress.Report("Loading completed");
    // ...
}
→ Ссылка