Quickstart: Direct Manipulation

This quickstart provides an outline of the API calls required to accomplish typical tasks when using Direct Manipulation. We explain these APIs, describe the order in which to call them, and describe the interfaces you must implement for callbacks (and what you should do in those callbacks).

This quickstart and the code examples should be considered basic recommendations only.

Prerequisites

  • C/C++
  • Microsoft Win32
  • Component Object Model (COM)
  • Windows User Interface Programming

Instructions

Initialize Direct Manipulation

Here we describe how to initialize Direct Manipulation and prepare the system for input processing.

Create an IDirectManipulationManager COM object from the UI thread through CoCreateInstance and activate it on an HWND.

IDirectManipulationManager* pManager = nullptr;

HRESULT hr = CoCreateInstance(
             CLSID_DirectManipulationManager,
             NULL,
             CLSCTX_INPROC_SERVER,
             IID_PPV_ARGS(&pManager));

if (SUCCEEDED(hr))
{
    hr = pManager->Activate(hwnd);
}

Associate a composition engine with Direct Manipulation

Here we describe the steps required to associate a composition engine with the Direct Manipulation Manager (pManager).

This example uses the system implementation of IDirectManipulationCompositor using DirectComposition. This COM object also implements the IDirectManipulationFrameInfoProvider interface.

Create an IDirectManipulationCompositor COM object through CoCreateInstance to create the system-provided compositor. Alternatively, you can get a pointer to the IDirectManipulationCompositor interface from the custom compositor implemented by the application.

IDirectManipulationCompositor* pCompositor = nullptr;

HRESULT hr = CoCreateInstance(
             CLSID_DCompManipulationCompositor,
             NULL,
             CLSCTX_INPROC_SERVER,
             IID_PPV_ARGS(&pCompositor));

Once the compositor is created, get the IDirectManipulationUpdateManager from the IDirectManipulationManager and set it to the IDirectManipulationCompositor, so that the compositor can trigger updates through calls to the update manager.

IDirectManipulationUpdateManager* pUpdateManager = nullptr;

HRESULT hr = pManager->GetUpdateManager(IID_PPV_ARGS(&pUpdateManager));

if (SUCCEEDED(hr))
{
    hr = pCompositor->SetUpdateManager(pUpdateManager);
}

Create and configure a viewport

Here we describe the steps required to create and configure a viewport for Direct Manipulation.

Create an IDirectManipulationViewport by calling CreateViewport on the IDirectManipulationUpdateManager.

IDirectManipulationViewport* pViewport = nullptr;

HRESULT hr = pManager->CreateViewport(IID_PPV_ARGS(&pViewport));

Once the viewport is created, you can enable the viewport by specifying its size, location, supported interactions, chaining behavior, snapping behavior, panning behavior, and event handlers.

if (SUCCEEDED(hr))
{
    RECT viewportRect = {0, 0, 100, 100};
    hr = pViewport->SetViewportRect(&viewportRect);
}

if (SUCCEEDED(hr))
{
    IDirectManipulationPrimaryContent* pPrimaryContent = nullptr;
    hr = pViewport->GetPrimaryContent(IID_PPV_ARGS(&pPrimaryContent));

    if (SUCCEEDED(hr))
    {
        RECT contentRect = {0, 0, 200, 200};
        hr = pPrimaryContent->SetContentRect(&contentRect);
        pPrimaryContent->Release();
    }

    if (SUCCEEDED(hr))
    {
        // Left-align the content.
        hr = pPrimaryContent->SetHorizontalAlignment(DIRECTMANIPULATION_HORIZONTALALIGNMENT_LEFT);
        pPrimaryContent->Release();
    }
}

if (SUCCEEDED(hr))
{
    // This configuration allows panning on both axes, scaling, and railing
    DIRECTMANIPULATION_CONFIGURATION config =
        DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
        DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
        DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
        DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA |
        DIRECTMANIPULATION_CONFIGURATION_RAILS_X |
        DIRECTMANIPULATION_CONFIGURATION_RAILS_Y |
        DIRECTMANIPULATION_CONFIGURATION_SCALING |
        DIRECTMANIPULATION_CONFIGURATION_SCALING_INERTIA;
    hr = pViewport->ActivateConfiguration(config);
}

if (SUCCEEDED(hr))
{
    // The viewport chains panning on the x and y axes.
    hr = pViewport->SetChaining(DIRECTMANIPULATION_MOTION_TRANSLATEX | 
                                DIRECTMANIPULATION_MOTION_TRANSLATEY);
}

if (SUCCEEDED(hr))
{
    // Put snap points on 20, 40, 60, 80, 100, and 120 in the x axis.
    FLOAT snapPoints[6] = {20, 40, 60, 80, 100, 120};
    hr = pViewport->SetSnapPoints(DIRECTMANIPULATION_MOTION_TRANSLATEX,
                                  snapPoints,
                                  ARRAYSIZE(snapPoints));
}

if (SUCCEEDED(hr))
{
    // Starting from 0.0f, put a snap point every 10.0f in the y axis.
    hr = pViewport->SetSnapInterval(DIRECTMANIPULATION_MOTION_TRANSLATEY,
                                    10.0f,
                                    0.0f);
}

    if (SUCCEEDED(hr))
    {
        hr = pViewport->AddContent(pVirtualViewport);
        pVirtualViewport->Release();
    }
}

Associate content with a composition surface

Here we describe the steps required to associate content with a composition surface.

This example uses the system implementation of IDirectManipulationCompositor (with DirectComposition) we created previously.

To associate content with a composition surface, call the AddContent method on the IDirectManipulationCompositor interface. In this example we show what the system-provided compositor expects to be called (with device and visual objects from DirectComposition).

Alternatively, you can create an application-defined compositor that accepts an arbitrary object that implements a custom interface to facilitate the communication between Direct Manipulation and the compositor (or nothing at all, as all three parameters are optional).

IDCompositionDevice* pDevice;
IDCompositionVisual* pParentVisual;
IDCompositionVisual* pChildVisual;

…

HRESULT hr = pCompositor->AddContent(pPrimaryContent,
                                     pDevice,
                                     pParentVisual,
                                     pChildVisual);

Associate a viewport with a contact

Here we describe how to associate a contact with a viewport.

Handle the WM_POINTERDOWN message in the window procedure of your application.

LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
…

    switch (message)
    {
    case WM_POINTERDOWN:
        HRESULT hr = pViewport->SetContact(GET_POINTERID_WPARAM(wParam));
        break;
    }
}

Handle callbacks from a viewport or content

Here we describe the steps required to handle callbacks from a viewport or its content.

Implement the IDirectManipulationViewportEventHandler interface. Pass a pointer to this interface to the AddEventHandler method on the viewport.

Note  This callback can also be used by the application-defined compositor to receive notifications on content movement so that it can reflect those changes through parameters in the composition tree.

IDirectManipulationViewportEventHandler* pViewportEventHandler;

…

DWORD cookie;

if (SUCCEEDED(hr))
{
    pViewport->AddEventHandler(hwnd, pViewportEventHandler, &cookie);
}

The callback itself is just a notification that something has changed in the viewport or content. Your application can query Direct Manipulation for the current content transform and update itself.

HRESULT CEventHandlerImpl::OnViewportStatusChanged(
    __in IDirectManipulationViewport *pViewport, 
    __in DIRECTMANIPULATION_STATUS current, 
    __in DIRECTMANIPULATION_STATUS previous)
{
    // Handle the status change, such as remembering the current state,
    // matching the transition back to READY with a previous transition
    // from READY, etc.
    return HandleStatusChange(pViewport, current, previous);
}
    
HRESULT CEventHandlerImpl::OnViewportUpdated(
    __in IDirectManipulationViewport *pViewport)
{
    // Handle any logic that needs to be run when all contents that changed
    // in this viewport have fired their OnContentUpdated callbacks.
    return HandleViewportUpdated(pViewport);
}

HRESULT CEventHandlerImpl::OnContentUpdated(
    __in IDirectManipulationViewport *pViewport,
    __in IDirectManipulationContent *pContent)
{
    float rgMatrix[6];
        
    HRESULT hr = pContent->GetContentTransform(rgMatrix, ARRAYSIZE(rgMatrix));

    if (SUCCEEDED(hr))
    {
        // Handle the updated content transform.
       _hr = SetTransform(rgMatrix);
    }

    return hr;
}

Manually process input from the UI thread

Here we describe how to process input manually from the UI thread.

There are two special circumstances where the application may want to pass window messages to Direct Manipulation:

Pass WM_KEYDOWN and WM_MOUSEWHEEL messages to Direct Manipulation by assigning a pseudo contact to the intended viewport and calling the ProcessInput method on the Direct Manipulation manager with the message.

LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
…

    switch (message)
    {
    case WM_KEYDOWN:
        if ((wParam == VK_LEFT) || (wParam == VK_RIGHT) || (wParam == VK_UP) || (wParam = VK_DOWN))
        {
            HRESULT hr = pViewport->SetContact(DIRECTMANIPULATION_KEYBOARDFOCUS);
            if (SUCCEEDED(hr))
            {
                bool bHandled = false;

                // Direct Manipulation does not care about the actual
                // location of the cursor because the SetContact() call
                // specifies which viewport should process this message.
                MSG msg = {hWnd, message, wPara, lParam, ::GetMessageTime(), {0, 0}};
                hr = pManager->ProcessInput(&msg, &bHandled);

                if (!SUCCEEDED(hr) || !bHandled)
                {
                    // Fall back to manual/default handling of the message.
                    lResult = ::DefWindowProc(hWnd, message, wParam, lParam);
                }
            }
        }
        break;
    }
…
}

Shut down Direct Manipulation

Besides releasing interface pointers to the Direct Manipulation objects, you must perform these tasks in the following order:

Abandon()    // Called on all viewports before releasing them.
Deactivate() // Called on the Direct Maniplation Manager.

Summary and next steps

You learned how to implement Direct Manipulation in a Windows 8 app.

This quickstart and the code examples should be considered as a recommendation only.

To learn more about specific APIs and their functionality, see the Direct Manipulation Reference.

Direct Manipulation

 

 

Send comments about this topic to Microsoft

Build date: 10/27/2012