Dragging a control without lifting pointer
-
quinta-feira, 2 de agosto de 2012 11:38
I've created a usercontrol that's draggable via the ManipulationDelta event. Basically, the control has a TranslateTransform object set to its RenderTransform property, and whenever the ManipulationDelta event fires I add the e.Delta.Translation.X and Y values to the translateTransform's X and Y properties. This works flawlessly, I can drag the control (basically just an image) around with inertia and everything.
However, now I wish for the control to spawn under the pointer (cursor, finger, stylus..) when another control is clicked/tapped. The idea is that you touch a large image, and then a smaller version of that image appears under your finger/cursor that you can then drag around. What I've done is add the draggable control to the page, setting visibility to collapsed. I've also added an event handler to the large image's PointerPressed event that will reposition the draggable control to a position under the pointer, and then sets its visibility to visible:
if (draggableImage.Visibility == Windows.UI.Xaml.Visibility.Collapsed) { var point = e.GetCurrentPoint(null); Canvas.SetLeft(draggableImage, point.Position.X - 50.0); Canvas.SetTop(draggableImage, point.Position.Y - 50.0); draggableImage.Visibility = Windows.UI.Xaml.Visibility.Visible; }This works. If you touch/click the large image, the smaller draggable image appears under the pointer. However, you can't drag it until you lift your finger/mouse button, and then touch it again. Apparently the manipulation events don't fire if the pointer was already down in the control's position when it becomes visible, so this approach isn't working. I've tried setting the focus to the draggable control after I set its Visibility property, but the Focus() method always returns false.
How can I achieve the desired functionality where you can touch a control, have the draggable control appear under your finger, and then drag it around without having to lift your finger?
Todas as Respostas
-
quinta-feira, 2 de agosto de 2012 18:33
Simplest way to achieve this would be to set up your ManipulationStarted and Delta events on the bigger image (setting ManipulationModes on it...), then use the Started event to set the position of the smaller image, and the Delta events (fired by the bigger image) to drive your transform.
Slightly more complex would be to use the GestureRecognizer class to drive your manipulations (Since this basically works at the window, not element level)
If you're not setting ManipulationMode and handling PointerPressed on the bigger image, you will see the issue you describe as this is will block Manipulation* events from firing until you are over such an element.
Hope this helps,
MattXAML SDET Lead : Input / DirectManipulation / Accessibility
- Marcado como Resposta Basputin sexta-feira, 3 de agosto de 2012 07:28
-
sexta-feira, 3 de agosto de 2012 07:34
This works a charm. I hadn't figured that the manipulation events would obviously still be firing even if the large image stays in place.
One caveat for anyone else who may want to try this: I have the large image defined in a datatemplate that is being used in a flipview. The thing to remember here is to set the ManipulationStarted and ManipulationDelta event handlers on the flipview itself, but to set the ManipulationMode property on the image control in the template. Then in the ManipulationStarted handler you check if e.OriginalSource refers to the image.
Thanks Matt!
-
sexta-feira, 3 de agosto de 2012 09:49
Another caveat: the ManipulationStarted event args give the pointer position relative to the originalsource. In other words, if you touch the top-left corner of the image, it returns 0,0 for the position. This causes trouble when trying to position the smaller image under the pointer, since you can't (to my knowledge) determine what the offset off the large image is to the top left corner of the screen. There's also no more Mouse.GetPosition() (or probably more accurately Pointer.GetPosition()) static method anymore, so you have no way of telling where on the screen to place the smaller image. I've handled it for now by adding a PointerMoved event handler to the flipview containing the large image, and simply writing the value returned by e.GetCurrentPoint(null) to a private field, to be used later in the ManipulationStarted event handler. This works but feels like a hack. I wonder if there's a better way.
-
sábado, 4 de agosto de 2012 05:47
These snippets should help on the "GetPosition" topic:
<Page x:Class="App5.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App5" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Name="myPage"> <Grid x:Name="myGrid" Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Canvas x:Name="myCanvas" Width="200" Height="200" Background="AliceBlue"> <Rectangle Fill="Red" Width="50" Height="50" Canvas.Top="25" Canvas.Left="25" PointerPressed="Rectangle_PointerPressed_1"/> <Ellipse Fill="Red" Width="50" Height="50" Canvas.Top="100" Canvas.Left="25" PointerPressed="Ellipse_PointerPressed_1"/> </Canvas> </Grid> </Page>--
using System.Diagnostics; using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Shapes; namespace App5 { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } private void Rectangle_PointerPressed_1(object sender, PointerRoutedEventArgs e) { Rectangle r = sender as Rectangle; Debug.WriteLine("Canvas:{0}", r.TransformToVisual(myCanvas).TransformPoint(new Point(0, 0))); Debug.WriteLine("Grid:{0}", r.TransformToVisual(myGrid).TransformPoint(new Point(0, 0))); Debug.WriteLine("Page:{0}", r.TransformToVisual(myPage).TransformPoint(new Point(0, 0))); Debug.WriteLine("Window:{0}", r.TransformToVisual(Window.Current.Content).TransformPoint(new Point(0, 0))); } private void Ellipse_PointerPressed_1(object sender, PointerRoutedEventArgs e) { Ellipse r = sender as Ellipse; Debug.WriteLine("Canvas:{0}", r.TransformToVisual(myCanvas).TransformPoint(new Point(0, 0))); Debug.WriteLine("Grid:{0}", r.TransformToVisual(myGrid).TransformPoint(new Point(0, 0))); Debug.WriteLine("Page:{0}", r.TransformToVisual(myPage).TransformPoint(new Point(0, 0))); Debug.WriteLine("Window:{0}", r.TransformToVisual(Window.Current.Content).TransformPoint(new Point(0, 0))); } } }
- Editado ForInfoMicrosoft Community Contributor sábado, 1 de dezembro de 2012 15:23
-
domingo, 5 de agosto de 2012 19:57Wonderful, that worked a charm. Much cleaner. Thank you very much!

