2010年2月24日 星期三

Silverlight 3.0 + Multi-touch

Silverlight是微軟最新的Web用戶介面技術,它能夠跨流覽器、跨平臺運行.
Silverlight 3.0 上也支援多點觸控功能 , 但目前功能還是相當有限, 只能取得觸控點的資料,
若要想要有手勢的功能的話, 需要透過觸控點另外作計算.


Silverlight的多點觸控架構如下:



從上圖可知, 要寫一個Silverlight的觸控程式, 必要元素要有: Touch Device + Windows 7 + IE 8

Windows Touch API 不一樣的地方, Silverlight 不需要註冊RegisterTouchWindow, 是以整個應用程式為範圍來註冊多點觸控事件, 而不是在UIElement 加入觸控事件(WPF).

撰寫Silverlight multi-touch application的流程如下:

(1)註冊多點觸控的處理常式 - TouchFrameReported(它是一個static function)
Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);

針對多點觸控所發生的事件,Silverlight預設將多點觸控事件提升為滑鼠事件, 若不希望升級成滑鼠事件, 可以呼叫 SuspendMousePromotionUntilTouchUp

(2)取得觸控點的資料
TouchPoint GetPrimaryTouchPoint(UIElement relativeTo) --> 取得主要觸控點資料
TouchPointCollection GetTouchPoints(UIElement relativeTo) --> 取得所有觸控點資料

傳入一個UIElement將回傳相對偏移的 Position 產生包含相對座標的 TouchPoint.
如果想要獲得觸控點的的絕對座標, 請將 relativeTo 指定為 null.

觸控點的其他資訊如下所示;

example code:
foreach (TouchPoint tp in e.GetTouchPoints(_canvas))
{
// 取得作業系統針對特定觸控裝置所提供的唯一 ID
tp.TouchDevice.Id;

// 取得當觸控點產生時,滑鼠指標所停留的最上層 UIElement 物件
tp.TouchDevice.DirectlyOver

// 取得觸控點的 X, Y 座標位置(相當於觸控區域的中心點位置)
tp.Position;

// 取得觸控點接觸區域(其區域為一個矩形)
tp.Size;
}


(3) 識別觸控的動作 -TouchAction
觸控點的動作可分成下列3種:


TouchAction.Down: 來自 TOUCHEVENTF_DOWN 訊息的觸控動作
TouchAction.Move: 來自 TOUCHEVENTF_MOVE 訊息的觸控動作

TouchAction.Up:來自 TOUCHEVENTF_UP 訊息的觸控動作


下圖為利用Silverlight開發的繪圖板. 因此在網頁上也可利用觸控作繪圖.




P.S: 若要使用Silverlight 來作為開發工具. 如果您沒有 Visual Web Developer,請使用 Web Platform Installer,如果您已有 Visual Studio, 請直接下載這個工具 ( Silverlight 3 SDK )

參考文章:
Silverlight 3 Multi-touch: The Basics
Silverlight 3 中的multi-Touch程式開發
Silverlight 3 Multi-Touch with Windows 7 and HP Touch Smart

2009年11月18日 星期三

WPF + Windows7 Multi-touch (Part 3)

目前在WPF中使用Multi-touch功能,必須透過P/Invoke的方式呼叫Win32 Touch API的功能(可參考這篇文章 : WPF + Windows7 Multi-touch (Part 2) ), 或是可以直接使用微軟提供的Windows 7 Multitouch .NET Interop Sample Library, 它提供 WPF WinForms 3.5 SP1 要開發Multi-Touch 程式所需的功能, 不過利用上述這些方法開發觸控功能時, 還是有稍嫌複雜, 也不是很直覺.

因此在WPF 4.0 已經將一些觸控功能放到UIElement, UIElement3DContentElement中,
Beta 1中, 將支援高階操作的觸控功能(Manipulation), 如下所示:
而在Beta 2 中, 除了原本高階操作的觸控功能(Manipulation)外, 還提供的Touch相關的event,如下所示:

下面我們將利用WPF 4.0 Beta2 Touch功能, 撰寫一個顯示目前觸控點的應用程式, 其步驟如下:

軟體和硬體需求
程式碼如下:

XAML檔的部份:


<Window x:Class="WPFTouchPoint.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="800" Width="800"
TouchDown="Window_TouchDown"
TouchMove="Window_TouchMove"
TouchUp="Window_TouchUp">
<Canvas Background="Black">
<Ellipse
Canvas.Left="0"
Canvas.Top="0"
Name="Touch1"
Stroke="Black"
Height="60"
Width="60"
Fill="LightGreen"
Visibility="Hidden">
<Ellipse.BitmapEffect>
<DropShadowBitmapEffect ShadowDepth="10"
Direction="270"
Color="White"
Opacity="0.5"
Softness="0.25"/>
</Ellipse.BitmapEffect>
</Ellipse>

<Ellipse
Canvas.Left="0"
Canvas.Top="0"
Name="Touch2"
Stroke="Black"
Height="60"
Width="60"
Fill="LightBlue"
Visibility="Hidden">
<Ellipse.BitmapEffect>
<dropshadowbitmapeffect shadowdepth="10" direction="270" color="White"
opacity="0.5" softness="0.25">
</dropshadowbitmapeffect>
</Ellipse>
</Canvas>
</Window>


C#檔的部分:


namespace WPFTouchPoint
{
///
/// Interaction logic for MainWindow.xaml
///

public partial class MainWindow : Window
{

private int Touch1ID = 0; // id for first touch contact
private int Touch2ID = 0; // id for second touch contact

public MainWindow()
{
InitializeComponent();
}


private void Window_TouchDown(object sender, TouchEventArgs e)
{
TouchPoint p = e.GetTouchPoint(this);

if (Touch1ID == 0)
{
// Show the touch point
Touch1.Visibility = Visibility.Visible;

Touch1ID = e.TouchDevice.Id;

// move the ellipse to the given location
Touch1.SetValue(Canvas.LeftProperty, p.Position.X - Touch1.Width / 2);
Touch1.SetValue(Canvas.TopProperty, p.Position.Y - Touch1.Height / 2);
}
else if (Touch2ID == 0)
{
Touch2.Visibility = Visibility.Visible;

Touch2ID = e.TouchDevice.Id;
// move the ellipse to the given location
Touch2.SetValue(Canvas.LeftProperty, p.Position.X - Touch2.Width / 2);
Touch2.SetValue(Canvas.TopProperty, p.Position.Y - Touch2.Height / 2);
}

}

private void Window_TouchMove(object sender, TouchEventArgs e)
{
TouchPoint p = e.GetTouchPoint(this);
// determine which contact this belongs to
if (Touch1ID == e.TouchDevice.Id)
{
Touch1.SetValue(Canvas.LeftProperty, p.Position.X - Touch1.Width / 2);
Touch1.SetValue(Canvas.TopProperty, p.Position.Y - Touch1.Height / 2);
}
else if (Touch2ID == e.TouchDevice.Id)
{
Touch2.SetValue(Canvas.LeftProperty, p.Position.X - Touch2.Width / 2);
Touch2.SetValue(Canvas.TopProperty, p.Position.Y - Touch2.Height / 2);
}
}

private void Window_TouchUp(object sender, TouchEventArgs e)
{
if (e.TouchDevice.Id == Touch1ID)
{
Touch1.Visibility = Visibility.Hidden;

Touch1ID = 0;
}
else if (e.TouchDevice.Id == Touch2ID)
{
Touch2.Visibility = Visibility.Hidden;

Touch2ID = 0;
}

}
}
}

顯示結果如下所示:


[圖 1] Multi-touch points in WPF4.0 Beta 2


不過目前這些觸控功能在Beta版還是有很多bug, 可參考這些文章測試出來的結果:
Multi-touch in WPF4.0 Beta2

WPF 4, Beta 2 expands multi-touch API but is buggy


參考文章:
Windows 7: Experimenting with Multi-Touch on Windows 7 ( Part 5 )

Windows using Multi-touch using WPF

Multi-touch in WPF4.0 and VS2010


Introduction to WPF 4 Multitouch


Walkthrough: Creating Your First Touch Application

What's New in WPF Version 4

2009年10月18日 星期日

WPF + Windows7 Multi-touch (Part 2)

WPF + Windows7 Multi-touch (Part 1) 這篇文章中, 我們可以透過 P/ Invoke 的方式來取得Windows 7 的手勢訊息, 並對物體作手勢操作.

不過當你用這個方法來取得觸控點的資訊時, 你會發現收不到WM_TOUCH 的訊息, 這個是因為WPF團隊還沒把這部分加進去(這篇
WPF Windows 7 Multi-touch.文章中, WPF團隊有回答此問題)

因此, 如果要在WPF程式中取得觸控點的資訊的話, 就必須採用下面步驟:

  • 新增一個"MicrosoftTabletPenServiceProperty"屬性到視窗上
             IntPtr hWnd = new WindowInteropHelper(this).Handle;
HwndSource src = HwndSource.FromHwnd(hWnd);
Win7TouchMethod.SetProp(src.Handle,
"MicrosoftTabletPenServiceProperty",
new IntPtr(0x01000000));

  • 新增StylusDown/StylusUp/StylusMove的事件, 來處理觸控的相關事件.

this.StylusDown += new StylusDownEventHandler(Window1_StylusDown);
this.StylusMove += new StylusEventHandler(Window1_StylusMove);
this.StylusUp += new StylusEventHandler(Window1_StylusUp);

下面的範例中, 我們會寫一個WPF的觸控應用程式, 當有觸控發生時, 畫面上會出現圓點(用來表示觸控點的位置)

source code如下:

<Window x:Class="WPFTouch.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="600" Width="800">
<Canvas>
<Ellipse
Canvas.Left="0"
Canvas.Top="0"
Name="Touch1"
Stroke="Black"
Height="30"
Width="30"
Fill="Blue"
Visibility="Hidden"/>

<Ellipse
Canvas.Left="0"
Canvas.Top="0"
Name="Touch2"
Stroke="Black"
Height="30"
Width="30"
Fill="Green"
Visibility="Hidden"/>
</Canvas>

</Window>


using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using Win7TouchInterop;

namespace WPFTouch
{
public partial class Window1 : Window
{

private int Touch1ID = 0; // id for first touch contact
private int Touch2ID = 0; // id for second touch contact

public Window1()
{
InitializeComponent();

Loaded += new RoutedEventHandler(Window1_Loaded);

// Add StylusDown/StylusMove/StylusUp events to handle the touch related events
this.StylusDown += new StylusDownEventHandler(Window1_StylusDown);
this.StylusMove += new StylusEventHandler(Window1_StylusMove);
this.StylusUp += new StylusEventHandler(Window1_StylusUp);
}

void Window1_Loaded(object sender, RoutedEventArgs e)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
HwndSource src = HwndSource.FromHwnd(hWnd);

Win7TouchMethod.SetProp(src.Handle,
"MicrosoftTabletPenServiceProperty",
new IntPtr(0x01000000));

}


void Window1_StylusDown(object sender, StylusDownEventArgs e)
{
Point p = e.GetPosition(this); // get the location for this contact


if (Touch1ID == 0)
{
// Show the touch point
Touch1.Visibility = Visibility.Visible;

Touch1ID = e.StylusDevice.Id;

// move the ellipse to the given location
Touch1.SetValue(Canvas.LeftProperty, p.X - Touch1.Width / 2);
Touch1.SetValue(Canvas.TopProperty, p.Y - Touch1.Height / 2);
}
else if (Touch2ID == 0)
{
Touch2.Visibility = Visibility.Visible;

Touch2ID = e.StylusDevice.Id;
// move the ellipse to the given location
Touch2.SetValue(Canvas.LeftProperty, p.X - Touch2.Width / 2);
Touch2.SetValue(Canvas.TopProperty, p.Y - Touch2.Height / 2);
}
}

void Window1_StylusMove(object sender, StylusEventArgs e)
{
Point p = e.GetPosition(this);
// determine which contact this belongs to
if (Touch1ID == e.StylusDevice.Id)
{
Touch1.SetValue(Canvas.LeftProperty, p.X - Touch1.Width / 2);
Touch1.SetValue(Canvas.TopProperty, p.Y - Touch1.Height / 2);
}
else if (Touch2ID == e.StylusDevice.Id)
{
Touch2.SetValue(Canvas.LeftProperty, p.X - Touch2.Width / 2);
Touch2.SetValue(Canvas.TopProperty, p.Y - Touch2.Height / 2);
}
}

void Window1_StylusUp(object sender, StylusEventArgs e)
{
if (e.StylusDevice.Id == Touch1ID)
{
Touch1.Visibility = Visibility.Hidden;

Touch1ID = 0;
}
else if (e.StylusDevice.Id == Touch2ID)
{
Touch2.Visibility = Visibility.Hidden;

Touch2ID = 0;
}
}
}
}


執行畫面如下:


[圖 1] WPF Touch Application

參考文章:
Windows 7 Multi-touch using WPF