主要內容:
1.指針事件
2.操作事件
1.指針事件
指針事件由各種活動輸入源引發,包括觸摸、觸摸板、筆和鼠標(它們替代傳統的鼠標事件)。指針事件基於單一輸入點(手指、筆尖、鼠標光標),但不支持基於速度的交互。下面是指針事件列表及其相關的事件參數列表:
事件或類 | 描述 |
---|---|
PointerPressed | 單根手指觸摸屏幕時發生。 |
PointerReleased | 該同一觸摸接觸抬起時發生。 |
PointerMoved | 在屏幕上拖動指針時發生。 |
PointerEntered | 在指針進入元素的點擊測試區時發生。 |
PointerExited | 在指針退出元素的點擊測試區時發生。 |
PointerCanceled | 異常丟失觸摸接觸時發生。 |
PointerCaptureLost | 當另一個元素捕獲指針時發生。 |
PointerWheelChanged | 當鼠標滾輪的增量值更改時發生。 |
PointerRoutedEventArgs | 為所有指針事件提供數據。 |
這些事件都是基於UI共同基類UIElement類的事件,對於大多數的UI元素都是適用的,利用上表中的前5個事件就基本可以實現單指操作的各種場景。一般用得最多的是PointerEntered和PointerExited兩個事件。使用時,我們既可以在XAML里面注冊這些事件,也可以在構造函數里面進行注冊。
然后我們在這些事件里通過Debug.WriteLine("觸發PointerXXX事件");打印出如下調試信息:
可以發現依次觸發的事件是PointerEntered、PointerPressed、PointerMoved、PointerReleased、PointerExisted,手指只要在屏幕上稍微滑動一下,就有很多很多的PointerMoved事件被觸發。。。
我們來看實例:
通過在圓上滑動來控制方塊在一個區域的移動。
XAML:在這里使用的布局容器是Canvas,便於后面方塊位置的控制。
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,24,0,28"> <TextBlock x:Name="ApplicationTitle" Text="鼠標事件" FontSize="20"/> <Ellipse Height="100" Width="100" Fill="Red" Name="ellipse1" /> </StackPanel> <Canvas x:Name="canvas" Background="DimGray" Height="400" Width="320" Grid.Row="1"> <Rectangle x:Name="rect" Fill="BurlyWood" Canvas.Left="140" Canvas.Top="180" Height="40" Width="40"></Rectangle> </Canvas>
C#代碼:判斷是點擊操作還是滑動操作,以及左滑右滑等是固定模式的代碼,能理解就理解,不用刻意琢磨。還需注意如何獲取控件在Canvas里的位置以及把方塊限定在一個區域內。
private void ellipse1_PointerExited(object sender, PointerRoutedEventArgs e) { Debug.WriteLine("觸發PointerExited事件"); Point end = e.GetCurrentPoint(ellipse1).Position; double angle = 0; double verticalDistance = (double)rect.GetValue(Canvas.TopProperty); double horizontalDistance = (double)rect.GetValue(Canvas.LeftProperty); if (Math.Abs(end.X - start.X) < 1 && Math.Abs(end.Y - start.Y) < 1) { angle = 0; } else if (end.X > start.X) { if (end.Y > start.Y) { angle = 360 - Math.Atan((end.Y - start.Y) * 1.0 / (end.X - start.X)) * 180 / Math.PI; } else { angle = Math.Atan((start.Y - end.Y) * 1.0 / (end.X - start.X)) * 180 / Math.PI; } } else if (end.X < start.X) { if (end.Y > start.Y) { angle = Math.Atan((end.Y - start.Y) * 1.0 / (start.X - end.X)) * 180 / Math.PI + 180; } else { angle = 180 - Math.Atan((start.Y - end.Y) * 1.0 / (start.X - end.X)) * 180 / Math.PI; } } if (angle == 0) { //點擊操作 } else if (angle >= 45 && angle < 135) { // 滑動操作:從下往上 if (verticalDistance >0) Canvas.SetTop(rect, (double)rect.GetValue(Canvas.TopProperty)-20); } else if (angle <= 45 || angle > 315) { // 滑動操作:從左向右滑 if (horizontalDistance < canvas.Width-40) Canvas.SetLeft(rect, (double)rect.GetValue(Canvas.LeftProperty) + 20); } else if (angle >= 135 && angle < 225) { //滑動操作:從右向左滑 if (horizontalDistance >0) Canvas.SetLeft(rect, (double)rect.GetValue(Canvas.LeftProperty) - 20); } else if (angle >= 225 && angle < 315) { //滑動操作:從上往下 if (verticalDistance <canvas.Height-40) Canvas.SetTop(rect, (double)rect.GetValue(Canvas.TopProperty) + 20); } } private void ellipse1_PointerEntered(object sender, PointerRoutedEventArgs e) { start = e.GetCurrentPoint(ellipse1).Position; }
來一個很小的預覽圖意思一下:
簡單的實現一個塗鴉板
XAML:定義了一個Canvas、三個AppBarButton。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Canvas Name="canvas" Background="Transparent" PointerEntered="canvas_PointerEntered" PointerPressed="canvas_PointerPressed" PointerMoved="canvas_PointerMoved" PointerExited="canvas_PointerExited"> </Canvas> </Grid> <Page.BottomAppBar> <CommandBar IsOpen="False"> <CommandBar.PrimaryCommands> <AppBarButton Icon="Back" Label="撤銷" Click="AppBarButton_Click"></AppBarButton> <AppBarButton Icon="FontColor" Label="顏色"> <AppBarButton.Flyout> <Flyout x:Name="colorflyout"> <ListView x:Name="colorlv" IsItemClickEnabled="True" ItemClick="colorlv_ItemClick" > <ListView.ItemTemplate> <DataTemplate> <TextBlock FontSize="12" Text="{Binding}"></TextBlock> </DataTemplate> </ListView.ItemTemplate> </ListView> </Flyout> </AppBarButton.Flyout> </AppBarButton> <AppBarButton Icon="FontSize" Label="字體"> <AppBarButton.Flyout> <Flyout x:Name="fontflyout" > <Slider x:Name="fontslider" Width="100" Minimum="2" Maximum="15" SmallChange="1"
Orientation="Horizontal" ValueChanged="Slider_ValueChanged" ></Slider> </Flyout> </AppBarButton.Flyout> </AppBarButton> </CommandBar.PrimaryCommands> </CommandBar> </Page.BottomAppBar>
C#代碼:
下面這段代碼主要用來畫線,涉及到PointerPressed和PointerMoved事件。
private void canvas_PointerPressed(object sender, PointerRoutedEventArgs e) { currentPoint = e.GetCurrentPoint(canvas).Position; lastPoint = currentPoint; } private void canvas_PointerMoved(object sender, PointerRoutedEventArgs e) { currentPoint = e.GetCurrentPoint(canvas).Position; Line line = new Line() { X1 = currentPoint.X, Y1 = currentPoint.Y, X2 = lastPoint.X, Y2 = lastPoint.Y }; line.Stroke = new SolidColorBrush(color); line.StrokeThickness = fontsize; line.StrokeLineJoin = PenLineJoin.Round; line.StrokeStartLineCap = PenLineCap.Round; line.StrokeEndLineCap = PenLineCap.Round; this.canvas.Children.Add(line); lastPoint = currentPoint; lineCount++; }
接着,我們考慮一下,該如何刪除最后畫在屏幕上的那條線呢?你可能會想到這句代碼this.canvas.Children.RemoveAt(this.canvas.Children.Count - 1);(如果用這句代碼,你可能要點很多下撤銷按鈕才能刪除最后那條線),然而我們從上面可以知道,只要手指有滑動,就會不斷觸發PointerMoved事件,每次觸發這個事件都會畫一條線,只是那些線都首尾相接了,所以我們最后畫在屏幕上的那條線其實是一系列的線首尾相接而成。具體的刪除方法,請看下面的代碼:
private void canvas_PointerEntered(object sender, PointerRoutedEventArgs e) { lineCount = 0; } private void canvas_PointerExited(object sender, PointerRoutedEventArgs e) { lineCountList.Add(lineCount); } private void AppBarButton_Click(object sender, RoutedEventArgs e) { if (lineCountList.Count > 0) { int lines = lineCountList[lineCountList.Count - 1]; for (int i = 0; i < lines; i++) { this.canvas.Children.RemoveAt(this.canvas.Children.Count - 1); } lineCountList.RemoveAt(lineCountList.Count - 1); } }
lineCount是定義在MainPage類中的變量(用來記錄在PointerMoved中畫的線的條數,也就是我們最近在屏幕上畫的那條線實際包含線的條數),我們在PointerEntered給lineCount賦初值0,然后在PointerMoved里面執行加1操作,最后在PointerExited事件中將lineCount添加到集合lineCountList中進行保存,然后在AppBarButton_Click中進行具體的刪除。至於一下刪除所有,那就直接 canvas.Children.Clear();
顏色設置和字號設置的代碼很簡單就不貼了,下面來看圖吧。我們這個示例最好在手機上調試,PC的話,只要鼠標在應用程序的區域移動就會畫線,有點煩人。
2.操作事件
如果需要在應用中支持多個手指或速度數據的交互,則要使用操作事件。我們還可以使用操作事件來檢測拖動、縮放和按住之類的交互。下表是操作事件及其相關的事件參數。
事件或類 | 描述 |
---|---|
ManipulationStarting event | 首次創建操作處理器時發生。 |
ManipulationStarted event | 當輸入設備在 UIElement 上開始操作時發生。 |
ManipulationDelta event | 當輸入設備在操作期間更改位置時發生。 |
ManipulationInertiaStarting event | 在操作過程中,當延遲開始時,如果輸入設備與 UIElement 對象失去聯系,則會發生。 |
ManipulationCompleted event | 當 UIElement 上的操作和延遲完成時發生。 |
ManipulationStartingRoutedEventArgs | 提供 ManipulationStarting 事件的數據。 |
ManipulationStartedRoutedEventArgs | 提供 ManipulationStarted 事件的數據。 |
ManipulationDeltaRoutedEventArgs | 提供 ManipulationDelta 事件的數據。 |
ManipulationInertiaStartingRoutedEventArgs | 提供 ManipulationInertiaStarting 事件的數據。 |
ManipulationVelocities | 描述操作發生的速度。 |
ManipulationCompletedRoutedEventArgs | 提供 ManipulationCompleted 事件的數據。 |
我們的手勢事件是由一系列操作事件組成。每個手勢都是從ManipulationStarted事件開始,當用戶觸摸屏幕時,將會觸發一個或多個ManipulationDelta事件,當手勢完成用戶手指離開屏幕時,會觸發ManipulationCompleted事件。ManipulationStarting事件則是Manipulation系列事件最開始觸發的事件,ManipulationInertialStarting事件則是在ManipulationDelta事件觸發的過程中,如果有延遲則會發生,並不是必然會觸發的事件。如下圖所示:
在這些事件中,最重要的一個事件就是ManipulationDelta事件,相關手勢的邏輯判斷都要依賴於這個事件,而在一次觸控當中,這個事件又可能被觸發多次,和上面提到的PointerMoved事件類似,所以處理起來還是比較麻煩的。ManipulationDelta事件通過參數ManipulationDeltaRoutedEventArgs對象來傳遞相關的觸摸和滑動信息,我們可以轉到定義看一下這個類它所提供的一些屬性,主要關注ManipulationDelta類型的兩個屬性Cumulative和Delta,Cumulative記錄自開始操作之后的全部更改,Delta則是當前操作的最近更改。ManipulationDelta類,我們也可以轉到定義看一下, 有System.Single(單精度浮點類型)類型的三個參數:Expansion(觸摸觸點間的距離更改,以DIP表示)、Rotation(旋轉角度的變化,以度為單位)、Scale(觸摸觸點間的距離更改,以百分比表示)和Point類型的Translation(表示平移距離)。
另外,我們在使用Manipulation系列事件的時候,還需要設置ManipulationMode屬性,其值為ManipulationModes枚舉,具體的枚舉值我們可以轉到定義進行查看。
(實例實例,,,,請看下回。。。。)
操作事件,尤其是ManipulationDelta事件用得還是比較多的,然后這一部分的內容也是有一定的難度,需要重點掌握一下。由於准備不足,這部分的內容下次再繼續討論咯。好了,晚安!
本次內容的Demo鏈接:http://pan.baidu.com/s/1jHaa2BO 密碼:5btc