Aggiunta del supporto per la manipolazione nel codice non gestito

Questa sezione illustra come aggiungere il supporto per la manipolazione al codice non gestito implementando un sink di eventi per l'interfaccia _IManipulationEvents .

L'immagine seguente descrive l'architettura di manipolazione.

illustrazione che mostra i messaggi di tocco di Windows passati al processore di manipolazione di un oggetto, che gestisce gli eventi con l'interfaccia -imanipulationevents

I dati di tocco ricevuti da WM_TOUCH messaggi vengono passati a IManipulationProcessor insieme all'ID contatto del messaggio di tocco. In base alla sequenza di messaggi, l'interfaccia IManipulationProcessor calcolerà il tipo di trasformazione da eseguire e i valori associati a questa trasformazione. IManipulationProcessor genererà quindi _IManipulationEvents gestiti da un sink di eventi. Il sink di eventi può quindi usare questi valori per eseguire operazioni personalizzate sull'oggetto da trasformare.

Per aggiungere il supporto per la manipolazione all'applicazione, è necessario seguire questa procedura:

  1. Implementare un sink di eventi per l'interfaccia _IManipulationEvents .
  2. Creare un'istanza di un'interfaccia IManipulationProcessor .
  3. Creare un'istanza del sink di eventi e configurare gli eventi di tocco.
  4. Inviare i dati degli eventi di tocco al processore di manipolazione.

Questa sezione illustra i passaggi da seguire per aggiungere il supporto per la manipolazione all'applicazione. Il codice viene fornito in ogni passaggio per iniziare.

Nota

Non è possibile usare le manipolazioni e i movimenti contemporaneamente perché i messaggi di movimento e tocco si escludono a vicenda.

Implementare un sink di eventi per l'interfaccia di _IManipualtionEvents

Prima di poter creare un'istanza del sink di eventi, è necessario creare una classe che implementa l'interfaccia _IManipulationEvents per l'evento. Questo è il sink dell'evento. Gli eventi generati dall'interfaccia IManipulationProcessor vengono gestiti dal sink di eventi. Il codice seguente mostra un'intestazione di esempio per una classe che eredita l'interfaccia _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;
};     

Dato l'intestazione, è necessario creare un'implementazione dell'interfaccia degli eventi in modo che la classe esegua le azioni che il processore di manipolazione deve eseguire. Il codice seguente è un modello che implementa la funzionalità minima di un sink di eventi per l'interfaccia _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;
    }
}         

Prestare particolare attenzione alle implementazioni dei metodi ManipulationStarted, ManipulationDelta e ManipulationCompleted nella classe . Si tratta dei metodi più probabili nell'interfaccia che richiederanno di eseguire operazioni in base alle informazioni di manipolazione passate nell'evento. Si noti anche che il secondo parametro nel costruttore è l'oggetto utilizzato nelle manipolazioni degli eventi. Nel codice usato per produrre l'esempio, hWnd per l'applicazione viene inviato al costruttore in modo che possa essere riposizionato e ridimensionato.

Creare un'istanza di un'interfaccia IManipulationProcessor

Nel codice in cui si useranno le modifiche, è necessario creare un'istanza di un'interfaccia IManipulationProcessor . Prima di tutto è necessario aggiungere il supporto per la classe manipulations. Il codice seguente illustra come eseguire questa operazione nella 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;     

Dopo aver creato la variabile per il processore di manipolazione e aver incluso le intestazioni per le modifiche, è necessario creare un'istanza dell'interfaccia IManipulationProcessor . Si tratta di un oggetto COM. Pertanto, è necessario chiamare CoCreateInstance e quindi creare un'istanza del riferimento a IManipulationProcessor. Il codice seguente illustra come creare un'istanza di questa interfaccia.

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

Creare un'istanza del sink di eventi e configurare Gli eventi di tocco

Includere la definizione per la classe sink di eventi al codice e quindi aggiungere una variabile per la classe sink di eventi di manipolazione. Nell'esempio di codice seguente è inclusa l'intestazione per l'implementazione della classe e viene impostata una variabile globale per archiviare il sink dell'evento.

//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;   

Dopo aver ottenuto la variabile e aver incluso la definizione per la nuova classe sink di evento, è possibile costruire la classe usando il processore di manipolazione configurato nel passaggio precedente. Il codice seguente mostra come verrà creata un'istanza di questa classe da OnInitDialog.

   g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, hWnd);


   RegisterTouchWindow(hWnd, 0);  

Nota

Il modo in cui si crea un'istanza del sink di eventi dipende dalle operazioni eseguite con i dati di manipolazione. Nella maggior parte dei casi, si creerà un sink di eventi del processore di manipolazione che non ha lo stesso costruttore di questo esempio.

Inviare i dati degli eventi touch al processore di manipolazione

Ora che hai configurato il processore di manipolazione e il sink di eventi, devi inserire i dati di tocco al processore di manipolazione per attivare gli eventi di manipolazione.

Nota

Questa è la stessa procedura descritta in Introduzione con i messaggi di Windows Touch.

Prima di tutto, si creerà codice per decodificare i messaggi WM_TOUCH e inviarli all'interfaccia IManipulationProcessor per generare eventi. Il codice seguente illustra un'implementazione di esempio che viene chiamata dal metodo WndProc e restituisce un LRESULT per la messaggistica.

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);
  }
}

Ora che è disponibile un metodo di utilità per decodificare il messaggio di WM_TOUCH , è necessario passare i messaggi WM_TOUCH alla funzione di utilità dal metodo WndProc . Il codice seguente illustra come eseguire questa operazione.

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;
}

I metodi personalizzati implementati nel sink di eventi dovrebbero ora funzionare. In questo esempio, toccando la finestra, verrà spostata.

Modifiche