1

In wpf application - UI getting freezed on button click - then how to show busy indicator while huge data processing?

I have tried background worker process, but its throwing the below exception.

The "calling thread" in the message is not your UI thread...

Sample Code:

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);

        BackgroundWorker worker = sender as BackgroundWorker;
        this.busyIndicator.Visibility = Visibility.Visible;
        busyIndicator.IsBusy = true;

        for (int k = 1; (k <= 10); k++)
        {
            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
                break;
            }
            else
            {
                int intAutomationID = 0;
                int intAutomation_SS_ID = 0;
                int intAS_ID = 0;
                string strProcedureName = "";
                //busyIndicator.Visibility = Visibility.Visible;
                try
                {

                    // Insert entry into AUTOMATION_PROCESS table.
                    intAutomationID = Pkg_TargetsIdentifiers.InsertAutomationProcess(Convert.ToInt32(cmbIdentifier.SelectedValue),
                        Convert.ToInt32(cmbSourceData.SelectedValue), "InProgress", 0, "Insert");

                    if (intAutomationID > 0)
                    {
                        for (int i = 0; i <= dgvProcessLists.Items.Count - 1; i++)
                        {
                            int j = 3;
                            strProcedureName = "";
                            strProcedureName = (dgvProcessLists.Items[i] as DataRowView).Row.ItemArray[j].ToString();
                            if (!string.IsNullOrEmpty(strProcedureName))
                            {

                                //AS_ID
                                // InitializeMouseHandlersForVisual(dgvProcessLists);
                                intAS_ID = Convert.ToInt32((dgvProcessLists.Items[i] as DataRowView).Row.ItemArray[0].ToString());

                                intAutomation_SS_ID = Pkg_TargetsIdentifiers.InsertAutomationStepsStatus(intAS_ID, intAutomationID,
                                 "Inprogress", 0, "Insert");

                                bool boolStatus = Pkg_TargetsIdentifiers.CallActionProcess(strProcedureName, intAutomationID);
                                if (boolStatus == true)
                                {
                                    //var selectedRow = DataProcessing.Class1.GetSelectedRow(this.dgvProcessLists);
                                    //var columnCell = DataProcessing.Class1.GetRow(this.dgvProcessLists,0);


                                    intAutomation_SS_ID = Pkg_TargetsIdentifiers.InsertAutomationStepsStatus(intAS_ID, intAutomationID,
                                     "Completed", intAutomation_SS_ID, "Update");
                                    intAS_ID = 0;
                                    strProcedureName = "";
                                    DataRowView row = (dgvProcessLists.Items[i] as DataRowView);
                                    if (row != null)
                                    {
                                        if (row.DataView.Table.Columns.Contains("Status"))
                                        {
                                            Type type = row["Status"].GetType();
                                            string status = row["Status"] == System.DBNull.Value ? null : (string)row["Status"];


                                            if (boolStatus == true)
                                            {
                                                Uri uri = new Uri("pack://application:,,,/Images/green.jpg");
                                                BitmapImage source = new BitmapImage(uri);


                                            }
                                            if (boolStatus == false)
                                            {
                                                Uri uri = new Uri("pack://application:,,,/Images/red.jpg");
                                                BitmapImage source = new BitmapImage(uri);

                                            }
                                        }
                                    }
                                    continue;
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                        intAutomationID = Pkg_TargetsIdentifiers.InsertAutomationProcess(Convert.ToInt32(cmbIdentifier.SelectedValue),
                        Convert.ToInt32(cmbSourceData.SelectedValue), "Completed", intAutomationID, "Update");

                    }

                    // Perform a time consuming operation and report progress.
                    System.Threading.Thread.Sleep(500);
                    worker.ReportProgress((k * 10));
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                { }
            }
        }
    }
    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);

        if ((e.Cancelled == true))
        {
            this.busyIndicator.Visibility = Visibility.Hidden;
            busyIndicator.IsBusy = false;
        }

        else if (!(e.Error == null))
        {
            this.busyIndicator.Visibility = Visibility.Hidden;
            busyIndicator.IsBusy = false;
        }

        else
        {
            this.busyIndicator.Visibility = Visibility.Hidden;
            busyIndicator.IsBusy = false;
        }
    }
    private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.busyIndicator.Visibility = Visibility.Visible;
        busyIndicator.IsBusy = true;
    }

    /// <summary>
    ///  btnStartProcess_Click
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnStartProcess_Click(object sender, RoutedEventArgs e)
    {
        try
        {
        if (bw.IsBusy != true)
        {
            bw.RunWorkerAsync();
        }

            //worker.RunWorkerAsync();
        //    //};

        //    //worker.RunWorkerCompleted += (o, ea) =>
        //    //{
        //    //    busyIndicator.Visibility = Visibility.Hidden;
        //    //    busyIndicator.IsBusy = false;
        //    //};

        //    //worker.RunWorkerAsync();
        //    //};
        //    //worker.RunWorkerAsync();

        //bw.RunWorkerCompleted += (o, ea) =>
        //{
        //    busyIndicator.IsBusy = false;
        //    busyIndicator.Visibility = Visibility.Hidden;
        //};
        //busyIndicator.IsBusy = true;
        //busyIndicator.Visibility = Visibility.Visible;
        //bw.RunWorkerAsync();
        }
        catch (Exception ex)
        {

            throw ex;
        }
    }

Please do the needful..

Thanks and Regards, Vijay Babu

Sheridan
  • 68,826
  • 24
  • 143
  • 183
Laxminarsimha
  • 31
  • 1
  • 6

3 Answers3

1

First, you need to get your BackgroundWorker code working. For this, I'd advise you to see my answer to the How to correctly implement a BackgroundWorker with ProgressBar updates? question, which provides a clear, concise code example.

I'm now assuming that you have correctly set up your BackgroundWorker, so we can move onto displaying a busy indicator. First, you need to be aware of the reason for your error:

We cannot call any functions on any UI objects from any other thread than the UI thread

Bearing this in mind, that means that we can't set the Visiblity of any busy indicator from a background thread. So, the easiest fix is to set it to Visibility.Visible before starting the BackgroundWorker:

busyIndicator.Visibility = Visibility.Visible;
busyIndicator.IsBusy = true;
backgroundWorker.RunWorkerAsync();

As we can see from the ooo page on MSDN:

You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events.

This basically means that we can handle these events on the UI thread and so we have access to our UI elements from these event handlers. Therefore, when your long running process is complete, the RunWorkerCompleted event will be called. If you handle this event, then you can hide your busy indicator in the relevant event handler:

private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    busyIndicator.Visibility = Visibility.Hidden;
    busyIndicator.IsBusy = false;
}

As it runs on the UI thread, there is no need to call Dispatcher.Invoke, and it's worth noting that your call to this method does nothing because you didn't provide any delegate method for it to run:

Dispatcher.Invoke(new Action(() => { /*No action*/ }), DispatcherPriority.ContextIdle);
Community
  • 1
  • 1
Sheridan
  • 68,826
  • 24
  • 143
  • 183
-1

Try this:

Dispatcher.Invoke(() => { busyIndicator.Visibility = Visibility.Visible; });
Eirik
  • 4,135
  • 27
  • 29
  • Why downvote? This code works and I've seen it lots of places (here on SO as well) when it comes to backgroundworkers and updating the UI while it's working... – Eirik Jun 19 '14 at 11:51
  • -1 Sorry, I normally leave a comment. I down voted your answer because you provided no context of when or where this should be called. Furthermore, it is also unnecessary to call `Dispatcher.Invoke` to set the `Visibility` to `Visible`, because that should be done *before* starting the `BackgroundWorker`, while execution is still on the UI thread. I accept that your call is not as pointless as the question author's call to `Dispatcher.Invoke`, but all the same, it is *not* required. – Sheridan Jun 19 '14 at 12:06
  • Thank you for the explanation and code suggestion! I have edited code as given - 1.Perform UI work before we start the new thread 2. Created new thread, start it. 3. done doing work, send result to the UI thread... – Laxminarsimha Jun 24 '14 at 09:31
-1

{

        System.Threading.ThreadStart start = delegate()
        {
            CallAutomationProcess();
            //done doing work, send result to the UI thread
            Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
                new Action(changest));

        };
        this.busyIndicator.IsBusy = true;
        this.busyIndicator.Visibility = Visibility.Visible;

        new System.Threading.Thread(start).Start();
Laxminarsimha
  • 31
  • 1
  • 6
  • -1 While this may be the solution that you ended using, it is a very poor solution. The `System.Threading.Thread` class is not really used directly in .NET anymore as it has been replaced with the `Task` class and in .NET 4.5, the `await` and `async` keywords. – Sheridan Oct 03 '14 at 13:56
  • I agree with you. Much of .NET 4.5 functionality has been changed to Tasks and BackgroundWorker is all but obsolete now. But I have been working on .Net 4.0 in some projects, so I have used those classes/methods. – Laxminarsimha Oct 17 '14 at 08:25
  • As @Sheridan indicated Task class can be used in .Net 4. – Dzyann Jun 02 '15 at 16:33