Search code examples
c#wpfmultithreadingbackgroundworker

UI is hung when BackgroundWorker() runs


I'm using BackgroundWorker for threading in my wpf application. But it makes the UI hung as I can't click anywhere of the UI. Here is my code snippet :

private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += delegate(object s, DoWorkEventArgs args)
        {
            Dispatcher.Invoke(new Action(() => ConnectFtp()));
        };
        worker.RunWorkerAsync();
    }

private void ConnectFtp()
{
     try
        {
            int port = string.IsNullOrEmpty(txtport.Text) ? 21 : Convert.ToInt32(txtport.Text);
            if (ftpserver1 == null)
            {
                ftpserver1 = new FtpClient(txtftpserver.Text, port);
                ftpserver1.ServerResponse += new EventHandler<FtpResponseEventArgs>(ftpserver2_ServerResponse);
                ftpserver1.ClientRequest += new EventHandler<FtpRequestEventArgs>(ftpserver2_ClientRequest);
                ftpserver1.TransferProgress += new EventHandler<TransferProgressEventArgs>(ftpserver2_TransferProgress);
                ftpserver1.TransferComplete += new EventHandler<TransferCompleteEventArgs>(ftpserver2_TransferComplete);
            }
            if (!ftpserver1.IsConnected)
            {
                Run r = new Run("Server1 Status:    Resolving address of " + txtftpserver.Text + "\n" + "Server1 Status:    Connection established, waiting for welcome message... \n");
                r.Foreground = System.Windows.Media.Brushes.Black;
                msg.Inlines.Add(r);
                msgscroll.ScrollToBottom();                   
                ftpserver2_OpenAsyncCompleted(ftpserver1, txtusername.Text, txtpassword.Password);
            }
        }
        catch { }
}

In the ConnectFtp() method i'm connecting to a ftp server. The UI doesn't work when the connection state of ftp server is 'connecting'. but after completing the connection everything is okay! Please help me out ! thanks in advance!


Solution

  • You are dumping the complete method over to UI thread Dispatcher from the background worker. Here

    Dispatcher.Invoke(new Action(() => ConnectFtp()));
    
    
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                ConnectFtp();
            };
            worker.RunWorkerAsync();
    }
    
    private void ConnectFtp()
    {
         // Here i'm connecting to a ftp server. 
         // Do some I/O operation
         // Now time to update UI controls so we invoke on Dispatcher UI thread
         Dispatcher.Invoke(new Action(() =>
         {
             lblMessage.Text = "Process finished";
             // Some other UI updates..
         }));
    }
    

    I suppose it would be better to take advantage of Task Parallel Api available in .NET 4.0 that works good on multi-core system i.e really parallel threading. Advantage of using TPL is that it uses closures on variables and UI elements so you can access the values on different thread and if it not work sometimes then create local variables before the Task starts and use it in Action method body and update UI controls using Dispatcher Invokes..

    For example:

    using System.Threading;
    using System.Threading.Tasks;
    
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
            Task TWorkOnFTP = new TaskFactory().StartNew(ConnectFtp);
    }
    
    private void ConnectFtp()
    {
         // Here i'm connecting to a ftp server. 
         // Do some I/O operation
         // Now time to update UI controls so we invoke on Dispatcher UI thread
         Dispatcher.Invoke(new Action(() =>
         {
             lblMessage.Text = "Process finished";
             // Some other UI updates..
         }));
    }
    

    Other related references:

    Update

    As per updated question the TPL can be used as follows:

        //Closures
        var strPort = txtport.Text;
        var strFTPServer = txtftpserver.Text;
        var strUserName = txtusername.Text;
        var strPassword = txtpassword.Password;
    
        //Start Task thread
        Task TProcessFTP = new TaskFactory().StartNew(new Action(() =>
        {
            try
            {
                int port = string.IsNullOrEmpty(strPort) ? 21 : Convert.ToInt32(strPort);
                if (ftpserver1 == null)
                {
                    ftpserver1 = new FtpClient(strFTPServer, port);
                    ftpserver1.ServerResponse += new EventHandler<FtpResponseEventArgs>(ftpserver2_ServerResponse);
                    ftpserver1.ClientRequest += new EventHandler<FtpRequestEventArgs>(ftpserver2_ClientRequest);
                    ftpserver1.TransferProgress += new EventHandler<TransferProgressEventArgs>(ftpserver2_TransferProgress);
                    ftpserver1.TransferComplete += new EventHandler<TransferCompleteEventArgs>(ftpserver2_TransferComplete);
                }
                if (!ftpserver1.IsConnected)
                {
                    //Update UI Controls
                    Dispatcher.Invoke(new Action(() =>
                    {
                        Run r = new Run("Server1 Status:    Resolving address of " + txtftpserver.Text + "\n" + "Server1 Status:    Connection established, waiting for welcome message... \n");
                        r.Foreground = System.Windows.Media.Brushes.Black;
                        msg.Inlines.Add(r);
                        msgscroll.ScrollToBottom();
                        ftpserver2_OpenAsyncCompleted(ftpserver1, strUserName, strPassword);
                    }));
                }
            }
            catch { }
        }));