If you are using multiple threads in your application, it's not convenient to show status on the user interface, because the current thread may not be the startup thread, which is the thread that must be used for updating any Windows Forms controls. This control has thread safe properties to update progress or status from any thread.
But you also have to figure out how to send the message to the control from some far-away thread where your code is working on some long running process like importing data.
Using the Code
This is a simple control that is a panel type of control containing a picture box and an icon. I found some icons on sites that allow free access to some icons without copyright issues.
The properties, e.g.
CheckState, etc. are all thread-safe.
Disclaimer: Although the project code you can download works perfectly, the code below, in the article, was 'air-coded' so I'm not 100% sure it will just work if you just paste it in your project. But I am 100% sure that the concepts are valid. Normally you would customize it for your use anyway, and you can always leave a message if you're having a problem with it, and I'll try to answer as I have time.
Say you are working in some thread and you want the user to see some status. Maybe you'd like to show the status on some control, like the one for this article,
You can just call a delegate that will marshall your status and text to the
sub that updates the
CoolCheck control. The code in your
sub that updates the control does not need to worry about thread safety when updating the control, because the control uses delegates internally to marshall the call to the startup (UI) thread.
But how do you get such a message to the control (which is on some UI), in the first place?
You use delegates, which are similar to Events, but the difference is that the delegate can be subscribed to by any class anywhere using the following pattern.
Here's how to set up such a delegate. In your working class, where the non-UI thread is crunching your stuff, declare a delegate, but do not instantiate it.
Public Class WorkBeingDone
Private mMessage_delegate As Message_from_WorkerThread
Public Delegate Sub Message_from_WorkerThread(Status as CoolCheck.CoolCheckStates, _
Message as String)
Public Sub New.....
Now in the constructor of the
WorkBeingDone class, you include a parameter for the above delegate, and assign it to the
private variable above:
Public Sub New (Item as blah, Zoom as blee, Message as Message_from_WorkerThread)
mMessage_delegate = Message
Then, when you are ready to New up your
WorkBeingDone class using the above constructor, you do these steps:
- Create a
sub that will get the message from the delegate. I called it "
- Instantiate a new variable for the delegate that is inside the
WorkBeingDone class. Part of that declaration is to specify the
sub in step 1, that will get actuated when the delegate is invoked. (using "
AddressOf" - see code below)
- Pass the variable created in step 2 - into the new
WorkBeingDone class you are instantiating.
It's a tricky mental exercise. You get the delegate out of the
WorkBeingDone class, then you pass it back in to that class via the constructor!! Why? Because when you "get" the delegate by making a new copy of it, that's when you also "hook it up" to the
sub that will be sent the message from the
WorkBeingDone class. Twisty, eh? If you don't get it, you're like me when I first started using them. You'll get it !! Try it.
You see in the following both the declaration of the
delegate, passing it back into the
WorkBeingDone class, and wiring it up to the
Public Class MyClass_That_Does_CoolThings
Private Sub StartupTheWorker
Dim Wallawalla as New blah
Dim Bumma as New blee
Dim Message as New WorkBeingDone.Message_from_WorkerThread (AddressOf MessageFromWorkerThread)
Dim WorkerClass as New WorkBeingDone (Wallawalla, Bumma, Message)
Private Sub MessageFromWorkerThread (Status as CoolCheck.CoolCheckStates, Message as String)
CoolCheckWorkerStatus.A_Status = Status
CoolCheckWorkerStatus.A_Caption = Message
Now the variable
mMessage_Delegate is wired from the
WorkBeingDone class directly to the class where you got the
WorkBeingDone class started. It can be called anywhere from inside the
WorkBeingDone class, wherever you are looping through stuff or whatever, by calling it like this:
mMessage_Delegate (CoolCheck.CoolCheckStates.Info, "Yeah, I'm still working. Go have coffee.")
Points of Interest
A control that is thread safe, and how to update it using a delegate from anywhere to anywhere else.