之前看到老大的一個QQ簽名,說想寫一個WP的程序,可是后來,后來就沒有后來了。我去年打算學一下WP程序開發的,一年了也無任何進展,我可不想后來,后來就沒有后來。於是抽時間來接觸一下。雖然都是用CShsarp,雖然都是拖控件,但是xaml不熟悉,界面的布局也不熟悉,就連手機的環境也不熟悉。以前寫winform程序時知道鼠標單擊事件是Click,鍵盤按鍵就keypress等。可是WP的控件沒有Click。於是這回就熟悉一下WP的重力感應,觸控和加速這三方面。
方向處理
在使用WP的時候就會發現,一個長方形的屏幕,有時候它的內容是豎屏顯示的,像開始菜單,QQ;有些內容是橫屏顯示的,像視頻的播放;有些內容是隨着重力傳感器傳來的信息自動切換橫豎屏顯示的,像一部分系統的頁面:信息啊,郵件啊,IE瀏覽器啊。當我們默認建立一個頁面的時候,無論怎么翻轉模擬器,它只能是豎屏顯示。此時需要設置PhoneApplicationPage標記下的SupportedOrientations屬性就可以了,這個屬性是一個枚舉,有三個值Portrait(豎屏),Landscape(橫屏)和PortraitOrLandscape(橫豎屏切換)。默認是Portrait。
在這個橫豎屏切換到的過程中,頁面上某些容器的大小Size會發生變化,例如整個頁面的LayoutRoot標題部分的TitlePanel還有頁面內容部分的ContentPanel。不過這些變化其實挺直觀的。
在方向變換的時候,都會觸發一個事件OrientationChanged,PhoneApplicationPage和PhoneApplicationFrame都擁有這個事件。當然除了通過給這個事件綁定方法外,還可以重寫PhoneApplicationPage的OnOrientationChanged(一般都這樣,每個事件多半會有個On作前綴的虛方法)。
例如在PhoneApplicationPage節點上綁定事件
OrientationChanged="PhoneApplicationPage_OrientationChanged"
方法定義這樣
1 private void PhoneApplicationPage_OrientationChanged(object sender, OrientationChangedEventArgs e) 2 { 3 this.txtDirection.Text = "Contentpanel\r\n" + ContentPanel.RenderSize.Width + " * " + ContentPanel.RenderSize.Height; 4 }
運行的效果是
觸控
觸屏手機相比起以前的按鍵手機而言,在人機交互方面就提升了不少用戶體驗。用戶直接點擊屏幕(類似使用電腦時用鼠標點擊)來輸入信息代替了啪啪啪地按手機鍵盤。
在WP里面處理觸控的有兩個層面,一個是底層的Touch類的靜態事件FrameReported;另一個是高層的UIElement類的Manipulation事件組。為啥說它是事件組,其實它是由三個事件組成:ManipulationStart、ManipulationDelta和ManipulationCompleted。那么下面就分別嘗試一下兩種方式的觸控處理
若要使用Touch的FrameReported,只能在相應的地方加上一下的代碼
Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);
我自己呢就那這個事件綁定的放在MainPage的構造函數里面。綁定的方法如下,其效果是點擊屏幕上的任意位置,該位置的坐標點會顯示在頁面標題的地方
void Touch_FrameReported(object sender, TouchFrameEventArgs e) { TouchPoint tp= e.GetTouchPoints(null).First(); this.PageTitle.Text = tp.Position.ToString(); }
可以看出Touch的FrameReported是個靜態事件,那么一旦注冊了這個事件,在程序上別的頁面都會觸發這個事件了。所以用的時候這一點要注意。
現在看會來事件的參數TouchFrameEventArgs,它里面有三個可調動的方法
方法名 |
參數 |
返回 |
描述 |
GetTouchPoints |
UIElement |
TouchPointCollection |
獲取相對UIElement於的多點觸控的TouchPoint集合 |
GetPrimaryTouchPoint |
UIElement |
TouchPoint |
獲取相對UIElement的主觸控點的TouchPoint |
SuspendMousePromotionUntilTouchUp |
- |
void |
掛起把觸控事件提升為鼠標事件 |
這個表格里頭有幾點又要說明一下
第一就是那個SuspendMousePromotionUntilTouchUp方法的,原本的觸摸事件提升為原本Silverlight桌面版本的鼠標事件的方法,也就是那個Moues的方法組。當調用了SuspendMousePromotionUntilTouchUp之后,綁定在控件上的Mouse事件就不會觸發了。
例如這里有一個TextBlock
<TextBlock x:Name="txtDirection" Text="Hello world" Height="64" Width="159" VerticalAlignment="Center" HorizontalAlignment="Center" SizeChanged="txtDirection_SizeChanged" MouseLeftButtonDown="txtDirection_MouseLeftButtonDown"></TextBlock>
txtDirection_MouseLeftButtonDown方法就是show一個MessageBox,說明單擊了鼠標左鍵。
但是如果調用了SuspendMousePromotionUntilTouchUp方法之后,那個MessageBox就不會顯示出來,調用的方式如下
void Touch_FrameReported(object sender, TouchFrameEventArgs e) { TouchPoint tp = e.GetPrimaryTouchPoint(null); if (tp != null && tp.Action == TouchAction.Down) e.SuspendMousePromotionUntilTouchUp(); }
第二點就是前兩個獲取觸控點的方法,獲取的點都是相對於傳入的UIElement的點的坐標,那個UIElement就是一個參照點,假如傳入的參數是null的,參照點則是正向(三個按鍵在下面的豎屏方向)的左上角了。
第三點就是介紹一下返回的觸控點TouchPoint實例,它有以下4個只讀屬性,
屬性名稱 |
類型 |
描述 |
Action |
TouchAction |
描述特定觸控點的操作,包括Up(抬起),Move(拖動),Down(放下) |
Position |
Point |
相對於參考元素左上角的位置 |
Size |
Size |
接觸的面積 |
TouchDevice |
TouchDevice |
生成這個TouchPoint的觸摸設備,通過其DirectlyOver屬性可以獲得手指下最頂層的元素 |
接着到Manipulation事件組了
Manipulation的三個事件名稱眼看都能知道他們觸發的時機,下面則單通過Manipulation事件來介紹,TextBlock按如下定義
<TextBlock x:Name="txtDirection" Text="Hello world" Height="364" Width="359" VerticalAlignment="Center" HorizontalAlignment="Center" ManipulationStarted="txtDirection_ManipulationStarted"></TextBlock>
綁定的方法如下
private void txtDirection_ManipulationStarted(object sender, ManipulationStartedEventArgs e) { double destinct = Math.Sqrt(e.ManipulationOrigin.X * e.ManipulationOrigin.X + e.ManipulationOrigin.Y * e.ManipulationOrigin.Y); Brush brush=new SolidColorBrush(Color.FromArgb(255,(byte)destinct,0,0 )); (e.OriginalSource as TextBlock).Foreground=brush; e.Complete(); e.Handled = true; }
這個事件參數ManipulationStartedEventArgs其實繼承RoutedEventArgs傳遞了事件的觸發源元素和觸控的位置等信息,其中的ManipulationContainer和RoutedEventArgs的OriginalSource都是獲取觸發事件的對象,在這里的話他們都是跟sender一樣引用這同一個對象。ManipulationStartedEventArgs的ManipulationOrigin其實跟TouchPoint的Position一樣。而Handled屬性和Complete()方法都是告知系統觸控處理已經完畢了,不需要在處理Manipulation事件了。這里其實還涉及到WPF里面的事件冒泡方面的知識。
加速計
加速計是位於WP手機內部的一個小硬件,用於測量力的。一個很簡單的例子,在重力場中的WP手機,這個加速計會響應WP手機的重力。它可以告知力的大小和方向,並通過時間換算出加速度。這個力是一個矢量,在這里是通過一個數學的空間直角坐標系來描述。那下面則通過一個demo使用這個加速計。
xaml文件的內容同樣是這樣子
<TextBlock x:Name="txtDirection" Text="Hello world" Height="364" Width="359" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
在MainPage的構造函數里面調用加速計
Accelerometer acc = new Accelerometer(); acc.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(acc_ReadingChanged); try { acc.Start(); } catch (Exception ex) { txtDirection.Text = ex.Message; }
當加速計有變化的時候就會觸發ReadingChanged事件,該事件的會傳入一個AccelerometerReadingEventArgs類型的實例作參數,里面有加速度的向量表示以及時間。
void acc_ReadingChanged(object sender, AccelerometerReadingEventArgs e) { StringBuilder sb = new StringBuilder(); sb.AppendFormat("X={0:F2}\r\n", e.X); sb.AppendFormat("Y={0:F2}\r\n", e.Y); sb.AppendFormat("Z={0:F2}\r\n", e.Z); sb.AppendFormat("Time={0}\r\n", e.Timestamp); txtDirection.Dispatcher.BeginInvoke(delegate() { txtDirection.Text = sb.ToString(); }); }
結果如圖所示
可惜只是在模擬器上運行,試不出真正效果,模擬器的加速度一直都是(0,0,-1)。
由於時間不是停止的,所以只要加速計還在運行(就是不調用Stop)ReadingChanged事件一直會觸發,acc_ReadingChanged一直在執行。而這個加速計貌似是開了另一個線程的,所以執行ReadingChanged事件時會涉及到線程安全的問題,就像平時在寫WinForm多線程程序時,從非窗體線程調用窗體的界面時,都要用Invoke 方法,這里就用BeginInvoke方法。
以上其實基本上是讀書筆記,有什么意見或建議不妨@一下。謝謝!