#blazor #blazor-server-side
Вопрос:
Когда пользователь нажимает на определенную кнопку, мне нужно показать анимацию внутри кнопки и отключить ее ( для выполнения метода требуется несколько секунд). Это мой html:
<div class="row">
<div class="col-lg-4">
@if (IsDownloading)
{
<button class="btn btn-primary float-right search disabled" disabled><span class='fa-left fas fa-sync-alt spinning'></span>Downloading...</button>
}
else
{
<button id="REC_PDF" class="btn btn-primary" @onclick="@(()=>getPDF())">Download Labels</button>
}
</div>
</div>
и это мой код:
protected bool IsDownloading { get; set; }
public async Task getPDF()
{
IsDownloading = true;
StateHasChanged();
await generate_pdf(labels);
IsDownloading = false;
StateHasChanged();
}
private async Task generate_pdf(List<Tuple<string, string, string, string>> filtered_label)
{
loading = true;
string PDF_PATH = "./PDF/" CurrentValue;
foreach (Tuple<string,string,string,string> label in filtered_label)
{
if (!Directory.Exists(PDF_PATH))
{
Directory.CreateDirectory(PDF_PATH);
}
.......
Код работает, но рендеринг страницы, вызванной StateHasChanged(); выполняется 2 раза, когда метод getPDF ( и generate_PDF последовательно) полностью выполнен.
Мне нужен рендеринг страницы при первом вызове, чтобы показать анимацию загрузки css.
( как предлагалось в другом посте, я использовал InvokeAsync(() => StateHasChanged()); но не работает)
Спасибо.
Комментарии:
1.
@onclick="@(()=>getPDF())"
<- Почему? Удалив всю информацию о задаче и, таким образом, ничего не ожидая.@onclick="getPDF"
это правильный путь… но не причина вашей проблемы.
Ответ №1:
Событие щелчка обрабатывается следующим образом в Blazor:
var task = InvokeAsync(EventMethod);
StateHasChanged();
if (!task.IsCompleted)
{
await task;
StateHasChanged();
}
Примечание StateHasChanged
на самом деле не выполняет повторную визуализацию компонента, он просто помещает RenderFragment
делегата в очередь визуализации.
Весь код выполняется в потоке «Пользовательский интерфейс», поэтому событие рендеринга, поставленное в очередь при вызове StateHasChanged
, выполняется только либо:
- в этот момент метод события дает контроль
- если выход не произойдет (потому что в блоке задач есть только код синхронизации) в конце.
Я не вижу никакого кода, который дает результат в вашем generate_pdf
.
Попробуйте сделать следующее:
public async Task getPDF()
{
IsDownloading = true;
await Task.Yield();
await generate_pdf(labels);
IsDownloading = false;
}
Примечание: Directory.CreateDirectory
не является асинхронным методом. Имеет только FileStream
асинхронные методы.
Комментарии:
1. Ладно, я понял. Это реализация в рабочем приложении, поэтому я пытался реализовать эту функцию без переписывания структуры метода. Большое спасибо.
2. Спасибо вам за объяснение. После 2 дней перепробования множества различных подходов, ВЫХОД был тем, что мне было нужно.
Ответ №2:
Проблема в том, что generate_pdf()
это не асинхронно. Добавления async
недостаточно. Вам нужно что-то асинхронное, чтобы включить рендеринг.
Поскольку это проблема пользовательского интерфейса, я предлагаю решить ее на верхнем уровне:
public async Task getPDF()
{
IsDownloading = true;
//StateHasChanged(); // not needed at start
await task.Delay(1); // allow the UI to update
try
{
await generate_pdf(labels);
}
finally
{
IsDownloading = false;
}
//StateHasChanged(); // not needed at the end
}
Когда функция generate_pdf() сама по себе ничего не делает await
, вы также можете сделать ее обычным методом void.
Ответ №3:
Попробуйте эти модификации:
public async Task getPDF()
{
if (IsDownloading) return;
IsDownloading = true;
StateHasChanged();
await generate_pdf(labels);
}
затем в конце generate_pdf
(лучше внутри блока try/finally) добавьте:
IsDownloading = false;
StateHasChanged();