简体   繁体   中英

How can I make my Rectangle react to touch events like it does for mouse/pointer events?

On my MainPage, I have a Stackpanel full of multicolored Rectangles, and they all start with Margin set to (0,5,0,5). I would like to allow the user to move these rectangles horizontally only. Since Rectangles do not have Mouse events, I use the Pointer to achieve this. The flow is: user taps on a Rectangle to select it, cursor changes to give user visual cue, user drags Rectangle left or right. (Note that the Stackpanel does have a vertical scrollviewer, if that matters). Here is my C# code:

private void Rectangle_FirstClick(object sender, EventArgs e)
{
    // Give rectangle a white border to imply selection
    Rectangle myControl = sender as Rectangle;
    myControl.StrokeThickness = 1;
    myControl.Stroke = new SolidColorBrush(Color.FromArgb(255, 240, 240, 240));

    // Provide visual cue to mouse users by changing the pointer immediately
    Window.Current.CoreWindow.PointerCursor = new Windows.UI.Core.CoreCursor(Windows.UI.Core.CoreCursorType.SizeWestEast, 0);

    // Handle mouse leaving/re-entering the track piece
    myControl.PointerEntered += MyControl_PointerEntered;
    myControl.PointerExited += MyControl_PointerExited;

    // Handle the drag event
    myControl.PointerPressed += MyControl_PointerPressed;
    myControl.PointerReleased += MyControl_PointerReleased;
    myControl.PointerMoved += MyControl_PointerMoved;
}

Here are the individual event handlers

bool PossibleDragStart = false;
Point InitialDragPoint = new Point(0, 0);

private void MyControl_PointerExited(object sender, PointerRoutedEventArgs e)
{
    Window.Current.CoreWindow.PointerCursor = new Windows.UI.Core.CoreCursor(Windows.UI.Core.CoreCursorType.Arrow, 0);
}

private void MyControl_PointerEntered(object sender, PointerRoutedEventArgs e)
{
    Window.Current.CoreWindow.PointerCursor = new Windows.UI.Core.CoreCursor(Windows.UI.Core.CoreCursorType.SizeWestEast, 0);
}

private void MyControl_PointerPressed(object sender, PointerRoutedEventArgs e)
{
    PossibleDragStart = true;
    InitialDragPoint = e.GetCurrentPoint(sender as Rectangle).Position;
}

private void MyControl_PointerReleased(object sender, PointerRoutedEventArgs e)
{
    PossibleDragStart = false;
}

private void MyControl_PointerMoved(object sender, PointerRoutedEventArgs e)
{
    // If pointer pressed without release, and then moved, we are dragging
    if (PossibleDragStart)
    {
        Point CurrentDragPoint = e.GetCurrentPoint(AudioTracksRightContainer).Position;
        StatusBar.Text = CurrentDragPoint.X.ToString();

        // Calculate drag offset by subtracting Current Position from Initial Position
        double SegmentMargin = CurrentDragPoint.X - InitialDragPoint.X;

        // Move the Rectangle with the pointer drag
        Rectangle trackSegment = sender as Rectangle;
        trackSegment.Margin = new Thickness(SegmentMargin, 5, 0, 5);
    }
}

This code works great for the mouse, but when I try to use it for touch, I have to press-and-hold to initiate drag, and even then, the rectangle only moves a few units before staying put. I'm not sure why the behaviors are different.

Another issue with this code is that, if the rectangle is narrow enough such that when I drag quickly and the rectangle can't keep up with the cursor, it stops dragging when the cursor has left the rectangle boundary. A much smaller issue than the touch not working, but a little annoying nonetheless.

It sounds like the ScrollViewer is capturing the touch events to do its own scrolling. Insead of handling the manipulations manually you can use Xaml's manipulation engine to register for manipulation events. This is both easier to code and more efficient. It will work for touch and mouse by default.

<Rectangle Margin="5" Fill="Orange" Height="100" Width="200"
           ManipulationMode="TranslateX"
           ManipulationDelta="Rectangle_ManipulationDelta"
           >
    <Rectangle.RenderTransform>
        <CompositeTransform />
    </Rectangle.RenderTransform>
</Rectangle>

In the Rectangle_ManipulationDelta method translate the rectangle:

private void Rectangle_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
    UIElement elem = sender as UIElement;
    CompositeTransform ct = elem.RenderTransform as CompositeTransform;

    ct.TranslateX += e.Delta.Translation.X;
}

If you only want translations you could use a TranslateTransform instead of a CompositeTransform, or you could add in rotations and scaling.

See Quickstart: Touch input (XAML) for more on using gestures and manipulation events.

I discuss the ScrollViewer taking over pointer handling in my blog entry Where did all my gestures go? If you really want to handle the pointer events yourself I discuss how to disable the ScrollViewer during dragging. You can handle the case where the finger moves faster than the target by capturing the pointer . This isn't needed if you handle the manipulation events.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM