Как не заморозить интерфейс при использовании asyn c Task ConfigureAwait (true) - PullRequest
0 голосов
/ 23 января 2020

У меня есть приложение WPF (UI), которое извлекает данные из базы данных SQL во время инициализации / загрузки. Чтобы ускорить время запуска приложения (и не зависать из-за загрузки данных), я попытался сделать несколько вещей, чтобы загрузить данные асинхронно, но без особой удачи с точки зрения UX, который я ожидал.

В основном, пользовательский интерфейс все еще зависает (даже если это ненадолго).

Вот что я пробовал:

Метод загрузки данных помечается asyn c Задача , Я вызываю этот метод следующим образом:

// Note that i use ConfigureAwait(true) because the data is being loaded from the UI.
LoadData().CongigureAwait(true);

Вот метод:

        private async void LoadData()
        {
            try
            {
                using (var context = new DataModel.BusinessData())
                {
                    var people= await context.People
                                            .ToListAsync()
                                            .ConfigureAwait(true);

                    foreach (var person in people)
                    {
                        this.People.Add(new PersonItem(person));
                    }
                }
            }
            catch (Exception ex)
            {
               throw;
            }
        }

Теперь, несмотря на асинхронный характер этого поиска данных, он обрабатывается обратно в главном потоке ( Пользовательский интерфейс). Опять же, это вызывается из пользовательского интерфейса (шаблон MVVM), поэтому мне нужно это.

Конечный результат таков:

Я все еще вижу короткий момент UI зависает во время запуска.

Вопрос:

Как данные могут быть получены и обработаны потоком пользовательского интерфейса БЕЗ замораживания пользовательского интерфейса? Возможно ли это даже при обработке потоком пользовательского интерфейса?

Ответы [ 2 ]

1 голос
/ 24 января 2020

Это только фактический вызов ToListAsync, который является асинхронным. Остальная часть вашего кода выполняется в потоке пользовательского интерфейса.

Если People содержит много предметов, рендеринг может быть медленным.

Вы можете попробовать передать свойство People в новую коллекцию вместо того, чтобы добавлять элементы в коллекцию с привязкой к данным один за другим:

private async Task LoadData()
{
    using (var context = new DataModel.BusinessData())
    {
        var people = await context.People.ToListAsync();
        this.People = await Task.Run(() => new ObservableCollection(people.Select(person => new PersonItem(person))));
    }
}

Не забудьте реализовать INotifyPropertyChanged и вызвать событие PropertyChanged для свойства People.

Вы также можете попытаться выполнить весь код в фоновом потоке, если реализация ToListAsync фактически блокирует:

private async Task LoadData()
{
    await Task.Run(() =>
    {
        using (var context = new DataModel.BusinessData())
        {
            this.People = new ObservableCollection(context.People.Select(person => new PersonItem(person)).ToArray());
        }
    });
}

Если он все еще медленный, вам нужно либо уменьшить количество отображаемых элементов, либо использовать более быстрый компонент для отображения элементов.

0 голосов
/ 24 января 2020

Попробуйте вызвать ваш метод с помощью Dispatcher. Это гарантирует, что ваш экран не зависает.

Dispatcher.Invoke(() =>
        {
            try
        {
            using (var context = new DataModel.BusinessData())
            {
                var people= await context.People
                                        .ToListAsync()
                                        .ConfigureAwait(true);

                foreach (var person in people)
                {
                    this.People.Add(new PersonItem(person));
                }
            }
        }
        catch (Exception ex)
        {
           /*throw; This exception will not be available outside.
           Use flag variable for operation success/failure */
        }
        });
...