views:

857

answers:

4

so i have

public class Form1 : Form {}

and

class Updater {}

and i have textBox1 on Form1, along with many other controls...

so here is my dilemma: i have a while(true) {} loop in Updater, and i couldn't stick it in the Form1 class, because it was preventing the form from loading. and i need to update a multi-lined textbox (textBox1) on Form1, from Updater. Updater is a tcp client, and when it receives information i need it to += its info into the textbox.. but i don't know how to access the textbox from a thread different from the one it was created in! please help!

Thanks.

edit: im looking for code examples please

+1  A: 

If Form1 has a reference to Updater then you can put an event on the Updater Class that Form1 can subscribe to. When Updater has data (or whatever reason it needs to update the form) it sets the event, the form catches the event and updates the textbox.

Example:

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Updater textboxUpdater = new Updater();
            textboxUpdater.Updated += s => {textBox1.Text = s;};
        }
    }


public class Updater
    {
        public delegate void UpdateEventHandler(string eventName);
        public event UpdateEventHandler Updated = delegate { };

        private bool needUpdating;
        public void Process()
        {
            while (true)
            {
                //Processing
                if (needUpdating)
                {
                    Updated("something");
                }
            }
        }

    }
CSharpAtl
One thing to watch out for: Updater will raise the event on its own (background) thread. So something -- either the event raiser or the event handler -- will need to marshal it across to the text box's thread using the technique Nate describes.
itowlson
i tried this method, to no avail. nothing happens.
Tommy
+1  A: 

Cross-threading is caused when a thread is used to access a control that did not create the control. To get around it you Invoke.

http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

Example:

    /// <summary>
    /// This is a thread safe operation.
    /// </summary>
    /// <param name="text"></param>
    public void SetTextBoxText(string text)
    {
        if (InvokeRequired)
        {
            Invoke((MethodInvoker)delegate { SetText(text); });
            return;
        }

        // To get to this line the proper thread was used (by invoking)
        myTextBoxt.Text += text;
    }

    /// <summary>
    /// This is an alternative way. It uses a Lambda and BeginInvoke
    /// which does not block the thread.
    /// </summary>
    /// <param name="text"></param>
    public void SetTextBoxText(string text)
    {
        if (InvokeRequired)
        {
            BeginInvoke((MethodInvoker)(() => { SetText(text); }));
            return;
        }

        // To get to this line the proper thread was used (by invoking)
        myTextBox.Text += text;
    }
No longer a user
+2  A: 

Why don't you declare an event in Updater class? Then you can raise this event when you get data from TCP.

public class Updater
{
    public delegate void DataReceivedEventHandler(object sender,DataEventArgs e);
    public event DataReceivedEventHandler DataReceived = delegate { };

    public void ReadData()
    {
        //here you will get data from what ever you like
        //upon recipt of data you will raise the event.

        //THIS LOOP IS FOR TESTING ONLY
        for (var i = 1; i < 101; i++)
        {
            //PASS REAL DATA TO new DataEventArgs
            DataReceived(this, new DataEventArgs("Event " + i));
            Thread.Sleep(500);
        }
    }
}

public class DataEventArgs : EventArgs
{
    public string Data { get; set; }
    public DataEventArgs(string data) : base()
    {
        Data = data;
    }
}

In you form:

    //you will setup "Updater" in some else way. I've written this function
    //which I call on a button click for testing
    private void Init()
    {
        var u = new Updater();
        u.DataReceived += delegate(object sender, DataEventArgs e) 
                                { SetTextboxText(e.Data); };

        BackgroundWorker bw = new BackgroundWorker();

        bw.DoWork += delegate(object sender, DoWorkEventArgs e) 
                { ((Updater)e.Argument).ReadData(); };
        bw.RunWorkerAsync(u);
    }

    private void SetTextboxText(string s)
    {
        if (TEXT_BOX.InvokeRequired)
        {
            //This techniques is from answer by @sinperX1
            BeginInvoke((MethodInvoker)(() => { SetTextboxText(s); }));
            return;
        }
        TEXT_BOX.Text += Environment.NewLine + s;
    }
TheVillageIdiot
I've added new code. It works I've tested it.
TheVillageIdiot
A: 

This article helped a lot. just in few seconds i am got clear my issue. Bunch of thanks Reddy

Reddy