Windows 10開發基礎——指針事件和操作事件(一)


主要內容:

     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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM