操作和延时示例
在从支持 Windows Touch 的计算机运行应用程序时,用户可单独操作带渐变的框。
注册触控窗口
必须先通过调用以下函数告知系统您的应用程序是 Windows Touch 应用程序,然后才能接收触控输入:
RegisterTouchWindow(g_hWnd, 0);
实现 _IManipulationEventSink 接口
_IManipulationEvents 事件接收器包含三个函数:ManipulationStarted、ManipulationDelta 和 ManipulationCompleted。IManipulationProcessor 接口和 IInertiaProcessor 接口使用上述回调函数,以返回处理器计算的值,这些值是调用 ProcessTime、ProcessUpWithTime、ProcessDownWithTime 和ProcessMoveWithTime 函数获得的结果。以下代码段演示了 _IManipulationEvents 接口的示例实现。
#include "cmanipulationeventsink.h" #include <math.h> CManipulationEventSink::CManipulationEventSink(HWND hWnd, CDrawingObject *dObj, int iTimerId, BOOL inertia) { // Manipulation & Inertia Processors m_manip = NULL; m_inert = NULL; // Connection points for COM. m_pConPointContainer = NULL; m_pConnPoint = NULL; // Reference to an object associated with this event sink. m_dObj = dObj; // Handle to the window used for computing boundaries. m_hWnd = hWnd; // The unique timer id for this manipulation event sink. m_iTimerId = iTimerId; m_bInertia = inertia; m_cRefCount = 1; } CManipulationEventSink::~CManipulationEventSink() { } HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationStarted( FLOAT x, FLOAT y) { KillTimer(m_hWnd, m_iTimerId); return S_OK; } HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationDelta( FLOAT x, FLOAT y, FLOAT translationDeltaX, FLOAT translationDeltaY, FLOAT scaleDelta, FLOAT expansionDelta, FLOAT rotationDelta, FLOAT cumulativeTranslationX, FLOAT cumulativeTranslationY, FLOAT cumulativeScale, FLOAT cumulativeExpansion, FLOAT cumulativeRotation) { FLOAT pivot = 0.0f; // Apply transformation based on rotationDelta (in radians). FLOAT rads = 180.0f / 3.14159f; m_dObj->Rotate(rotationDelta*rads, x, y); // Apply translation based on scaleDelta. m_dObj->Scale(scaleDelta); // Apply translation based on translationDelta. m_dObj->Translate(translationDeltaX, translationDeltaY); if(!m_bInertia) { // Set values for one-finger rotations. FLOAT fPivotRadius = (FLOAT)(sqrt(pow(m_dObj->GetWidth()/2, 2) + pow(m_dObj->GetHeight()/2, 2)))*0.4f; FLOAT fPivotPtX = m_dObj->GetCenterX(); FLOAT fPivotPtY = m_dObj->GetCenterY(); m_manip->put_PivotPointX(fPivotPtX); m_manip->put_PivotPointY(fPivotPtY); m_manip->put_PivotRadius(fPivotRadius); } return S_OK; } HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationCompleted( FLOAT x, FLOAT y, FLOAT cumulativeTranslationX, FLOAT cumulativeTranslationY, FLOAT cumulativeScale, FLOAT cumulativeExpansion, FLOAT cumulativeRotation) { if(!m_bInertia) { SetupInertia(); // Kick off timer that handles inertia. SetTimer(m_hWnd, m_iTimerId, DESIRED_MILLISECONDS, NULL); } else { // Stop timer that handles inertia. KillTimer(m_hWnd, m_iTimerId); } return S_OK; }
创建 COM 对象并设置 IManipulationProcessor 和 IInertiaProcessor 接口
API 提供了 IManipulationProcessor 和 IInertiaProcessor 接口的实现。应从先前已实现的 IManipulationEvents 事件接收器实例化和引用 COM 对象。
处理 WM_TOUCH 消息
必须从 WM_TOUCH 消息中提取输入数据,并稍后将该数据传递给正确的操作处理器。
switch (msg) { case WM_TOUCH: iNumContacts = LOWORD(wParam); hInput = (HTOUCHINPUT)lParam; pInputs = new TOUCHINPUT[iNumContacts]; // Get each touch input info and feed each // tagTOUCHINPUT into the process input handler. if(pInputs != NULL) { if(GetTouchInputInfo(hInput, iNumContacts, pInputs, sizeof(TOUCHINPUT))) { for(int i = 0; i < iNumContacts; i++) { // Bring touch input info into client coordinates. ptInputs.x = pInputs[i].x/100; ptInputs.y = pInputs[i].y/100; ScreenToClient(g_hWnd, &ptInputs); pInputs[i].x = ptInputs.x; pInputs[i].y = ptInputs.y; g_ctDriver->ProcessInputEvent(pInputs[i]); } } } delete [] pInputs; break; }
注意 若要使用 ScreenToClient 函数,您的应用程序必须支持高 DPI。有关支持高 DPI 的更多信息,请参见 MSDN 的高 DPI(可能为英文网页)部分。
将 TOUCHINPUT 结构传递给适当的处理器
在使用 GetTouchInputInfo 函数从 WM_TOUCH 消息中提取数据之后,通过根据 TOUCHINPUT 结构中的 dwFlag 集来调用ProcessUpWithTime、ProcessDownWithTime 或 ProcessMoveWithTime 函数,以将数据传入操作处理器。
注意 当支持多个操作时,如果必须使用 TOUCHINPUT 结构中定义的 dwID 将数据发送给正确的 IManipulationProcessor 对象,则必须创建新的操作处理器。
CoreObject* coCurrent = m_coHead; while(coCurrent!=NULL && !bFoundObj) { if(dwEvent & TOUCHEVENTF_DOWN) { DownEvent(coCurrent, inData, &bFoundObj); } else if(dwEvent & TOUCHEVENTF_MOVE) { MoveEvent(coCurrent, inData); } else if(dwEvent & TOUCHEVENTF_UP) { UpEvent(coCurrent, inData); } coCurrent = coCurrent->coNext; } VOID CComTouchDriver::DownEvent(CoreObject* coRef, tagTOUCHINPUT inData, BOOL* bFound) { DWORD dwPCursor = inData.dwID; DWORD dwPTime = inData.dwTime; int x = inData.x; int y = inData.y; // Check that the user has touched within an object's region and fed to the object's manipulation processor. if(coRef->doDrawing->InRegion(x, y) && !HasCursor(coRef, dwPCursor)) { ... // Feed values to the Manipulation Processor. coRef->manipulationProc->ProcessDownWithTime(dwPCursor, (FLOAT)x, (FLOAT)y, dwPTime); ... } }
在 ManipulationCompleted 中设置延时
在调用 ManipulationCompleted 方法之后,IManipulationProcessor 对象必须为链接到 IManipulationProcessor 的 IInertiaProcessor 对象设置值以调用延时。以下代码段演示如何从 IManipulationProcessor 方法 ManipulationCompleted 内设置 IInertiaProcessor 对象。
int iVWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN); int iVHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN); RECT rc; GetClientRect(m_hWnd, &rc); FLOAT lCWidth = (FLOAT)rc.right; FLOAT lCHeight = (FLOAT)rc.bottom; // Set properties for inertia events. // Deceleration for tranlations in pixel / msec^2. m_inert->put_DesiredDeceleration(0.001f); // Deceleration for rotations in radians / msec^2. m_inert->put_DesiredAngularDeceleration(0.00001f); // Calculate borders and elastic margin to be set. // They are relative to the width and height of the object. FLOAT fHOffset = m_dObj->GetWidth() * 0.5f; FLOAT fVOffset = m_dObj->GetHeight() * 0.5f; // Elastic margin is in pixels - note that it offsets the boundary. FLOAT fHElasticMargin = 25.0f; FLOAT fVElasticMargin = 25.0f; FLOAT fBoundaryLeft = fHOffset + fHElasticMargin; FLOAT fBoundaryTop = fVOffset + fVElasticMargin; FLOAT fBoundaryRight = lCWidth - fHOffset - fHElasticMargin; FLOAT fBoundaryBottom = lCHeight - fVOffset - fVElasticMargin; // Set borders and elastic margin. m_inert->put_BoundaryLeft(fBoundaryLeft); m_inert->put_BoundaryTop(fBoundaryTop); m_inert->put_BoundaryRight(fBoundaryRight); m_inert->put_BoundaryBottom(fBoundaryBottom); m_inert->put_ElasticMarginLeft(fHElasticMargin); m_inert->put_ElasticMarginTop(fVElasticMargin); m_inert->put_ElasticMarginRight(fHElasticMargin); m_inert->put_ElasticMarginBottom(fVElasticMargin); // Set initial origins. m_inert->put_InitialOriginX(m_dObj->GetCenterX()); m_inert->put_InitialOriginY(m_dObj->GetCenterY()); FLOAT fVX; FLOAT fVY; FLOAT fVR; m_manip->GetVelocityX(&fVX); m_manip->GetVelocityY(&fVY); m_manip->GetAngularVelocity(&fVR); // Set initial velocities for inertia processor. m_inert->put_InitialVelocityX(fVX); m_inert->put_InitialVelocityY(fVY); m_inert->put_InitialAngularVelocity(fVR);
清理 COM 对象
在应用程序关闭时,您必须清理 COM 对象。以下代码演示如何释放示例中已分配的资源。
CComTouchDriver::~CComTouchDriver(VOID) { CoreObject* coCurrent = m_coHead; // Clean up COM objects. while(coCurrent!=NULL) { coCurrent->inertiaEventSink->Release(); coCurrent->manipulationEventSink->Release(); coCurrent->inertiaProc->Release(); coCurrent->manipulationProc->Release(); coCurrent = coCurrent->coNext; } }