c# - 循环同步下载不更新标签
问题描述
所以我试图通过同步下载下载一堆文件(非常小的文件) - 同步因为文件非常小,我不希望用户在下载批处理运行时对界面做任何事情。
不知何故,尽管位于循环内的界面更新不会被执行。我知道,同步下载会阻塞整个线程,但它不应该在下载之间执行所有这些接口更新吗?
bool error = false;
int counter = 1;
while (!error)
{
//The label I want to update with status updates
lblProgress.Content = "Downloading page " + counter.ToString() + ":";
using (WebClient wc = new WebClient())
{
try
{
wc.DownloadFile(new Uri(URL), targetPath);
FileInfo finfo = new FileInfo(targetPath);
}
//I'm using the catch statement because I download a bunch of files
//without knowing how many there are - so when I get the 404 I
//know, it's done.
catch { error = true; }
}
counter++;
}
感谢您的任何回复!
编辑:我刚刚意识到,整个方法中的所有其他接口更改在下载完成之前都不会执行。该方法应在该循环前几行将标签设置为另一个文本,即使这也没有显示...
解决方案
只是为了回答您的问题,我创建了一个示例(不使用 MVVM)。以下代码使用 async 方法来确保 UI 线程没有被从 Internet 下载文件的代码阻塞。
现在,要回答有关下载完成后呈现界面更改的问题,下载文件的事件处理程序在 UI 线程上执行,并且当您从处理程序更改其他控件上的任何属性时(例如,设置标签/texblock 的文本) 控件将理解更改并在 UI 线程可用时将其呈现,但由于您仍在 UI 线程上下载文件,因此当 UI 线程再次空闲时,将呈现从偶数处理程序中对 UI 控件的任何更改,这将在事件处理程序完成执行后发生。我希望您现在对当您从事件处理程序更改标签的文本时为什么不立即更新 UI 有一个很好的了解。
带有异步方法的代码:
主窗口.xaml
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="600">
<Window.Resources>
</Window.Resources>
<StackPanel>
<TextBox x:Name="txtUrl" />
<Button Content="Start" x:Name="btnStart" Click="Button_Click" />
<Label x:Name="lblProgress" />
</StackPanel>
</Window>
主窗口.xaml.cs
using System;
using System.Net;
using System.Threading.Tasks;
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
bool error = false;
string url = txtUrl.Text;
int counter = 1;
btnStart.IsEnabled = false;
await Task.Factory.StartNew(() =>
{
while (!error)
{
Dispatcher.Invoke(() => { lblProgress.Content = "Downloading page " + counter.ToString() + ":"; });
using (WebClient wc = new WebClient())
{
try
{
string targetPath = $"c:\\temp\\downloads\\file{counter}.tmp";
wc.DownloadFile(new Uri(url), targetPath);
}
catch { error = true; }
}
counter++;
}
});
btnStart.IsEnabled = true;
}
}
}
另一种方法可以只是等待该DownloadFileTaskAsync
方法,从而避免需要Task.Factory.StartNew
and Dispatcher.Invoke
:
private async void Button_Click(object sender, RoutedEventArgs e)
{
bool error = false;
string url = txtUrl.Text;
int counter = 1;
btnStart.IsEnabled = false;
using (var webClient = new WebClient())
{
while (!error)
{
lblProgress.Content = "Downloading page " + counter.ToString() + ":";
string targetPath = $"c:\\temp\\downloads\\file{counter}.tmp";
try
{
await webClient.DownloadFileTaskAsync(url, targetPath);
}
catch
{
error = true;
}
counter++;
}
}
btnStart.IsEnabled = true;
}
推荐阅读
- forms - 无法访问表单识别器的私人预览 - 缺少下拉值
- swift - 在集合视图中选择单元格时,Swift 动画下划线栏?
- javascript - 如何在 Laravel 中没有打印预览和对话框的情况下进行静默打印?
- php - PHP:显示从数据库中检索到的数据使用 DB::query (Silverstripe CMS)
- scala - 为什么编译器不推断依赖类型?
- wordpress - 有没有办法将用户元数据复制到其他表?
- django - 如何使用 pymongo 访问用 mongodb 编写的 system.views?
- java - 使用 Apache Camel 和 ActiveMQ 时如何处理系统崩溃
- mule - 如何在 ESB 中使用 db 连接器检索数据
- java - 优化服务器端实验跟踪事件