Methodinvoker VS Action for Control.Begininvoke

MethodInvoker vs Action for Control.BeginInvoke

Both are equally correct, but the documentation for Control.Invoke states that:

The delegate can be an instance of
EventHandler, in which case the sender
parameter will contain this control,
and the event parameter will contain
EventArgs.Empty. The delegate can also
be an instance of MethodInvoker, or
any other delegate that takes a void
parameter list. A call to an
EventHandler or MethodInvoker delegate
will be faster than a call to another
type of delegate.

So MethodInvoker would be a more efficient choice.

MethodInvoker vs Control.Invoke

UpdateSystemMode.Invoke(new UpdateSystemModeDelegate(UpdateSystemMode));

and

this.Invoke((MethodInvoker)delegate
{
systemMode.Text = systemMode.ToString();
});

is absolutely same
as well as

this.Invoke((Action)(()=> systemMode.Text = systemMode.ToString()));

right way:

public void UpdateSystemMode()
{
if (this.InvokeRequired)
this.BeginInvoke((Action)UpdateSystemMode);
else
systemMode.UpdateSystemMode();
}

MethodInvoker vs new MethodInvoker

In the code snippets above the author wants to create a delegate given a lambda expression. Lambda expressions do not have a CLR type (they do not have a delegate type). They are convertible to delegates, though.

In one case a lambda is converted to a delegate type. In the other case constructor syntax is being used to perform the conversion. The two variants do the same thing. You can do whatever you like.

C# Winforms Control.BeginInvoke with ActionT

BeginInvoke is used to execute some code in the thread in which the control was created. It's mandatory update controls in their own thread (usually in main thread) or you get an exception. So, your use or BeginInvoke is correct.

The problem is that, when you are running in the main thread and are be able to update the control, you delegate in an action the update. The action run in other thread and you are "cancelling" de BeginInvoke and getting the expected exception trying to update a control in other thread.

I use SynchronizationContext for this kind of things. In your form's code, add a variable:

private static SynchronizationContext Context;

UPDATE: And initialize in the constructor:

    public YourForm()
{
this.InitializeComponent();

Context = SynchronizationContext.Current;

// Other code
}

Add this method:

private static void RunInMainThread(Action operation)
{
if (Context != SynchronizationContext.Current)
{
Context.Post(o => operation(), null);
}
else
{
operation();
}
}

If you already are running in main thread, your code run inmediatly. In other case, Post action run asynchronously in the main thread. You can use Send instead or Post to run synchronously. And use it when you need access to controls:

public void OnMqttServerApplicationMessageReceived(MqttApplicationMessageReceivedEventArgs e)
{
var msg = string.Format(
"Client[{0}]>> Topic:{1} Payload:{2} Qos:{3} Retain:{4}",
e.ClientId,
e.ApplicationMessage.Topic,
e.ApplicationMessage.Payload.ToString(),
e.ApplicationMessage.QualityOfServiceLevel,
e.ApplicationMessage.Retain);

RunInMainThread(() =>
{
MsgList.Items.Add(msg);
// Other code...
}
}

You can create an extension methods (Post and Send) for SynchronizationContext instead of RunInMainThread and reuse in your projects.

Control.BeginInvoke() not working without MethodInvoker - But why?

When you call BeginInvoke you are scheduling a UI update to happen, and then continuing along with your program without waiting for that UI update to happen. When you do this just a few times you're fine, but the problem that you're having is that you're sending in 100,000 requests all at once, and it's going to take the UI some time to get through all of those requests, and nothing else is going to be able to be done in that time because any new UI updates go to the end of the line, and won't be performed until the other requests are finished.

While there are ways to keep your general approach the same and try to let other operations cut to the front of the line, the proper approach is to avoid the problem in the first place. You have no need to be sending 100,000 updates to single textbox at once.

If you want the textbox to have the appearance of a clock, in which it ticks up, then a Timer would be a good tool for the job; you can handle the Tick event to update the textbox every second, quarter second, or some other more "human time" interval.

If the idea is to update the UI with the progress of some long running operation, then you simply want to ensure that you don't update progress quite so often. Update progress every few dozen iterations of your loop, instead of every single one, for example.

How to use Invoke or BeginInvoke method to handle controls in windows form

I can't really tell from your code what exactly the problem is. But it will most certainly be a multitasking problem.

The Exception is easy to reproduce in a very simple Application by just calling Invoke before the InitializeComponent call.

Since all your shown code looks synchronous and is beyond Registry_Scan's InitializeComponent I don't think the problem is there.

So you should check StartScanning.GeekCreateControl() and other calls for possible multitasking issue which will trigger an Invoke before InitializeComponent like this one:

public partial class Form1 : Form
{
public Form1()
{
Task.Factory.StartNew(() =>
{
this.Invoke(new Action(() =>
{
this.Text = "UI Change";
}));
});

InitializeComponent();
}
}

Hope this helps

Can you use pre-defined ActionT in Control.BeginInvoke

You have to pass a plain delegate to BeginInvoke. You can use variable capture to wrap up your parameters like this:

private void MakeItHappen(string InputA, string InputB)
{
if (this.InvokeRequired)
{
this.BeginInvoke((Action)delegate () {
MakeItHappen(InputA, InputB);
});
}
else
{
// do stuff with InputA and InputB
}
}

Reasons that Control.BeginInvoke would not execute a delegate?

According to MSDN InvokeRequired can return false even in cases where InvokeRequired should be true - namely in the case that you access InvokeRequired before the Handle of that control/form (or a parent of it) has been created.

Basically your check is incomplete which leads to the result you see.

You need to check IsHandleCreated - if that is false then you are in trouble because an Invoke/BeginInvoke would be necessary BUT won't work robustly since Invoke/BeginInvoke check which thread created Handle to do their magic...

Only if IsHandleCreated is true you act based on what InvokeRequired returns - something along the lines of:

if (control.IsHandleCreated)
{
if (control.InvokeRequired)
{
control.BeginInvoke(action);
}
else
{
action.Invoke();
}
}
else
{
// in this case InvokeRequired might lie - you need to make sure that this never happens!
throw new Exception ( "Somehow Handle has not yet been created on the UI thread!" );
}

Thus the following is important to avoid this problem

Always make sure that the Handle is already created BEFORE the first access on a thread other than the UI thread.

According to MSDN you just need to reference control.Handle in the UI thread to force it being created - in your code this must happen BEFORE the very first time you access that control/form from any thread that is not the UI thread.

For other possibilities see the answer from @JaredPar .

Dispatcher Invoke(...) vs BeginInvoke(...) confusion

When you use Dispatcher.BeginInvoke it means that it schedules the given action for execution in the UI thread at a later point in time, and then returns control to allow the current thread to continue executing. Invoke blocks the caller until the scheduled action finishes.

When you use BeginInvoke your loop is going to run super fast since BeginInvoke returns right away. This means that you're adding lot and lots of actions to the message queue. You're adding them much faster than they can actually be processed. This means that there's a long time between when you schedule a message and when it actually gets a chance to be run.

The actual action that you're running uses the field _number. But _number is being modified by the other thread very quickly and while the action is in the queue. This means that it won't display the value of _number at the time you scheduled the action, but rather what it is after it has been continuing on in it's very tight loop.

If you use Dispatcher.Invoke instead then it prevents the loop from "getting ahead of itself" and having multiple scheduled events, which ensures that the value that it's writing is always the "current" value. Additionally, by forcing each iteration of the loop to wait for the message to be run it makes the loop a lot less "tight", so it can't run as quickly in general.

If you want to use BeginInvoke the first thing you really need to do is slow down your loop. If you want it to update the text every second, or ever 10ms, or whatever, then you can use Thread.Sleep to wait the appropriate amount of time.

Next, you need to take a copy of _number before passing it to the Dispatcher so that it displays the value at the time you scheduled it, not at the time it is executed:

while (true)
{
if (_number++ > 10000)
_number = 0;
int copy = _number;
this.Dispatcher.BeginInvoke(new Action(() => UpdateText(copy))
, System.Windows.Threading.DispatcherPriority.Background, null);
Thread.Sleep(200);
}

private void UpdateText(int number)
{
this.Text = number.ToString();
}


Related Topics



Leave a reply



Submit