2016-12-21 52 views
0

私はWPFでウェブスクラップする方法を学んでいます。 20秒ごとにサイトをチェックし、検索結果に従ってObservableCollection(myClients)を更新し、Listview(myList)に表示します。私は2つのボタンを持っています、1つは検索を開始し、もう1つはそれを停止します。WPF、async/awaitの実装方法は?

ボタンオートクレークをX秒ごとに実装する方法はわかりませんでしたが(これは私のすべての問題を解決するものでしょうか?)、私はTask.Delay(20000)を使用しなければなりませんでした。プログラムは動作しますが、Thread.Sleep()を使用した場合のように開始時にはフリーズしませんが、Stopボタンを押してからStartを押すとすべてがフリーズします。

私は問題のように思われるコードの一部のみをアップロードします。私がまだ初心者であるので、現在のプログラム全体は、いくつかの異なるプログラムからリバースエンジニアリングされていることに注意してください。

 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!"; 
    } 
+0

なぜタイマを使用するだけではありませんか? – EJoshuaS

+0

これまでは存在することが分かりませんでした(動作していたと思われる非同期ルートの開始)。それを今見てみましょう。 – CsharpNoob

答えて

1

UIスレッドでwhileループを実行すると、UIスレッドがUIイベントを処理してループを実行したり、他の処理を同時に行うことができないため、アプリケーションがフリーズすることがあります。

もしx秒ごとに何かしたいのなら、EJoshuaSの示唆どおりにタイマーを使うことができます。 Intervalプロパティで指定された間隔でUIスレッドでTickイベントを発生させるDispatcherTimerクラスがWPFにあります。https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer%28v=vs.110%29.aspx

UIスレッドでWebサーバーにGET要求を実行したくないおそらくSystem.Timer.Timer:https://msdn.microsoft.com/en-us/library/system.timers.timer(v=vs.110).aspxを使用するべきです。これは、バックグラウンドスレッドで実行される別の種類のタイマーです。

元々作成されたスレッド、つまりUIスレッドのTextBlocksやListBoxなどのUIコントロールにしかアクセスできないため、ディスパッチャを使用して、これらのコントロールにアクセスするコードをマーシャリングして戻る必要があります。あなたのElapsedイベントハンドラで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

+0

これは現時点で私の頭を少し上回っているようですが、私はそれを働かせようとします、ありがとうございます。私が理解するように、私はサイトをチェックする "スタート"ボタンは必要ない、私は同じ間隔でGETを行うTimer.Timerを使用する? ObservableCollectionとListviewの更新用にのみGET用に使用しますか? – CsharpNoob

+0

スタートボタンは本当に必要ありません。 Start()メソッドをどこかで呼び出すことによってタイマーを開始することを忘れないでください。 Start()を呼び出すと、Elapsedイベントは間隔ごとに1回発生します。前述のように、ListViewとObservableCollectionはUIスレッド上で更新されるべきです。したがって、バックグラウンドスレッドで実行されるElapsedイベントハンドラでこれらにアクセスする場合は、説明したようにディスパッチャを使用する必要があります。 – mm8

0

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"); 
    } 
} 

基本的に、これは、所定の間隔でイベントを発生させるであろう。 System.Threadingのように、Windowsフォームalso has a timerがありますが、それらよりもむしろDispatcherTimerを使用してください。特に、System.Threadingのものは、スレッドプール上でアクションを実行し、特にWPFはバックグラウンドスレッドからUIを更新する方法が非常に難しいため、UIとうまく混合しない傾向があります。

私がリンクしているドキュメントとthis answerには、これに関する詳細も記載されています。

+0

タイマーを説明してくれてありがとう!ユーザーmm8が以下に述べたGETのためのTimer.Timerについてどういうことを言っていますか? DispatcherTimerは私のcurent async/awaitより優れていますが、GETリクエストではまだ悪いですか? – CsharpNoob

+0

@CsharpNoob私は本当にそのコンポーネントに精通しているわけではありませんが、それもオプションになる可能性があります。 – EJoshuaS

関連する問題