Click here to Skip to main content
15,883,919 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi all,

I'm working on an app in UWP. I realized that drag and drop didn't work as expected so I eventually created a new UWP app and try to drag and drop things there.

I can get DragEnter to fire, but I can't get the Drop event to work. Whatever I drag, I always get a "stop sign" symbol, and if I set a breakpoint on the Drop event it doesn't fire.

Here's my code:

HTML
<Page
    x:Class="DragDropExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DragDropExample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid x:Name="tb" removed="#FFFD3535">
        <TextBlock x:Name="txtDrag" HorizontalAlignment="Left" Margin="10,136,0,0" TextWrapping="Wrap" Text="This is just a test" VerticalAlignment="Top" CanDrag="True" Foreground="#FF487EF1"/>

        <TextBox x:Name="tbDrop" removed="#FF36DA12" Width="100" Height="100" AllowDrop="True" Drop="tbDrop_Drop" DragEnter="tbDrop_DragEnter" />
        <Grid x:Name="gridDrop" HorizontalAlignment="Left" Margin="100, 100, 0, 0" removed="#FFE81DF1" AllowDrop="True" Height="100" Width="100" VerticalAlignment="Bottom"  />
        <Canvas x:Name="canvasDrag" HorizontalAlignment="Left" Height="50" Margin="230,10,0,0" VerticalAlignment="Top" Width="50" Background="#FFF7F71A" CanDrag="True" Drop="canvasDrag_Drop"/>

    </Grid>
</Page>


C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace DragDropExample
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private void tbDrop_Drop(object sender, DragEventArgs e)
        {
            tbDrop.Text = "Dropped";
        }

        private void tbDrop_DragEnter(object sender, DragEventArgs e)
        {
            tbDrop.Text = "Entered";
        }

        private void canvasDrag_Drop(object sender, DragEventArgs e)
        {
            tbDrop.Text = "Dropped on canvas";
        }
    }
}


What am I missing?

I also wonder how I can actually drag the objects rather than just making copies of them. (The textblock gets dragged, but what is dragged is a copy, and the original textblock is still there.)

Note that the example code is silly - I just need something that works and that doesn't involve items in a listview or similiar. Preferably, I'd like to be able to drag images or canvases with images in them (for a chess game).

Thanks!

Petter
Posted

I did not know UWP. But I know about Drag & Drop with Windows and think that this answers your questions.

You are missing that you must also handle the DragOver event and that the handlers must set the DragEventArgs Class[^] Effect Property[^] to indicate what is allowed.

The effect returned by DragEnter and DragOver indicates if dropping is possible and if so if data are copied or moved (usually by checking the KeyState property). This return value is used by the system to generate the drag cursor (e.g. stop sign if dropping not possible).

When the mouse button is released, the system calls once DragOver to get the effect. If dropping is possible, the Drop handler is called. Otherwise the DragLeave handler is called (this must only be implemented when you need to perform some cleanup).
[EDIT: The above is wrong]
When the mouse button is released, the system calls once the DragOver handler to set the effect and then the Drop handler.

The optional DragLeave handler is called when leaving the window with the mouse button still down. It can be used for cleanup. Note that this is not called when the drop handler has been called and it may be necessary to add similar cleanup code to the drop handler.
[/EDIT]

The drag source can use the DropCompleted event to check what has happened. When data has been moved, it should delete it's own data.

[UPDATE]
Note again that I don't know UWP and these examples might not work. But they should show how to do it.

So you should add the DragOver handler and modify the others:
C#
private void tbDrop_DragEnter(object sender, DragEventArgs e)
{
    // Check here if data can be generally dropped:
    // - Control is active and not read only
    // - Drag source provides data of a matching type (e.g. a canvas and not text)
    // The result may be stored in a class member variable so the check
    //  must not be executed again with each DragOver event.
    // When using such a variable, it must be cleared inside the 
    //  Drop and DragLeave handlers.
    this.canDrop = CanDrop(sender, e);
    // Call DragOver to check at the actual position and set the effect.
    DragOver(sender, e);
}

private void tbDrop_DragOver(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.None;
    // Check here if dropping is possible in general and at the actual position.
    if (this.canDrop && CanDropHere(sender, e))
    {
        // See also MSDN example code for the Effect Property
        if ((e.KeyState & 8) == 8 && 
        (e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy) 
        {
            // CTL KeyState for copy.
            e.Effect = DragDropEffects.Copy;
        }
        else
        {
            // Move by default.
            e.Effect = DragDropEffects.Move;
        }
    }
}

private void tbDrop_Drop(object sender, DragEventArgs e)
{
    // DragOver() has been called by the system just before this and
    //  the effect has been set.
    if (e.Effect != DragDropEffects.None)
    {
        // Drop data here.
        // ...
    }
    // Cleanup (similar code as in DragLeave()).
    this.canDrop = false;
}

private void tbDrop_DragLeave(object sender, DragEventArgs e)
{
    // Cleanup when leaving.
    this.canDrop = false;
}


On the drag source side use the DropCompleted handler to detect if dropping has been occured and if data should be moved or copied.

C#
private void tbDragSource_DropCompleted(UIElement elem, DropCompletedEventArgs e)
{
    if (e.DropResult == DataPackageOperation.Move)
    {
        // Delete item here
    }
}

[/UPDATE]
 
Share this answer
 
v3
Comments
petter2012 10-Jan-16 5:27am    
Hi,

Thanks, I guess that too (that it didn't have anything to do with UWP really but how drag and drop works in general).

The problem actually arises right when I start dragging the object (such as the textblock). The object gets copied instead of moved and the stops sign appears at once - which may not be so strange given that the object can't be dropped on the main grid.

So I guess I should write something in the DragStarting event but I don't know what to write and can't find anyting suitable online (to my surprise).
Jochen Arndt 10-Jan-16 6:43am    
See my updated answer.

I looked also for online examples but can't find anything useful for the source side. But you can try my example code for the DropCompleted handler (which handles copy/move). The DragStarting event should be implemented similar.
petter2012 11-Jan-16 6:49am    
Hi again,

Thank you so much for your code!

Regrettably the DragDropEffects and a few others are not available in UWP, so I needed to look further.

Here's some code that I got to work, apart from that the original image becomes duplicated when I drag it. However, it gets removed and placed correctly when I release the mouse button.

<grid x:name="gridMain" removed="#FFFD3535" height="640" verticalalignment="Top">
<Canvas x:Name="cnvDrop" AllowDrop="True" DragOver="cnvDrop_DragOver" Drop="cnvDrop_Drop"
Background="LightBlue" Margin="10,138,140,134">
<Image x:Name="imgDrag" HorizontalAlignment="Left" Height="68" VerticalAlignment="Top" Width="64" CanDrag="True" Source="img/board.png" DragStarting="imgDrag_DragStarting" Canvas.Left="12" Canvas.Top="16" Stretch="Fill"/>
</Canvas>



private void imgDrag_DragStarting(UIElement sender, DragStartingEventArgs args)
{
//cnvDrop.Children.Remove(imgDrag);
}

private void cnvDrop_DragOver(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Move;
}

private void cnvDrop_Drop(object sender, DragEventArgs e)
{
var point = e.GetPosition(cnvDrop);
Canvas.SetLeft(imgDrag, (point.X));
Canvas.SetTop(imgDrag, (point.Y));
//cnvDrop.Children.Add(imgDrag);
}

...so I'm not sure I do this the easiest way, but then again this seems unnecessarily hard (after all, I think a drag and drop operation should per default result in, well, an object being dragged and dropped...). And the only thing that remains is to somehow get rid of the original image in the DragStarting event.

(The commented code takes care of this problem, but it also deletes the image being moved.)
Jochen Arndt 11-Jan-16 7:19am    
You are using
e.AcceptedOperation = DataPackageOperation.Move;

If this does the job it might work also for the other handlers instead of setting the effect.

While https://msdn.microsoft.com/library/windows/apps/windows.ui.xaml.drageventargs.acceptedoperation.aspx describes this property, following the link on that page back to DragEventArgs that property is not mentioned.

Regarding the duplicated image I can't help much. In fact I did not understand what really happens. Do you mean there are two images while dragging?

Overall it seems that with UWP some tasks are already handled by the system and that the documentation is quite incomplete.
petter2012 11-Jan-16 8:01am    
Hi again,

Yes, you are quite correct: during dragging there are two images present.

I tried your suggestion and played around with DataPackageOperation, but nothing happened with the code below:

private void imgDrag_DragStarting(UIElement sender, DragStartingEventArgs args)
{
args.Data.RequestedOperation = DataPackageOperation.Move;

I guess that this wasn't really what was needed after all.

Yes, i agree with you that the UWP documentation seems very incomplete. I've tried more things which I think should be rather startightforward but it looks like uncharted territory. :)
Actually a solution was found by people in the UWP forum at MSDN. Basically it worked when the image was set to a bitmap and the bitmap's source was set to null upon DragOver.


[UWP]Drag and drop objects within an UWP app[^]
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900