Cross-Threading Exception writing to RTF Box

Answered Cross-Threading Exception writing to RTF Box

  • 2012年4月13日 15:08
     
     

    Hello,

    I am trying to write an application that is multi-threaded so that the UI is updated separately from the processing thread.

    It seems as if toolstripstatuslabel updates work fine but updating a RTF box on the UI causes a cross-thread exception.

    Does anyone know why the two and treated differently?

    Regards,

    Steve

    • 移動 CoolDadTxMVP 2012年4月13日 17:43 Winforms related (From:Visual C# General)
    •  

すべての返信

  • 2012年4月13日 15:12
     
     
    ToolStripItems do not inherit from System.Windows.Forms.Control, so they do not have cross-thread access issues.  RichTextBoxes do, so you have to call Invoke() on them.
  • 2012年4月13日 15:19
     
     回答済み
    IF you have some code that would like to "write" some data from a new thread to UI thread, you can start thinking of using delegates, and Invoke (InvokeRequited) method, to update control on UI thread.

    Mitja

  • 2012年4月13日 16:14
     
     回答の候補

    Hi,

    Also,

    Yes, ToolStripStatusLabel does not derive from the control. It seems by initial search I find there are few topics how to avoid Cross-Thread Exception for ToolStripStatusLabel.

    I would suggest for your case you can use BackGroundWorker, which follows EAP(event based asynchronous pattern) and specially for this purpose and is discussed in following post

    http://social.msdn.microsoft.com/Forums/eu/csharpgeneral/thread/6a937def-a07c-47ca-8586-814b14f2afd3

    I hope this helps you..


    If this post answers your question, please click "Mark As Answer". If this post is helpful please click "Mark as Helpful".


  • 2012年4月13日 16:19
     
     回答の候補 コードあり

    The problem you are having is because one thread can't access objects running in another thread directly without using a delegate. This is why you are getting the cross thread exception. Here is a loose interpretation of this logic, and take note, it was written a few years ago. There are probably better ways to implement threading today.

    Firstly, create a delegate

    public delegate void UpdateLabelCallBack(string strStatus);

    Then create a method for updating the label.

    private void UpdateStatus(string _Status)
    {
        lblProcessHidden.Text = _Status.Trim();
        lblProcess.Text = lblProcessHidden.Text;
    }

    In the process button logic, the new thread is created that will execute the long running task. (Note that I am passing parameters to this thread which you might not want to do. Also take note, the declaration of the thread isn't shown. So declare the variable th1 first.

    string p1 = glbSheetName;
    object[] parameters = new object[] { p1 };

    th1 = new Thread(new ParameterizedThreadStart(this.ExecuteProcess));

    th1.Start(parameters);

    Then in the method called ExecuteProcess() which runs on the new thread performing the long running task, I update a label on my main windows form (GUI) by invoking the following code:

    lblProcessHidden.Invoke(new UpdateLabelCallBack(this.UpdateStatus), new Object[] { "Processing Data..." });

    This then calls the delegate from the new thread and passes the string to update the text on the label on the UI. 

    But after all this talk about Threading, might I suggest another solution? Why don't you take a look into tasks? It also exists in the Threading Namespace, but if you are using the .NET Framework 4, read this post on Code Project called Multi core programming using Task Parallel Library with .NET 4.0 written by Varun Manipal.

    I hope that I could have helped you a bit. Please let me know if you need any more information. Good luck :)

  • 2012年4月13日 16:24
     
     回答済み コードあり

    How about something like???

    delegate void updateRichTextboxTextDelegate(string newItem);
            private void updateRichTextboxText(string newText)
            {
                if (rtBox.InvokeRequired)
                {
                    // this is a non UI thread
                    updateRichTextboxTextDelegate del = new updateRichTextboxTextDelegate(updateRichTextboxText);
                    rtBox.Invoke(del, new object[] { newText });
                }
                else
                {
                    // this is UI thread
                    rtBox.Text = newText;
                }
            }
    
    private void sommeEvent(object sender, EventArgs e)
            {
               updateRichTextboxText("Text text");
    
            }

  • 2012年4月13日 16:37
     
     
    Looks good to me.
  • 2012年4月13日 17:43
     
     回答済み コードあり

    Hello,

    You should know the risks before using InvokeRequired and check whether the handle was actually created; otherwise, it may fail on you.

    It's best advice to either use SyncheonizedContext (WindowsFormsSynchronizationContext  in WinForms) or the TaskSchedular.FromSynchronizedContext()

    Here is a complete example that I already posted in another thread using InvokeRequired.

    namespace Threading.InvokeRequired
    {
    	using System;
    	using System.Diagnostics;
    	using System.Globalization;
    	using System.Threading;
    	using System.Windows.Forms;
    
    	public partial class AppMain : Form
    	{
    		private readonly Action<int> updateCounter;
    
    		public AppMain()
    		{
    			InitializeComponent();
    
    			Debug.WriteLine("UI running on thread '{0}'.", Thread.CurrentThread.ManagedThreadId);
    
    			updateCounter = UpdateCounter;
    		}
    
    		private void Count_Click(object sender, EventArgs e)
    		{
    			updateCounter.BeginInvoke(0, null, null);
    		}
    
    		private void UpdateCounter(int i)
    		{
    			// Checks whether we have a handle otherwise InvokeRequired might fail
    			if (!IsHandleCreated)
    			{
    				// Do something
    			}
    
    			// Checks whether the current thread and the thread that the control was created on are equal
    			if (InvokeRequired)
    			{
    				Debug.WriteLine("Executing on thread '{0}'.", Thread.CurrentThread.ManagedThreadId);
    
    				// Just for the example so we would have a chance to see on what thread it executes the work
    				// Thread.Sleep(3000);
    
    				while (true)
    				{
    					IAsyncResult r = BeginInvoke(updateCounter, i++);
    
    					r.AsyncWaitHandle.WaitOne();
    				}
    			}
    
    			Debug.WriteLine("Updating on thread '{0}'.", Thread.CurrentThread.ManagedThreadId);
    
    			// Updating the counter
    			uxCounter.Text = i.ToString(CultureInfo.InvariantCulture);
    		}
    	}
    }



    Eyal (http://shilony.net), Regards.