Automating the Invokerequired Code Pattern

Automating the InvokeRequired code pattern

Lee's approach can be simplified further

public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
// See Update 2 for edits Mike de Klerk suggests to insert here.

if (control.InvokeRequired) {
control.Invoke(action);
} else {
action();
}
}

And can be called like this

richEditControl1.InvokeIfRequired(() =>
{
// Do anything you want with the control here
richEditControl1.RtfText = value;
RtfHelpers.AddMissingStyles(richEditControl1);
});

There is no need to pass the control as parameter to the delegate. C# automatically creates a closure.

If you must return a value, you can use this implementation:

private static T InvokeIfRequiredReturn<T>(this Control control, Func<T> function)
{
if (control.InvokeRequired) {
return (T)control.Invoke(function);
} else {
return function();
}
}

UPDATE:

According to several other posters Control can be generalized as ISynchronizeInvoke:

public static void InvokeIfRequired(this ISynchronizeInvoke obj,
MethodInvoker action)
{
if (obj.InvokeRequired) {
var args = new object[0];
obj.Invoke(action, args);
} else {
action();
}
}

DonBoitnott pointed out that unlike Control the ISynchronizeInvoke interface requires an object array for the Invoke method as parameter list for the action.


UPDATE 2

Edits suggested by Mike de Klerk (see comment in 1st code snippet for insert point):

// When the form, thus the control, isn't visible yet, InvokeRequired  returns false,
// resulting still in a cross-thread exception.
while (!control.Visible)
{
System.Threading.Thread.Sleep(50);
}

See ToolmakerSteve's and nawfal's comments below for concerns about this suggestion.

Automating the InvokeRequired code pattern

Lee's approach can be simplified further

public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
// See Update 2 for edits Mike de Klerk suggests to insert here.

if (control.InvokeRequired) {
control.Invoke(action);
} else {
action();
}
}

And can be called like this

richEditControl1.InvokeIfRequired(() =>
{
// Do anything you want with the control here
richEditControl1.RtfText = value;
RtfHelpers.AddMissingStyles(richEditControl1);
});

There is no need to pass the control as parameter to the delegate. C# automatically creates a closure.

If you must return a value, you can use this implementation:

private static T InvokeIfRequiredReturn<T>(this Control control, Func<T> function)
{
if (control.InvokeRequired) {
return (T)control.Invoke(function);
} else {
return function();
}
}

UPDATE:

According to several other posters Control can be generalized as ISynchronizeInvoke:

public static void InvokeIfRequired(this ISynchronizeInvoke obj,
MethodInvoker action)
{
if (obj.InvokeRequired) {
var args = new object[0];
obj.Invoke(action, args);
} else {
action();
}
}

DonBoitnott pointed out that unlike Control the ISynchronizeInvoke interface requires an object array for the Invoke method as parameter list for the action.


UPDATE 2

Edits suggested by Mike de Klerk (see comment in 1st code snippet for insert point):

// When the form, thus the control, isn't visible yet, InvokeRequired  returns false,
// resulting still in a cross-thread exception.
while (!control.Visible)
{
System.Threading.Thread.Sleep(50);
}

See ToolmakerSteve's and nawfal's comments below for concerns about this suggestion.

Cleaning up code littered with InvokeRequired

Well how about this:

public static class ControlHelpers
{
public static void InvokeIfRequired<T>(this T control, Action<T> action) where T : ISynchronizeInvoke
{
if (control.InvokeRequired)
{
control.Invoke(new Action(() => action(control)), null);
}
else
{
action(control);
}
}
}

Use it like this:

private void UpdateSummary(string text)
{
summary.InvokeIfRequired(s => { s.Text = text });
}

Is InvokeRequired required?

If you are confident that a particular route can only be reached by a callback thread, then I'd be inclined to agree with you - not really for the purpose of avoiding the Invoke, but simply to avoid any duplication. If a path can be reached from multiple routes, it may be preferable to have the check to avoid any overheads in the non-threaded case, but: refactoring so that each code-path knows what it is doing (just calling a utility method) may be preferable, i.e. the UI thread just calls Foo(), where as the worker thread uses Invoke / MethodInvoker to call Foo().

not yet another invoke required ( + background worker)

Well, it doesn't matter at all for a Button control. Windows has a hard rule that all the child windows of a window must be owned by the same thread. So by that rule, both the form's and the button's Begin/Invoke() methods will marshal the call to the correct thread.

Do note that it gets to be awkward when you need to update a property of a component that doesn't have a Handle property. Like a ToolStripButton. Now you have to pick another control. So favor consistency and always use the form.

And sure, do favor BackgroundWorker. It has a knack for helping you to get this right by limiting the number of ways you can shoot your foot.

How do I implement InvokeRequired UI pattern inside an async method?

When using async-await with a custom awaiter such as the common TaskAwaiter, the SynchronizationContext is being implicitly captured for you. Later, when the asynchronous method completes, the continuation (any code after the await) is being marshaled back to that same sync context using SynchronizationContext.Post.

This altogether eliminates the need to use InvokeRequired and other techniques used to mainpulate work on the UI thread. In order to do that, you'll have to trace your method calls all the way to the top level ones and refactor them to use async-await probably.

But, to address the specific problem as is, what you can do is capture the WinFormSynchronizationContext when your Form initializes:

partial class SomeForm : Form
{
private TaskScheduler _uiTaskScheduler;
public SomeForm()
{
_uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
}

And later use it when you want to await:

if (InvokeRequired)
{
Task uiTask = new Task(() => DoSomethingToUserInterface());
uiTask.RunSynchronously(_uiTaskScheduler);
}
else
{
// Do async work
}

Neat way of calling InvokeRequired and Invoke

The SO question here addresses this issue from a C# perspective, and any of the answers can probably be tailored to VB easily enough.

Although my answer wasn't the accepted one, I find using MethodInvoker anonymous method approach to be the most straightforward.

Hope this helps.

Weird InvokeRequired issue

It is a standard threading race. You are starting the thread too soon, before the TreeView is created. So your code sees InvokeRequired as false and fails when a split second later the native control gets created. Fix this by only starting the thread when the form's Load event fires, the first event that guarantees that all the control handles are valid.

Some mis-conceptions in the code btw. Using lock is unnecessary, both InvokeRequired and Begin/Invoke are thread-safe. And InvokeRequired is an anti-pattern. You almost always know that the method is going to be called by a worker thread. So use InvokeRequired only to throw an exception when it is false. Which would have allowed diagnosing this problem early.



Related Topics



Leave a reply



Submit