Question

I have a custom control's library. Now there's a control which looks like a panel, and when it opens up I want to animate its vertical growing like this:

For h As Single = 0 To finalHeight Step 0.5
    Me.Height = CInt(h)
    '  HERE I WANT TO CALL DoEvents'
Next
Me.Height = finalHeight 

If I don't call DoEvents in the loop then the animation is not shown, I only get the final height without a visual feedback along the way.

I can call DoEvents from inside my main WinForm project, but can't inside a library.

How can I do that, without drowning into the deep threads waters?

Was it helpful?

Solution 6

This is what I've found: the timer, even at fast intervals, it is really slow. I don't know why but the animation is very jumpy with the timer. Simplified code:

 rolex = New Timer()
 rolex.Interval = 150
 AddHandler rolex.Tick,
            Sub(sender As Object, e As EventArgs)

                Me.Height += 5

                If Me.Height < finalHeight Then Exit Sub

                rolex.Stop()
                rolex = Nothing

                Me.Height = finalHeight 
            End Sub
 rolex.Start()

Without the timer I use a loop:

For i As Single = 0 To finalHeight Step 0.5
            Height = CInt(i)
            Application.DoEvents()
Next
Height = finalHeight 

It works now, but the problem is that the animation speed too much depends on the machine where the loop is executed. For this reason I'd like to use a Timer, but as I said it's too slow.

Any hints?

OTHER TIPS

Sorry, but it is completely impossible to make using DoEvents safe here. Nothing good is going to happen when the user closes the form while your animation is going. It will crash the program with an ObjectDisposed exception. Making DoEvents safe requires setting the form's Enabled property to false so that the user cannot accidentally cause mishaps like this. A control can not reasonable set the form's Enabled property to false, especially not for an animation.

The workaround is simple enough, just use a Timer with an Interval of 15 msec. Plenty fast enough to make the animation look smooth. You'll find sample code that does this in my answer in this thread.

Maybe you are just missing a reference to (or import of) System.Windows.Forms? DoEvents is a static method of Application, so you should be able to call it from a library as well.

Imports System.Windows.Forms

...

    Application.DoEvents()

(You already seem to know that using DoEvents is a dangerous thing, so I'll skip the usual lecture here.)

Yes, you should be able to call

System.Windows.Forms.Application.DoEvents()

From within your code library. It seems that you understand that DoEvents is a Bad Idea, so I'm not sure why you're calling it. I'm guessing that you have this put inside an override like OnVisibleChanged, or OnPaint - if this is the case you will most likely not get the results you are after, as control refreshing will be suspended during these operations.

What you probably want to do is create a single-tick timer, and on tick increase the height of the control -then disable the timer when finalheight is reached, or schedule another tick if not. Or, create a timer and put your above loop in it on each tick. Make sure you're aware of InvokeRequired and cross-thread calls depending on what type of timer you use.

Failure to reproduce.

Just tested with a simple setup, UserControl in an assembly.

A timer on the MainForm keeps ticking when the UC calls DoEvents() in a loop.

So: Look again for your problem, it isn't where you think it is.

In your original for loop, place Me.Refresh where you wanted to call the doevents.

For h As Single = 0 To finalHeight Step 0.5
    Me.Height = CInt(h)
    Me.refresh
Next
Me.Height = finalHeight
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top