Ajout de la prise en charge de la manipulation dans du code non managé

Cette section explique comment ajouter la prise en charge de la manipulation au code non managé en implémentant un récepteur d’événements pour l’interface _IManipulationEvents .

L’image suivante présente l’architecture de manipulation.

illustration montrant les messages tactiles Windows passés au processeur de manipulation d’un objet, qui gère les événements avec l’interface -imanipulationevents

Les données tactiles reçues de WM_TOUCH messages sont transmises au IManipulationProcessor avec l’ID de contact du message tactile. En fonction de la séquence de message, l’interface IManipulationProcessor calcule le type de transformation en cours d’exécution et les valeurs associées à cette transformation. IManipulationProcessor génère ensuite des _IManipulationEvents qui sont gérées par un récepteur d’événements. Le récepteur d’événements peut ensuite utiliser ces valeurs pour effectuer des opérations personnalisées sur l’objet en cours de transformation.

Pour ajouter la prise en charge de la manipulation à votre application, vous devez suivre les étapes suivantes :

  1. Implémentez un récepteur d’événements pour l’interface _IManipulationEvents .
  2. Créez un instance d’une interface IManipulationProcessor.
  3. Créez un instance de votre récepteur d’événements et configurez des événements tactiles.
  4. Envoyer des données d’événement tactile au processeur de manipulation.

Cette section explique les étapes que vous devez suivre pour ajouter la prise en charge de la manipulation à votre application. Le code est fourni à chaque étape pour vous aider à démarrer.

Notes

Vous ne pouvez pas utiliser les manipulations et les mouvements en même temps, car les messages de mouvement et tactiles s’excluent mutuellement.

Implémenter un récepteur d’événements pour _IManipualtionEvents interface

Avant de pouvoir créer une instance de votre récepteur d’événements, vous devez créer une classe qui implémente l’interface _IManipulationEvents pour les événements. Il s’agit de votre récepteur d’événements. Les événements générés par l’interface IManipulationProcessor sont gérés par votre récepteur d’événements. Le code suivant montre un exemple d’en-tête pour une classe qui hérite de l’interface _IManipulationEvents .

// Manipulation Header Files
#include <comdef.h>
#include <manipulations.h>
#include <ocidl.h>

class CManipulationEventSink : _IManipulationEvents
{
public:
    CManipulationEventSink(IManipulationProcessor *manip, HWND hWnd);

    int GetStartedEventCount();
    int GetDeltaEventCount();
    int GetCompletedEventCount();
    double CManipulationEventSink::GetX();
    double CManipulationEventSink::GetY();        

    ~CManipulationEventSink();

    //////////////////////////////
    // IManipulationEvents methods
    //////////////////////////////
    virtual HRESULT STDMETHODCALLTYPE ManipulationStarted( 
        /* [in] */ FLOAT x,
        /* [in] */ FLOAT y);
    
    virtual HRESULT STDMETHODCALLTYPE ManipulationDelta( 
        /* [in] */ FLOAT x,
        /* [in] */ FLOAT y,
        /* [in] */ FLOAT translationDeltaX,
        /* [in] */ FLOAT translationDeltaY,
        /* [in] */ FLOAT scaleDelta,
        /* [in] */ FLOAT expansionDelta,
        /* [in] */ FLOAT rotationDelta,
        /* [in] */ FLOAT cumulativeTranslationX,
        /* [in] */ FLOAT cumulativeTranslationY,
        /* [in] */ FLOAT cumulativeScale,
        /* [in] */ FLOAT cumulativeExpansion,
        /* [in] */ FLOAT cumulativeRotation);
    
    virtual HRESULT STDMETHODCALLTYPE ManipulationCompleted( 
        /* [in] */ FLOAT x,
        /* [in] */ FLOAT y,
        /* [in] */ FLOAT cumulativeTranslationX,
        /* [in] */ FLOAT cumulativeTranslationY,
        /* [in] */ FLOAT cumulativeScale,
        /* [in] */ FLOAT cumulativeExpansion,
        /* [in] */ FLOAT cumulativeRotation);

    ////////////////////////////////////////////////////////////
    // IUnknown methods
    ////////////////////////////////////////////////////////////
    STDMETHOD_(ULONG, AddRef)(void);
    STDMETHOD_(ULONG, Release)(void);
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObj);

private:
    double m_fX;
    double m_fY;

    int m_cRefCount;
    int m_cStartedEventCount;
    int m_cDeltaEventCount;
    int m_cCompletedEventCount;

    IManipulationProcessor* m_pManip;

    IConnectionPointContainer* m_pConPointContainer;
    IConnectionPoint* m_pConnPoint;
    
    HWND m_hWnd;
};     

Compte tenu de l’en-tête, vous devez créer une implémentation de l’interface des événements afin que votre classe effectue les actions que vous souhaitez que le processeur de manipulation effectue. Le code suivant est un modèle qui implémente les fonctionnalités minimales d’un récepteur d’événements pour l’interface _IManipulationEvents .

#include "stdafx.h"
#include "cmanipulationeventsink.h"

CManipulationEventSink::CManipulationEventSink(IManipulationProcessor *manip, HWND hWnd)
{
    m_hWnd = hWnd;

    //Set initial ref count to 1.
    m_cRefCount = 1;

    m_pManip = manip;
    m_pManip->put_PivotRadius(-1);

    m_cStartedEventCount = 0;
    m_cDeltaEventCount = 0;
    m_cCompletedEventCount = 0;

    HRESULT hr = S_OK;

    //Get the container with the connection points.
    IConnectionPointContainer* spConnectionContainer;
    
    hr = manip->QueryInterface(
      IID_IConnectionPointContainer, 
          (LPVOID*) &spConnectionContainer
        );
    //hr = manip->QueryInterface(&spConnectionContainer);
    if (spConnectionContainer == NULL){
        // something went wrong, try to gracefully quit
        
    }

    //Get a connection point.
    hr = spConnectionContainer->FindConnectionPoint(__uuidof(_IManipulationEvents), &m_pConnPoint);
    if (m_pConnPoint == NULL){
        // something went wrong, try to gracefully quit
    }

    DWORD dwCookie;

    //Advise.
    hr = m_pConnPoint->Advise(this, &dwCookie);
}

int CManipulationEventSink::GetStartedEventCount()
{
    return m_cStartedEventCount;
}

int CManipulationEventSink::GetDeltaEventCount()
{
    return m_cDeltaEventCount;
}

int CManipulationEventSink::GetCompletedEventCount()
{
    return m_cCompletedEventCount;
}

double CManipulationEventSink::GetX()
{
    return m_fX;
}

double CManipulationEventSink::GetY()
{
    return m_fY;
}

CManipulationEventSink::~CManipulationEventSink()
{
    //Cleanup.
}

///////////////////////////////////
//Implement IManipulationEvents
///////////////////////////////////

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationStarted( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y)
{
    m_cStartedEventCount ++;
    
    return S_OK;
}

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationDelta( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y,
    /* [in] */ FLOAT translationDeltaX,
    /* [in] */ FLOAT translationDeltaY,
    /* [in] */ FLOAT scaleDelta,
    /* [in] */ FLOAT expansionDelta,
    /* [in] */ FLOAT rotationDelta,
    /* [in] */ FLOAT cumulativeTranslationX,
    /* [in] */ FLOAT cumulativeTranslationY,
    /* [in] */ FLOAT cumulativeScale,
    /* [in] */ FLOAT cumulativeExpansion,
    /* [in] */ FLOAT cumulativeRotation)
{
    m_cDeltaEventCount ++;
    
    RECT rect;
        
    GetWindowRect(m_hWnd, &rect);
    
    int oldWidth =  rect.right-rect.left;
    int oldHeight = rect.bottom-rect.top;            

    // scale and translate the window size / position    
    MoveWindow(m_hWnd,                                                     // the window to move
               static_cast<int>(rect.left + (translationDeltaX / 100.0f)), // the x position
               static_cast<int>(rect.top + (translationDeltaY/100.0f)),    // the y position
               static_cast<int>(oldWidth * scaleDelta),                    // width
               static_cast<int>(oldHeight * scaleDelta),                   // height
               TRUE);                                                      // redraw
                     
    return S_OK;
}

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationCompleted( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y,
    /* [in] */ FLOAT cumulativeTranslationX,
    /* [in] */ FLOAT cumulativeTranslationY,
    /* [in] */ FLOAT cumulativeScale,
    /* [in] */ FLOAT cumulativeExpansion,
    /* [in] */ FLOAT cumulativeRotation)
{
    m_cCompletedEventCount ++;

    m_fX = x;
    m_fY = y;

    // place your code handler here to do any operations based on the manipulation   

    return S_OK;
}


/////////////////////////////////
//Implement IUnknown
/////////////////////////////////

ULONG CManipulationEventSink::AddRef(void) 
{
    return ++m_cRefCount;
}

ULONG CManipulationEventSink::Release(void)
{ 
    m_cRefCount --;

    if(0 == m_cRefCount) {
        delete this;
        return 0;
    }

    return m_cRefCount;
}

HRESULT CManipulationEventSink::QueryInterface(REFIID riid, LPVOID *ppvObj) 
{
    if (IID__IManipulationEvents == riid) {
        *ppvObj = (_IManipulationEvents *)(this); AddRef(); return S_OK;
    } else if (IID_IUnknown == riid) {
        *ppvObj = (IUnknown *)(this); AddRef(); return S_OK;
    } else {
        return E_NOINTERFACE;
    }
}         

Prêtez une attention particulière aux implémentations des méthodes ManipulationStarted, ManipulationDelta et ManipulationCompleted dans la classe. Il s’agit des méthodes les plus probables de l’interface qui vous obligeront à effectuer des opérations basées sur les informations de manipulation transmises dans l’événement. Notez également que le deuxième paramètre du constructeur est l’objet utilisé dans les manipulations d’événements. Dans le code utilisé pour produire l’exemple, le hWnd de l’application est envoyé au constructeur afin qu’il puisse être repositionné et redimensionné.

Créer un instance d’une interface IManipulationProcessor

Dans le code où vous allez utiliser des manipulations, vous devez créer une instance d’une interface IManipulationProcessor. Tout d’abord, vous devez ajouter la prise en charge de la classe manipulations. Le code suivant montre comment procéder dans votre classe.

//Include windows.h for touch events
#include "windows.h"  

// Manipulation implementation file
#include <manipulations_i.c>
    
// Smart Pointer to a global reference of a manipulation processor, event sink
IManipulationProcessor* g_pIManipProc;     

Une fois que vous avez votre variable pour le processeur de manipulation et que vous avez inclus les en-têtes pour les manipulations, vous devez créer un instance de l’interface IManipulationProcessor. Il s’agit d’un objet COM. Par conséquent, vous devez appeler CoCreateInstance, puis créer un instance de votre référence à IManipulationProcessor. Le code suivant montre comment créer une instance de cette interface.

   HRESULT hr = CoInitialize(0);
        
   hr = CoCreateInstance(CLSID_ManipulationProcessor,
       NULL,
       CLSCTX_INPROC_SERVER,
       IID_IUnknown,
       (VOID**)(&g_pIManipProc)
   );

Créez un instance de votre récepteur d’événements et configurez les événements tactiles

Incluez la définition de votre classe de récepteur d’événements à votre code, puis ajoutez une variable pour la classe récepteur d’événements de manipulation. L’exemple de code suivant inclut l’en-tête de l’implémentation de classe et configure une variable globale pour stocker le récepteur d’événements.

//Include your definition of the event sink, CManipulationEventSink.h in this case
#include "CManipulationEventSink.h"    
     
// Set up a variable to point to the manipulation event sink implementation class    
CManipulationEventSink* g_pManipulationEventSink;   

Une fois que vous avez la variable et que vous avez inclus votre définition pour la nouvelle classe de récepteur d’événements, vous pouvez construire la classe à l’aide du processeur de manipulation que vous avez configuré à l’étape précédente. Le code suivant montre comment créer une instance de cette classe à partir de OnInitDialog.

   g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, hWnd);


   RegisterTouchWindow(hWnd, 0);  

Notes

La façon dont vous créez une instance de votre récepteur d’événements dépend de ce que vous faites avec les données de manipulation. Dans la plupart des cas, vous allez créer un récepteur d’événements de processeur de manipulation qui n’a pas le même constructeur que cet exemple.

Envoyer des données d’événement tactile au processeur de manipulation

Maintenant que votre processeur de manipulation et votre récepteur d’événements sont configurés, vous devez envoyer des données tactiles au processeur de manipulation pour déclencher des événements de manipulation.

Notes

Il s’agit de la même procédure que celle décrite dans Prise en main avec les messages tactiles Windows.

Tout d’abord, vous allez créer du code pour décoder les messages WM_TOUCH et les envoyer à l’interface IManipulationProcessor pour déclencher des événements. Le code suivant montre un exemple d’implémentation appelé à partir de la méthode WndProc et qui retourne un LRESULT pour la messagerie.

LRESULT OnTouch(HWND hWnd, WPARAM wParam, LPARAM lParam )
{
  UINT cInputs = LOWORD(wParam);
  PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
  
  BOOL bHandled = FALSE;
  
  if (NULL != pInputs) {
    if (GetTouchInputInfo((HTOUCHINPUT)lParam,
      cInputs,
      pInputs,
      sizeof(TOUCHINPUT))) {      
      for (UINT i=0; i<cInputs; i++){
        if (pInputs[i].dwFlags & TOUCHEVENTF_DOWN){
            g_pIManipProc->ProcessDown(pInputs[i].dwID, static_cast<FLOAT>(pInputs[i].x), static_cast<FLOAT>(pInputs[i].y));
            bHandled = TRUE;
        }
        if (pInputs[i].dwFlags & TOUCHEVENTF_UP){
            g_pIManipProc->ProcessUp(pInputs[i].dwID, static_cast<FLOAT>(pInputs[i].x), static_cast<FLOAT>(pInputs[i].y));
            bHandled = TRUE;
        }
        if (pInputs[i].dwFlags & TOUCHEVENTF_MOVE){
            g_pIManipProc->ProcessMove(pInputs[i].dwID, static_cast<FLOAT>(pInputs[i].x), static_cast<FLOAT>(pInputs[i].y));
            bHandled = TRUE;
        }
      }      
    } else {
      // GetLastError() and error handling
    }
    delete [] pInputs;
  } else {
    // error handling, presumably out of memory
  }
  if (bHandled){
    // if you don't want to pass to DefWindowProc, close the touch input handle
    if (!CloseTouchInputHandle((HTOUCHINPUT)lParam)) {
        // error handling
    }
    return 0;
  }else{
    return DefWindowProc(hWnd, WM_TOUCH, wParam, lParam);
  }
}

Maintenant que vous disposez d’une méthode utilitaire pour décoder le message WM_TOUCH , vous devez passer les WM_TOUCH messages à la fonction utilitaire à partir de votre méthode WndProc . Le code suivant montre comment procéder.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_TOUCH:
        return OnTouch(hWnd, wParam, lParam);
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // TODO: Add any drawing code here...
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

Les méthodes personnalisées que vous avez implémentées dans votre récepteur d’événements doivent maintenant fonctionner. Dans cet exemple, le fait de toucher la fenêtre la déplace.

Manipulations