在非托管代码中添加操作支持
本部分介绍如何通过实现 _IManipulationEvents 接口的事件接收器,向非托管代码添加操作支持。
下图概述了操作体系结构。
从 WM_TOUCH 消息收到的触摸数据与来自触摸消息的联系人 ID 一起传递到 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 触摸消息入门中所述的过程相同。
首先,你将创建一些代码来解码 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;
}
在事件接收器中实现的自定义方法现在应该可以正常工作。 在此示例中,触摸窗口将移动窗口。
相关主题