Step 1: 利用PInvoke的機制呼叫user32.dll中定義Touch / Gesture的資料型態, 結構和方法
此方法可參考這篇文章 : 如何在C#中使用Unmanaged dll
例如;我們要使用Winuser.h的 RegisterTouchWindow函式, 其轉換如下:
C
BOOL WINAPI RegisterTouchWindow( __in HWND hWnd, __in ULONG ulFlags );
C#
[Flags, Serializable]
public enum RegisterTouchFlags
{
TWF_NONE = 0x00000000,
TWF_FINETOUCH = 0x00000001
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool RegisterTouchWindow(IntPtr hwnd,
[MarshalAs(UnmanagedType.U4)] RegisterTouchFlags flags);
Step 2: 在WPF Window中利用HwndSource物件來取得的Windows訊息
由於WPF Window不能像Win32 Window, 可利用WndProc函式處理WM_TOUCH / WM_GESTURE訊息,但WPF Window可以用HwndSource來裝載最上層HWND的所有內容.
其方法如下;
(1) 利用 System.Windows.Interop.WindowInteropHelper類別來取得任何WPF Window的HWND
example:
using System.Windows.Interop;
IntPtr _hwnd = new WindowInteropHelper(this).Handle;
(2) 利用HWND取得相關的HwndSource物件(用HwndSource.FromHwnd)
example:
HwndSource src = HwndSource.FromHwnd(_hwnd);
(3) 利用HwndSource的AddHook方法, 即可攔截視窗訊息
example:
src.AddHook(WndProc);
private IntPtr WndProc( IntPtr hwnd,
int msg,
IntPtr wParam,
IntPtr lParam,
ref bool handled)
{
switch (msg)
{
case TouchMessage.WM_GESTURE:
OnGesture(lParam);
// 記得將handle設為true,告訴系統你已經處理該訊息
handled = true;
break;
default:
break;
}
return IntPtr.Zero;
}
看完上面的介紹, 我們就來寫一個 WPF + Win7 Gesture 的應用程式, 我們會在WPF Window上繪製一個rectangle, 並透過一些手勢操作來對此rectangle作放大,縮小,平移,旋轉和改變顏色(twp finger tap)
其步驟如下:
其步驟如下:
- 首先,我們會寫一個Win7TouchInterop.cs檔, 目的要將Winuser.h中的 touch和gesture資料作包裝
- 在WPF的程式碼中, 使用Win7TouchInterop的命名空間 using Win7TouchInterop;
- 利用HwndSource物件來取得的Windows訊息
- 處理手勢訊息
程式碼如下所示;
XAML檔 :
<Window x:Class="WPFGestureMessage.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Touch SDK+WPF" Height="600" Width="800"> <Grid> <Grid.Resources> <Storyboard x:Key="_twoFingerTap"> <ColorAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty= "(Rectangle.Fill).(SolidColorBrush.Color)" From="SkyBlue" To="YellowGreen" Duration="00:00:00.5" AutoReverse="true" FillBehavior="HoldEnd" /> </Storyboard> </Grid.Resources> <Rectangle x:Name="rect" RadiusX="5" RadiusY="5" Width="300" Height="200" Stroke="DarkBlue" StrokeThickness="5" Fill="YellowGreen" RenderTransformOrigin="0.5,0.5"> <Rectangle.RenderTransform> <TransformGroup> <ScaleTransform x:Name="_scaleTransform" ScaleX="1" ScaleY="1" /> <RotateTransform x:Name="_rotateTransform" Angle="0" /> <TranslateTransform x:Name="_translateTransform" X="0" Y="0" /> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> </Grid> </Window>
C#檔 :
using System;
using System.Windows;
using Win7TouchInterop;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.Windows.Media.Animation;
namespace WPFGestureMessage
{
///
/// Interaction logic for Window1.xaml
///
public partial class Window1 : Window
{
private IntPtr _hwnd;
const double scaleSensitivityMagicFactor = 200.0;
const double rotateSensitivityMagicFactor = 25.0;
private long _beginScale;
private bool captureAngle;
private double _beginAngle;
private Point firstPoint;
Storyboard _twoFingerTap;
public Window1()
{
InitializeComponent();
Loaded += new RoutedEventHandler(Window1_Loaded);
_twoFingerTap = rect.FindResource("_twoFingerTap") as Storyboard;
}
void Window1_Loaded(object sender, RoutedEventArgs e)
{
_hwnd = new WindowInteropHelper(this).Handle;
HwndSource src = HwndSource.FromHwnd(_hwnd);
if (src != null)
src.AddHook(WndProc);
// Setup the gestures
var gConfig = new[]
{
new GESTURECONFIG
{
dwID = GestureID.GID_ALL,
dwWant = WantGestures.GC_ANY
}
};
Win7TouchMethod.SetGestureConfig(_hwnd,
0,
gConfig.Length,
gConfig,
Marshal.SizeOf(gConfig[0]));
}
private Point ConvertPoint(POINTS pts)
{
var pt = new POINT { X = pts.X, Y = pts.Y };
Win7TouchMethod.ScreenToClient(_hwnd, ref pt);
return new Point(pt.X, pt.Y);
}
private IntPtr WndProc(IntPtr hwnd,
int msg,
IntPtr wParam,
IntPtr lParam,
ref bool handled)
{
switch (msg)
{
case Win7TouchMessages.WM_GESTURE:
OnGesture(lParam);
handled = true;
break;
default:
break;
}
return IntPtr.Zero;
}
private void OnGesture(IntPtr gestureMsg)
{
GESTUREINFO gesture = new GESTUREINFO();
gesture.cbSize = Marshal.SizeOf(gesture);
if (Win7TouchMethod.GetGestureInfo(gestureMsg, out gesture))
{
switch (gesture.dwID)
{
case GestureID.GID_PAN:
OnPan(gesture.dwInstanceID,
ConvertPoint(gesture.ptsLocation),
gesture.dwFlags);
break;
case GestureID.GID_ROTATE:
OnRotate(gesture.ullArguments,
gesture.dwFlags);
break;
case GestureID.GID_TWOFINGERTAP:
_twoFingerTap.Stop();
_twoFingerTap.Begin();
break;
case GestureID.GID_ZOOM:
OnZoom(ConvertPoint(gesture.ptsLocation),
gesture.ullArguments,
gesture.dwFlags);
break;
default:
break;
}
}
Win7TouchMethod.CloseGestureInfoHandle(gestureMsg);
}
private void OnZoom(Point ptCenter,
long scale,
GestureFlags gestureFlags)
{
if ((gestureFlags & GestureFlags.GF_BEGIN) > 0)
{
_beginScale = scale;
}
else
{
double delta = scale - _beginScale;
_beginScale = scale;
double scaleX = (delta / scaleSensitivityMagicFactor);
double scaleY = (delta / scaleSensitivityMagicFactor);
_scaleTransform.ScaleX += scaleX;
_scaleTransform.ScaleY += scaleY;
}
}
private void OnPan(uint id,
Point point,
GestureFlags gestureFlags)
{
if ((gestureFlags & GestureFlags.GF_BEGIN) > 0)
{
firstPoint = point;
}
else
{
Point curPoint = point;
double xDelta = curPoint.X - firstPoint.X;
double yDelta = curPoint.Y - firstPoint.Y;
_translateTransform.X += xDelta;
_translateTransform.Y += yDelta;
firstPoint = curPoint;
}
}
private void OnRotate(double radians,
GestureFlags gestureFlags)
{
double angle = Win7TouchMethod.ROTATE_ANGLE_FROM_ARGUMENT(radians) * (180.0 / Math.PI);
if ((gestureFlags & GestureFlags.GF_BEGIN) > 0)
{
captureAngle = true;
}
else
{
if (captureAngle)
{
_beginAngle = angle;
captureAngle = false;
}
else
{
double delta = angle - _beginAngle;
_rotateTransform.Angle += 0 - (delta / rotateSensitivityMagicFactor);
if ((gestureFlags & GestureFlags.GF_END) > 0)
{
captureAngle = true;
_beginAngle = 0;
}
}
}//end else
}//end
}
}
成果如下圖所示:

[圖 1] 初始畫面

[圖 2] 縮小

[圖 3] 放大

[圖 3] 平移+inertia (因為有開啟GC_PAN_INERIA)

[圖 5] 旋轉

[圖 6] Two finger tap -->改變顏色
參考文章:
Windows 7 Multi-touch Using WPF
Windows 7: Experimenting with Multi-Touch on Windows 7
MSDN -WindowInteropHelper 類別
版主可以提供Win7TouchInterop 類別.cs 嗎? 我現在卡在這邊. 謝謝
回覆刪除