在 Unmanaged 程式碼中新增操作支援
本節說明如何藉由實作 _IManipulationEvents 介面的事件接收,將操作支援新增至 Unmanaged 程式碼。
下圖概述操作架構。
從 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 介面的實例。 首先,您必須新增操作類別的支援。 下列程式碼示範如何在 類別中執行這項操作。
//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)
);
建立事件接收的實例,並設定觸控事件
將事件接收類別的定義包含在程式碼中,然後新增操作事件接收類別的變數。 下列程式碼範例包含 類別實作的 標頭,並設定全域變數來儲存事件接收。
//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 Touch 訊息消費者入門中所討論的程式相同。
首先,您將建立一些程式碼來解碼 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訊息的公用程式方法,您必須從WndProc方法將WM_TOUCH訊息傳遞至公用程式函式。 下列程式碼示範如何執行這項操作。
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;
}
您在事件接收中實作的自訂方法現在應該可以運作。 在此範例中,觸控視窗將會移動它。
相關主題