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 嗎? 我現在卡在這邊. 謝謝
回覆刪除