Hi,

I've got an application at the moment that has to process a fairly meaty job. Instead of making the user sit looking at a dead form, I figured it'd be nice to show a "Program is working" message to them... And to make it pretty, slapped an ani' gif on there for good measure.

The result was not good. The form loads, but only just. The labels with the brief message and the gif do not show and parts of the form background are transparent where they havn't loaded properly.

I've tried several way around this:-

1) Opening the form but make it invisible, only making it 'visible = true' when I need it. I had hoped that since the form was already loaded it would display. It didn't.

2) keeping the form opena nd visible but moving outside the bounds of the display. Move it to centre screen when I want to see it. No change in behaviour, still doesn't load properly.

3) Opening the seperate form and then on that form load, call the big sub from the main form. This one just completely refused to show even the outline of the new form and then tried to close it before it had even opened it.

I've done some research on the internet. All the examples I can find are either poorly explained (for my level) or are just so unfathomably complicated that it makes me want to uninstall VB.Net in a fit of rage.

So, to summarise: Form1 has a big job to do. Form5 is a small wait message with an ani' gif. While Form1 is working its mojo, Form5 needs to show in its entirety with the gif spinning around in circles.

I think this might be something to do with the DoEvents thing, but I have no knowledge of these functions and am struggling to understand where it is meant to go.

If you can help, I would be enormously grateful. And if I can make sense of the solution you offer, I may have to propose marriage. but we'll cross that bridge when we come to it.

Many thanks in advance,

Rob.

Recommended Answers

All 13 Replies

Small amount of progress but I still require help if anyone can offer a solution.

I now have the form to display its components properly and load up having fitted it with a timer. This means that when the form loads, it counts to a set value and then activates the main sub-routine in Form1.

Yay-me!

However, the 'Please wait' gif does not animate while the big sub from Form1 is running. This is another one fo those things I've researched at great length on the Web and have yet to get a firm grasp of anything that has been said.

So I call upon the DaniWeb elite once more for help. If the marriage proposal was putting anyone off replying, I can scale it back to just being a stalker if you prefer.

Cheers,

Rob.

Member Avatar for Unhnd_Exception

This should be all you need to play an animated gif.

Public Class Form1
    Private AnimatedImage As Image

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        'Start the image animator
        AnimatedImage = New Bitmap("FILECOPY_16.gif") 'Your animated gif file
        ImageAnimator.Animate(AnimatedImage, New EventHandler(AddressOf Me.OnFrameChanged))
    End Sub

    Private Sub OnFrameChanged(ByVal o As Object, ByVal e As EventArgs)
        'Get the next Image and draw it.
        ImageAnimator.UpdateFrames()

        Dim G As Graphics = PictureBox1.CreateGraphics
        G.Clear(PictureBox1.BackColor)
        G.DrawImage(animatedImage, PictureBox1.ClientRectangle)
        G.Dispose()
    End Sub
End Class

Many thanks for the reply.

I've put the 'Private Animated Image as Image' at the top between the opening of the Form Class and the first sub and tagged the rest of your code to the end of my form code and after updating the picture box references and the filename, there are no squiggly underlines. So that's already got an advantage over anything else I've found elsewhere.

However, when I run it, I get "ArgumentException was unhandled: Parameter is not Valid."

The only changes I made were:-
"FILECOPY_16.gif" is now "gears_animated.gif"
All instances of PictureBox1 now reference picWait
Form1.Load has been changed to Form5.load because Form5 is the one with the gif.

Is that enough to determine what I've done wrong?

Cheers,

Rob.

Here worked fine. The only problem is that the image had a problem with refreshing. I could see the form background while it refreshed the frame.

Member Avatar for Unhnd_Exception

If your using the name only for the image,"gears_animated.gif", the image needs to be added to the project and its copy to output needs to be set to copy if newer or copy always.

Otherwise you would need to use the full file path or add the image as a resource and access it with my.resources


Also, You need to stop the animator

You could do something like

Private Sub Form1_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
        ImageAnimator.StopAnimate(AnimatedImage, AddressOf OnFrameChanged)
 End Sub

The above refresh problem may be able to be solved by setting the forms double buffered property to true. If that doesn't work then the image would need to be drawn with doublebuffered graphics.

And how we set the image to be double buffered?

Edit:

I've set the form to be double buffered and haven't saw any diference. Seems that I need to set the picture box to be double buffered, but I don't know how :P

Member Avatar for Unhnd_Exception

Swap out the onFrameChange sub with this one.

This uses bufferedgraphics.

Private Sub OnFrameChanged(ByVal o As Object, ByVal e As EventArgs)
        'Get the next Image and draw it.
        ImageAnimator.UpdateFrames()

        Dim G As Graphics = PictureBox1.CreateGraphics
        Dim BG As BufferedGraphics = BufferedGraphicsManager.Current.Allocate(G, PictureBox1.ClientRectangle)

        BG.Graphics.Clear(PictureBox1.BackColor)
        BG.Graphics.DrawImage(AnimatedImage, PictureBox1.ClientRectangle)

        BG.Render()
        BG.Dispose()
        G.Dispose()
    End Sub

You need to use a background thread for the work that is being done. That way the main UI thread is free to animate a giff.

Just make sure that when the workerthread is done, and you're ready to consume the results (display on UI), that you invoke back to the UI thread. This will prevent cross-threading exceptions.

oh, and make sure that any methods running on the background thread DO NOT touch anything on the UI.

Here's some sample code to start you off:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Try
            'Display some fancy animated giff here if you want

            Me.Button1.Enabled = False
            Dim t As New Threading.Thread(AddressOf MyWorkMethod)
            t.Start() ' Start the worker thread
        Catch ex As Exception
            MsgBox(ex.ToString)
            Me.Button1.Enabled = True
        End Try

    End Sub

    Private Sub MyWorkMethod()
        'Do some work such as getting data from database
        'Perform some calculations or whatever

        'Pretend we're doing something really heavy (sleep 10 seconds)
        Threading.Thread.Sleep(10000) '10000 = 10 seconds

        Dim msg As String ' Can be any type or no parameters at all
        msg = "Done!"

        ConsumeData(msg)
    End Sub

    Private Delegate Sub ConsumeData_Delegate(ByVal msg As String)
    Private Sub ConsumeData(ByVal msg As String)
        Try
            If InvokeRequired Then
                Dim delg As New ConsumeData_Delegate(AddressOf ConsumeData)
                Dim obj(0) As String
                obj(0) = msg
                Me.Invoke(delg, obj)
            Else
                Me.Text = msg
                MsgBox(msg)

            End If
        Catch ex As Exception
            MsgBox(ex.ToString)
        Finally
            Me.Button1.Enabled = True
        End Try
    End Sub
Member Avatar for Unhnd_Exception

I think the image animator already runs on a background thread. Like the timer class.

I have no problems calling the form with the animator on it like this.

This burns up the UI thread but has no affect on the animator.

Private Sub Form2_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
        My.Forms.Form1.Show()
        ThisIsGoingToTakeALongTime()
End Sub

Private Sub ThisIsGoingToTakeALongTime()
    For i = 0 To 1000000000000000

    Next
    My.Forms.Form1.Dispose()
End Sub

I've never used the ImageAnimator so I can't really say anything about it. We use Infragistics controls for many of our apps and simply use an UltraPictureBox to show an animated GIF which animates the image for us. However, if we don't use worker threads for long calls to a database or for other non-UI tasks, the animation will stop during the calls. This indicates to me that whatever Infragistics is doing behind the scenes for animating the image, it is on the UI thread.

Another benefit of using a worker thread is that the UI itself is responsive. I can resize, move, perform other tasks (if allowed), etc... while waiting for the first task to complete.

People does not realize that gif animation inside picture box works only if the main thread message loop is not busy, because windows messages are handled while idle. If you want to show your animated gif running smooth, you must execute the form containing the animation in it's own thread, with higher priority. I have added here down a snippet of reusable code.

Public Class BusyThread
    Implements IDisposable

    Dim t As Threading.Thread = Nothing
    Dim b As BusyForm = Nothing

    Public Sub New()
        t = New Threading.Thread(AddressOf _ShowBusy)
        t.SetApartmentState(Threading.ApartmentState.STA)
        t.Priority = Threading.ThreadPriority.Highest
        t.Name = "BusyThread"
    End Sub

    Public Sub Show()
        t.Start()
    End Sub

    Public Sub Close()
        If b Is Nothing Then Exit Sub
        b.Close()
        b = Nothing
        If t Is Nothing Then Exit Sub
        t.Join()
    End Sub

    Private Sub _ShowBusy()
        b = New BusyForm
        b.ShowDialog()
    End Sub

#Region "IDisposable Support"
    Private disposedValue As Boolean ' Per rilevare chiamate ridondanti

    ' IDisposable
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' TODO: eliminare stato gestito (oggetti gestiti).
                Close()
            End If

            ' TODO: liberare risorse non gestite (oggetti non gestiti) ed eseguire l'override del seguente Finalize().
            ' TODO: impostare campi di grandi dimensioni su null.
        End If
        Me.disposedValue = True
    End Sub

    ' TODO: eseguire l'override di Finalize() solo se Dispose(ByVal disposing As Boolean) dispone del codice per liberare risorse non gestite.
    'Protected Overrides Sub Finalize()
    '    ' Non modificare questo codice. Inserire il codice di pulizia in Dispose(ByVal disposing As Boolean).
    '    Dispose(False)
    '    MyBase.Finalize()
    'End Sub

    ' Questo codice è aggiunto da Visual Basic per implementare in modo corretto il modello Disposable.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Non modificare questo codice. Inserire il codice di pulizia in Dispose(ByVal disposing As Boolean).
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class

BusyForm is a simple modal form containing an animated gif into a simple picturebox.

You can reuse easily this class anywhere you have a very heavy computation for showing a busy popup modal form while the main thread is working.

public sub DoHeavyStuff()
  Dim busy as new BusyThread
  try
    busy.Show()

    ''''' DO THE DIRTY HEAVY WORK HERE

    'Note: You do not need to explicitly close the busy form, will be
    'closed automaticaly on dispose, but if you have to warn user with
    'a message box bette to close it before

    busy.Close()

  catch ex as Exception
    busy.Close() 'Remember to close modal form before showing the exception
    Msgbox ex.Message
  finally
    busy.Dispose 'Close form and terminate busy thread
  end try
end sub

Hope this helps.

Sorry, i have found a bug into Close() method that raise a cross-thread warning. Please modify like below:

    Public Sub Close()
        If b Is Nothing Then Exit Sub
        b.Invoke(New MethodInvoker(AddressOf _Close))
        If t Is Nothing Then Exit Sub
        t.Join()
    End Sub

    Private Sub _Close()
        If b Is Nothing Then Exit Sub
        b.Close()
        b = Nothing
    End Sub

The b.close cannot be executed in a thread that is different from the thread the form has been created with.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.