Mushfiq
Mushfiq

Reputation: 769

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!

Upvotes: 1

Views: 630

Answers (2)

Harsh Baid
Harsh Baid

Reputation: 7249

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

Upvotes: 1

aqwert
aqwert

Reputation: 10789

You are Invoking back onto the UI thread within the worker for the ConnectFTP method. You need to have this...

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

private void ConnectFtp()
{
 //here i'm connecting to a ftp server. 
}

Upvotes: 7

Related Questions