Windows Touch 操作示例 (MTManipulation)
本节介绍 Windows Touch 操作示例。
此 Windows Touch 操作示例演示如何使用 IManipulationProcessor 接口并实现 _IManipulationEvents 事件接收器,从而平移、旋转和缩放对象。下图演示了应用程序运行时的外观。
对于此示例,创建一个可以编程方式进行平移、旋转或缩放的 CDrawingObject 类。实例化 IManipulationProcessor 接口。创建一个操作事件接收器,它接受指向 CDrawingObject 类及其构造函数上的 IManipulationProcessor 接口的指针。在操作事件接收器实现中创建一个到 IManipulationProcessor 的连接点,以便事件接收器接收由 IManipulationProcessor 引发的事件。将触控数据传送给 IManipulationProcessor 接口,然后该接口将引发 _IManipulationEvent 事件。CManipulationEventSink 类中的事件处理程序将通过对指向 CDrawingObject 的指针调用访问器,更新 CDrawingObject 的方向。
以下代码演示如何为设置触控窗口,以及如何实例化 CDrawingObject 和 IManipulationProcessor 并将它们传递给 CManipulationEventSink 构造函数。
CDrawingObject g_cRect; // CDrawingObject class holds information about the rectangle // and it is responsible for painting the rectangle. (...) BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { (...) // Register application window for receiving multi-touch input. Use default settings. if (!RegisterTouchWindow(hWnd, 0)) { MessageBox(hWnd, L"Cannot register application window for multi-touch input", L"Error", MB_OK); return FALSE; } ASSERT(IsTouchWindow(hWnd, NULL)); // Instantiate the ManipulationProcessor object HRESULT hr = CoCreateInstance(__uuidof(ManipulationProcessor), NULL, CLSCTX_ALL, IID_PPV_ARGS(&g_pIManipProc)); if (FAILED(hr)) { ASSERT(SUCCEEDED(hr) && L"InitInstance: failed to instantiate the ManipulationProcessor object"); return FALSE; } // Instantiate the event sink with the manipulation processor and pointer to the rectangle object g_pManipulationEventSink = new CManipulationEventSink(&g_cRect); if (g_pManipulationEventSink == NULL) { ASSERT(g_pManipulationEventSink && L"InitInstance: failed to instantiate the CManipulationEventSink class"); g_pIManipProc->Release(); g_pIManipProc = NULL; return FALSE; } // Establish the link between ManipulationEventSink and ManipulationProcessor if (!g_pManipulationEventSink->Connect(g_pIManipProc)) { ASSERT(FALSE && L"InitInstance: failed to connect ManipulationEventSink and ManipulationProcessor"); g_pIManipProc->Release(); g_pIManipProc = NULL; g_pManipulationEventSink->Release(); g_pManipulationEventSink = NULL; return FALSE; }
以下代码演示操作事件接收器 CManipulationEventSink 的构造函数。
CManipulationEventSink::CManipulationEventSink(CDrawingObject* pcDrawingObject) : m_cRefCount(1), m_pConnection(NULL), m_dwCookie(0), m_pcDrawingObject(pcDrawingObject) { ASSERT((pcDrawingObject != NULL) && L"CManipulationEventSink constructor: incorrect argument"); }
以下代码演示如何将事件接收器连接到操作处理器。
bool CManipulationEventSink::Connect(IManipulationProcessor* pManipulationProcessor) { // Check input arguments if (pManipulationProcessor == NULL) { ASSERT((pManipulationProcessor != NULL) && L"CManipulationEventSink::Create : incorrect arguments"); return false; } // Check object state if ((m_dwCookie != 0) || (m_pConnection != NULL)) { ASSERT((m_dwCookie == 0) && (m_pConnection == NULL) && L"CManipulationEventSink::Connect : connection already established"); return false; } // Get the container with the connection points. IConnectionPointContainer* pConnectionContainer = NULL; HRESULT hr = pManipulationProcessor->QueryInterface(&pConnectionContainer); if (FAILED(hr)) { ASSERT(SUCCEEDED(hr) && L"CManipulationEventSink::Connect : failed to get the container with the connection points"); return false; } // Get a connection point. hr = pConnectionContainer->FindConnectionPoint(__uuidof(_IManipulationEvents), &m_pConnection); if (FAILED(hr)) { ASSERT(SUCCEEDED(hr) && L"CManipulationEventSink::Connect : failed to get a connection point"); pConnectionContainer->Release(); return false; } // Release the connection container. pConnectionContainer->Release(); // Advise. Establishes an advisory connection between the connection point and the // caller's sink object. hr = m_pConnection->Advise(this, &m_dwCookie); if (FAILED(hr)) { ASSERT(SUCCEEDED(hr) && L"CManipulationEventSink::Connect : failed to Advise"); m_pConnection->Release(); m_pConnection = NULL; return false; } return true; }
以下代码演示如何将触控数据传递给操作事件接收器。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { (...) switch (message) { (...) // WM_TOUCH message handlers case WM_TOUCH: { // WM_TOUCH message can contain several messages from different contacts // packed together. // Message parameters need to be decoded: UINT cInputs = (int) wParam; // Number of actual per-contact messages TOUCHINPUT* pInputs = new TOUCHINPUT[cInputs]; // Allocate the storage for the parameters of the per-contact messages if (pInputs == NULL) { break; } // Unpack message parameters into the array of TOUCHINPUT structures, each // representing a message for one single contact. if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) { // For each contact, dispatch the message to the appropriate message // handler. for (unsigned int i = 0; i < cInputs; i++) { if (pInputs[i].dwFlags & TOUCHEVENTF_DOWN) { g_pIManipProc->ProcessDown(pInputs[i].dwID, (FLOAT)pInputs[i].x, (FLOAT)pInputs[i].y); } else if (pInputs[i].dwFlags & TOUCHEVENTF_MOVE) { g_pIManipProc->ProcessMove(pInputs[i].dwID, (FLOAT)pInputs[i].x, (FLOAT)pInputs[i].y); } else if (pInputs[i].dwFlags & TOUCHEVENTF_UP) { g_pIManipProc->ProcessUp(pInputs[i].dwID, (FLOAT)pInputs[i].x, (FLOAT)pInputs[i].y); } } } else { // error handling, presumably out of memory ASSERT(FALSE && L"Error: failed to execute GetTouchInputInfo"); delete [] pInputs; break; } if (!CloseTouchInputHandle((HTOUCHINPUT)lParam)) { // error handling, presumably out of memory ASSERT(FALSE && L"Error: failed to execute CloseTouchInputHandle"); delete [] pInputs; break; } delete [] pInputs; // Force redraw of the rectangle InvalidateRect(hWnd, NULL, TRUE); } break;
以下代码演示事件处理程序如何更新操作增量事件的对象方向和大小。
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_pcDrawingObject->ApplyManipulationDelta(translationDeltaX,translationDeltaY,scaleDelta,rotationDelta); return S_OK; }
以下代码是 CDrawingObject 类中的 ApplyManipulationDelta 实现。
// This function is responsible for manipulation of the rectangle. // It is called from CManipulationEventSink class. // in: // translationDeltaX - shift of the x-coordinate (1/100 of pixel units) // translationDeltaY - shift of the y-coordinate (1/100 of pixel units) // scaleDelta - scale factor (zoom in/out) // rotationDelta - rotation angle in radians void CDrawingObject::ApplyManipulationDelta( const FLOAT translationDeltaX, const FLOAT translationDeltaY, const FLOAT scaleDelta, const FLOAT rotationDelta ) { _ptCenter.x += (LONG) (translationDeltaX / 100.0); _ptCenter.y += (LONG) (translationDeltaY / 100.0); _dScalingFactor *= scaleDelta; _dRotationAngle -= rotationDelta; // we are substracting because Y-axis is down }
在更新 CDrawingObject 的中心点、缩放因子和旋转角度之后,该对象将绘制经过平移的自身。