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

WPF应用更新器UI不更新但Debug.WriteLine输出正常问题求助

应用更新器UI无法同步下载进度问题

已完成应用更新器核心逻辑,但遇到UI无法同步下载进度的问题:实现了async异步任务和OnPropertyChanged事件,Debug.WriteLine能正确输出进度值,断点显示OnPropertyChanged已接收更新,但ProgressBar与TextBlock控件的UI始终不更新。

相关代码

下载方法代码

private async Task DownloadUpdateAsync(string url, string destinationPath)
{
using (var client = new HttpClient())
{
    var response = await client.GetAsync(url, 
    HttpCompletionOption.ResponseHeadersRead);
    response.EnsureSuccessStatusCode();

    var totalBytes = response.Content.Headers.ContentLength ?? 0;
    var readBytes = 0L;
    var buffer = new byte[8192];
    using (var stream = await response.Content.ReadAsStreamAsync())
    using (var fileStream = new FileStream(destinationPath, FileMode.Create, 
    FileAccess.Write, FileShare.None))
    {
        await Task.Run(async () =>
        {
            while (true)
            {
                var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                if (bytesRead == 0)
                {
                    await Task.Yield();
                    break;
                }

                await fileStream.WriteAsync(buffer, 0, bytesRead);
                readBytes += bytesRead;

                Application.Current.Dispatcher.Invoke(() => {
                    DownloadProgress = (int)((readBytes / (float)totalBytes) * 100);
                    Process = DownloadProgress.ToString();
                    OnPropertyChanged(nameof(DownloadProgress));
                    OnPropertyChanged(nameof(Process));
                    Debug.WriteLine(Process);
                });
            }
        });
    }
}

DownloadProgress属性定义

public int DownloadProgress
{
    get => downloadProgress;
    set
    {
        if (downloadProgress != value)
        {
            downloadProgress = value;
            OnPropertyChanged(nameof(DownloadProgress));
        }
    }
}

XAML代码

<Window.DataContext>
    <viewmodel:UpdateViewModel/>
</Window.DataContext>

<Window.Resources>
    <Style x:Key="CustomProgressBar" TargetType="ProgressBar">
        <Setter Property="Foreground" Value="#DCA3FF"/>
        <Setter Property="Background" Value="#7C8093"/>
        <Setter Property="BorderBrush" Value="Transparent"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="Height" Value="20"/>
    </Style>
</Window.Resources>

<Grid>
    <Border Background="#7C8093" CornerRadius="5">
        <StackPanel>
            <TextBlock Foreground="White" Text="An update is available."/>
            <TextBlock Foreground="White" Text="Change Log: update test"/>
            <Button  Content="Update Now" Command="{Binding UpdateCommand}" />
            <ProgressBar Height="20" Value="{Binding DownloadProgress, Mode=OneWay}" />
            <TextBlock Width="700" Text="{Binding Process, UpdateSourceTrigger=PropertyChanged}"/>
            <Button Content="Test Progress" Command="{Binding TestProgressCommand}" />
        </StackPanel>
    </Border>
</Grid>

问题根源与修复方案

  1. 移除冗余的Task.Run与Dispatcher调用
    异步流操作本身会自动捕获UI上下文(如果调用起始于UI线程),嵌套Task.Run会导致不必要的线程切换,加上Dispatcher.Invoke的冗余调用反而可能引发同步问题。直接在异步循环中更新属性即可。

  2. 避免重复触发OnPropertyChanged
    DownloadProgress的setter已经包含OnPropertyChanged触发逻辑,无需在Dispatcher中手动调用。同时需要给Process属性补充完整的setter通知逻辑。

  3. 处理Content-Length为0的边界情况
    当服务器未返回Content-Length时,避免除以0的计算错误,给出友好提示。

  4. 确保ViewModel的INotifyPropertyChanged实现正确
    确认ViewModel正确继承接口,且OnPropertyChanged方法实现无误。

修改后的核心代码

下载方法
private async Task DownloadUpdateAsync(string url, string destinationPath)
{
    using (var client = new HttpClient())
    {
        var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
        response.EnsureSuccessStatusCode();

        var totalBytes = response.Content.Headers.ContentLength ?? 0;
        var readBytes = 0L;
        var buffer = new byte[8192];
        using (var stream = await response.Content.ReadAsStreamAsync())
        using (var fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            while (true)
            {
                var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                if (bytesRead == 0)
                    break;

                await fileStream.WriteAsync(buffer, 0, bytesRead);
                readBytes += bytesRead;

                if (totalBytes > 0)
                {
                    DownloadProgress = (int)((readBytes / (float)totalBytes) * 100);
                    Process = $"{DownloadProgress}%";
                }
                else
                {
                    Process = "正在下载...";
                }
            }
        }
    }
}
Process属性实现
private string process;
public string Process
{
    get => process;
    set
    {
        if (process != value)
        {
            process = value;
            OnPropertyChanged(nameof(Process));
        }
    }
}
ViewModel基础实现示例
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class UpdateViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // 其他属性和命令定义...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 14:19:54