1

i wrote extension class, for textbox control.

public static class Extensions
    {
    public static void AddMessage(this System.Windows.Forms.TextBox textbox,string message){
            textbox.AppendText(DateTime.Now.ToString() + " " + message + "\r\n");
    }
}

my textbox control, passed to the "Core" class via constructor, from form, like:

public static TextBox text;
public Core(TextBox text)
{
    Core.text = text;
}

and here is event:

...{
change.DataChanged += new Opc.Da.DataChangedEventHandler(OnDataChange);
}...

callback:

public void OnDataChange(object subscriptionHandle, object requestHandle, Opc.Da.ItemValueResult[] values)
    {
        Send(values,configuration.service.adress);
    }

public static void Send(Opc.Da.ItemValueResult[] values,string addr) {
     text.AddMessage("test"); //here comes exception
}

and at the text.AddMessage(...) i have: "Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException"

EDIT: i think my problem not in function Add.Message, cause textbox coming to extension already with exception, what im misssing? so i think i even can not call "text" from "Send" function

EDIT: exception for "text" comming from

change.DataChanged += new Opc.Da.DataChangedEventHandler(OnDataChange);
eba
  • 673
  • 2
  • 11
  • 22
  • See http://stackoverflow.com/questions/661561/how-to-update-gui-from-another-thread-in-c – alxx Nov 23 '10 at 07:26

3 Answers3

3

You can't access or change UI contents from threads other than the one responsible for the control in question.

You can use Control.Invoke/BeginInvoke for Windows Forms or Dispatcher.BeginInvoke for WPF/Silverlight to marshal a delegate call to the right thread. Alternatively, BackgroundWorker can also make life easier for you.

For example, your extension class could be changed to:

public static class Extensions  
{
    public static void AddMessage(this TextBox textbox, string message) {
        Action action = () => {
            textbox.AppendText(DateTime.Now.ToString() + " " + message + "\r\n");
        };
        textBox.BeginInvoke(action);
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • text.Invoke(new MethodInvoker(() => {text.AddMessage("test");})); smth like this? its also doesnt work... – eba Nov 23 '10 at 07:29
  • @eba: That should have worked, yes, although I prefer to separate out the action. When you say "its also doesnt work" - could you be more specific? – Jon Skeet Nov 23 '10 at 07:30
  • @eba: Please post the full exception stack trace. That should make it clearer where the problem is coming from. – Jon Skeet Nov 23 '10 at 09:28
1
public static void AddMessage(this TextBox textbox, string message)
{
    Action<string> del = 
        msg => textbox.AppendText(DateTime.Now.ToString() + " " + msg + "\r\n");
    if (textbox.InvokeRequired)
    {
        textyBox.Invoke(del, message);        
    }
    else
    {
        del(message);
    }
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
0

Like the others said you cannot access any UI control from a thread other than the one hosting the control. You can always marshal the execution of a delegate onto the UI thread by using the Control.Invoke method. However, this might not be the best approach in your case. Often it is better to have the UI thread poll a shared data structure that gets updated by the worker thread. The idea is that you will have System.Windows.Forms.Timer periodically check the data structure from the Tick event handler. The Opc.Da.DataChangedEventHandler event handler will just update the data structure. This has many advantages.

  • It breaks the tight coupling between the UI and worker threads that Control.Invoke imposes.
  • It puts the responsibility of updating the UI thread on the UI thread where it should belong anyway.
  • The UI thread gets to dictate when and how often the update should take place.
  • There is no risk of the UI message pump being overrun as would be the case with the marshaling techniques initiated by the worker thread.
  • The worker thread does not have to wait for an acknowledgement that the update was performed before proceeding with its next steps (ie. you get more throughput on both the UI and worker threads).

One problem with this approach is that the data structure will contain only the latest value received from the OPC server. If you need to capture every change for some reason then you could just as easily load them into a queue in your OnDatachange method. The UI thread could then periodically unload the queue.

Brian Gideon
  • 47,849
  • 13
  • 107
  • 150