所以我试图通过同步下载来下载一堆文件(非常小的文件)-同步,因为文件很小,并且我不希望用户在下载批处理运行时对界面执行任何操作。
尽管以某种方式,位于循环内的接口更新未执行。我知道,同步下载会阻止整个线程,但是它不应该在下载之间仍然执行所有这些接口更新吗?
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++;
}
谢谢您的回应!
编辑: 我刚刚意识到,整个方法中的所有其他接口更改都不会在下载完成之前执行。 该方法应该在循环之前多行将标签设置为另一个文本,甚至不会显示出来...
答案 0 :(得分:2)
为回答您的问题,我创建了一个示例(不使用MVVM)。以下代码使用async方法来确保UI线程不会被从Internet下载文件的代码阻止。
现在,要回答有关下载完成后呈现的界面更改的问题,将在UI线程上执行下载文件的事件处理程序,以及从处理程序更改其他控件上的任何属性时(例如,设置标签/ texblock)控件将了解更改并在UI线程可用时将其呈现为自身,但是当您仍在UI线程上下载文件时,当UI线程变为空闲时,即使处理程序也会对UI控件进行任何更改再次发生在事件处理程序完成执行之后。我希望您现在对如何在事件处理程序中更改标签的文本时为什么不立即更新UI有了个好主意。
带有异步方法的代码:
Mainwindow.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>
Mainwindow.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
和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;
}
答案 1 :(得分:1)
我知道,同步下载会阻塞整个线程,但是它不应该在下载之间仍然执行所有这些接口更新吗?
在下载文件或执行while循环时如何更新UI?一个线程不能同时做两件事。
如果希望在此期间更新UI,则应该在后台线程上下载文件,或者最好使用异步DownloadFileAsync
方法,例如:
using (WebClient wc = new WebClient())
{
try
{
lblProgress.Content = "Downloading page " + counter.ToString();
await wc.DownloadFileAsync(new Uri(URL), targetPath);
FileInfo finfo = new FileInfo(targetPath);
counter++;
}
catch
{
//...
return;
}
}
在示例代码中,由于您似乎要重新下载同一文件,因此不清楚为什么要使用循环。