Best Way to Invoke Any Cross-Threaded Code

Best Way to Invoke Any Cross-Threaded Code?

You also could use an extension method and lambdas to make your code much cleaner.

using System.ComponentModel;
public static class ISynchronizeInvokeExtensions
{
public static void InvokeEx<T>(this T @this, Action<T> action) where T : ISynchronizeInvoke
{
if (@this.InvokeRequired)
{
@this.Invoke(action, new object[] { @this });
}
else
{
action(@this);
}
}
}

So now you can use InvokeEx on any ISynchronizeInvoke and be able to access the properties and fields of implementing class.

this.InvokeEx(f => f.listView1.Items.Clear());

Cleanest Way to Invoke Cross-Thread Events

A couple of observations:

  • Don't create simple delegates explicitly in code like that unless you're pre-2.0 so you could use:
   BeginInvoke(new EventHandler<CoolObjectEventArgs>(mCoolObject_CoolEvent), 
sender,
args);
  • Also you don't need to create and populate the object array because the args parameter is a "params" type so you can just pass in the list.

  • I would probably favor Invoke over BeginInvoke as the latter will result in the code being called asynchronously which may or may not be what you're after but would make handling subsequent exceptions difficult to propagate without a call to EndInvoke. What would happen is that your app will end up getting a TargetInvocationException instead.

Cross thread invoke exception

This is due to the fact that the Invoke method has a signature of:

object Invoke(Delegate method, params object[] args)

The params keyword in front of the args parameter indicates that this method can take a variable number of objects as parameters. When you supply an array of objects, it is functionally equivalent to passing multiple comma-separated objects. The following two lines are functionally equivalent:

Invoke(new Action<object[]>(MethodParamIsObjectArray), new object[] { 3, "test" });
Invoke(new Action<object[]>(MethodParamIsObjectArray), 3, "test");

The proper way to pass an object array into Invoke would be to cast the array to type Object:

Invoke(new Action<object[]>(MethodParamIsObjectArray), (object)new object[] { 3, "test" });

Cross Thread Operation - Invoked function calls another function C#

You need to .Invoke onto the main thread to change any controls.

Image image;
if (this.currColor == Color.Blue)
image = bluePencil;
else
image = redPencil;

this.Invoke(new MethodInvoker(() => pencils[currIndex--].Image = image));

The => is the syntax for a lambda (called anonymous method in other languages). Think about it as a one-line function.

() => pencils[currIndex--].Image = image

is the same as:

void SetImage(Image image, ref int currIndex) {
pencils[currIndex--].Image = image;
}

MethodInvoker provides a simple delegate that is used to invoke a method with a void parameter list

Cross-thread operation

Nothing wrong with what you've got. If you don't want to make a recursive call, you could just throw an anonymous delegate in the Invoke() call:

private void SetText(string text)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
this.textBox1.Text = text;
});
}
else
{
this.textBox1.Text = text;
}
}

Cross-thread operation

You can’t directly access from a thread other than the thread it was created on. You can set that property value by using MethodInvoker.

lblDisplay.Invoke((MethodInvoker)(() => { lblDisplay.Visible = true; }));

This the way you need to access the control in different thread.

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on

As per Prerak K's update comment (since deleted):

I guess I have not presented the question properly.

Situation is this: I want to load data into a global variable based on the value of a control. I don't want to change the value of a control from the child thread. I'm not going to do it ever from a child thread.

So only accessing the value so that corresponding data can be fetched from the database.

The solution you want then should look like:

UserContrl1_LOadDataMethod()
{
string name = "";
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
}
if(name == "MyName")
{
// do whatever
}
}

Do your serious processing in the separate thread before you attempt to switch back to the control's thread. For example:

UserContrl1_LOadDataMethod()
{
if(textbox1.text=="MyName") //<<======Now it wont give exception**
{
//Load data correspondin to "MyName"
//Populate a globale variable List<string> which will be
//bound to grid at some later stage
if(InvokeRequired)
{
// after we've done all the processing,
this.Invoke(new MethodInvoker(delegate {
// load the control with the appropriate data
}));
return;
}
}
}


Related Topics



Leave a reply



Submit