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

MudDialog写入XML时进度对话框无法实时弹出问题排查

问题:进度对话框在任务完成后才显示,而非处理过程中

我创建了一个包含MudProgressCircular的自定义对话框组件(Child.razor),原本应该在将零件详情写入XML输出文件时显示进度,但实际问题是对话框不会在保存文件的过程中打开,而是要等整个任务完成后才弹出。

Parent.razor

<MudButton Class="mt-2" OnClick="@(() => OnClickedAsync())"  Color="Color.Primary" Size="Size.Small" Edge="Edge.Start">Process Parts</MudButton>

@code {
    private List<Part> partItems;

    private async Task OnClickedAsync()
    {
        using (var handler = new HttpClientHandler())
        {
            using (var httpClient = new HttpClient(handler))
            {
                serviceEndpoint = $"{Config.GetValue<string>("API")}/Parts";
                var parts = await httpClient.GetFromJsonAsync<Part[]>(serviceEndpoint);
                partItems = parts.ToList();
            }
        }

        ProcessParts(partItems);
    }

    private void ProcessParts(List<Part> parts)
    {
        var options = new DialogOptions
            {
                FullWidth = true,
                MaxWidth = MaxWidth.ExtraExtraLarge,
                CloseButton = true,
                Position = DialogPosition.Center,
                CloseOnEscapeKey = true
            };
        var param = new DialogParameters();

        param.Add("Parts", parts);

        var dialogRef = Dialogsrv.Show<Child>($"Processing parts", param, options);
}

Child.razor

<MudDialog Style="width:fit-content">
    <DialogContent>
        <MudProgressCircular Color="Color.Primary" Value="@_value" Size="Size.Large">
            @_value.ToString("N0") %
        </MudProgressCircular>
    </DialogContent>
    <DialogActions>
        <MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="Close">Close</MudButton>
    </DialogActions>
</MudDialog>

@code {
    [CascadingParameter]
    private IMudDialogInstance ThisDialog { get; set; }

    [Parameter] public List<Part> Parts { get; set; }

    private int count = 0;
    private double _value;

    protected override void OnInitialized()
    {
        ProcessParts(Parts);
    }

    private void ProcessParts(List<Part> parts)
    {
        XDocument doc = XDocument.Load("Contents\\Template.xml");
        XElement root = doc.Root;
        foreach (var part in parts)
        {
            root.Element("Parts").Add(
                new XElement("Part",
                    new XElement("PartNo", part.Part),
                    new XElement("Filename", part.FileName),
                    new XElement("Material", part.Material),
                    new XElement("CustomerName", part.CustomerName)
            ));
            count++;
            _value = 100 * count / parts.Count;
        }

        string fullPath = Path.Combine(_filePath, "Parts.xml");
        using (StreamWriter stream = new StreamWriter(fullPath, false, Encoding.GetEncoding("Windows-1252")))
        {
            doc.Save(stream);
        }
    }
}

已确认事项

  • MainLayout.razor中已存在MudDialogProvider
  • 对话框服务已在依赖注入中注册

我曾以为是async/await问题,把Child.razor中的方法改成了同步,但问题依然存在。


解决方案

问题核心是同步耗时操作阻塞了Blazor的UI线程,导致对话框无法及时渲染,进度更新也无法实时显示。Blazor的UI更新依赖于同步上下文,长时间的同步任务会占据线程,直到任务结束才会处理UI渲染请求。

修改Child.razor的关键步骤:

  1. 将初始化方法改为异步的OnInitializedAsync,避免阻塞组件初始化流程
  2. 将耗时的ProcessParts操作放到后台线程执行(用Task.Run)
  3. 在进度更新后,调用InvokeAsync(StateHasChanged)通知UI更新

修改后的Child.razor代码:

<MudDialog Style="width:fit-content">
    <DialogContent>
        <MudProgressCircular Color="Color.Primary" Value="@_value" Size="Size.Large">
            @_value.ToString("N0") %
        </MudProgressCircular>
    </DialogContent>
    <DialogActions>
        <MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="Close" Disabled="@_isProcessing">Close</MudButton>
    </DialogActions>
</MudDialog>

@code {
    [CascadingParameter]
    private IMudDialogInstance ThisDialog { get; set; }

    [Parameter] public List<Part> Parts { get; set; }

    private int count = 0;
    private double _value;
    private bool _isProcessing;

    protected override async Task OnInitializedAsync()
    {
        _isProcessing = true;
        // 将耗时操作放到后台线程,避免阻塞UI
        await Task.Run(() => ProcessParts(Parts));
        _isProcessing = false;
        // 任务完成后可选择自动关闭对话框,或保留让用户手动关闭
        // ThisDialog.Close(DialogResult.Ok(true));
    }

    private void ProcessParts(List<Part> parts)
    {
        XDocument doc = XDocument.Load("Contents\\Template.xml");
        XElement root = doc.Root;
        foreach (var part in parts)
        {
            root.Element("Parts").Add(
                new XElement("Part",
                    new XElement("PartNo", part.Part),
                    new XElement("Filename", part.FileName),
                    new XElement("Material", part.Material),
                    new XElement("CustomerName", part.CustomerName)
            ));
            count++;
            _value = 100 * (double)count / parts.Count;
            
            // 通知UI更新进度
            InvokeAsync(StateHasChanged);
            
            // 可选:模拟慢进度用于测试
            // Thread.Sleep(50);
        }

        string fullPath = Path.Combine(_filePath, "Parts.xml");
        using (StreamWriter stream = new StreamWriter(fullPath, false, Encoding.GetEncoding("Windows-1252")))
        {
            doc.Save(stream);
        }
    }

    private void Close() => ThisDialog.Close(DialogResult.Cancel());
}

额外说明:

  • 使用Task.Run将IO/CPU密集型操作移到后台线程,释放UI线程处理渲染和用户交互
  • InvokeAsync(StateHasChanged)确保进度更新能触发UI重新渲染(后台线程无法直接更新Blazor UI)
  • 添加_isProcessing变量禁用Close按钮,避免用户在任务未完成时关闭对话框
  • 修正路径转义符:原代码中"Contents\Template.xml"会被解析为转义字符,改为"Contents\\Template.xml"避免文件找不到问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 21:05:16