Freigeben über


Hinzufügen von Manipulationsunterstützung in nicht verwaltetem Code

In diesem Abschnitt wird erläutert, wie Sie nicht verwalteten Code Manipulationsunterstützung hinzufügen, indem Sie eine Ereignissenke für die _IManipulationEvents-Schnittstelle implementieren.

Die folgende Abbildung zeigt die Bearbeitungsarchitektur.

Abbildung: Windows-Touchnachrichten, die an den Bearbeitungsprozessor eines Objekts übergeben werden, das Ereignisse mit der -imanipulationevents-Schnittstelle verarbeitet

Touchdaten, die von WM_TOUCH Nachrichten empfangen werden, werden zusammen mit der Kontakt-ID aus der Touchnachricht an den IManipulationProcessor übergeben. Basierend auf der Nachrichtensequenz berechnet die IManipulationProcessor-Schnittstelle , welche Art von Transformation ausgeführt wird und welche Werte dieser Transformation zugeordnet sind. Der IManipulationProcessor generiert dann _IManipulationEvents die von einer Ereignissenke behandelt werden. Die Ereignissenke kann diese Werte dann verwenden, um benutzerdefinierte Vorgänge für das zu transformierende Objekt auszuführen.

Um Ihrer Anwendung Manipulationsunterstützung hinzuzufügen, müssen Sie die folgenden Schritte ausführen:

  1. Implementieren Sie eine Ereignissenke für die _IManipulationEvents-Schnittstelle .
  2. Erstellen Sie eine instance einer IManipulationProcessor-Schnittstelle.
  3. Erstellen Sie eine instance Ihrer Ereignissenke, und richten Sie Touchereignisse ein.
  4. Senden sie Touchereignisdaten an den Bearbeitungsprozessor.

In diesem Abschnitt werden die Schritte erläutert, die Sie ausführen müssen, um Ihrer Anwendung Bearbeitungsunterstützung hinzuzufügen. Für die ersten Schritte wird Code bereitgestellt.

Hinweis

Manipulationen und Gesten können nicht gleichzeitig verwendet werden, da Gesten- und Touchnachrichten sich gegenseitig ausschließen.

Implementieren einer Ereignissenke für _IManipualtionEvents-Schnittstelle

Bevor Sie eine instance Ihrer Ereignissenke erstellen können, müssen Sie eine Klasse erstellen, die die _IManipulationEvents-Schnittstelle für die Ereigniserstellung implementiert. Dies ist Ihre Ereignissenke. Ereignisse, die von der IManipulationProcessor-Schnittstelle generiert werden, werden von Ihrer Ereignissenke behandelt. Der folgende Code zeigt einen Beispielheader für eine Klasse, die die _IManipulationEvents-Schnittstelle erbt.

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

Angesichts des Headers müssen Sie eine Implementierung der Ereignisschnittstelle erstellen, damit Ihre Klasse die Aktionen ausführt, die der Bearbeitungsprozessor ausführen soll. Der folgende Code ist eine Vorlage, die die Mindestfunktionalität einer Ereignissenke für die _IManipulationEvents-Schnittstelle implementiert.

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

Achten Sie besonders auf Implementierungen der Methoden ManipulationStarted, ManipulationDelta und ManipulationCompleted in der -Klasse. Dies sind die wahrscheinlichsten Methoden in der Schnittstelle, für die Sie Vorgänge basierend auf den Manipulationsinformationen ausführen müssen, die im Ereignis übergeben werden. Beachten Sie auch, dass der zweite Parameter im Konstruktor das Objekt ist, das bei den Ereignisbearbeitungen verwendet wird. Im Code, der zum Erstellen des Beispiels verwendet wird, wird der hWnd für die Anwendung an den Konstruktor gesendet, sodass er neu positioniert und die Größe geändert werden kann.

Erstellen einer instance einer IManipulationProcessor-Schnittstelle

Im Code, in dem Sie Bearbeitungen verwenden, müssen Sie eine instance einer IManipulationProcessor-Schnittstelle erstellen. Zuerst müssen Sie Unterstützung für die Manipulationsklasse hinzufügen. Der folgende Code zeigt, wie Sie dies in Ihrer Klasse tun können.

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

Nachdem Sie über Ihre Variable für den Bearbeitungsprozessor verfügen und die Header für Bearbeitungen eingeschlossen haben, müssen Sie eine instance der IManipulationProcessor-Schnittstelle erstellen. Dies ist ein COM-Objekt. Daher müssen Sie CoCreateInstance aufrufen und dann eine instance Ihres Verweises auf den IManipulationProcessor erstellen. Der folgende Code zeigt, wie Sie eine instance dieser Schnittstelle erstellen können.

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

Erstellen Sie eine instance Ihrer Ereignissenke, und richten Sie Touchereignisse ein.

Fügen Sie die Definition für Ihre Ereignissenkenklasse in Ihren Code ein, und fügen Sie dann eine Variable für die Klasse der Manipulationsereignissenke hinzu. Das folgende Codebeispiel enthält den Header für die Klassenimplementierung und richtet eine globale Variable zum Speichern der Ereignissenke ein.

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

Nachdem Sie über die Variable verfügen und Ihre Definition für die neue Ereignissenkenklasse eingeschlossen haben, können Sie die -Klasse mithilfe des Bearbeitungsprozessors erstellen, den Sie im vorherigen Schritt eingerichtet haben. Der folgende Code zeigt, wie ein instance dieser Klasse aus OnInitDialog erstellt wird.

   g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, hWnd);


   RegisterTouchWindow(hWnd, 0);  

Hinweis

Wie Sie eine instance Ihrer Ereignissenke erstellen, hängt davon ab, was Sie mit den Bearbeitungsdaten tun. In den meisten Fällen erstellen Sie eine Manipulationsprozessorereignissenke, die nicht über denselben Konstruktor wie in diesem Beispiel verfügt.

Senden von Touchereignisdaten an den Bearbeitungsprozessor

Nachdem Sie den Bearbeitungsprozessor und die Ereignissenke eingerichtet haben, müssen Sie Touchdaten an den Bearbeitungsprozessor einspeisen, um Manipulationsereignisse auszulösen.

Hinweis

Dies ist das gleiche Verfahren wie in Erste Schritte mit Windows-Touchnachrichten beschrieben.

Zunächst erstellen Sie Code, um die WM_TOUCH Nachrichten zu decodieren und sie an die IManipulationProcessor-Schnittstelle zu senden, um Ereignisse auszulösen. Der folgende Code zeigt eine Beispielimplementierung, die von der WndProc-Methode aufgerufen wird und ein LRESULT für Messaging zurückgibt.

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

Nachdem Sie nun über eine Hilfsprogrammmethode zum Decodieren der WM_TOUCH Nachricht verfügen, müssen Sie die WM_TOUCH Nachrichten von Ihrer WndProc-Methode an die Hilfsprogrammfunktion übergeben. Der folgende Code zeigt, wie Sie dies tun können.

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

Die benutzerdefinierten Methoden, die Sie in Ihrer Ereignissenke implementiert haben, sollten jetzt funktionieren. In diesem Beispiel wird es durch Berühren des Fensters verschoben.

Manipulationen