36

In my app, a user can select an Image and drag it onto a Grid, to play with it. I do this by handling the PointerEntered event of the Grid. Here I detect if the user had an image selected and if the user is holding the mouse button.

Now I want to place the Image on the grid, and pass on the (still pressed down) pointer to my Image, so the Image uses its own ManipulationStarted, ManipulationDelta and ManipulationCompleted events. This should let the user drag the image in one smooth movement from the list of images to the Grid, instead of having to release and click on the element.

I have tried releasing the pointer from the sender in PointerEntered, and capturing it using CapturePointer, but that doesn't seem to work, even though the CapturePointer returns true.

Here is the code I use for the PointerEntered event:

private void DrawingArea_OnPointerEntered(object sender, PointerRoutedEventArgs e)
{
    // If we enter the grid while dragging and we have an image that was dragged
    if (e.Pointer.IsInContact && CurrentDraggedImage != null)
    {
        DrawingArea.Children.Add(CurrentDraggedImage);

        // Move it to the location we're currently at
        var transform = (CurrentDraggedImage.RenderTransform as CompositeTransform);
        transform.TranslateX += e.GetCurrentPoint(DrawingArea).RawPosition.X - DrawingArea.ActualWidth / 2;
        transform.TranslateY += e.GetCurrentPoint(DrawingArea).RawPosition.Y - DrawingArea.ActualHeight/2;

        // This works (I think)
        (sender as UIElement).ReleasePointerCaptures();
        // This doesn't work (or it isn't what I need), but returns true
        CurrentDraggedImage.CapturePointer(e.Pointer);

        // Get ready for a new image
        CurrentDraggedImage = null;
    }
}

My manipulation code is in this answer:

https://stackoverflow.com/a/32230733/1009013

Community
  • 1
  • 1
vrwim
  • 13,020
  • 13
  • 63
  • 118
  • Have you considered a different approach - use the built-in CanDragItems from ListView and set AllowDrop to be true on your drawing area? – Justin XL Sep 02 '15 at 09:40
  • @JustinXL Nope, but I guess that this gives the same roadblock... I'll check it out and get back to you. – vrwim Sep 02 '15 at 09:43
  • @JustinXL yup, same problem, can't move the pointer from one drag & drop action to the other... – vrwim Sep 07 '15 at 08:05

1 Answers1

0

Why don't you just use drag-n-drop? Create a grid containing your toolbar (e.g. a list of images to drag) and a target grid that responds to dragdrop commands:

<Grid>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <ListBox Background="AliceBlue" MouseMove="OnMouseMove">

        <ListBox.Resources>
            <Style TargetType="{x:Type Image}">
                <Setter Property="Width" Value="64" />
                <Setter Property="Height" Value="64" />
            </Style>
        </ListBox.Resources>

        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_pawn_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_rook_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_knight_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_bishop_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_queen_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_king_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_pawn_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_rook_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_knight_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_bishop_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_queen_T.png" />
        <Image Source="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_king_T.png" />
    </ListBox>

    <GridSplitter Grid.Column="1" Width="5" Background="LightGray" />

    <Grid x:Name="targetGrid" Grid.Column="2" AllowDrop="True" DragEnter="OnDragEnter" DragOver="OnDragMove" DragLeave="OnDragLeave" Drop="OnDrop" Background="Transparent"/>

</Grid>

Your listbox needs a MouseMove handler to detect when an image is being dragged and your command handlers simply respond to the various events as required, cloning the require image and dragging them across the face of the grid accordingly:

public partial class MainWindow : Window
{
    private Image DragImage = null;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
        // make sure we have an image
        var image = e.OriginalSource as Image;
        if (image == null)
            return;

        // make sure we've started dragging
        if (e.LeftButton != MouseButtonState.Pressed)
            return;

        DragDrop.DoDragDrop(image, image, DragDropEffects.Copy);
    }

    private void OnDragEnter(object sender, DragEventArgs e)
    {
        // make sure we have an image
        if (!e.Data.GetDataPresent(typeof(Image)))          
        {
            e.Effects = DragDropEffects.None;
            return;
        }

        // clone the image
        var image = e.Data.GetData(typeof(Image)) as Image;
        e.Effects = DragDropEffects.Copy;
        this.DragImage = new Image { Source = image.Source, Width=64, Height=64 };
        var position = e.GetPosition(this.targetGrid);
        this.DragImage.SetValue(Grid.MarginProperty, new Thickness(position.X-32, position.Y-32, 0, 0));
        this.DragImage.SetValue(Grid.HorizontalAlignmentProperty, HorizontalAlignment.Left);
        this.DragImage.SetValue(Grid.VerticalAlignmentProperty, VerticalAlignment.Top);
        this.DragImage.IsHitTestVisible = false; // so we don't try and drop it on itself

        // add it to the target grid
        targetGrid.Children.Add(this.DragImage);
    }

    private void OnDragMove(object sender, DragEventArgs e)
    {
        var position = e.GetPosition(this.targetGrid);
        this.DragImage.SetValue(Grid.MarginProperty, new Thickness(position.X - 32, position.Y - 32, 0, 0));
    }

    private void OnDragLeave(object sender, DragEventArgs e)
    {
        targetGrid.Children.Remove(this.DragImage);
        this.DragImage = null;
    }


    private void OnDrop(object sender, DragEventArgs e)
    {
        this.DragImage.IsHitTestVisible = true;
        this.DragImage = null;
    }

}

Result:

enter image description here

I've done things the horrible and ugly WPF way here instead of clean and elegant MVVM, but you get the idea. I also don't get why you want to drag things around a Grid instead of a Canvas?

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • I'm only now coming back to this issue, and I will now try to make the drag and drop work in a civilised manner (currently it's tap to add). I remember using a Grid because I didn't know the Canvas, and most other elements didn't seem to be the right choice or did not allow for multiple children. – vrwim May 10 '16 at 13:51
  • I have implemented it now using Drag&Drop, UWP does not seem to have the `DragDrop.DoDragDrop`, so I needed to use `CanDragItems`. Also, your way of capturing `OnMouseMove` does not work on UWP, as the ListView captures that to scroll (the application comes on a tablet). – vrwim May 12 '16 at 07:34