Добавление поддержки манипуляций в неуправляемом коде
В этом разделе объясняется, как добавить поддержку манипуляций в неуправляемый код путем реализации приемника событий для интерфейса _IManipulationEvents .
На следующем рисунке показана архитектура манипуляции.
Сенсорные данные, полученные от WM_TOUCH сообщений, передаются в IManipulationProcessor вместе с идентификатором контакта из сенсорного сообщения. На основе последовательности сообщений интерфейс IManipulationProcessor вычисляет, какой тип преобразования выполняется и какие значения связаны с этим преобразованием. Затем IManipulationProcessor создаст _IManipulationEvents , которые обрабатываются приемником событий. Затем приемник событий может использовать эти значения для выполнения пользовательских операций с преобразуемого объекта.
Чтобы добавить в приложение поддержку манипуляций, необходимо выполнить следующие действия.
- Реализуйте приемник событий для интерфейса _IManipulationEvents .
- Создайте экземпляр интерфейса IManipulationProcessor .
- Создайте экземпляр приемника событий и настройте события касания.
- Отправка данных о событиях касания в обработчик манипуляций.
В этом разделе описываются шаги, которые необходимо выполнить, чтобы добавить поддержку манипуляций в приложение. Код предоставляется на каждом шаге, чтобы начать работу.
Примечание
Нельзя одновременно использовать манипуляции и жесты, так как жесты и сенсорные сообщения являются взаимоисключающими.
Реализация приемника событий для интерфейса _IManipualtionEvents
Перед созданием экземпляра приемника событий необходимо создать класс, реализующий интерфейс _IManipulationEvents для обработки событий. Это приемник событий. События, созданные интерфейсом IManipulationProcessor , обрабатываются приемником событий. В следующем коде показан пример заголовка для класса, который наследует интерфейс _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;
};
С учетом заголовка необходимо создать реализацию интерфейса событий, чтобы класс выполнял действия, которые должен выполнять обработчик манипуляций. Следующий код представляет собой шаблон, реализующий минимальную функциональность приемника событий для интерфейса _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;
}
}
Обратите особое внимание на реализации методов ManipulationStarted, ManipulationDelta и ManipulationCompleted в классе . Это наиболее вероятные методы в интерфейсе, которые требуют выполнения операций на основе сведений о манипуляциях, передаваемых в событии. Обратите также внимание, что вторым параметром в конструкторе является объект , используемый в манипуляциях с событиями. В коде, используемом для создания примера, hWnd для приложения отправляется конструктору, чтобы его можно было изменить положение и изменить его размер.
Создание экземпляра интерфейса IManipulationProcessor
В коде, в котором будут использоваться манипуляции, необходимо создать экземпляр интерфейса IManipulationProcessor . Сначала необходимо добавить поддержку класса manipulations. В следующем коде показано, как это можно сделать в классе.
//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;
После получения переменной для обработчика манипуляций и добавления заголовков для манипуляций необходимо создать экземпляр интерфейса IManipulationProcessor . Это COM-объект. Поэтому необходимо вызвать CoCreateInstance, а затем создать экземпляр ссылки на IManipulationProcessor. В следующем коде показано, как создать экземпляр этого интерфейса.
HRESULT hr = CoInitialize(0);
hr = CoCreateInstance(CLSID_ManipulationProcessor,
NULL,
CLSCTX_INPROC_SERVER,
IID_IUnknown,
(VOID**)(&g_pIManipProc)
);
Создание экземпляра приемника событий и настройка событий Touch
Включите определение для класса приемника событий в код, а затем добавьте переменную для класса приемника событий манипуляции. Следующий пример кода включает заголовок для реализации класса и настраивает глобальную переменную для хранения приемника событий.
//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;
Получив переменную и включив определение для нового класса приемника событий, можно создать класс с помощью обработчика манипуляций, настроенного на предыдущем шаге. В следующем коде показано, как будет создан экземпляр этого класса из OnInitDialog.
g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, hWnd);
RegisterTouchWindow(hWnd, 0);
Примечание
Способ создания экземпляра приемника событий зависит от действий с данными манипуляции. В большинстве случаев создается приемник событий обработчика манипуляций, который не имеет того же конструктора, что и в этом примере.
Отправка данных о событиях touch в обработчик манипуляций
Теперь, когда у вас есть обработчик манипуляции и приемник событий, необходимо передать сенсорные данные обработчику манипуляции, чтобы активировать события манипуляции.
Примечание
Это та же процедура, что и в разделе начало работы с сенсорными сообщениями Windows.
Сначала вы создадите код для декодирования WM_TOUCH сообщений и их отправки в интерфейс IManipulationProcessor для создания событий. В следующем коде показан пример реализации, который вызывается из метода WndProc и возвращает LRESULT для обмена сообщениями.
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);
}
}
Теперь, когда у вас есть служебный метод для декодирования сообщения WM_TOUCH , необходимо передать WM_TOUCH сообщения в служебную функцию из метода WndProc . В следующем коде показано, как это сделать.
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;
}
Пользовательские методы, реализованные в приемнике событий, теперь должны работать. В этом примере при касании окна оно переместится.
Связанные темы