Re: [RESOLVED] [2005] Setting text property across threads
Firstly, for correctness, DoWork is an event, not a method.
I think you may be mistaken about this error. I just tested it and it worked fine. That stands to reason because the ToolStripStatusLabel is not a control, nor does it host a control. Something like the ToolStripTextBox would be a different story, because it inherits ToolStripControlHost and does host a genuine control. In that case you'd do this:
vb.net Code:
If Me.ToolStripTextBox1.Control.InvokeRequired Then
The ToolStripStatusLabel doesn't inherit ToolStripControlHost though, so it has no Control property. Can you do as I've done and add exception handling and attach a screen shot of the result?
Re: [RESOLVED] [2005] Setting text property across threads
I did the same as you and created a test project and it worked fine, however in my main project it still throws an exception if I try to access the ToolStripStatusLabel (which I've creatively called lbl2 )
The two lines of code commented out above the Try - Catch block (which I've added just to generate the exception and get the exception message) are what Ive done to resolve this, and it works ok, but it's confusing that I'm getting this error when logically I shouldn't
Here's a more detailed exception message..
Last edited by Tinbeard; Jul 13th, 2007 at 05:51 AM.
Re: [RESOLVED] [2005] Setting text property across threads
As your error message says, it's the StatusStrip that's causing the issue, not the ToolStripStatusLabel. I'm guessing that you have properties like auto-sizing the label to the text, so changing the text would cause the StatusStrip to execute code on the wrong thread. The StatsuStrip IS a control, unlike the ToolStripStatusLabel, so it DOES have an InvokeRequired property and an Invoke method, so your original issue doesn't exist. The control that's causing the issue is the StatusStrip so you use the members of the StatusStrip to marshal the cal to the UI thread.
Re: [RESOLVED] [2005] Setting text property across threads
I just tried fiddling with a StatusStrip with two ToolStripStatusLabels in various configurations and I couldn't get it to throw that exception. I'd be interested to know what items you've added to your StatusStrip and what the property values are for the strip itself and each item.
Re: [RESOLVED] [2005] Setting text property across threads
I currently only have the one label on the StatusStrip and all the properties are default.
Just in case something had become corrupt I deleted the label and added a new one but still got the same results
So I deleted the StatusStrip and added a new one populated with the same label and.......... it works fine.
The only thing I could think of is I had the original StatusStrip docked inside a ToolStripContainer, it's now docked within the form itself.
So I added a new status strip with a label to the ToolStripContainer and when I accessed the text property from the doWork event the exception was thrown.
Re: [RESOLVED] [2005] Setting text property across threads
I couldn't help but notice that you said you had tried to access the ToolStripStatusLabel from within the DoWork event.
That's not allowed since the DoWork event runs on a different thread than the UI thread.
The BackGroundWorker ProgressChanged event runs in a handler that is in the UI thread and can be access from the form.
Just remember to set the WorkerReportsProgress property to True or else you'll get an exception.
So, if you actually did what you said you did and your code accesses anything from the DoWork event (whether or not it appears to be working) you should change the code so that it uses the ProgressChanged event instead.
In fact, it might be good for you to re-run the test you set up and see if it works both ways. If it does, then you'll know that the problem was trying to access from the Worker thread in an improper way.
Re: [RESOLVED] [2005] Setting text property across threads
Accessing a ToolStripStatusLabel from a worker thread IS allowed. The issue with multi-threading is that you cannot access members of a control from a thread that does not own its handle. The ToolStripStatusLabel is NOT a control, so there is no such restriction. If there was then it would have InvokeRequired and Invoke members.
The problem here is that making a change to the label caused it to raise an event that was then handled by some other control. Thus some member of some control was accessed on a thread that didn't own its handle. From the details provided it appears that that control was the ToolStripContainer. The ToolStripContainer IS a control and therefore has Invoke and InvokeRequired members, thus allowing you to delegate.
Using the BackgroundWorker's ProgressChanged event is a simple way to ensure that you don't run into this issue, but it's not suitable if you need to access different controls at different times. If you would need to do different things each time you call ReportProgress then I'd suggest using explicit delegation instead.
Re: [RESOLVED] [2005] Setting text property across threads
Well I'm not familiar with the ToolStripStatusLabel; however, when I looked up ToolStripStatusLabel I found the following article in online help: ToolStripStatusLabel Control Overview.
That did throw me off a bit, but I'll take your word for it, if it's not a control.
However as you pointed out and as I suggesed, regardless of which was the offending control, using ReportProgress and Progress changed would simply eliminate any control on the UI thread from causing a problem since the call to update the label would then be coming from the UI thread.
Therefore that was at least one safe solution.
Just as a learning point for me... When you mentioned using explicit delegation were you refering to simply invoking a delegate and calling BeginInvoke on the Delegate and passing (possibly) a parameter, the CallBack function, and a state object; and then retrieving the IAsyncResult AsyncState to determine which was control would receive an update (as per appropriate state info being passed in)?
Re: [RESOLVED] [2005] Setting text property across threads
The problem with using the ReportProgress method and ProgressChanged event of a BackgroundWorker is that you can only pass two parameters to ReportProgress: a number representing the percentage progress and an Object. That Object can be whatever you want but it can only be one object and it is accessed in the ProgressChanged event handler via an Object reference.
That's fine if you only ever want to do one thing. You know exactly what that object is so you can cast and use it. If you might want to access multiple controls at different times then things soon become very messy. You'd have to test the type of the object before casting, plus you'd have to have a different type of object for each task you wanted to perform. Let's say that you might want to add an item to a ListView or display an image in a PictureBox. How would you do both at different times via the ReportProgress method? It would be possible but messy.
It would be preferable to write your own methods and delegates to perfrom those tasks and invoke them directly rather than relying on the BackgroundWorker to marshal the calls to the UI thread.
Re: [RESOLVED] [2005] Setting text property across threads
As I said, if the situation is simple, i.e. you only want to perform one cross-thread task, then using the BackgroundWorker's ReportProgress method is generally a good option. If there are multiple tasks to be perfromed at different times then it is not because the code would get messy.
Re: [RESOLVED] [2005] Setting text property across threads
I'm sorry I meant the last part of my post:
Just as a learning point for me... When you mentioned using explicit delegation were you refering to simply invoking a delegate and calling BeginInvoke on the Delegate and passing (possibly) a parameter, the CallBack function, and a state object; and then retrieving the IAsyncResult AsyncState to determine which was control would receive an update (as per appropriate state info being passed in)?
Are you talking about using a class to maintain info on the calling object?
What I mean is this, create an object to hold info on which control should be accessed on the UI and then let that object ride round trip throught the Delegate and the CallBack?
Or just making different delegates for each situation?
Re: [RESOLVED] [2005] Setting text property across threads
Originally Posted by FourBlades
I'm sorry I meant the last part of my post:
Just as a learning point for me... When you mentioned using explicit delegation were you refering to simply invoking a delegate and calling BeginInvoke on the Delegate and passing (possibly) a parameter, the CallBack function, and a state object; and then retrieving the IAsyncResult AsyncState to determine which was control would receive an update (as per appropriate state info being passed in)?
Are you talking about using a class to maintain info on the calling object?
What I mean is this, create an object to hold info on which control should be accessed on the UI and then let that object ride round trip throught the Delegate and the CallBack?
Or just making different delegates for each situation?
If you only have one task to perform, like set the Text of the same Label over and over, then I'd use the ReportProgress method of a BackgroundWorker. If you have to perform the same task but on different controls at different times, then I'd create a single method and a single delegate to handle them all. If you have different tasks to perform on different controls at different times then I'd create a distinct method and delegate for each. There's no need for any IAsynchResults at all.