Problem getting BusyIndicator to display

Aug 10, 2011 at 11:08 AM

I have content (stackpanel with stuff inside it) inside a BusyIndicator.  In my code I am trying to turn it on prior to a long-time operation then turn it off:

            {
                BusyIndicatorControl.IsBusy = true;
                CalendarWeekly cw = new CalendarWeekly();
                StackPanel sp = cw.CreateWeeklyCalendar(newSelection, 0, 7);
                CalendarPanel.Children.Clear();
                CalendarPanel.Children.Add(sp);
               
            }
            BusyIndicatorControl.IsBusy = false;

but it doesn't show up.  If I comment out the BusyIndicatorControl.IsBusy = false; statement it displays, which tells me that the issue is due to the long-running process being done on the main UI thread.

So, I went down the path of doing this using a BackgroundWorker, but I get a runtime error that the BusyIndicator cannot be accessed because another thread owns it.

I'm stuck.  Any thoughts?  This is a great control I just need to figure out how to use it.

Aug 10, 2011 at 1:22 PM

The reason it is not working for you is because you are doing eveything on the UI thread.  The BusyIndicator is meant for long running processes the occur on a background thread. This allows the UI to remain responsive.  One thing to keep in mind is that you cannot access a control that is on the UI in a background thread.  So you would do something like this:

BackgroundWorker bw = ......;
.......
IsBusy = true;
bw.RunWorkAsync();

.......

private void BackgroundWorkerCompletedHandler(....)
{
    IsBusy = false;
}

As you can see you set the IsBusy before running your background process, then when the background worker completes you set IsBusy to fasle in the completed event.  I Would suggest reading this post to become more familiar with threading:

http://elegantcode.com/2009/07/03/wpf-multithreading-using-the-backgroundworker-and-reporting-the-progress-to-the-ui/

Aug 10, 2011 at 10:50 PM
Edited Aug 11, 2011 at 1:12 AM

Ok, got it now, working with my test.

However, when I add back in my real "time consuming" code, which involved running code in a bunch of other classes, I get the:

The calling thread must be STA, because many UI components require this.

error.  I have tried using the directive [STAThread] but that doesn't work and the reading I've done says that .Net will override to MTA anyway (BackgroundWorker doesn't support STA).

Do I need to use dispatcher threading or other form of threading and if so, will that work with the BusyIndicator?

 

Thanks.


Aug 11, 2011 at 1:36 PM

This is a threadining issue.  You cannot access UI components on a background worker thread.  Also, if your real code is creating UI elements on a background worker it will not work.  You will need to utilize the Dispatcher to propagate objects to the UI.

Aug 11, 2011 at 8:05 PM

Well, some progress.  I have it coded this way now:

       System.Windows.Threading.DispatcherTimer timer = null;

        public delegate void DoWorkDelegate(string Parameter);
        DispatcherOperation operation;
       
...
            {
                BusyIndicatorControl.IsBusy = true;

                operation = Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DoWorkDelegate(DoWork), newSelection);

                timer = new System.Windows.Threading.DispatcherTimer();
                timer.Tick += new EventHandler(TimerEvent);
                timer.Interval = new TimeSpan(0, 0,1);
                timer.Start();               
            }
            else BusyIndicatorControl.IsBusy = false;
            LastShown = newSelection;
        }

        public void DoWork(string newSelection)
        {
            BusyIndicatorControl.IsBusy = true;
  (long running operation here)     
        }

        public void TimerEvent(object sender, EventArgs e)
        {
            if (operation.Status == DispatcherOperationStatus.Completed)
            {
                BusyIndicatorControl.IsBusy = false;
                timer.Stop();
            }
        }
    }

 

I added a timer to watch the thread that is running the long operation, so that when it's done it can set the IsBusy flag to False.

What happens is that the busyindicator does not display until after the long-running operation completes... defeating the purpose.

Ugh.  not sure what I am doing wrong here... help!

thanks.

Aug 11, 2011 at 8:38 PM

Like I said you CANNOT access a UI control on a background thread.  Here is a simple example:

    <Grid>
        <extToolkit:BusyIndicator x:Name="_busyIndicator">
            <Button Click="Button_Click">Start Process</Button>
        </extToolkit:BusyIndicator>
    </Grid>

 

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += (o, ea) =>
                {
                    Thread.Sleep(3000);
                };
            bw.RunWorkerCompleted += (o, ea) =>
                {
                    _busyIndicator.IsBusy = false;
                };
            _busyIndicator.IsBusy = true;
            bw.RunWorkerAsync();
        }
Aug 11, 2011 at 9:38 PM

Ok, yes I understand that... but this block of code, where I set the IsBusy parameter, is not in a background thread.. it's in the main thread:

       {
                BusyIndicatorControl.IsBusy = true;

                operation = Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DoWorkDelegate(DoWork), newSelection);

                timer = new System.Windows.Threading.DispatcherTimer();
                timer.Tick += new EventHandler(TimerEvent);
                timer.Interval = new TimeSpan(0, 0,1);
                timer.Start();               
            }

 

that's what I don't follow.

I can't use the Backgroundworker because of the apartment issues.

sorry don't mean to be thick-headed :-(

Aug 11, 2011 at 9:40 PM
Edited Aug 11, 2011 at 9:43 PM

EDIT: I understand what you mean now, but this will not behave properly.  BeginInvoke and an async process.  please run my example.

Aug 11, 2011 at 10:03 PM

Yeah... I tried a scenario similar to your example and it worked fine.  But I can't use Backgroundworker due to the STA issue.  Hmm.

Aug 15, 2011 at 10:09 PM

Got it working.. simple solution.

I execute only the slow, non-UI code (which does a bunch of database reads) inside the BackgroundWorker, and everything else inside the main code.  Works fine.

Thanks a bunch for a super control!

Oct 24, 2013 at 2:54 AM
I have the same problem, not sure about the issue. The sample given in working, but the same seems to be not working in real system.
BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += (o, ea) =>
            {
                //My Stuff is running here which is working without any busy indicator
            };
        bw.RunWorkerCompleted += (o, ea) =>
            {
                _busyIndicator.IsBusy = false;
            };
        _busyIndicator.IsBusy = true;
        bw.RunWorkerAsync();
  • The above code is working but busy indicator is not working. Busy indicator is inside a usercontrol. inside busy indicator grid tabcontrol etc..