Adicionando suporte a manipulação em código não gerenciado
Esta seção explica como adicionar suporte de manipulação ao código não gerenciado implementando um coletor de eventos para a interface _IManipulationEvents .
A imagem a seguir descreve a arquitetura de manipulação.
Os dados de toque recebidos de WM_TOUCH mensagens são passados para o IManipulationProcessor junto com a ID de contato da mensagem de toque. Com base na sequência de mensagens, a interface IManipulationProcessor calculará que tipo de transformação está sendo executada e quais são os valores associados a essa transformação. O IManipulationProcessor gerará _IManipulationEvents que são manipuladas por um coletor de eventos. O coletor de eventos pode usar esses valores para executar operações personalizadas no objeto que está sendo transformado.
Para adicionar suporte de manipulação ao seu aplicativo, você deve seguir estas etapas:
- Implemente um coletor de eventos para a interface _IManipulationEvents .
- Crie uma instância de uma interface IManipulationProcessor .
- Crie uma instância do coletor de eventos e configure eventos de toque.
- Enviar dados de evento de toque para o processador de manipulação.
Esta seção explica as etapas que você deve seguir para adicionar suporte de manipulação ao seu aplicativo. O código é fornecido em cada etapa para você começar.
Observação
Você não pode usar manipulações e gestos ao mesmo tempo porque mensagens de gesto e toque são mutuamente exclusivas.
Implementar um coletor de eventos para _IManipualtionEvents interface
Antes de criar uma instância do coletor de eventos, você deve criar uma classe que implemente a interface _IManipulationEvents para eventos. Esse é o coletor de eventos. Os eventos gerados pela interface IManipulationProcessor são manipulados pelo coletor de eventos. O código a seguir mostra um cabeçalho de exemplo para uma classe que herda a 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;
};
Dado o cabeçalho , você deve criar uma implementação da interface de eventos para que sua classe execute as ações que você deseja que o processador de manipulação execute. O código a seguir é um modelo que implementa a funcionalidade mínima de um coletor de eventos para a 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;
}
}
Preste atenção extra às implementações dos métodos ManipulationStarted, ManipulationDelta e ManipulationCompleted na classe . Esses são os métodos mais prováveis na interface que exigirão que você execute operações com base nas informações de manipulação passadas no evento. Observe também que o segundo parâmetro no construtor é o objeto usado nas manipulações de eventos. No código usado para produzir o exemplo, o hWnd para o aplicativo é enviado ao construtor para que ele possa ser reposicionado e redimensionado.
Criar uma instância de uma interface IManipulationProcessor
No código em que você usará manipulações, você deve criar uma instância de uma interface IManipulationProcessor . Primeiro, você deve adicionar suporte para a classe de manipulações. O código a seguir mostra como você pode fazer isso em sua 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;
Depois de ter sua variável para o processador de manipulação e incluir os cabeçalhos para manipulações, você precisará criar uma instância da interface IManipulationProcessor . Este é um objeto COM. Portanto, você deve chamar CoCreateInstance e, em seguida, criar uma instância de sua referência ao IManipulationProcessor. O código a seguir mostra como você pode criar uma instância dessa interface.
HRESULT hr = CoInitialize(0);
hr = CoCreateInstance(CLSID_ManipulationProcessor,
NULL,
CLSCTX_INPROC_SERVER,
IID_IUnknown,
(VOID**)(&g_pIManipProc)
);
Criar uma instância do coletor de eventos e configurar eventos de toque
Inclua a definição da classe do coletor de eventos ao código e adicione uma variável para a classe de coletor de eventos de manipulação. O exemplo de código a seguir inclui o cabeçalho para a implementação da classe e configura uma variável global para armazenar o coletor de eventos.
//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;
Depois de ter a variável e incluir sua definição para a nova classe de coletor de eventos, você poderá construir a classe usando o processador de manipulação configurado na etapa anterior. O código a seguir mostra como uma instância dessa classe seria criada a partir de OnInitDialog.
g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, hWnd);
RegisterTouchWindow(hWnd, 0);
Observação
A maneira como você cria uma instância do coletor de eventos depende do que você está fazendo com os dados de manipulação. Na maioria dos casos, você criará um coletor de eventos do processador de manipulação que não tem o mesmo construtor que este exemplo.
Enviar dados de evento de toque para o processador de manipulação
Agora que você tem o processador de manipulação e o coletor de eventos configurados, você deve alimentar dados de toque para o processador de manipulação para disparar eventos de manipulação.
Observação
Esse é o mesmo procedimento discutido em Introdução com mensagens de toque do Windows.
Primeiro, você criará algum código para decodificar as mensagens WM_TOUCH e enviá-las para a interface IManipulationProcessor para gerar eventos. O código a seguir mostra um exemplo de implementação que é chamado do método WndProc e retorna um LRESULT para mensagens.
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);
}
}
Agora que você tem um método utilitário para decodificar a mensagem WM_TOUCH , você deve passar as mensagens WM_TOUCH para a função de utilitário do método WndProc . O código a seguir mostra como você pode fazer isso.
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;
}
Os métodos personalizados que você implementou no coletor de eventos agora devem funcionar. Neste exemplo, tocar na janela a moverá.
Tópicos relacionados