2

I need to invoke this: string input_ip_r = listView1.Items[lc].SubItems[1].Text; so I used

if (InvokeRequired)
{
    this.Invoke(new MethodInvoker(function));
    return;
}

This worked but now I have put it into a BackgroundWorker and using this

if (InvokeRequired)
{
    this.Invoke(new MethodInvoker(bw.RunWorkerAsync));
    return;
}

it gives an error that you can only run BackgroundWorker one at a time.

So how do I invoke while in the Backgroundworker?

Servy
  • 202,030
  • 26
  • 332
  • 449
Dasher Labs
  • 99
  • 1
  • 3
  • 10
  • 2
    Ok I'm confused. **a)** why put the code to access the UI in a background worker? **b)** are you trying to run `bw.RunWorkerAsync` from within `bw.DoWork`? – Mike Park Dec 19 '12 at 20:17
  • 1
    What do you mean? The first code is supposed to be run from the background thread (calling the UI thread). The second code looks like it's trying to call the background thread. – Mario S Dec 19 '12 at 20:18
  • The string needs to be invoked and I cant work out how to do this during the background worker, the "new MethodInvoker" trys to run it a second time – Dasher Labs Dec 19 '12 at 20:23

2 Answers2

8

1) Don't put RunWorkerAsync as the method to invoke. It's not actually running the method that you think. What you should really put there is something like this:

this.Invoke(new MethodInvoker(MethodToUpdateUI));

MethodToUpdateUI should be some new method that you create that specifically does whatever UI updates should be made in this context.

2) There's no need for InvokeRequired. You're in a background thread. Invoking will always be required.

To be honest, the entire patter of if(invoke required) call myself else do stuff is an odd construct which I dislike. InvokeRequired should pretty rarely be used. You should almost always know whether you're in the UI thread or a background thread, if you don't, chances are something wrong (either you're always in one or the other and you just don't know which, or it shouldn't be non-deterministic). Usually this means having methods that must be run in the UI thread. If you're already in the UI thread you just call them, if you're in a background thread and know it then you call Invoke first.

On top of that, Invoke works just fine even if you call it when you're already in the UI thread, so there's really no significant negative consequences to just calling Invoke regardless of whether you're in a background thread or already in the UI thread.

3) Usually it's best to separate code for solving business problems from UI code. It's code smell to be invoking from within DoWork's handler. If this is right near the end, you should probably be adding an event handler to RunWorkerCompleted. If you're calling this periodically to update the UI with progress of the worker, you should be using ReportProgress and handling the ProgressReported event. For getting info from the UI for use in a long running task you should access it before starting the background task. For exceptional cases that aren't any of those, it may be appropriate to use Invoke, but the remaining cases ought to be rare.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • 1
    I need to set this value `listView1.Items[lc].SubItems[1].Text` inside the background worker without cross thread calls – Dasher Labs Dec 19 '12 at 21:21
  • @user1335937 Okay. That's what my answer is helping you address. – Servy Dec 19 '12 at 21:23
  • I'm quite new to c#, so I have a loop to get each listview item from subitem 2 and how to do get it inside the background worker to use? – Dasher Labs Dec 19 '12 at 21:37
  • @user1335937 Take all of the items out of the listview and put them into a data structure, such as a `List` or array. Then store that as a private instance field and use it in the background worker. That way you're not accessing the UI from the background worker in the first place. – Servy Dec 19 '12 at 21:40
6

I'm not quite sure how you want to use the values, but just to give you an example, you could easily just do this in the BackgroundWorker thread:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    string input_ip_r = "";
    this.Invoke(new Action(() => 
    {
        // Don't know what "lc" is (a loop variable?)
        input_ip_r = listView1.Items[lc].SubItems[1].Text;
    }));
}

See this answer for other ways of doing the same (this is for >= .Net 3.5)

Community
  • 1
  • 1
Mario S
  • 11,715
  • 24
  • 39
  • 47
  • I'm glad it worked for you =) Remember though, that this is just one of the ways you can go with it. @Servy answer has points that also should be considered. – Mario S Dec 19 '12 at 21:49