Как получить задание для возврата в поток пользовательского интерфейса в WPF - PullRequest
0 голосов
/ 05 сентября 2018

Справочная информация: я относительно опыт в C #, но совершенно не знаком с WPF.

У меня есть приложение WPF, которое будет использоваться внутри для простого мониторинга. У меня есть вызов базы данных, и возвращенные данные затем отображаются в виде дерева, и хотя это происходит, есть наложение с надписью «Загрузка ...», прежде чем данные возвращаются. Моя текущая реализация выглядит так:

await WithOverlay("Loading...", async () =>
{
    MyControl.Items = await _database.Retrieve(messageSummary.Id);
});

, где WithOverlay выглядит так:

private async Task WithOverlay(string overlayMessage, Func<Task> func)
{
    Overlay.Content = overlayMessage;
    Overlay.Visibility = Visibility.Visible;

    await func();

    Overlay.Visibility = Visibility.Hidden;
}

Теперь, это работает просто отлично, но поскольку много раз (в зависимости от того, что именно пользователь просматривает в другом месте приложения), вызов базы данных действительно возвращается быстро наложение «Загрузка ...» просто появляется как раздражающее мерцание. Это всего лишь маленькая вещь, но это беспокоит меня, поэтому я решил, что могу исправить это, вставив небольшой задержка до появляется оверлей; скажем, четверть секунды. Это была моя попытка изменить метод WithOverlay:

private async Task WithOverlay(string overlayMessage, Func<Task> func)
{
    var tokenSource = new CancellationTokenSource();
    var token = tokenSource.Token;

    var overlayTask = Task.Delay(250, token).ContinueWith(_ => {
        Overlay.Content = overlayMessage;
        Overlay.Visibility = Visibility.Visible;
    });

    await func();

    if (token.CanBeCanceled) tokenSource.Cancel();

    Overlay.Visibility = Visibility.Hidden;
}

Моя идея была:

  • создать задачу с задержкой в ​​250 миллисекунд, которая при завершении отобразит оверлей
  • ждем завернутую функцию
  • когда завернутая функция завершается, если задача наложения еще не завершена, отмените ее
  • скрыть наложение (независимо от его текущего состояния)

К сожалению, хотя это не работает; в строке Overlay.Content = overlayMessage я получаю исключение "Вызывающий поток не может получить доступ к этому объекту, поскольку он принадлежит другому потоку." ".

У меня есть подозрение, что это как-то связано с контекстом синхронизации Задачи (если я правильно помню свои технические демонстрации), но я не могу понять, как это контролировать, чтобы получить продолжение возобновить в той же теме.

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Спасибо за ваш ответ, @ mm8. Итак, я сделал это так, мне было бы интересно услышать ваши мысли о том, что это хуже, чем ваш ответ:

    private async Task WithOverlay(string overlayMessage, Func<Task> func)
    {
        var delayTask = Task.Delay(250);
        var wrappedTask = func();

        var completedTask = await Task.WhenAny(delayTask, wrappedTask);

        if (completedTask == delayTask)
        {
            Overlay.Content = overlayMessage;
            Overlay.Visibility = Visibility.Visible;
        }

        await wrappedTask;

        Overlay.Visibility = Visibility.Hidden;
    }

Итак, я запускаю задачу с задержкой 250 мс . Я запускаю завернутую задачу, затем вижу, какая из них закончилась первой, используя Task.WhenAny; если задание на задержку завершено первым, то я отображаю наложение. Затем я жду обернутую задачу и скрываю оверлей.

0 голосов
/ 05 сентября 2018

Вы можете связать TaskScheduler с задачей продолжения, чтобы принудительно установить делегат, который устанавливает свойства Content и Visibility в потоке пользовательского интерфейса:

var overlayTask = Task.Delay(250, token).ContinueWith(_ => {
    Overlay.Content = overlayMessage;
    Overlay.Visibility = Visibility.Visible;
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
...