[英]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.