none
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.

    问题

  •  

    Good day! can anyone please help me understand why i am getting below error. I got this code form http://www.codeproject.com/KB/cs/wintail.aspx its actually working but everything the log file updates i get below error. you can open the link for detailed ref.

    Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.

     

    private void myTail_MoreData(object tailObject, string newData)

    {

    int maxChars = (int) this.maxCharsNumericUpDown.Value;

                //label1.Text = tailFileContentsTextbox.Text.Length.ToString()+ "  "+ maxChars.ToString();

    if(newData.Length > maxChars)

    {

    newData = newData.Remove(0, newData.Length-maxChars);

    }

    int newLength = this.tailFileContentsTextbox.Text.Length + newData.Length;

    if (newLength >= maxChars)

    {

    this.tailFileContentsTextbox.Text = this.tailFileContentsTextbox.Text.Remove(0, newLength-(int)maxChars);

     

                }

     

    this.tailFileContentsTextbox.AppendText(newData);

    }

    2011年12月1日 19:54

答案

  • You will always get the Cross-thread error when you try to update a UI element from any thread it was not created on. This is normal behavior.

    You can easily demonstrate this by creating a simple form with a single button and a single textbox. Add a System.Timers.Timer which runs on a different thread and try to update the textbox  in the Elapsed event of the timer.

        public partial class Form1 : Form
        {
            //Runs on a separate thread
            System.Timers.Timer sysTimer;
    
            public Form1()
            {
                InitializeComponent();
                sysTimer = new System.Timers.Timer(5000);
                sysTimer.Elapsed += new ElapsedEventHandler(sysTimer_Elapsed);
                sysTimer.Start();
            }
    
            void sysTimer_Elapsed(object sender, ElapsedEventArgs e)
            {
              UpdateText(textBox1, "Updated from non UI thread");
            }
    
            delegate void delUpdateText(Control ctl, string text);
            //Can update the text for any UI control that supports .Text
            private void UpdateText(Control ctl, string text)
            {
                if (ctl.InvokeRequired)
                {
                    delUpdateText callbackMethod = new delUpdateText(UpdateText);
                    this.Invoke(callbackMethod,ctl,"Has Been Invoked-" + text);
                }
                else
                {
                    ctl.Text = text;
                }
            }
    
            //Called on the UI thread
            private void button1_Click(object sender, EventArgs e)
            {
                this.UpdateText(textBox1,"Updated From UI thread!");
            }
    
        }
    
    

    You can easily see that the textbox does not require invoking from the button1_Click event, but does from sysTimer_Elapsed event.

    Notice the delegate takes (Control ctl, string text) so that any control that has Text can be passed and the delegate can update it.

    You can use this same pattern and create delegates to update the most common properties of most controls.

    If you plan on writing very many Forms based applications, I suggest you put these delegate methods in a separate code file so you can

    easily use them in any project.

     

    Regards

    2011年12月2日 22:47

全部回复

  • In order to update a component or anything on the user interface, it has to be done on the UI thread. Therefore you thread will need to have access to the Form and use the Form.Invoke method. This will have the method run on the UI thread and not your thread that was created. This prevents some side-effects and allow the standard Form logic follow such as when to repaint and other such logic.

     

    Thanks,

     

    Brad

    2011年12月1日 20:20
  • The InvokeRequired property http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx can to be used to determine when invoking the UI thread is needed.
    2011年12月1日 20:49
  • thanks for the replies. so i will just add invoke property between tho mytail_moredata method am i correct? 
    2011年12月1日 21:00
  • Instead of just set text something like this:
    delegate void UpdateTextMethod(String text);
    
    MyForm.Invoke(
                        new UpdateTextMethod(UpdateText), yourText
    );
    
    private void UpdateText(string text)
    {
    //set text here
    }
    

    Define the deleage inside or outside the method, MyForm is a reference to your Form, and the Update Text is a method in the class where the code is executing.

     

     

    Thanks,

     

    Brad

    2011年12月1日 21:29
  • If you try to change a UI element from a thread other than the UI thread you will see this kind of error message. You need to have methods in the UI class that manipulate controls on the form ensure that the request is being processed by the UI thread. For example, if you are trying to change the text in a textBox you need to use the following:

            delegate void ChangeTextCallback(string text);
    
            private void ChangeText(string text)
            {
                if (this.textBox1.InvokeRequired)
                {
                    ChangeTextCallback MethodCallback = new ChangeTextCallback(ChangeText);
                    this.Invoke(MethodCallback, new object[] { text });
                }
                else
                {
                    this.textBox1.Text = text;
                }
            }
    


     You can read more on the topic at these links: http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired(v=vs.90).aspx http://stackoverflow.com/questions/527947/invoke-required


    If this post answers your question, please click "Mark As Answer". If this post is helpful please click "Vote as Helpful". Just because the code works, doesn't mean it is good code.

    • 已编辑 BillW33 2011年12月1日 22:10
    2011年12月1日 21:42
  • Thanks! ok i'll try above suggestions...
    2011年12月2日 0:08
  • hi guys.. i tried to implement above and even tried to read about delegates etc but still cannot make my code run im still getting he same error can somebdy please show me how/whats wrong.. please see below codes. what im trying do here is that it will read the last part of a log file and if it sees an error sample "ORA" it will then send to me the that certain line via sms.

     

     

     FileSystemWatcher watcher = new FileSystemWatcher();

            public delegate void SimpleDelegate();

            public Form1()

            {

                InitializeComponent();

     

            }

            private void Form1_Load(object sender, EventArgs e)

            {

     

                watcher.Path = @"C:\DR activity logs\_SENDER";

                watcher.NotifyFilter = NotifyFilters.LastAccess |

                                        NotifyFilters.LastWrite |

                                        NotifyFilters.FileName |

                                        NotifyFilters.DirectoryName;

                watcher.Filter = "*.log";

                watcher.Changed += new FileSystemEventHandler(OnChanged);

                watcher.Created += new FileSystemEventHandler(OnChanged);

                watcher.Deleted += new FileSystemEventHandler(OnChanged);

                watcher.Renamed += new RenamedEventHandler(OnRenamed);

                this.watcher.EnableRaisingEvents = false;

            }

            SimpleDelegate simpleDelegate = new SimpleDelegate(OnChanged);

            public void OnChanged(object source, FileSystemEventArgs e)

     

     public void OnChanged(object source, FileSystemEventArgs e)

            {

     

                textBox2.Text = ReadEndTokens(textBox1.Text, 15, Encoding.ASCII, "\n");

                string sPattern = "ORA-";

                //string match = "(" + string.Join("|", sPattern);

     

                for (int ctr = 0; ctr < textBox2.Lines.Length; ctr++)

                {

                    Match match1 = Regex.Match(textBox2.Lines[ctr].ToString(), sPattern);

                    if (match1.Success)

                    {

     

                        textBox3.AppendText(textBox2.Lines[ctr].ToString());

                        //send_text("alerts", "09082817245", textBox2.Lines[ctr].ToString());

                    }

                }

                //MessageBox.Show("Event Fired");

            }

    2011年12月2日 21:37
  • by the way i get this error when i implemented the filesystemwatcher. does it mean that the watcher is always running on separate thread? please excuse my terms for i am still a newbie. cheers!
    2011年12月2日 21:40
  • The FileSystemWatcher registers with the OS that should the condition be met to signal an event that in your process. The event is being "watched" by some ThreadPool threads. When the event is signaled a ThreadPool thread will being executing the delegate you associated with the watcher.

    So yes, the OnChanged method will execute on the UI thread (unless of course you call it from the UI thread, but that would be pointless).

    2011年12月2日 22:34
  • Carefully study the help file entry for FileSystemWatcher.SynchronizingObject.
    2011年12月2日 22:38
  • You will always get the Cross-thread error when you try to update a UI element from any thread it was not created on. This is normal behavior.

    You can easily demonstrate this by creating a simple form with a single button and a single textbox. Add a System.Timers.Timer which runs on a different thread and try to update the textbox  in the Elapsed event of the timer.

        public partial class Form1 : Form
        {
            //Runs on a separate thread
            System.Timers.Timer sysTimer;
    
            public Form1()
            {
                InitializeComponent();
                sysTimer = new System.Timers.Timer(5000);
                sysTimer.Elapsed += new ElapsedEventHandler(sysTimer_Elapsed);
                sysTimer.Start();
            }
    
            void sysTimer_Elapsed(object sender, ElapsedEventArgs e)
            {
              UpdateText(textBox1, "Updated from non UI thread");
            }
    
            delegate void delUpdateText(Control ctl, string text);
            //Can update the text for any UI control that supports .Text
            private void UpdateText(Control ctl, string text)
            {
                if (ctl.InvokeRequired)
                {
                    delUpdateText callbackMethod = new delUpdateText(UpdateText);
                    this.Invoke(callbackMethod,ctl,"Has Been Invoked-" + text);
                }
                else
                {
                    ctl.Text = text;
                }
            }
    
            //Called on the UI thread
            private void button1_Click(object sender, EventArgs e)
            {
                this.UpdateText(textBox1,"Updated From UI thread!");
            }
    
        }
    
    

    You can easily see that the textbox does not require invoking from the button1_Click event, but does from sysTimer_Elapsed event.

    Notice the delegate takes (Control ctl, string text) so that any control that has Text can be passed and the delegate can update it.

    You can use this same pattern and create delegates to update the most common properties of most controls.

    If you plan on writing very many Forms based applications, I suggest you put these delegate methods in a separate code file so you can

    easily use them in any project.

     

    Regards

    2011年12月2日 22:47
  • THANKS alot guys!! so far its working already.
    2011年12月3日 18:08