2017-02-18 67 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冻结

组合框中充满了来自网页的元素,这个过程需要一段时间才能完成。

主要问题是,当我在comboBox中写入某些内容(是的,它是可编辑的)时,我的应用程序冻结,直到带有元素的列表被创建。为了摆脱这个冻结,我试图使用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是的,这是组合框 我使用类Movies来创建电影列表。该课是冻结的原因,我需要一个不同的线程。 – Marius

回答

4

您的设计存在一些问题。后台工作者在一个单独的线程中调用bgWorker_dowork,但是所有的逻辑都由调度程序执行,因此逻辑仍然在ui线程中执行。

我找不到你的代码中的重逻辑是(它是电影的构造函数?)。无论如何,将调用lambda方法之外的重逻辑移动到里面的组合框中。

+1

if(!bgWorker.IsBusy) bgWorker.RunWorkerAsync();他呼吁在事件 –

+0

嗯,这是不可能的...我想要一个像谷歌搜索框。当你写东西时,它会给你建议。所以我需要searchBar_DataContextChanged事件,这就是为什么我的两个线程需要一起工作的原因。 – Marius

0

像Micael指出的那样,了解BackgroundWorker的工作方式是必需的。

UI(UserInterface)控件由主线程(UI线程)处理,但DoWork内的代码由Background线程处理。

但是,如果使用Dispatcher.Invoke包装DoWork中的所有代码,则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中的代码,但最好还是做一些其他必要的工作,或者处理发生的错误。

更新 -

我认为可能性之一是,从网站上下载的时候可能会有一些问题。 与网站工作总是异步的,这意味着加载时间会因互联网流量而有所不同。但目前,我猜连接可能有问题,因此,您的应用程序正在等待,直到Web连接的默认超时。

+0

movieList = new Movies(searchText).movi​​eList; ? 这是使我产生问题的线 – Marius

+0

只需将它放在DoWork内的适当位置即可。重点是冻结可以解决。对于您的信息,有许多已经制作的解决方案免费提供诸如谷歌在Github,Nuget.org等网站上搜索的功能,尽管您可能已经知道了。 –

+0

在我自己的工作中有点混乱。我不知道你的确切代码,但我的建议更新了我的答案。 –