2017-02-18 2 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(); 
     } 
    } 
} 

J'ai un comboBox. Chaque élément de combobox possède une étiquette et une image. La comboBox est remplie avec des éléments d'une page Web, processus qui prend un certain temps jusqu'à ce que ce soit fait.UserInterface se bloque lorsque BackgroundWorker fonctionne

Le problème principal est que lorsque j'écris quelque chose dans comboBox (oui, c'est modifiable), mon application se bloque jusqu'à ce que la liste avec les éléments soit créée. Pour se débarrasser de ce gel, j'ai essayé d'utiliser BackgroundWorker, mais ça ne fonctionne pas ... Des idées pourquoi? J'ai essayé d'utiliser le thread sur la classe qui génère la liste, mais rien ne se passe.

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

Je suppose que 'searchbar' est le' ComboBox'. – xoxox

+0

Quel est le nombre de 'movieList' dans' bgWorker_Completed'? – xoxox

+0

@xoxox Oui, c'est la ComboBox J'utilise la classe Films pour créer la liste des films. Cette classe est la raison de geler et j'ai besoin d'un fil différent pour cela. – Marius

Répondre

4

Il ya des problèmes avec votre conception. L'agent d'arrière-plan appelle bgWorker_dowork dans un thread séparé, mais toute la logique est exécutée par le répartiteur et, par conséquent, la logique est toujours exécutée dans le thread ui.

Je ne peux pas trouver où se trouve la logique lourde dans votre code (est-ce le constructeur Movie?). Quoi qu'il en soit, déplacez la logique lourde en dehors de la méthode invoke lambda et inscrivez-la simplement dans la zone de liste déroulante à l'intérieur.

+1

if (! bgWorker.IsBusy) bgWorker.RunWorkerAsync(); il appelle dans l'événement –

+0

Eh bien, ce n'est pas possible ... Je veux faire une boîte de recherche comme google. Quand vous écrivez quelque chose, cela vous donne des suggestions. J'ai donc besoin de l'événement searchBar_DataContextChanged, c'est la raison pour laquelle mes deux threads doivent fonctionner ensemble. – Marius

0

Comme Micael l'a fait remarquer, la compréhension du fonctionnement de BackgroundWorker est nécessaire.

Les contrôles UI (UserInterface) sont traités par le thread principal (thread UI) mais le code dans DoWork est traité par le thread d'arrière-plan. Cependant, si vous encapsulez tout le code dans DoWork avec Dispatcher.Invoke, BackgroundWorker n'est plus un BackgroundWorker.

La solution est vraiment simple qu'emballer les choses nécessaires avec Dispatcher.Invoke comme ci-dessous.

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

contrôles de l'interface utilisateur sont tels que comboBox, textBox, étiquette, grille, StackPanel, Button, etc.

BackgroundWorker est vraiment pratique, mais un peu de compréhension de bit est nécessaire au début. Parce que le code dans DoWork vous a été demandé par le thread principal (UI), votre application a dû être gelée pour terminer le travail demandé à effectuer.

Et vous n'avez pas besoin de répéter le code dans Terminé, mais mieux de faire un autre travail nécessaire ou de gérer l'erreur si elle s'est produite.

Update-

Je pense que l'une des possibilités est qu'il pourrait y avoir un problème lors du téléchargement sur le site Web. L'utilisation de sites Web est toujours asynchrone, ce qui signifie que le temps de chargement varie en fonction du trafic Internet. Mais à ce moment, je suppose que la connexion peut avoir un problème et que votre application attend le délai d'attente par défaut de la connexion web.

+0

Où est movieList = new Movies (searchText) .movieList; ? Ceci est la ligne qui me pose problème – Marius

+0

Mettez-le juste à un endroit approprié dans DoWork. Le point est la congélation peut être résolu. Et pour votre information, il existe de nombreuses solutions déjà faites gratuitement pour la fonctionnalité comme Google recherche sur les sites comme Github, Nuget.org si vous le savez déjà. –

+0

Un peu déroutant pendant mon propre travail. Je ne connais pas vos codes exacts mais ma recommandation est mise à jour dans ma réponse. –