You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server(.NET6)加载模态框仅在方法完成后显示问题

Blazor Server加载模态框延迟显示问题解决

问题背景

在.NET 6 Blazor Server项目中,需要实现点击按钮后立即弹出加载模态框,显示“loading...”状态,等待5秒后切换为“Ready”并更新计数器数值。但实际运行时,模态框要等5秒延迟结束后才会打开,直接显示“Ready”状态,完全达不到预期效果。

复现代码

Counter.razor

@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<Modal @ref="modal">
    <Title>This is a <em>Title!</em></Title>
    <Body>
        @if (loading)
        {
            <p>
                Incrementing please wait...
            </p>
        }
        else
        {
            <p>
                Ready
            </p>
        }
    </Body>
    <Footer>
        <button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="() => modal.Close()">Close</button>
    </Footer>
</Modal>
@code {
    private Modal modal;
    private int currentCount = 0;
    private bool loading;
    private async void IncrementCount()
    {
        loading = true;
        modal.Open();
        StateHasChanged();
        Task.Delay(5000).Wait();
        currentCount++;
        loading = false;
    }
}

Modal.razor

<div class="modal @modalClass" tabindex="-1" role="dialog" style="display:@modalDisplay; overflow-y: auto;">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">@Title</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                @Body
            </div>
            <div class="modal-footer">
                @Footer
            </div>
        </div>
    </div>
</div>

@if (showBackdrop)
{
    <div class="modal-backdrop fade show"></div>
}

@code {
    [Parameter]
    public RenderFragment Title { get; set; }

    [Parameter]
    public RenderFragment Body { get; set; }

    [Parameter]
    public RenderFragment Footer { get; set; }

    private string modalDisplay = "none;";
    private string modalClass = "";
    private bool showBackdrop = false;

    public async void Open()
    {
        modalDisplay = "block;";
        modalClass = "show";
        showBackdrop = true;
    }

    public void Close()
    {
        modalDisplay = "none";
        modalClass = "";
        showBackdrop = false;
    }
}

问题原因

  1. 同步阻塞UI线程:使用Task.Delay(5000).Wait()进行同步等待,直接阻塞了Blazor的UI主线程,导致所有UI更新操作(包括模态框显示)都被暂停,直到阻塞结束后才会一次性执行。
  2. 状态更新未正确触发:Modal组件的Open方法修改了状态变量,但没有调用StateHasChanged()通知框架重新渲染组件;Counter组件中调用StateHasChanged()后,由于UI线程被阻塞,无法立即执行渲染。
  3. 不当使用async void:虽然Blazor事件处理方法允许使用async void,但配合正确的异步操作,使用async Task会更规范。

正确解决方案

1. 修改Counter组件的IncrementCount方法

将同步等待改为异步等待,确保UI线程不被阻塞,并正确触发状态更新:

private async Task IncrementCount()
{
    loading = true;
    modal.Open();
    // 触发UI更新,立即显示模态框和loading状态
    await InvokeAsync(StateHasChanged);
    
    // 异步等待,释放UI线程处理渲染
    await Task.Delay(5000);
    
    currentCount++;
    loading = false;
    // 更新UI,显示Ready状态和新计数
    await InvokeAsync(StateHasChanged);
}

2. 修正Modal组件的Open方法

移除不必要的async,并添加状态更新触发:

public void Open()
{
    modalDisplay = "block;";
    modalClass = "show";
    showBackdrop = true;
    // 通知组件状态变化,触发渲染
    StateHasChanged();
}

临时替代方案

如果需要快速验证,也可以通过后台线程触发UI更新(但不推荐作为长期方案):

private async void IncrementCount()
{
    await Task.Run(() => {
        loading = true;
        modal.Open();
        InvokeAsync(StateHasChanged);
    });
    Task.Delay(2000).Wait();
    currentCount++;
    loading = false;
    StateHasChanged();
}

说明:该方案通过Task.Run将模态框显示操作放到后台线程,再通过InvokeAsync触发UI更新,避免阻塞主线程。但仍然使用了同步等待Wait(),不符合异步编程规范,仅作临时验证使用。

内容的提问来源于stack exchange,提问作者mariano desivo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:57:09