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
overBeginInvoke
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 toEndInvoke
. What would happen is that your app will end up getting aTargetInvocationException
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
Validation: How to Inject a Model State Wrapper with Ninject
How to Upload Files in ASP.NET Core
Merging Dlls into a Single .Exe with Wpf
Converting Svg to Png Using C#
How to Get the CPU Temperature
Email Address Validation Using ASP.NET MVC Data Type Attributes
How to Read an Excel File in C# Without Using Microsoft.Office.Interop.Excel Libraries
Produce a Random Number in a Range Using C#
How to Elegantly Check If a Number Is Within a Range
Visual Studio: Contextswitchdeadlock
How to Send HTML-Formatted Email
Difference Between Await and Continuewith
Bind Textbox on Enter-Key Press
How to Fast Get Hardware-Id in C#
Retrying Httpclient Unsuccessful Requests
How to Get Linq to Return the Object Which Has the Max Value for a Given Property