1

I'm writing a form app in c# and I need to be able to change the contents of a Rich Text Box from any thread, I tried using a delegate and InvokeRequired, but the delegate I made still gives me a cross-thread call error, and InvokeRequired crashes the form, without giving an error. Function I need to be able to execute from any thread:

    public static void updateSub(int what)
    {
        subDisplay.subBox.Text = tb[what];
    }

The delegate I tried to use:

    public delegate void UpdateDelegateVoid(int what);
    static public UpdateDelegateVoid uSub = new UpdateDelegateVoid(updateSub);
    uSub(0);

My InvokeRequired code:

    public static void updateSub(int what)
    {
        if (subDisplay.subBox.InvokeRequired)
        {
            subDisplay.subBox.Invoke(new MethodInvoker(finish));
        }
        else
        {
            subDisplay.subBox.Text = tb[what];
        }
    }

I'm not really sure why the code above isn't working. Thanks!

George Korac
  • 133
  • 2
  • 4
  • 9
  • 1
    Sorry but, what is `finish`? What is `MethodInvoker`? – R. Martinho Fernandes Apr 28 '11 at 08:52
  • Whats this bit do `subDisplay.subBox.Invoke(new MethodInvoker(finish))` and why is it in a static method, surely it should on the instance of `subDisplay`. Is `subDisplay` a static property? – Jodrell Apr 28 '11 at 08:56
  • @Martinho: [`MethodInvoker`](http://msdn.microsoft.com/en-us/library/system.windows.forms.methodinvoker.aspx) exists till .Net 1.1 and was used before [`Action`](http://msdn.microsoft.com/en-us/library/system.action.aspx) arised. – Oliver Apr 28 '11 at 08:57
  • To get more closer to the point where the exception occurs you should open in Visual Studio *Debug - Exceptions* and check all checkboxes. In this case the compiler will break before the exception will be thrown. – Oliver Apr 28 '11 at 09:03

3 Answers3

3

Strictly speaking, when you check InvokeRequired and find it's true, you should marshall the call to the same method. I'm not sure it fixes your specific problem (I'd need to see more exception details and code) but this is what I mean:

public static void updateSub(int what)
{
    if (subDisplay.subBox.InvokeRequired)
    {
        subDisplay.subBox.Invoke(new Action<int>(updateSub), what);
    }
    else
    {
        subDisplay.subBox.Text = tb[what];
    }
}

If you're getting "weird behaviour", then check that the form is actually created on the main application thread. In WinForms this isn't forced (as it is in WPF) so it's just possible that the thread that the form was created on isn't actually the root thread of the app.

Neil Barnwell
  • 41,080
  • 29
  • 148
  • 220
  • But this won't work. [MethodInvoker](http://msdn.microsoft.com/en-us/library/system.windows.forms.methodinvoker.aspx) is a delegate for invoking methods without parameters and return value. But updateSub has an int parameter ... – takrl Apr 28 '11 at 09:05
  • @Neil Umm, the reason MethodInvoker was 'crashing' the form is that I accidentally put MethodInvoker(finish) instead of MethodInvoker(updateSub), I assume it would work if I'd change that... Though, I have found a way to make a valid cross-thread call: ` displaySub.Invoke((MethodInvoker)delegate { subDisplay.subBox.Text = tb[what]; });` – George Korac Apr 28 '11 at 09:06
  • @George: so, you weren't actually getting a cross-thread error, right? – R. Martinho Fernandes Apr 28 '11 at 09:09
  • @takrl Good point, and exactly why SO should have a compiler built-in. :) I've updated to the generic `Action` instead. – Neil Barnwell Apr 28 '11 at 09:09
  • @Martinho: I was getting a cross-thread error when I used a delegate, though the 'form crashing' I was getting while using MethodInvoke was just cause by my stupidity... :) – George Korac Apr 28 '11 at 09:11
  • Yes, SO definetly needs a compiler ... and Notepad++ as well ;-) – takrl Apr 28 '11 at 09:11
  • @takrl: it doesn't really shine in a WinForms question like this, but maybe you should try [LINQPad](http://linqpad.net). – R. Martinho Fernandes Apr 28 '11 at 09:35
2

I mostly use this, and it works perfectly. For the exact same purpose are what you are intending.

public void UpdateSub(string message)
{
    subDisplay.subBox.Invoke((Action)delegate {
        subDisplay.subBox.Text = message;
    });
}

Hope it help's your or someone else with it!

Wesley
  • 798
  • 3
  • 8
  • 15
1

Try this - where you call the same method if an invoke is required.

public void UpdateSub(string message)
{
    if (!subDisplay.subBox.InvokeRequired)
    {
        subDisplay.subBox.Text = message;
    }
    else
    {
        var d = new UpdateFormText(UpdateSub);
        Invoke(d, new object[] { message });
    }
}

Where UpdateFormText is the delegate

public delegate void UpdateFormText(string message);
Peter Kelly
  • 14,253
  • 6
  • 54
  • 63