2

Hello I am having some trouble with updating a binding from a background thread. I am displaying an IsBusy indicator while some background processing goes on, then hiding the indicator when finished.

Notice if I set IsLoading to false inside of my background worker (but by invoking it on the UI thread) it doesn't ever update the UI.

If I call it immediately after, on the UI thread. It works.

What am I missing?

        private void BeginValidation()
    {
        m_ValidationWorker = new BackgroundWorker();

        m_ValidationWorker.WorkerReportsProgress = false;
        m_ValidationWorker.DoWork += (_sender, _args) =>
        {
            foreach (DataRecord record in DatabaseViewModel.Instance.Records)
            {
                record.Init();

                Application.Current.Dispatcher.Invoke(()=> { record.IsLoading = false; }); //THIS DOESN'T WORK
            }
        };

        m_ValidationWorker.RunWorkerCompleted += (_sender, _args) =>
        {
            foreach (DataRecord record in DatabaseViewModel.Instance.Records)
            {
             record.IsLoading = false;//THIS WORKS
            }
        };

        m_ValidationWorker.RunWorkerAsync();
    }

And the xaml just for information.

            <telerik:RadBusyIndicator IsBusy="{Binding FirstRecord.IsLoading}" IsIndeterminate="True" DisplayAfter="0" BusyContent="Processing" Style="{StaticResource RadBusyIndicatorStyle}">
            <Grid>
                    <ScrollViewer HorizontalScrollBarVisibility="Disabled" Padding="5">
                        <ItemsControl ItemsSource="{Binding FirstRecord.Fields}" ItemTemplateSelector="{StaticResource FormView_TypeSelector}"/>
                    </ScrollViewer>
            </Grid>
        </telerik:RadBusyIndicator>
Asheh
  • 1,547
  • 16
  • 25
  • 2
    The whole *point* of a BGW is to do non-UI work in the `DoWork` handler and update the UI using the other handlers. You defeat the entire purpose of using a BGW if you ignore all of the other events and manually marshal to the UI thread. – Servy May 26 '15 at 15:26
  • You should update the UI in the `ProgressChanged` event handler. In your `DoWork` handler, you would need to call `_sender.ReportProgress` method which triggers the `ProgressChanged` event. I believe that you don't need to do any marshaling in the `ProgressChanged` event handler as it is already marshaled. – Chris Dunaway May 26 '15 at 19:16

1 Answers1

0

You're getting the current dispatcher from the non-UI thread, and trying to use that.

While you could get and store the current dispatcher from the UI thread, and then use that dispatcher later, what you should really do is just let the Background Worker handle marshaling to the UI thread for you, as you showed how to do in your working example, rather than trying to manually marshal to the UI thread.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • Im not quite sure I understand. How do I get the BGW handle the marshalling to the UI thread? – Asheh May 26 '15 at 15:29
  • @Asheh You showed exactly how to do it in your question, when you showed the example that actually worked. Specifically, you update the UI in `RunWorkerCompleted`, not `DoWork`. – Servy May 26 '15 at 15:30
  • Are you saying I should split up each record load into a separate thread? That would create over 10,000 threads. I dont want to have to wait for EVERY record to load, before each record is told that it is loaded. – Asheh May 26 '15 at 15:31
  • 1
    @Servy Does `Application.Current.Dispatcher` not get the UI dispatcher then? I appreciate `Dispatcher.CurrentDispatcher` would get the wrong one. – GazTheDestroyer May 26 '15 at 15:32
  • 1
    Ive just realised, I can use a progress reporter – Asheh May 26 '15 at 15:32
  • @GazTheDestroyer Getting the current dispatcher gets the current dispatcher. It doesn't get the UI dispatcher. If the current thread's dispatcher isn't the UI dispatcher, then it doesn't return the UI dispatcher. – Servy May 26 '15 at 15:41
  • @Servy But I thought the `Application` dispatcher was the dispatcher associated with the first thread in the app? ie the UI dispatcher. If this is not the case then I have lots of broken code! – GazTheDestroyer May 26 '15 at 15:52
  • @GazTheDestroyer You can have any number of dispatchers in your program. If you're not in the context of any particular UI, how is it supposed to determine the correct one? – Servy May 26 '15 at 15:54
  • @Servy Because regardless of our current context we're referencing the static Application class, which has a primary (UI) thread, which has a dispatcher? – GazTheDestroyer May 26 '15 at 15:56
  • @GazTheDestroyer You're accessing the current dispatcher, but *there is no current dispatcher* on the context of your call. – Servy May 26 '15 at 15:58
  • @Servy: Really sorry but I'm still not getting this :-(. I accept there in no current dispatcher in the context of the background thread. However, the app DOES have a dispatcher on the UI thread, and this dispatcher is accessible via the static Application.Current class? It's the current application, not the current dispatcher. – GazTheDestroyer May 26 '15 at 16:06
  • 1
    @Servy - I think Asheh's difficulty revolves around your suggestion to update the UI in the `RunWorkerCompleted` event. This event is only fired at the end. I think the UI should be updated in the `ProgessChanged` event instead. – Chris Dunaway May 26 '15 at 19:13