Как обрабатывать индикатор занятости в WPF при использовании асинхронных методов при повторных входящих вызовах? - PullRequest
0 голосов
/ 07 сентября 2018

В элементах управления WPF (например, сетке) мы обычно можем установить логическое свойство, показывающее, что элемент управления занят загрузкой данных, и в пользовательском интерфейсе это приведет к появлению индикатора «Загрузка ...».

Когдаиспользуя async методы, мы просто должны убедиться, что мы поворачиваем IsBusy = "true" перед вызовом метода и IsBusy="false" после await.

Однако в случае, когда я могу вызвать метод загрузки сетки несколько разпосле завершения первого вызова индикатор занятости будет выключен даже во время второго вызова.

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

Пример сценария

На изображении ниже я могу искать имена учеников, и для каждого имени моя служба будет возвращать детали (оценки и т. Д.) И отображать их во второй сетке..

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

При вводе имени следующий методназывается:

Представьте себе GetStudentResults занимает 5 секунд (для каждого звонка).Я ввожу имя в 0 секунд, затем через 3 секунды я ввожу другое имя.Теперь через 5 секунд первый звонок возвращается, и он отключает индикатор занятости, в то время как детали второго имени не возвращаются.Вот чего я хочу избежать.

private async void SearchName(string name)
{
    ResultDisplayGrid.IsBusy = true;
    await GetStudentResults();
    ResultDisplayGrid.IsBusy = false;
}

enter image description here enter image description here

Ответы [ 3 ]

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

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

Самым быстрым и простым методом, по моему мнению, было бы предотвращение взаимодействия пользователя с текстовым полем или графическим интерфейсом пользователя после начала поиска, что предотвращает дополнительные поиски до завершения предыдущего. Это, конечно, будет означать, что пользователям нужно будет ждать завершения каждого поиска, прежде чем начнется следующий.

Мой следующий подход - сохранить задачу GetStudentResults и использовать CancellationToken. Например, SearchName может стать:

private CancellationTokenSource ctsSearch;
private Task tSearch;

private async void SearchName(string name)
{
    if(ctsSearch != null)
    {
        ctsSearch.Cancel();

        if(tSearch != null)
            await tSearch;
    }

    ctsSearch = new CancellationTokenSource();

    ResultDisplayGrid.IsBusy = true;
    tSearch = GetStudentResults(ctsSearch.Token);
    await tSearch;
    ResultDisplayGrid.IsBusy = false;

}

В приведенном выше коде мы отменяем предыдущую задачу, прежде чем снова попытаться запустить GetStudentResults. В вашем методе GetStudentResults вам нужно будет найти места, которые вы можете вставить:

if(token.IsCancellationRequested)
    return Task.FromResult(false); //Replace this return type with whatever suits your GetStudentResults return type.

Мой метод GetStudentResults был:

private Task<bool> GetStudentResults(CancellationToken token)
{
    for(int i = 0; i < 10000; i++)
    {
        if (token.IsCancellationRequested)
            return Task.FromResult(false);

        Console.WriteLine(i);
    }
    return Task.FromResult(true);
}

У кого-то могут быть другие идеи, но для меня это самые простые подходы.

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

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

private CancellationTokenSource tokenSource;

public async void Search(string name)
{
    this.tokenSource?.Cancel();
    this.tokenSource = new CancellationTokenSource();
    var token = this.tokenSource.Token;

    this.IsBusy = true;
    try
    {
        // await for the result from your async method (non void)
        var result = await this.GetStudentResults(name, token);

        // If it was cancelled by re-entry, just return
        if (token.IsCancellationRequested)
        {
            return;
        }

        // If not cancelled then stop busy state
        this.IsBusy = false;
        Console.WriteLine($"{name} {result}");
    }
    catch (TaskCanceledException ex)
    {
        // Canceling the task will throw TaskCanceledException so handle it
        Trace.WriteLine(ex.Message);
    }
}

Кроме того, ваш GetStudentResults должен учитывать токен и прекращать какую-либо фоновую обработку, которую он выполняет, если token.IsCancellationRequested имеет значение true.

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

Попробуйте обернуть ваш асинхронный вызов в блок try-finally, когда все будет сделано, он вызовет finally, чтобы установить для флага IsBusy значение false.

  private async void SearchName(string name)
    {
        ResultDisplayGrid.IsBusy = true;

            try{
                await GetStudentResults();
            }
            finally{
                ResultDisplayGrid.IsBusy = false;
            }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...