0

I am facing to a very strange issue when I use dispatcher.invoke method in wpf.

Background:

I defined a user control there is a DoWorkEventArgs to support some async work:

public class MyUserControl : UserControl
{
    private BackgroundWorker bw;

    public MyUserControl()
    {
        bw.DoWork += new DoWorkEventHandler(DoWorkMethod);
    }

    public void StartWork()
    {
        bw.RunWorkerAsync();
    }

    void DoWorkMethod(object sender, DoWorkEventArgs e)
    {
        this.Dispatcher.Invoke((System.Action)delegate()
        {
            //Add some item in a ListBox, this ListBox is defined in the user control.
            TextBlock b = new TextBlock();
            //some code
            Listbox.Items.Add(b);
        }
    }
}

When a button click I created 2 instance of this user control and call there StartWork method:

MyUserControl control1 = new MyUserControl();
MyUserControl control2 = new MyUserControl();

control1.StartWork();
control2.StartWork();

Here is the problem, sometimes the ListBox in usercontrol1 is not updated, there is no item in it, sometimes this situation happend in the ListBox of usercontrol2, I debug them and I found the code runs normal, the ListBox.Items.Add method runs, and the results just don't come out. If I change Dispatcher.Invoie to Dispatcher.BeginInvoke, then it's normal. Is anyone know the reason?

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
Jay
  • 1
  • 1
  • look at http://stackoverflow.com/questions/19009174/dispatcher-invoke-vs-begininvoke-confusion – Agent_L Aug 20 '14 at 09:07
  • 1
    If this is your real code look like, You don't need a `BackgroundWorker` at all. You can add TextBlock to Listbox inside `StartWork` method directly. – Sriram Sakthivel Aug 20 '14 at 09:10
  • You create BackgroundWorker which is supposed to be working on sth not connected with UI and once it starts you switch back to UI. Think it over first. – Maximus Aug 20 '14 at 09:10
  • 1
    Your use of the `BackgroundWorker` is invalid because you are trying to run the `DowWork` method on the UI thread, but the whole purpose of that method is that it runs on a background thread. – Sheridan Aug 20 '14 at 09:12
  • @Agent_L, did you even read this question? – Sheridan Aug 20 '14 at 09:12
  • @Sheridan I wanted to ask same question to you. His code is standard use of `BackgroundWorker` - if you assume there is intensive operation in `DoWorkMethod` before calling dispatcher to display results. – Agent_L Aug 20 '14 at 09:21
  • *I wanted to ask same question to you... if you assume there is intensive operation in DoWorkMethod before calling dispatcher*... I've been here long enough to know *not* to make any assumptions about users' code, but your linked question clearly has nothing to do with this issue. Thanks all the same. – Sheridan Aug 20 '14 at 09:26
  • @Agent_L No. Using a backgroundworker to do UI work is the exact opposite of what background worker is used for. You better not comment on things when you're spreading false information. – Dbl Aug 20 '14 at 09:51
  • The reason probably is that sometimes your ctor is not finished when the code in the Invoke method starts. – Oleg Ignatov Aug 20 '14 at 15:22

1 Answers1

0

Firstly, your use of the BackgroundWorker is invalid because you are trying to run the DowWork method on the UI thread, but the whole purpose of that method is that it runs on a background thread. If you want to know how to correctly implement a BackgroundWorker, then please see my answer to the How to correctly implement a BackgroundWorker with ProgressBar updates? question here on Stack Overflow.

However, if you just want to run some code asynchronously, you really don't need to use a BackgroundWorker these days. Instead, you can use the Task class to do something like this:

Task.Factory.StartNew(() => NameOfMethodToRunAsynchronously);

If you need to run part of the NameOfMethodToRunAsynchronously method on the UI thread, then you can return to your Dispatcher.Invoke call:

private void NameOfMethodToRunAsynchronously()
{
    // Some long running process
    Dispatcher.Invoke((System.Action)delegate()
    {
        //Add some item in a ListBox, this ListBox is defined in the user control.
        TextBlock b = new TextBlock();
        //some code
        Listbox.Items.Add(b);
    }
}

Finally, to answer your original concern about the Dispatcher class, please see the Dispatcher Class page on MSDN. From that page:

Provides services for managing the queue of work items for a thread.

Note that it says a thread and not the UI thread. This means that it could be the UI thread like you want, but not necessarily. In order to ensure that it will be for the UI thread, we can simply set it on the UI thread in the constructor of MainWindow.xaml.cs:

Dispatcher uiDispatcher = Application.CurrentDispatcher;

So to ensure that your Dispatcher will run on the UI thread, just use that instance:

uiDispatcher.Invoke((System.Action)delegate()
{
    //Add some item in a ListBox, this ListBox is defined in the user control.
    TextBlock b = new TextBlock();
    //some code
    Listbox.Items.Add(b);
}
Community
  • 1
  • 1
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • The question was: Why it sometimes fails with `Invoke` but works with `BeginInvoke`. How the external thread for `Invoke` is created seems irrelevant. – Agent_L Aug 20 '14 at 09:27
  • Retaliatory down vote, eh? Ok that's pathetic of you... feeling better now @Agent_L? The question author said *Here is the problem, sometimes the ListBox in usercontrol1 is not updated*... I am helping them to solve their problem, so how can that be bad? I suggest that you grow up. – Sheridan Aug 20 '14 at 09:31
  • Nothing retaliatory here, I simply believe it's not a good answer. IMHO it's "if you don't know how to fix what's broken then fix something else". He's using `Dispatcher` tied to his `UserControl`, what's wrong with that? Why change to `Application.Current` ? – Agent_L Aug 20 '14 at 09:35
  • You *appear* to know what you're talking about... please feel free to provide your own answer. – Sheridan Aug 20 '14 at 09:38