Timer-Driven Animation

To use timer-driven animation in your application, you must connect the animation manager to the timer and provide a timer event handler. To avoid unnecessary drawing when no animations are playing, you should configure the timer to disable itself automatically.

The following steps continue those in Adding Animation to an Application.

Step 4: Connecting the Animation Manager to the Timer

The UIAnimationManager object implements the IUIAnimationTimerUpdateHandler interface, which allows it to be connected to a UIAnimationTimer object. When the animation manager is connected to a timer, the timer can tell the manager when the animation state should be updated as time progresses. The animation manager can in turn tell the timer when there are animations playing, so the timer can shut itself off during idle periods when redrawing is unnecessary.

Connect the two objects by setting the manager as the timer's update handler.

// Connect the animation manager to the timer.
// UI_ANIMATION_IDLE_BEHAVIOR_DISABLE tells the timer to shut itself
// off when there is nothing to animate.

IUIAnimationTimerUpdateHandler *pTimerUpdateHandler;
hr = m_pAnimationManager->QueryInterface(
    IID_PPV_ARGS(&pTimerUpdateHandler)
    );
if (SUCCEEDED(hr))
{
    hr = m_pAnimationTimer->SetTimerUpdateHandler(
        pTimerUpdateHandler
        UI_ANIMATION_IDLE_BEHAVIOR_DISABLE
        );
    pTimerUpdateHandler->Release();
    if (SUCCEEDED(hr))
    {
        ...
    }
}

If UI_ANIMATION_IDLE_BEHAVIOR_DISABLE is not used, it is also necessary to enable the timer to start it ticking.

Step 5: Define and Register a Timer Event Handler

The application should draw a frame for each timer tick.

First, define a timer event handler COM class. In the example below, the timer event handler invalidates the window's client area to cause a repaint after each update of the animation state.

class CTimerEventHandler :
    public IUIAnimationTimerEventHandler
{
public:

    static HRESULT CreateInstance
    (
         CMainWindow *pMainWindow,
         IUIAnimationTimerEventHandler **ppTimerEventHandler
    )
    {
        *ppTimerEventHandler = NULL;

        CTimerEventHandler *pTimerEventHandler = new CTimerEventHandler(
            pMainWindow
            );
        if (pTimerEventHandler == NULL)
        {
            return E_OUTOFMEMORY;
        }

        *ppTimerEventHandler = static_cast<IUIAnimationTimerEventHandler *>(pTimerEventHandler);
        (*ppTimerEventHandler)->AddRef();

        return S_OK;        
    }

    // IUnknown

    ...

    // IUIAnimationTimerEventHandler

    IFACEMETHOD(OnPreUpdate)
    (
    )
    {
        return S_OK;
    }

    IFACEMETHOD(OnPostUpdate)
    (
    )
    {
        m_pMainWindow->Invalidate();
        return S_OK;
    }

    IFACEMETHOD(OnRenderingTooSlow)
    (
        UINT32 /* fps */
    )
    {
        return S_OK;
    }

protected:

    CTimerEventHandler(CMainWindow *pMainWindow)
      : m_pMainWindow(pMainWindow)
    {
    }

    CMainWindow *m_pMainWindow;
};

Then, in the initialization code, create an instance of the timer event handler and pass it to the timer.

IUIAnimationTimerEventHandler *pTimerEventHandler;
hr = CTimerEventHandler::CreateInstance(
    this,
    &pTimerEventHandler
    );
if (SUCCEEDED(hr))
{
    hr = m_pAnimationTimer->SetTimerEventHandler(
        pTimerEventHandler
        );
    pTimerEventHandler->Release();
    if (SUCCEEDED(hr))
    {
        ...
    }
}

Because the timer event handler retains a reference to the main window object, the timer event handler should be cleared (by passing NULL to SetTimerEventHandler) or the timer completely released before the main window is destroyed.

With this code in place, the application can play animations by building and scheduling storyboards.

See Also

Using Windows Animation

Send comments about this topic to Microsoft

Build date: 10/21/2009

Tags :


Community Content

treckle
Don't forget the Window Message Pump
I guess it should have been obvious to have a Window Message Pump to run the timer driven animation, but I just wanted to play around with a simple animation that printed something to the console so I could test out the transitions and stuff. So I created an empty project in VS and went through this walk-through (replacing the window Invalidate call in the TimerEventHandler with a printf that printed the current values of the animation variables). I couldn't figure out why it wasn't working until I downloaded the timer sample and stepped through it in the debugger. I saw that WM_TIMER messages were being processed by the sample, and then I realized that a message pump was needed to drive this (I had wondered how the values were being updated). Oops! Adding the Get/Translate/DispatchMessage loop got me up and running.

Yeah, you'll probably never use this without a window to draw things anyway, but just in case you're like me and like to play with an API before starting a project, don't forget the message pump. Anyway, this looks cool and I look forward to using it with GDI and Direct2D eventually.
Tags :

Page view tracker