Solved

Multiple Picturesboxes or Instantiated UserControls to Show Multiple Anumated Gifs

Posted on 2013-06-01
28
195 Views
Last Modified: 2013-06-04
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.  



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

Open in new window

0
Comment
Question by:lep1
  • 14
  • 14
28 Comments
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
"Need way to dispose of picturebox with name "wgname" here"

As simple as:
       If onoff = False Then
            If htWaitGifs.ContainsKey(wgname) = True Then
                htWaitGifs.Remove(wgname)
                
                Dim ctl As Control = Me.Controls(wgname)
                If Not IsNothing(ctl) Then
                    ctl.Dispose()
                End If

            End If
        End If

Open in new window

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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.
0
 

Author Comment

by:lep1
Comment Utility
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.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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.
0
 

Author Comment

by:lep1
Comment Utility
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?
0
 

Author Comment

by:lep1
Comment Utility
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.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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.
0
 

Author Comment

by:lep1
Comment Utility
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(????)


        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

Open in new window

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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()
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
*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...
0
 

Author Comment

by:lep1
Comment Utility
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?
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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.
0
 

Author Comment

by:lep1
Comment Utility
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

 Public Delegate Sub ShowWaitGifDelegate(ByVal wgname, ByVal onoff, ByVal left, ByVal top)
 

Open in new window


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)

Open in new window

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Is Form1 the startup object?
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 

Author Comment

by:lep1
Comment Utility
Yes, it's the "Starting Form" in the Application tab of Project-->Properties
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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!...  =\
0
 

Author Comment

by:lep1
Comment Utility
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?
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
If you give each UserControl a unique name you use the same technique I showed you before...
http://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/Q_28144877.html#a39213076
0
 

Author Comment

by:lep1
Comment Utility
Igloo led at that.  The only question is how do you make a class subscribe to the existence of another class?
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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?
0
 

Author Comment

by:lep1
Comment Utility
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?
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 500 total points
Comment Utility
I don't understand how your controls are "not being found".

Are you adding the controls to the Form?   "Me.Controls.Add"
...or to a different container?  "Panel1.Controls.Add"

When you iterate over controls with "Me.Controls" you will only find things that are directly contained by the form itself.  If the control is inside something else then it will not be seen.  In that case you'd either iterate over that control's collection (replace "Me" with "Panel1" for instance), or you could search for a match like this:
        Dim ctlName As String = "something"
        Dim matches() As Control = Me.Controls.Find(ctlName, True) ' True searches RECURSIVELY thru all child controls
        If matches.Length > 0 Then
            ' ... do something with matches(0) ..

            ' if you need to cast, then:
            If TypeOf matches(0) Is PictureBox Then
                Dim pb As PictureBox = DirectCast(matches(0), PictureBox)
                ' ... do something with "pb" ...
            End If
        End If

Open in new window

0
 

Author Comment

by:lep1
Comment Utility
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.  

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

Open in new window

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Did Controls.Find() fix the problem?  I hadn't looked closely at your last post yet...
0
 

Author Comment

by:lep1
Comment Utility
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).
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Glad you figured it out.  I'm not seeing which part of the code that applies too though.  Is that posted somewhere above?
0
 

Author Comment

by:lep1
Comment Utility
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.
0
 

Author Comment

by:lep1
Comment Utility
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:
 
 
Dim taskcontrolpb As PictureBox = Me.Controls(CurrentTaskControlName)
  taskcontrolpb.Controls.Add(wg)
 

Open in new window


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?
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

1.0 - Introduction Converting Visual Basic 6.0 (VB6) to Visual Basic 2008+ (VB.NET). If ever there was a subject full of murkiness and bad decisions, it is this one!   The first problem seems to be that people considering this task of converting…
Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now