[英]WPF, how to implement async/await?
我正在学习如何在WPF中进行网络抓取。 我每20秒检查一次网站,根据搜索结果更新我的ObservableCollection(myClients),并将其显示在Listview(myList)中。 我有2个按钮,一个开始搜索,一个停止搜索。
我不知道如何每X秒实现一次按钮自动点击(这将解决我的所有问题,对吗?),所以我不得不使用Task.Delay(20000)。 程序正常工作,它不会像我曾经使用过Thread.Sleep()那样一开始就冻结,但是如果我先按“停止”按钮,然后再按“开始”,则一切都会冻结。
我将仅上载似乎是问题的部分代码。 请注意,由于我仍然是初学者,因此目前整个程序大部分都是从几个不同的程序反向工程而来的。
private async void Button_Click(object sender, RoutedEventArgs e) //Start button
{
string car;
string price;
string link;
wantToAbort = false;
while (!wantToAbort)
{
// ----Simulate GET request----
//-----End GET----
myList.ItemsSource = myClients;
string searchCar = txtBlock.Text + " " + txtBlock2.Text;
var articleNodes = htmlDoc.DocumentNode.SelectNodes($"//*[@id='main_content']/div[1]/div[2]/ul[1]//*[text()[contains(., '{searchCar}')]]");
if (articleNodes != null && articleNodes.Any())
{
foreach (var articleNode in articleNodes)
{
car = WebUtility.HtmlDecode(articleNode.InnerText);
price = WebUtility.HtmlDecode(articleNode.ParentNode.ParentNode.SelectSingleNode("span").InnerText);
link = WebUtility.HtmlDecode(articleNode.ParentNode.ParentNode.Attributes["href"].Value);
var tempUser = new User(car, price, link);
if (!myClients.Any(x=>x.Link == tempUser.Link))
{
myClients.Insert(0, tempUser); //Inserts new item if Links are different
txtBlock3.Text = "Searching...";
}
}
await Task.Delay(20000); //This seems to be an issue
}
}
}
private void Button_Click_1(object sender, RoutedEventArgs e) //Stop button
{
wantToAbort = true;
txtBlock3.Text = "Ready to search again!";
}
在UI线程上运行while循环可能会冻结应用程序,因为UI线程无法同时处理UI事件和执行循环或同时执行其他任何操作。
如果您想每隔x秒钟执行一次操作,则可以使用EJoshuaS建议的计时器。 WPF中有一个DispatcherTimer类,它以Interval属性指定的时间间隔在UI线程上触发Tick事件: https : //msdn.microsoft.com/zh-cn/library/system.windows.threading.dispatchertimer%28v = vs.110%29.aspx
但是,您不想在UI线程上对Web服务器执行GET请求,因此您可能应该使用System.Timer.Timer: https : //msdn.microsoft.com/zh-cn/library/system.timers .timer(v = vs.110).aspx 。 这是在后台线程上运行的另一种计时器。
由于您只能在最初创建它们的线程(即UI线程)上访问UI控件(例如TextBlocks和ListBoxes),因此您将不得不使用分派器将访问这些控件的所有代码编组回您的UI线程中。过去的事件处理程序:
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
//call the web server here....
//dispatch any access to any UI control
txtBlock3.Dispatcher.Invoke(new Action(() = > { txtBlock3.Text = "Searching..."; }));
}
维护响应式应用程序的黄金法则是在后台线程上执行所有长时间运行的代码,但是您必须仅在UI线程上重新访问UI控件。 有关WPF中的线程模型的更多信息,请参考MSDN: https : //msdn.microsoft.com/en-us/library/ms741870(v= vs.110) .aspx
在这种情况下, DispatcherTimer可能是更好的解决方案,例如以下示例:
public partial class MainWindow : Window
{
private DispatcherTimer timer;
public MainWindow()
{
InitializeComponent();
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 220);
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
// Do something on your UI
Trace.TraceInformation("Timer expired");
}
}
基本上,这将在给定的时间间隔引发一个事件。 请注意,Windows Forms和System.Threading一样 , 还有一个计时器 ,但是您要确保使用DispatcherTimer
而不是它们。 特别是,System.Threading中的一个往往不能与UI很好地混合使用,因为它在线程池上运行其动作,尤其是WPF对于如何从后台线程更新UI非常挑剔。
我链接到的文档以及此答案也提供了详细信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.