tags:

views:

135

answers:

2

Hello,

I have been searching stackoverflow for ways to marshal data back from a thread to the UI thread, and have found various ways to do it in 3.5.

One of the more elegant solutions to me; although I am still learning lambdas and closures is this solution http://stackoverflow.com/questions/783925/control-invoke-with-input-parameters.

I don't completely understand the code, but I do understand how to use it and it doesn't quite solve my problem.

I would like to call invoke and pass a string to another method (DisplayStatusUpdate(msg)). Any pointers would be appreciated.

    private void FireEventAppender_OnMessageLogged(object sender, MessageLoggedEventArgs e)
    {
        DisplayStatusUpdate(e.LoggingEvent.RenderedMessage);
    }

    private void DisplayStatusUpdate(string text)
    {
         _StatusTextBox.Text = _StatusTextBox.Text + text;
         _StatusTextBox.Text = String.Format("{0}\r\n", _StatusTextBox.Text);
         _StatusTextBox.SelectionStart = _StatusTextBox.Text.Length - 1;
         _StatusTextBox.ScrollToCaret();
    }
+4  A: 

You can do this with as many parameters as you like, for example:

private void FireEventAppender_OnMessageLogged(object sender, MessageLoggedEventArgs e)
{
    DisplayStatusUpdate(e.LoggingEvent.RenderedMessage);
}

private delegate void DisplayStatusUpdateDelegate(string text);

private void DisplayStatusUpdate(string text)
{
     if(InvokeRequired)
         this.Invoke(new DisplayStatusUpdateDelegate(DisplayStatusUpdate), text);
     else
     {
         _StatusTextBox.Text = _StatusTextBox.Text + text;
         _StatusTextBox.Text = String.Format("{0}\r\n", _StatusTextBox.Text);
         _StatusTextBox.SelectionStart = _StatusTextBox.Text.Length - 1;
         _StatusTextBox.ScrollToCaret();
     }
}

There is another method that I have been using in a current project that can reduct the code significantly. Details are here.

If you implement this method, you would need to:

Create the form using the AOP factory:

Form f = AOPFactory.Create<Form1>();
Application.Run(f);

And then you just decorate the event handler with the [RunInUIThread] attribute. It uses Castle's method interception model to automatically invoke if need be.

Thus, the above code becomes:

private void FireEventAppender_OnMessageLogged(object sender, MessageLoggedEventArgs e)
{
    _StatusTextBox.Text = _StatusTextBox.Text + text;
    _StatusTextBox.Text = String.Format("{0}\r\n", _StatusTextBox.Text);
    _StatusTextBox.SelectionStart = _StatusTextBox.Text.Length - 1;
    _StatusTextBox.ScrollToCaret();
}

Performance wise it seems to be comparable, slightly slower, but depending on what you are doing it may be ok, it definitely takes less code.

Brandon
Nice, thank you Brandon.I knew the signature was going to change a bit for DisplayStatusUpdate, but just couldn't nail it.
Stephen Patten
sorry, the signature is the same but you knew what I meant.
Stephen Patten
+2  A: 

If you are using C# 3.5 (or later) you can use captured variables, which may simplify the code:

private void DisplayStatusUpdate(string text)
{
    this.Invoke(new MethodInvoker(() => 
    {
         _StatusTextBox.Text = _StatusTextBox.Text + text;
         _StatusTextBox.Text = String.Format("{0}\r\n", _StatusTextBox.Text);
         _StatusTextBox.SelectionStart = _StatusTextBox.Text.Length - 1;
         _StatusTextBox.ScrollToCaret();
    }));
}

What the compiler will do behind the scenes here is to generate a class to hold the text, create a MethodInvoker delegate, and pass that delegate and an instance of the generated class to the Invoke method. In the above design this will of course do the unnecessary work of creating a delegate and calling Invoke even if the code is already running on the same thread; but I believe that a method like this should not be called that often that it would be a performance issue.

If you want to you can replace the use of MethodInvoker with the parameter-less Action delegate, or any other parameter-less delegate that returns void.

Fredrik Mörk
That is awesome!
Brandon