2017-02-18 24 views
-4
using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Windows; 
using System.Windows.Input; 
using System.Windows.Media.Imaging; 

namespace IMDBWpf 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    /// 
    public partial class MainWindow : Window 
    { 
     private List<Movie> movieList; 
     BackgroundWorker bgWorker; 
     private string searchText; 

     public MainWindow() 
     { 
      InitializeComponent(); 

      bgWorker = new BackgroundWorker(); 
      bgWorker.DoWork += bgWorker_doWork; 
      bgWorker.RunWorkerCompleted += bgWorker_Completed; 
     } 

     private void bgWorker_Completed(object sender, RunWorkerCompletedEventArgs e) 
     { 
      Dispatcher.Invoke(() => 
      { 
       movieList = new Movies(searchText).movieList; 
       searchBar.ItemsSource = movieList; 
      }); 
     } 

     private void bgWorker_doWork(object sender, DoWorkEventArgs e) 
     { 
      Dispatcher.Invoke(() => 
      { 
       var loadingMovie = new Movie("src\\loader.gif", "Loading..."); 
       movieList = new List<Movie>(); 
       movieList.Add(loadingMovie); 
       searchBar.ItemsSource = movieList; 
       searchBar.IsDropDownOpen = true; 
      }); 
     } 

     private void searchBar_DataContextChanged(object sender, RoutedEventArgs e) 
     { 
      searchText = searchBar.Text; 

      if(!bgWorker.IsBusy) 
       bgWorker.RunWorkerAsync(); 
     } 
    } 
} 

私はコンボボックスを持っています。コンボボックスの各要素にはラベルとイメージがあります。BackgroundWorkerが動作しているときにUserInterfaceがフリーズする

コンボボックスは、完了するまでに時間がかかるウェブページの要素で満たされています。

主な問題は、コンボボックスに何かを書き込むと(はい、編集可能です)、要素を含むリストが作成されるまでアプリがフリーズすることです。このフリーズを取り除くために、私はBackgroundWorkerを使用しようとしましたが、うまくいきません。 リストを生成するクラスでスレッドを使用しようとしましたが、何も起こりません。

using HtmlAgilityPack; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace IMDBWpf 
{ 
    struct Movie 
    { 
     public Movie(string movieImg, string movieT) 
     { 
      movieTitle = movieT; 
      movieImage = movieImg; 
     } 
     public string movieTitle { get; set; } 
     public string movieImage { get; set; } 
    } 

    class Movies 
    { 
     public List<Movie> movieList { get; set; } 
     public Movies(string movieName) 
     { 
      if (movieName.Length > 0) 
      { 
       populateList(movieName); 
      } 
     } 

     private void populateList(string movieName) 
     { 
      var webSite = new HtmlAgilityPack.HtmlWeb(); 
      var siteAddress = "http://www.imdb.com/find?ref_=nv_sr_fn&q=" + movieName + "&s=tt"; 
      HtmlDocument htmlPage = webSite.Load(siteAddress); 
      movieList = new List<Movie>(); 
      int index = 0; 

      while (htmlPage.DocumentNode.Descendants("td").ElementAt(index).Descendants("a").Any()) 
      { 
       var movie = new Movie(); 

       movie.movieImage = htmlPage.DocumentNode.Descendants("td").ElementAt(index++).Descendants("a").ElementAt(0).Descendants("img").ElementAt(0).GetAttributeValue("src", ""); 
       movie.movieTitle = htmlPage.DocumentNode.Descendants("td").ElementAt(index++).InnerText; 

       movieList.Add(movie); 
      } 
     } 
    } 
} 
+0

私は 'searchbar'が' ComboBox'だとします。 – xoxox

+0

'bgWorker_Completed'の' movieList'の数はいくらですか? – xoxox

+0

@xoxoxはい、それはコンボボックスです ムービーのリストを作成するにはムービークラスを使用します。そのクラスはフリーズの理由であり、私はそれのために別のスレッドが必要です。 – Marius

答えて

4

デザインにはいくつかの問題があります。バックグラウンドワーカーは別のスレッドでbgWorker_doworkを呼び出しますが、すべてのロジックはディスパッチャによって実行されるため、論理は引き続きUIスレッドで実行されます。

あなたのコードのどこに重いロジックがありますか(これはMovieコンストラクタですか?)とにかく、重いロジックをinvoke lambdaメソッドの外に移動し、内部のコンボボックスに登録するだけです。

+1

if(!bgWorker.IsBusy) bgWorker.RunWorkerAsync();彼はイベントで呼び出す –

+0

まあ、それはできない...私はGoogleのような検索ボックスを作りたい。あなたが何かを書くとき、それはあなたに提案を与える。だから私はsearchBar_DataContextChangedイベントが必要です、これは私の2つのスレッドが一緒に動作する必要がある理由です。 – Marius

0

Micaelは指摘したように、BackgroundWorkerの仕組みを理解する必要があります。

UI(UserInterface)コントロールはメインスレッド(UIスレッド)によって処理されますが、DoWork内のコードはバックグラウンドスレッドによって処理されます。

しかし、DoWork内のすべてのコードをDispatcher.Invokeでラップすると、BackgroundWorkerはこれ以上BackgroundWorkerになりません。

Dispatcher.Invokeで以下のように必要なものをラップすることは、とても簡単です。

private void bgWorker_doWork(object sender, DoWorkEventArgs e) 
{ 
    searchBar.Text= "src\\loader.gif", "Loading..."; 

    movieList = new Movies(searchText).movieList; 

    Dispatcher.Invoke(() => 
    { 
     searchBar.ItemsSource = movieList; 
     searchBar.IsDropDownOpen = true; 
    }); 
} 

private void bgWorker_Completed(object sender, RunWorkerCompletedEventArgs e) 
{ 
    if (e.Error != null) 
    { 
     Dispatcher.Invoke(() => 
     { 
      MessageBox.Show(e.Error.ToString()); 
     }); 
    }  
} 

UIコントロールが

などコンボボックステキストボックス、ラベル、グリッド、StackPanelの、ボタン、通りですBackgroundWorkerのは本当に便利ですが、少しの理解は、最初に必要とされます。

DoWork内のコードはMain(UI)スレッドを介して指示されているため、指示された作業を完了するためにアプリケーションをフリーズする必要がありました。

あなたはCompletedでコードを繰り返す必要はありませんが、他に必要な作業やエラーが発生した場合にはそれを処理する方がよいでしょう。

Update-

私は可能性の一つは、ウェブサイトからダウンロードする際、いくつかの問題があるかもしれないということだと思います。 ウェブサイトの操作は常に非同期です。つまり、インターネットのトラフィックによって読み込み時間が異なることを意味します。しかし、現時点では、接続に問題があると思われるため、Web接続のデフォルトタイムアウトまで待機しています。

+0

movieList = new Movies(searchText)はどこですか?.movi​​eList; ? これは私に問題を引き起こす行です – Marius

+0

DoWorkの適切な場所に置いてください。凍結が解決できるという点がある。また、既に知っているかもしれないが、Github、Nuget.orgのようなサイトでのGoogle検索のような機能のために、多くの既製のソリューションが無料で提供されています。 –

+0

自分の仕事の中でちょっと混乱します。あなたの正確なコードはわかりませんが、私の答えは私の推薦が更新されています。 –

関連する問題