BackgroundWorker工作时,UserInterface冻结

时间:2017-02-18 20:19:10

标签: c# user-interface backgroundworker freeze

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

我有一个comboBox。组合框中的每个元素都有一个标签和一个图像。

comboBox充满了来自网页的元素,这个过程需要一些时间才能完成。

主要的问题是,当我在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);
            }
        }
    }
}

2 个答案:

答案 0 :(得分:4)

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

我无法在你的代码中找到重逻辑的位置(它是Movie构造函数吗?)。无论如何,将重逻辑移到invoke lambda方法之外,然后将其注册到里面的组合框中。

答案 1 :(得分:0)

像Micael指出的那样,需要了解BackgroundWorker的工作原理。

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

但是,如果使用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控件如comboBox,textBox,label,Grid,StackPanel,Button等。

BackgroundWorker非常方便,但一开始就需要了解一点。

由于DoWork中的代码是由您通过Main(UI)线程完成的,因此必须冻结您的应用程序以完成指示的工作。

您不需要在Completed中重复代码,但最好还是做一些其他必要的工作或处理错误(如果发生)。

更新 -

我认为可能的一种可能性是从网站下载时可能存在一些问题。 使用网站始终是异步的,意味着加载时间会因互联网流量而异。但是在这个时候,我猜连接可能有一些问题,因此,你的应用程序正在等待网络连接的默认超时。