lep1
asked on
Multiple Picturesboxes or Instantiated UserControls to Show Multiple Anumated Gifs
I am currently using the code below to create dynamically new pictureboxes with an animated gif, so they can be shown near controls whose processes are running. The method (watigifimg) is called before instantiating an object, which starts a threaded method from within) that does the work. A hashtable is used to keep a directory of the current pictureboxes.
The question is: Is there any advantage over using a usercontrol, which could simply contain the animated gif, and then be instantiated as an object and placed on Form1?
First things first, I have not finished how to dispose of each picturebox (via its name) and need a recommendation at the line below with "***-->" in it.
The question is: Is there any advantage over using a usercontrol, which could simply contain the animated gif, and then be instantiated as an object and placed on Form1?
First things first, I have not finished how to dispose of each picturebox (via its name) and need a recommendation at the line below with "***-->" in it.
Private Sub waitgifimg(ByVal wgname As String, ByVal onoff As Boolean, ByVal left As Integer, ByVal top As Integer)
If onoff = True Then
If htWaitGifs.ContainsKey(wgname) = True Then
Cnt = htWaitGifs(wgname)
htWaitGifs(wgname) = Cnt + 1
End If
If htWaitGifs.ContainsKey(wgname) = False Then
htWaitGifs.Add(wgname, 1)
End If
Dim gifpb As New PictureBox
gifpb.Image = My.Resources.ajax_loader
gifpb.SizeMode = PictureBoxSizeMode.AutoSize
gifpb.Visible = True
gifpb.Left = left - 10
gifpb.Top = top - 55
' gifpb.Width = 17
' gifpb.Height = 17
gifpb.BringToFront()
gifpb.Name = wgname
Me.Controls.Add(gifpb)
End If
If onoff = False Then
If htWaitGifs.ContainsKey(wgname) = True Then
htWaitGifs.Remove(wgname)
'***-->Need way to dispose of picturebox with name "wgname" here
End If
End If
End Sub
I think a UserControl would be beneficial if it encapsulated BOTH the PictureBox and the Class (along with its contained Thread). Then each UserControl has access to its own associated PictureBox (since it is contained within), and also has access to the associated Class and Thread.
ASKER
Something that would also be tricky would be to set the threadname of the current instantiated class (represented by the waitgif) to the unique name of the waitgif.
Is there a way to send a thread name to the threadpooler to perform an action when the thread (method) with a given name is automatically aborted? I could dispose of the animated gif (pb) at the same time.
Is there a way to send a thread name to the threadpooler to perform an action when the thread (method) with a given name is automatically aborted? I could dispose of the animated gif (pb) at the same time.
That's where the UserControl would come in really handy. You could make your custom class raise a custom event when the thread is complete. Make the UserControl subscribe to the custom event of the class and then it can dispose (or hide) the picturebox accordingly.
ASKER
At present, let me go through the flow that starts a class (method), and spawns the animated gif. I don't think I want to start the worker class in the class that starts the animated gif.
1. Instantiate worker class (call this "workerobject"). Load the waitgif and send the waitgifname as parameter to the object.
2. In the object, thread the method (of choice) and send the waitgifname to the method that's threaded.
3. When the method is done, that is, at the end of the method, call Form1's public waitgifimg method to dispose of the picturebox using waitgifname.
Question: if I make "waitgifname" a global string and update the name by adding waitgifname = waitgifname & NumWaiGifs, and the users starts another class (worker method) and changes the global waitgifname while the first method is running, will the running method see the change -- such that when you try to dispose of the waitgif it will use the wrong name?
1. Instantiate worker class (call this "workerobject"). Load the waitgif and send the waitgifname as parameter to the object.
2. In the object, thread the method (of choice) and send the waitgifname to the method that's threaded.
3. When the method is done, that is, at the end of the method, call Form1's public waitgifimg method to dispose of the picturebox using waitgifname.
Question: if I make "waitgifname" a global string and update the name by adding waitgifname = waitgifname & NumWaiGifs, and the users starts another class (worker method) and changes the global waitgifname while the first method is running, will the running method see the change -- such that when you try to dispose of the waitgif it will use the wrong name?
ASKER
Keep in mind, each worker class has a lot of methods, and I would rather not send a "waitgifname" to each as a parameter, and then go to the end of each method and paste the code to kill its waitgif.
This is why I was asking for a way to send the waitgifname as a threadname for a given method (in an object) to the threadpooler so that the threadpooler can kill the waitgif when the thread automatically ends.
In summary, at present there are hundreds of methods in classes. I don't want to modify all these methods because of a waitgif. Hence, I need to stay away from methods, and start animated gif based on where the user control is for the generic worker class, and then kill the waitgif when the thread is aborted.
This is why I was asking for a way to send the waitgifname as a threadname for a given method (in an object) to the threadpooler so that the threadpooler can kill the waitgif when the thread automatically ends.
In summary, at present there are hundreds of methods in classes. I don't want to modify all these methods because of a waitgif. Hence, I need to stay away from methods, and start animated gif based on where the user control is for the generic worker class, and then kill the waitgif when the thread is aborted.
Using global values is definitely NOT a good design decision.
I still say the UserControl is the way to go. You can do #s 1 thru 3 from within the UserControl. In fact, it might make sense to encapsulate a BackgroundWorker() control inside the UserControl. You threaded work will be done in the DoWork() handler. When all the threaded work is done the RunWorkerCompleted() event would fire and from there you can turn off the GIF associated (and encapsulated) within that same UserControl.
I still say the UserControl is the way to go. You can do #s 1 thru 3 from within the UserControl. In fact, it might make sense to encapsulate a BackgroundWorker() control inside the UserControl. You threaded work will be done in the DoWork() handler. When all the threaded work is done the RunWorkerCompleted() event would fire and from there you can turn off the GIF associated (and encapsulated) within that same UserControl.
ASKER
I can't see the picturebox in Me.Controls after it is added using Me.Controls.Add(gifpb).
Also, I am sending the currentwaitgif name when turning off the pb.
I can see the pb on Form1 at run-time and know it has a valid name before adding to Me.Controls, but cannot see it in Me.Controls when I try to dispose it.
I will go back to your IsNothing method, but that didn't work since it is not appearing in Me.Controls --> but is on the Form1(????)
Also, I am sending the currentwaitgif name when turning off the pb.
I can see the pb on Form1 at run-time and know it has a valid name before adding to Me.Controls, but cannot see it in Me.Controls when I try to dispose it.
I will go back to your IsNothing method, but that didn't work since it is not appearing in Me.Controls --> but is on the Form1(????)
If onoff = True Then
Dim gifpb As New PictureBox
gifpb.Image = My.Resources.ajax_loader
gifpb.SizeMode = PictureBoxSizeMode.AutoSize
gifpb.Visible = True
gifpb.Left = left - 10
gifpb.Top = top - 55
gifpb.BringToFront()
NumWaitGifs += 1
CurrentWaitGifName = wgname + CStr(NumWaitGifs)
htWaitGifs.Add(CurrentWaitGifName, 1)
gifpb.Parent = Me
gifpb.Name = CurrentWaitGifName
Me.Controls.Add(gifpb)
End If
If onoff = False Then
For Each pb As PictureBox In Me.Controls.OfType(Of PictureBox)()
If pb.Name = wgname Then pb.Dispose()
Next
For Each cntl As Control In Me.Controls
If TypeOf (cntl) Is PictureBox Then
MsgBox(cntl.Name)
If cntl.Name = wgname Then cntl.Dispose()
End If
Next
For Each de As DictionaryEntry In htWaitGifs
If de.Key = wgname Then
htWaitGifs.Remove(wgname)
End If
Next
End If
At your line #10 and #13, you set the name to:
CurrentWaitGifName = wgname + CStr(NumWaitGifs)
...
gifpb.Name = CurrentWaitGifName
But at line #18, you're only using "wgname", therefore you'll never get a match:
If pb.Name = wgname Then pb.Dispose()
CurrentWaitGifName = wgname + CStr(NumWaitGifs)
...
gifpb.Name = CurrentWaitGifName
But at line #18, you're only using "wgname", therefore you'll never get a match:
If pb.Name = wgname Then pb.Dispose()
*Also, you can't Dispose() of the control that you currently have in a For Each loop (lines #20 thru 25). That modifies the collection being iterated by the For Each and will thus throw an exception.
**All your loops suffer from this problem...
**All your loops suffer from this problem...
ASKER
I actually send the value of the global currentwaitgifname to the method when turning it off, so it comes through as wgname, and I confirmed it is validly getting through.
Interestingly, I checked the InnerList count in Me.Controls right after adding a gifpb, and can see the gifpb and the count is 26.
However, the second time I come into this "waitgifimg" method, the InnerList count is 13, (much lower), so I think there is a property of controls that prevents them from being registered in Me.Controls --> although they can still be seen on Form1.
After attempting to remove the single animated gif (gifpb), that is, after going through this method, that can't detect it in Me.Controls, the gif is sitting on the form working.
What causes a drop in the Innerliist count of Me.Controls?
Interestingly, I checked the InnerList count in Me.Controls right after adding a gifpb, and can see the gifpb and the count is 26.
However, the second time I come into this "waitgifimg" method, the InnerList count is 13, (much lower), so I think there is a property of controls that prevents them from being registered in Me.Controls --> although they can still be seen on Form1.
After attempting to remove the single animated gif (gifpb), that is, after going through this method, that can't detect it in Me.Controls, the gif is sitting on the form working.
What causes a drop in the Innerliist count of Me.Controls?
It's hard to tell where the problem lies as the design is hard to visualize without seeing a good portion of the code. The addition of global variables being used by multiple methods and/or threads only makes matters even more complicated...
Can you boil the problem down into a test project we can play with? It could be uploaded to ee-stuff:
http://www.ee-stuff.com/
*This is a site run by Experts-Exchange as well.
Can you boil the problem down into a test project we can play with? It could be uploaded to ee-stuff:
http://www.ee-stuff.com/
*This is a site run by Experts-Exchange as well.
ASKER
The reduction in the number of controls in Me.Controls is occurring during the invoke from the threaded method. I tried running the method 2 and 3 times and when I come into "waitgifimg" to add a picture box there were 26,27 and 28 controls each time and I can see all newly added gifpb's.
However, when I enter waitgifimg from the threaded method using the invoke, the number of controls in Me.Controls is half and the gifpb's are not there.
I am defining the global delegate in a Declarations Module as
Then inside the threaded method, at the end when done, I am using:
However, when I enter waitgifimg from the threaded method using the invoke, the number of controls in Me.Controls is half and the gifpb's are not there.
I am defining the global delegate in a Declarations Module as
Public Delegate Sub ShowWaitGifDelegate(ByVal wgname, ByVal onoff, ByVal left, ByVal top)
Then inside the threaded method, at the end when done, I am using:
Dim deleg = New ShowWaitGifDelegate(AddressOf Form1.waitgifimg)
deleg.Invoke(CurrentWaitGifName, False, 0, 0)
Is Form1 the startup object?
ASKER
Yes, it's the "Starting Form" in the Application tab of Project-->Properties
I'm not sure where the problem lies. The overall design is "messy" to say the least. I'd really need to see more complete code and/or be able to play with it to figure it out. Sorry!... =\
ASKER
I was able to use a usercontrol to show the animated gif. Question now is when I Dim the new usercontrol should I exploit the name or tag of the object, since I can't use the git name as the name of the object.
How do you close a user control (instantiated object ) if you know its name or tag?
How do you close a user control (instantiated object ) if you know its name or tag?
If you give each UserControl a unique name you use the same technique I showed you before...
https://www.experts-exchange.com/questions/28144877/Multiple-Picturesboxes-or-Instantiated-UserControls-to-Show-Multiple-Anumated-Gifs.html?anchorAnswerId=39213076#a39213076
https://www.experts-exchange.com/questions/28144877/Multiple-Picturesboxes-or-Instantiated-UserControls-to-Show-Multiple-Anumated-Gifs.html?anchorAnswerId=39213076#a39213076
ASKER
Igloo led at that. The only question is how do you make a class subscribe to the existence of another class?
You either use the AddHandler() command, or declare the variable as "WithEvents" so you can later use "Handles xxx.yyy" on the end of an appropriately typed method.
Do you need an example? I think we're talking about the UserControl subscribing to the Class events, with the Class being contained within the UserControl?...or something else?
Do you need an example? I think we're talking about the UserControl subscribing to the Class events, with the Class being contained within the UserControl?...or something else?
ASKER
At present it's almost working. The code runs fine and before threading the worker method in the worker class, the user control is instantiated. Thus, the user control is visible above the UI form control. I am setting the name and tag of the user control but don't know how to kill it using its name or tag. The usercontrol is also not listed in Me.Controls due to threading and instance differences.
I wonder, since the user control (with animated gif) is an object from the UserControlLibrary Namespace, is there a way to enter active components in a namespace to kill it?
I wonder, since the user control (with animated gif) is an object from the UserControlLibrary Namespace, is there a way to enter active components in a namespace to kill it?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Below is the full example code. If you can spawn and then dispose of a user control with an animated gif without cross-threading then it will work. I can't use BackgrounderWorkers because a user cant start another worker thread while a backgroundeworker is busy. The method below uses straighforward threading.
The apartment state on the thread needs to be STA and the invoke method at the end of the method needs to stay the same or the invoke won't work. It's actually taken several days just to piece this together so that the invoke works correctly.
The apartment state on the thread needs to be STA and the invoke method at the end of the method needs to stay the same or the invoke won't work. It's actually taken several days just to piece this together so that the invoke works correctly.
Module Declarations
Public Delegate Sub ShowWaitGifDelegate(ByVal wgname, ByVal onoff, ByVal left, ByVal top)
Public NumWaitGifs as Integer
Public CurrentWaitGifName as String
End Module
Public Class Form1
Public Sub Button1_Click_1(sender As System.Object, e As System.EventArgs) Handles Button1.Click
waitgifimg("methodA", True, 200, 200)
Dim work As New WorkerClass("methodA")
End Sub
Public Sub waitgifimg(ByVal wgname As String, ByVal onoff As Boolean, ByVal left As Integer, ByVal top As Integer)
If onoff = True Then
Dim wg As New MyUserControlLibrary.mywaitgif
NumWaitGifs += 1
CurrentWaitGifName = wgname + CStr(NumWaitGifs)
wg.Tag = CurrentWaitGifName
wg.Name = CurrentWaitGifName
wg.Top = top - 55
wg.Left = left - 10
wg.BringToFront()
Me.Controls.Add(wg)
End If
If onoff = False Then
Dim form As Form1 = CType(Application.OpenForms(0), Form1)
For Each cntl As Control In form.Controls
MsgBox(cntl.Name)
If cntl.Name = wgname Then
cntl.Hide()
End If
Next
End If
End Sub
End Class
Public Class WorkerClass
Sub New(method)
If method = "methodA" Then
Dim t As New Thread(AddressOf worker1)
t.TrySetApartmentState(ApartmentState.STA)
t.Start()
End If
End Sub
Sub worker1()
Thread.Sleep(2000)
Dim deleg = New ShowWaitGifDelegate(AddressOf Form1.waitgifimg)
deleg.Invoke(CurrentWaitGifName, False, 0, 0)
End Sub
End Class
Did Controls.Find() fix the problem? I hadn't looked closely at your last post yet...
ASKER
No, you have to cast the form1 before the invoke. Once this is done, recasting is not needed before disposal of the wait gif in waitgifimg (onoff=false).
Glad you figured it out. I'm not seeing which part of the code that applies too though. Is that posted somewhere above?
ASKER
Move line 27 to after line 48. In line 49 change form1 to form. You might then have to play with various forms of beginivoke if the "deleg" doesn't work.
ASKER
Did you get your version of the code above to work?
Just thought of something. The animated waitgif that appears above the moveable control that a user right clicks on needs to be added to the control's controls and not Me.Controls. This is because a user may move the moveable control at-run time, which would leave the animated gif by itself -- not good.
Thus at line 24, i changed adding the waitgif usercontrol (wg) to Me and am instead trying to add it to the moveable pb control, using, for example:
But the waitgif doesn't show above the control like it does with adding it to Me.Controls. FYI - I confirmed that CurrentTaskControlName is true because when I used .visible=false it disappeared.
Is the wg really there but maybe behind to pb due to default positioning when adding a control to a pb's controls?
Just thought of something. The animated waitgif that appears above the moveable control that a user right clicks on needs to be added to the control's controls and not Me.Controls. This is because a user may move the moveable control at-run time, which would leave the animated gif by itself -- not good.
Thus at line 24, i changed adding the waitgif usercontrol (wg) to Me and am instead trying to add it to the moveable pb control, using, for example:
Dim taskcontrolpb As PictureBox = Me.Controls(CurrentTaskControlName)
taskcontrolpb.Controls.Add(wg)
But the waitgif doesn't show above the control like it does with adding it to Me.Controls. FYI - I confirmed that CurrentTaskControlName is true because when I used .visible=false it disappeared.
Is the wg really there but maybe behind to pb due to default positioning when adding a control to a pb's controls?
As simple as:
Open in new window