views:

84

answers:

4

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?

+2  A: 

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.)

Heinzi
I can call DoEvents but it doesn't show the animation.
vulkanino
@vulkanino: And the *same code* works when it's located in the WinForm project?
Heinzi
yes, that's exaclty what it happens.
vulkanino
@vulkanino: Are you sure? Exactly the same UserControl .vb file, the only difference being that it's either located in the library or in the main project?
Heinzi
+1  A: 

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.

Philip Rieck
+3  A: 

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.

Hans Passant
That's not true. While the animations runs, the user can't close the form. User input is blocked. The timer works but it is very slow.
vulkanino
Erm, it is blocked now, yes. But not when you start calling DoEvents.
Hans Passant
Oh, and 15 msec is *not* slow. That's 60 updates per second, well over twice as fast as what you see when you watch a movie. It looks slow because your increment of 0.5 pixel is way too small. Make it 5.
Hans Passant
A: 

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?

vulkanino