處理 Unmanaged 程式碼中的慣性
本節說明如何使用 IInertiaProcessor 介面來處理非受控程式碼中的慣性。
概觀
若要在 Unmanaged 程式碼中使用慣性,您必須同時實作操作處理器和慣性處理器的事件接收。 首先,將操作支援新增至您的應用程式,如 將操作支援新增至 Unmanaged 程式碼一節所述。 請注意,操作支援需要您使用觸控訊息,而不是手勢訊息,將事件資料摘要至操作處理器。 操作運作之後,您也必須針對 IInertiaProcessor 介面將產生的事件實作第二個事件接收,或必須修改現有的事件接收,以容納 IInertiaProcessor 和 IManipulationProcessor 介面所產生的兩個事件。 針對此範例的目的,從為將操作支援新增至 Unmanaged Code 一節所建立的事件接收開始,並新增第二個建構函式來處理慣性處理器,而不是操作處理器。 如此一來,事件接收實作可以針對操作處理器或慣性處理器運作。 除了新增第二個建構函式之外,事件接收也會有變數,指出它是否會根據慣性輸入執行作業,而不是操作輸入。
將慣性支援新增至操作處理器事件接收
下列程式碼顯示新的事件接收建構函式、 IInertiaProcessor 介面的新成員變數,以及指出接收是否推斷慣性旗標。
CManipulationEventSink(IManipulationProcessor *pManip, IInertiaProcessor *pInert, HWND hWnd);
CManipulationEventSink(IInertiaProcessor *pInert, HWND hWnd);
IInertiaProcessor* m_pInert;
BOOL fExtrapolating;
類別標頭具有新的建構函式和旗標,指出您是否要外推,您可以實作事件接收,以針對 IManipulationProcessor 事件和 IInertiaProcessor 事件有個別的處理區塊。 接受 IManipulationProcessor 和 IInertiaProcessor 的建構函式應該將 fExtrapolating 旗標設定為 false,這表示這是 IManipulationProcessor 事件處理常式。 下列程式碼示範如何使用 IManipulationProcessor 實作之事件接收的建構函式。
CManipulationEventSink::CManipulationEventSink(IManipulationProcessor *pManip, IInertiaProcessor *pInert, HWND hWnd)
{
m_hWnd = hWnd;
//Set initial ref count to 1.
m_cRefCount = 1;
fExtrapolating=FALSE;
m_pManip = pManip;
m_pInert = pInert;
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 = pManip->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);
}
下列程式碼示範如何使用 IInertiaProcessor 實作之事件接收的建構函式。 這個建構函式會將 fExtrapolating 旗標設定為 true,指出事件接收類別的這個實例將會執行外推,並執行先前由操作處理器事件執行的任何移動作業。
CManipulationEventSink::CManipulationEventSink(IInertiaProcessor *pInert, HWND hWnd)
{
m_hWnd = hWnd;
m_pInert = pInert;
//Set initial ref count to 1.
m_cRefCount = 1;
fExtrapolating=TRUE;
m_cStartedEventCount = 0;
m_cDeltaEventCount = 0;
m_cCompletedEventCount = 0;
HRESULT hr = S_OK;
//Get the container with the connection points.
IConnectionPointContainer* spConnectionContainer;
hr = pInert->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);
}
注意
操作處理器事件接收的事件接收類別實作會重複使用為慣性處理器的事件接收。
現在當您建構這個類別 CManipulationEventSink時,它可以建構為操作處理器的事件接收,或做為慣性處理器的事件接收。 建構為慣性處理器事件接收時,它會將 fExtrapolating 旗標設定為 true,表示應該推斷操作事件。
注意
ManipulationStarted 會由 IManipulationProcessor 和 IInertiaProcessor 介面引發。
操作開始時,會設定 IInertiaProcessor 介面屬性。 下列程式碼示範如何處理已啟動的事件。
HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationStarted(
/* [in] */ FLOAT x,
/* [in] */ FLOAT y)
{
m_cStartedEventCount ++;
// set origins in manipulation processor
m_pInert->put_InitialOriginX(x);
m_pInert->put_InitialOriginY(y);
RECT screenRect;
HWND desktop = GetDesktopWindow();
GetClientRect(desktop, &screenRect);
// physics settings
// deceleration is units per square millisecond
m_pInert->put_DesiredDeceleration(.1f);
// set the boundaries
screenRect.left-= 1024;
m_pInert->put_BoundaryLeft ( static_cast<float>(screenRect.left * 100));
m_pInert->put_BoundaryTop ( static_cast<float>(screenRect.top * 100));
m_pInert->put_BoundaryRight ( static_cast<float>(screenRect.right * 100));
m_pInert->put_BoundaryBottom( static_cast<float>(screenRect.bottom * 100));
// Elastic boundaries - I set these to 90% of the screen
// so... 5% at left, 95% right, 5% top, 95% bottom
// Values are whole numbers because units are in centipixels
m_pInert->put_ElasticMarginLeft (static_cast<float>(screenRect.left * 5));
m_pInert->put_ElasticMarginTop (static_cast<float>(screenRect.top * 5));
m_pInert->put_ElasticMarginRight (static_cast<float>(screenRect.right * 95));
m_pInert->put_ElasticMarginBottom(static_cast<float>(screenRect.bottom * 95));
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;
}
在此範例中,操作已完成的事件會啟動或停止計時器,以在IInertiaProcessor介面上呼叫Process。 下列程式碼示範如何處理已完成的操作事件。
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
if (fExtrapolating){
//Inertia Complete, stop the timer used for processing
KillTimer(m_hWnd,0);
}else{
// setup velocities for inertia processor
float vX = 0.0f;
float vY = 0.0f;
float vA = 0.0f;
m_pManip->GetVelocityX(&vX);
m_pManip->GetVelocityY(&vY);
m_pManip->GetAngularVelocity(&vA);
// complete any previous processing
m_pInert->Complete();
// Reset sets the initial timestamp
m_pInert->Reset();
//
m_pInert->put_InitialVelocityX(vX);
m_pInert->put_InitialVelocityY(vY);
m_pInert->put_InitialOriginX(x);
m_pInert->put_InitialOriginY(y);
// Start a timer
SetTimer(m_hWnd,0, 50, 0);
}
return S_OK;
}
下列程式碼示範如何在WndProc中解譯WM_TIMER訊息,以在IInertiaProcessor介面上執行Process的呼叫。
case WM_TIMER:
if (g_pIInertProc){
BOOL b;
g_pIInertProc->Process(&b);
}
break;
共同初始化慣性處理器和操作處理器,並初始化事件接收
修改事件接收以支援 IManipulationProcessor 和 IInertiaProcessor之後,您就可以初始化事件接收,並設定它們從您的應用程式執行。 下列程式碼示範如何配置介面指標。
//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;
IInertiaProcessor* g_pIInertProc;
下列程式碼範例示範如何具現化介面。
HRESULT hr = CoInitialize(0);
hr = CoCreateInstance(CLSID_ManipulationProcessor,
NULL,
CLSCTX_INPROC_SERVER,
IID_IUnknown,
(VOID**)(&g_pIManipProc)
);
hr = CoCreateInstance(CLSID_InertiaProcessor,
NULL,
CLSCTX_INPROC_SERVER,
IID_IUnknown,
(VOID**)(&g_pIInertProc)
);
下列程式碼範例示範如何根據介面指標建構事件接收器,並註冊觸控輸入的視窗。
g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, g_pIInertProc, hWnd);
g_pManipulationEventSink = new CManipulationEventSink(g_pIInertProc, hWnd);
RegisterTouchWindow(hWnd, 0);
相關主題