C# Control.Invoke a method group - c#

I am using threads to run long operations in my program's UI so that it doesn't lock up. However, in those tasks I need to update controls, which is impossible not from the thread they were created on. It is suggested to use control.BeginInvoke(Delegate) to execute the method you want.
However, to do that you have to declare a delegate type and only then you can call them.
So, it goes like this: if I want to execute method void Update(), i have to go:
delegate void CallbackVoid();
void Update() {...}
...(in task code)...
this.BeginInvoke(new CallbackVoid(Update));
This is rather tiresome to do for every single method out there. Can't I just somehow do it naturally, like:
void Update() {...}
this.BeginInvoke(Update);

One option which simplified things is to add an extension method:
public static void BeginInvokeAction(this Control control, Action action)
{
control.BeginInvoke(action);
}
Then you can just use:
this.BeginInvokeAction(action);
The reason this works is that we're now providing a concrete delegate type for the compiler to convert the method group to.

UPDATED: WORKS FOR WPF!!!
You can use short syntax with anonymous methods, without even declaring your methods
Dispatcher.BeginInvoke(DispatcherPriority.Background, new MethodInvoker(() =>
{
//Your Update code
}));

Try the following:
if (this.controlname.InvokeRequired && !this.controlname.IsDisposed)
{
Invoke(new MethodInvoker(delegate()
{
//Update control on GUI here!
}));
else if(!this.controlname.IsDisposed)
{
//AND here!
}

BeginInvoke is asynchronous, Invoke is synchronous, which one you use depends on what you're trying to do. If you need the call to complete before you move on, then you want synchronous calls.
Here's my favorite construct for synchronous invokes:
static void InvokeIfRequired(Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action.Invoke();
}
}
Used:
void MyTestFunction()
{
InvokeIfRequired(myControl, () =>
{
MyFunction();
MyOtherFunction();
});
// Or more simply:
InvokeIfRequired(myControl, () => MyFunction());
}
There is a little overhead in the creation of the Action, but it simplifies the code quite a bit to not have to think about the details everywhere.

Related

Calling Compile().DynamicInvoke within an extension method causes a "target of an invocation" error in WPF, but not WinForms

I have an extension that works in WinForms and I'm trying to get it to work in WPF, but it's throwing the following exception when ic all .Compile().DynamicInvoke().
Here is how I'm calling the method:
spDetailControls.InvokeThreadSafeMethod(() => spDetailControls.Children.Add(_generalDetail));
And here is the extension method:
public static void InvokeThreadSafeMethod(this System.Windows.FrameworkElement control, Expression<Action> method)
{
if (!control.Dispatcher.CheckAccess())
{
var del = new InvokeThreadSafeMethodDelegate(InvokeThreadSafeMethod);
control.Dispatcher.Invoke(del, control, method);
}
else
{
method.Compile().DynamicInvoke();
}
}
Is there something else I should be calling in WPF? Or something else I'm missing?
EDIT:
Per LucasTrzesniewski's suggestion, I changed Expression to just Action. It no longer throws that error, but instead throws a cross thread exception error. The whole point of this extension method is so that it can be called within a background thread.
Here's the changed code. It's likely that I changed something wrong
public static void InvokeThreadSafeMethod(this System.Windows.FrameworkElement control, Action method)
{
if (!control.Dispatcher.CheckAccess())
control.Dispatcher.Invoke(new InvokeThreadSafeMethodDelegate(InvokeThreadSafeMethod), control, method);
else
method.DynamicInvoke();
}
I had to change the delegate as well:
private delegate void InvokeThreadSafeMethodDelegate(System.Windows.FrameworkElement control, Action method);
Your extension method is unnecessarily complicated, and may be obfuscating an error originating in your delegate. This is all you need:
public static void InvokeThreadSafeMethod(
this System.Windows.FrameworkElement control,
Action method)
{
if (control.Dispatcher.CheckAccess())
method();
else
control.Dispatcher.Invoke(method);
}
If you're getting a cross-thread exception while using the implementation above, then your delegate is probably touching a UI element that was created on a different thread than control.
For example, if this is your actual code:
spDetailControls.InvokeThreadSafeMethod(
() => spDetailControls.Children.Add(_generalDetail));
...then maybe you _generalDetail on a background thread?

Invoke without Func in Property's getter for .Net 2.0

I would like to use Invoke in getter, how to do it when using .Net 2.0 not e.g. 4.0? For .Net > 2.0 we can use Func and what is replacement for .Net 2.0?
Here is example for .Net 4.0 (from link)
public ApplicationViewModel SelectedApplication
{
get {
if (this.InvokeRequired)
{
return (ApplicationViewModel)this.Invoke(new Func<ApplicationViewModel>(() => this.SelectedApplication));
}
else
{
return _applicationsCombobox.SelectedItem as ApplicationViewModel;
}
}
}
Since you're using .NET 2.0, you won't have the Func delegate available to you, but you can use the MethodInvoker delegate.
You won't be able to use the lambda expression syntax with .NET 2.0, but you can use the "anonymous delegate" syntax (which is pretty much the same thing), as shown in the code example below.
Querying data in UI controls from a non-UI thread is generally an uncommon thing to do; usually your UI controls trigger events that execute on the UI thread, so you gather the data you need from your UI controls at that time and then pass that data on to some other function, so you don't need to worry about doing an Invoke.
In your case, though, you should be able to do something like this:
public ApplicationViewModel SelectedApplication
{
get
{
if (this.InvokeRequired)
{
ApplicationViewModel value = null; // compiler requires that we initialize this variable
// the call to Invoke will block until the anonymous delegate has finished executing.
this.Invoke((MethodInvoker)delegate
{
// anonymous delegate executing on UI thread due calling the Invoke method
// assign the result to the value variable so that we can return it.
value = _applicationsCombobox.SelectedItem as ApplicationViewModel;
});
return value;
}
else
{
return _applicationsCombobox.SelectedItem as ApplicationViewModel;
}
}
}
EDIT: Now that I look at your .NET 4.0 code sample and also look at the Invoke function, I see how it can return a value (not something that I've had a reason to use before).
Well, the MethodInvoker delegate does not expect a return value, but as #haiyyu pointed out, you could define your own delegate. For instance, you would just need to define your own Func<TResult> delegate, and the original code would probably work fine:
// this is all that is required to declare your own Func<TResult> delegate.
delegate TResult Func<TResult>();
Sample code from the MSDN page:
public partial class Form1 : Form
{
public Form1()
{
// Create a timer that will call the ShowTime method every second.
var timer = new System.Threading.Timer(ShowTime, null, 0, 1000);
}
private void ShowTime(object x)
{
// Don't do anything if the form's handle hasn't been created
// or the form has been disposed.
if (!this.IsHandleCreated && !this.IsDisposed) return;
// Invoke an anonymous method on the thread of the form.
this.Invoke((MethodInvoker) delegate
{
// Show the current time in the form's title bar.
this.Text = DateTime.Now.ToLongTimeString();
});
}
}
Use delegates, they are a sort of typed function pointers.
Here's some more reading: http://msdn.microsoft.com/en-us/library/ms173171%28v=vs.80%29.aspx

What is considered good programming practice in multi-threaded winform applications with delegate usage?

I'm modifying an application written in C# that makes heavy-use of multi-threading to play audio files and display images to a user. Given that it is multi-threaded, I need to use the Invoke method often to change form elements. I'm running into a pattern that I'm not very comfortable with, where I find myself writing frequent, small, delegate methods that typically only do one thing. An example of this is as follows:
delegate void setImageCallback(Image img);
private void setImage(Image img)
{
this.pictureBox1.Image = img;
}
private void someOtherMethod()
{
...
if (this.pictureBox1.InvokeRequired)
{
this.Invoke(new setImageCallback(setImage), Image.FromFile("example.png");
}
else
{
this.pictureBox1.Image = Image.FromFile("example.png");
}
...
}
How do people generally handle these situations, so that you don't find yourself writing an absurd number of delegates and methods just to remain thread-safe? Obviously, consolidation of similar methods is great, but if I potentially need to update every form element on my form, I don't want to have a "modify" delegate and method for each of these.
Thanks.
A good example is here.
this.BeginInvoke( (Action) (()=>
{
pictureBox1.Image = Image.FromFile("example.png");
}));
You definitely don't need a separate delegate for each. You can use Action delegates and lambda expressions to simplify it, like this:
private void SomeOtherMethod()
{
Action action = () => pictureBox1.Image = Image.FromFile("example.png");
if (pictureBox1.InvokeRequired)
{
Invoke(action);
}
else
{
action();
}
}
Or you can separate out the if statement and InvokeRequired check and generalize it even more, like this:
public static void InvokeIfRequired(Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action();
}
}
private void SomeOtherMethod()
{
InvokeIfRequired(() => pictureBox1.Image = Image.FromFile("example.png");
}
I would use the MethodInvoker type in conjunction with an anonymous method or lambda expression. I would also build the invocation logic into the method itself, rather than using a separate thread-safe method:
void SomeMethod(/* with whatever args */) {
if (InvokeRequired)
Invoke(new MethodInvoker(() => SomeMethod(/* args used to call method */)));
else
// the method body itself
}

Solve a cross-threading Exception in WinForms

Presently I'm working with WinForms(in C#) and I have to run the application in the background. For this purpose I'm using asynchronous. When I run the application it's showing an exception like
"Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on."
How can I solve this error?
When making method calls to a control, if the caller is on a different thread than the one the control was created on, you need to call using Control.Invoke. Here is a code sample:
// you can define a delegate with the signature you want
public delegate void UpdateControlsDelegate();
public void SomeMethod()
{
//this method is executed by the background worker
InvokeUpdateControls();
}
public void InvokeUpdateControls()
{
if (this.InvokeRequired)
{
this.Invoke(new UpdateControlsDelegate(UpdateControls));
}
else
{
UpdateControls();
}
}
private void UpdateControls()
{
// update your controls here
}
Hope it helps.
Most often, the best way to do this sort of thing with WinForms is to use BackgroundWorker, which will run your work on a background thread, but provide you with a nice clean way to report status back to the UI.
In a lot of everyday .NET programming, explicitly creating threads or calling .Invoke is a sign that you're not using the framework to its full advantage (of course, there are lots of legitimate reasons to do low-level stuff too, it's just that they're less common that people sometimes realise).
You need to check if Invoke is required for the control you're trying to update. Something like this:
Action<Control, string> setterCallback = (toSet, text) => toSet.Text = text;
void SetControlText(Control toSet, string text) {
if (this.InvokeRequired) {
this.Invoke(setterCallback, toSet, text);
}
else {
setterCallback(toSet, text);
}
}
Updated from Invoke to begin Invoke
// you can define a delegate with the signature you want
public delegate void UpdateControlsDelegate();
public void SomeMethod()
{
//this method is executed by the background worker
InvokeUpdateControls();
}
public void InvokeUpdateControls()
{
if (this.InvokeRequired)
{
this.BeginInvoke(new UpdateControlsDelegate(UpdateControls));
}
else
{
UpdateControls();
}
}
private void UpdateControls()
{
// update your controls here
}
A pattern you might find useful is to do a check at the top of functions that interact with the GUI to see whether you are running on the correct thread or not and have the function invoke itself if required. Like this:
public delegate void InvocationDelegate();
public void DoGuiStuff(){
if (someControl.InvokeRequired){
someControl.Invoke(InvocationDelegate(DoGuiStuff));
return;
}
//GUI manipulation here
}
Using this pattern - if you are on the correct thread when the method is called it doesn't invoke itself, but if you are on a different thread it will invoke itself and then return (so the GUI manipulation logic is only ever called once either way).
The UI changes can be done with Control.Invoke() methods, this cross thread exception can be solved using below code snippet.
void UpdateWorker()
{
//Here ddUser is the user control
//Action to be performed should be called within { } as like below code
if (this.ddUser.InvokeRequired)
ddUser.Invoke(new MethodInvoker(() => { ddUser.Size = new Size(100, 100); }));
}
I knew the topic is 10 years old, but I would like to improve the solution for generic through lambda selector instead of defining of each type of setter
private void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value)
{
if (this.InvokeRequired)
this.Invoke(MyUtils.GetSetter(selector), control, value);
else
DataCrawlerUtils.GetSetter(selector)(control, value);
}
Or static
public static void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value) where C : Control
{
if (control.InvokeRequired)
control.Invoke(DataCrawlerUtils.GetSetter(selector), control, value);
else
DataCrawlerUtils.GetSetter(selector)(control, value);
}
GetSetter method from here to assign value to a property has been selected through lambda
public static Action<T, TProperty> GetSetter<T, TProperty>(
Expression<Func<T, TProperty>> pExpression
)
{
var parameter1 = Expression.Parameter(typeof(T));
var parameter2 = Expression.Parameter(typeof(TProperty));
// turning an expression body into a PropertyInfo is common enough
// that it's a good idea to extract this to a reusable method
var member = (MemberExpression)pExpression.Body;
var propertyInfo = (PropertyInfo)member.Member;
// use the PropertyInfo to make a property expression
// for the first parameter (the object)
var property = Expression.Property(parameter1, propertyInfo);
// assignment expression that assigns the second parameter (value) to the property
var assignment = Expression.Assign(property, parameter2);
// then just build the lambda, which takes 2 parameters, and has the assignment
// expression for its body
var setter = Expression.Lambda<Action<T, TProperty>>(
assignment,
parameter1,
parameter2
);
return setter.Compile();
}
Then the using is pretty simple
SetControlSafety(txtStatus, x => x.Text, "Loading resources...");
BeginInvoke
It is a good way to prevent a cross-thread exception. I read it in a book "The C# Programmer’s Study Guide (MCSD"
You can use BeginInvoke
BeginInvoke method is used to change values of UI control from other threads. It does it in a thread-safe way. It requires a delegate; it tells which UI control needs to change its value.
private async void button1_Click(object sender, EventArgs e)
{
Task task = Task.Run(() =>
{
this.BeginInvoke(new Action(() =>
{
label1.Text = "Hello";
}));
});
await task;
}
The value of label1.Text shall be changed to “Hello” and no exception will arise because it’s a threadsafe operation.

summarize silverlight multithreading access rules

Can someone be so kind as to give me a concise (general is fine) set of rules for what data/methods can and cannot be accessed from a secondary (non-UI) thread?
I would say that it is any DependencyObject that was created by the UIThread.
I would suggest using this extension method, when you are not sure...
public static class Extensions
{
public static void FastInvoke(this Dispatcher dispatcher, Action action)
{
if (dispatcher.CheckAccess())
action.Invoke();
else
dispatcher.BeginInvoke(action);
}
}
Use it like this:
Dispatcher.FastInvoke(delegate
{
StatusMessageText.Text = "OK";
});

Categories

Resources