none
Cross-thread operation works sometimes. RRS feed

  • Question

  • Hi, I have a project that has heaps of cross thread operations in it, like changing a control's Width from a Timer's Elapsed event thread thingy, and it works when I don't debug the project, but I have a Timer that is supposed to change a TextBox's ForeColor after a second. The Timer gets called, but the ForeColor stays Red.

    My Timer code:

        Private Sub Timer4_Tick(sender As Object, e As EventArgs) Handles Timer4.Tick
            TextBox1.ForeColor = Color.Black
            Timer4.Enabled = False
        End Sub

    Thanks





    • Edited by M4DH4X3R Tuesday, October 1, 2013 11:52 PM
    Friday, September 27, 2013 12:19 AM

Answers

  • Hi M4DH4X3R,

    C# is a thread-safe language. So it asks the owner of the control invoke its method in the same thread. And Framework provides a thread-safe method to perform. See http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx for more information about Invoke method.

    To save your time, I have built a simple project for you to try, I create a System.Timers.Timer instance and System.Threading.Thread instance to meet your requirement. The code like below:

    //a delegate for change Label's location
        public delegate void ChangeLocationHandler(int x,int y);
        // a delegate for change Label's forecolor
        public delegate void ChangeTextForeColorHandler(System.Drawing.Color color);
        public partial class Form1 : Form
        {
            private System.Timers.Timer timer;
            private System.Threading.Thread thread;
            public Form1()
            {
                InitializeComponent();
    
                timer = new System.Timers.Timer();
                timer.Interval = 800;
                timer.Elapsed += timer_Elapsed;
                timer.Start();
    
    
                thread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ChangeLabelTextForeColor));
            }
          
            private void ChangeLabelTextForeColor(object color)
            {
                //thread-safe method to call control's method in the owner thread
                label1.Invoke((MethodInvoker)(() => ChangeLabelTextForeColor((Color)color)));
            }
    
            void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
            {
                label1.Invoke((MethodInvoker)(() => ChangeLabelSize(label1.Location.X + 10, label1.Location.Y + 10)));
            }
            
            private void ChangeLabelTextForeColor(Color color)
            {
                label1.ForeColor = color;
            }
    
            private void ChangeLabelSize(int x,int y)
            {
                label1.Location = new Point(x, y);
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                thread.Start(Color.OrangeRed);
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
    
            }
    }

    Hope useful to you.

    Best Regards,

    Hetro


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.


    Monday, September 30, 2013 2:37 AM
    Moderator
  • It just doesn't stay Red, but the application should have hung. As everybody mentioned, it's due to non-UI thread accessing UI element. So modify your code slightly as below,

    Private Sub Timer4_Tick(sender As Object, e As EventArgs) Handles Timer4.Tick
        TextBox1.Invoke(Sub() TextBox1.ForeColor = Color.Black)
        Timer4.Enabled = False
    End Sub

    I hope this helps.

    Please mark this post as answer if it solved your problem. Happy Programming!

    • Marked as answer by M4DH4X3R Tuesday, October 1, 2013 11:48 PM
    Tuesday, October 1, 2013 4:25 PM

All replies

  • Hi M4DH4X3R,

    C# is a thread-safe language. So it asks the owner of the control invoke its method in the same thread. And Framework provides a thread-safe method to perform. See http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx for more information about Invoke method.

    To save your time, I have built a simple project for you to try, I create a System.Timers.Timer instance and System.Threading.Thread instance to meet your requirement. The code like below:

    //a delegate for change Label's location
        public delegate void ChangeLocationHandler(int x,int y);
        // a delegate for change Label's forecolor
        public delegate void ChangeTextForeColorHandler(System.Drawing.Color color);
        public partial class Form1 : Form
        {
            private System.Timers.Timer timer;
            private System.Threading.Thread thread;
            public Form1()
            {
                InitializeComponent();
    
                timer = new System.Timers.Timer();
                timer.Interval = 800;
                timer.Elapsed += timer_Elapsed;
                timer.Start();
    
    
                thread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ChangeLabelTextForeColor));
            }
          
            private void ChangeLabelTextForeColor(object color)
            {
                //thread-safe method to call control's method in the owner thread
                label1.Invoke((MethodInvoker)(() => ChangeLabelTextForeColor((Color)color)));
            }
    
            void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
            {
                label1.Invoke((MethodInvoker)(() => ChangeLabelSize(label1.Location.X + 10, label1.Location.Y + 10)));
            }
            
            private void ChangeLabelTextForeColor(Color color)
            {
                label1.ForeColor = color;
            }
    
            private void ChangeLabelSize(int x,int y)
            {
                label1.Location = new Point(x, y);
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                thread.Start(Color.OrangeRed);
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
    
            }
    }

    Hope useful to you.

    Best Regards,

    Hetro


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.


    Monday, September 30, 2013 2:37 AM
    Moderator
  • "I have a project that has heaps of cross thread operations in it, like changing a control's Width from a Thread, and it works when I don't debug the project"

    The cross-thread debugging tool is only available in a debug compile.  Use it in a debug compile to eliminate the cross-thread calls it can find.  It isn't a very robust tool as it only finds first order cross-threading.  Check the thread id in portions of your code you suspect a cross-thread call might exist.  Careful, knowledgeable  initial coding is the best way to avoid threading errors.

    Monday, September 30, 2013 7:18 AM
  • It just doesn't stay Red, but the application should have hung. As everybody mentioned, it's due to non-UI thread accessing UI element. So modify your code slightly as below,

    Private Sub Timer4_Tick(sender As Object, e As EventArgs) Handles Timer4.Tick
        TextBox1.Invoke(Sub() TextBox1.ForeColor = Color.Black)
        Timer4.Enabled = False
    End Sub

    I hope this helps.

    Please mark this post as answer if it solved your problem. Happy Programming!

    • Marked as answer by M4DH4X3R Tuesday, October 1, 2013 11:48 PM
    Tuesday, October 1, 2013 4:25 PM
  • It just doesn't stay Red, but the application should have hung. As everybody mentioned, it's due to non-UI thread accessing UI element. So modify your code slightly as below,

    Private Sub Timer4_Tick(sender As Object, e As EventArgs) Handles Timer4.Tick
        TextBox1.Invoke(Sub() TextBox1.ForeColor = Color.Black)
        Timer4.Enabled = False
    End Sub

    I hope this helps.

    Please mark this post as answer if it solved your problem. Happy Programming!

    The Timer4.Tick event runs on the UI thread using a message posted to the message pump. The problem code is in the starting of the timer, which has to send a message to the correct pump.
    Tuesday, October 1, 2013 4:51 PM