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>
问题根源与修复方案
移除冗余的Task.Run与Dispatcher调用
异步流操作本身会自动捕获UI上下文(如果调用起始于UI线程),嵌套Task.Run会导致不必要的线程切换,加上Dispatcher.Invoke的冗余调用反而可能引发同步问题。直接在异步循环中更新属性即可。避免重复触发OnPropertyChanged
DownloadProgress的setter已经包含OnPropertyChanged触发逻辑,无需在Dispatcher中手动调用。同时需要给Process属性补充完整的setter通知逻辑。处理Content-Length为0的边界情况
当服务器未返回Content-Length时,避免除以0的计算错误,给出友好提示。确保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
相关产品推荐
相关产品推荐

