Drag drop with feedback

I put together a little Silverlight app demonstrating how to drag and drop between different areas of your Silverlight app.  (This isn't drag and drop in the OLE sense, for security reasons Silverlight doesn't support dragging between processes)  I actually did two versions, one in JavaScript and one in C# ((Silverlight 1.0 and Silverlight 1.1 alpha, respectively), although the approach is the same for both.

The experience I wanted to build was that some areas of the application used auto layout, so that when you dropped an item in that area, the item would snap into place.  And I wanted some kind of visual feedback to show where the item would be put.  So I created an element which I called the shadow element, which is that feedback -- shadow element goes where the item will when you release the mouse button.  So when the mouse moves during a drag, we update the location of the shadow element:

 function handleMouseMove (sender, args) {
        var item = sender;
        if (isMouseCaptured) {
            ...
            moveShadow(overPanel);
        }
 }

        function moveShadow(overPanel)
        {
            hideShadow();

            if (overPanel != null && !overPanel.Equals(workspace2)) {
                overPanel.Children.Add(shadowElement);
                arrangePanel(overPanel);
            }
        }

I wanted some of the panels to have auto-layout, a stack panel-like effect, so I wrote a method to do that:

        function arrangeInRow(canvas)
        {
            var next = 10;
            var i;
            for (i = 0; i <canvas.Children.count; i ++) {
                var element = canvas.children.GetItem(i);
                if (element.toString() == "Ellipse") {
                    var child = element;
                    child["Canvas.Left"] = next;
                    child["Canvas.Top"] = 10;
                    next += child.Width;
                    next += 10;
                }
            }
        }

(The if statement in there is a bit of a hack, I was lazy when I wrote the xaml and so my container had some elements I wanted to be laid out -- the ellipses -- and some elements I didn't want layout to move...)  Dave Relyea has a series of layout samples on https://blogs.msdn.com/devdave/archive/2007/05/17/silverlight-1-1-alpha-layout-system-and-controls-framework.aspx.

As the mouse moves around, you need to figure out what it's over.  Scott Barnes describes a technique for doing this in https://blogs.msdn.com/msmossyblog/archive/2007/06/16/performing-a-hittest-with-silverlight.aspx, my code is slightly different since I wrote the first iteration of couple months ago, but it's the same technique.

As you drag an item between containers, you want the item to be on top of all the other containers.  If you don't do anything special, the item you're moving around will remain part of its original container, and the item will be part of its container's z-order -- so your item will appear on top of some containers but underneath others.  Simplest solution is to change the item's parent, remove it from its container and add it to the root element.  When you do that, you'll need to adjust the Canvas.Left and Canvas.Top properties, which is what this function does:

        function changeParentKeepPosition(item, newParent, e)
        {
            var parent = item.getParent();
            item["Canvas.Top"] = translateY(item["Canvas.Top"], parent, newParent, e);
            item["Canvas.Left"] = translateX(item["Canvas.Left"], parent, newParent, e);
            item.getParent().Children.Remove(item);
            newParent.Children.Add(item);
        }

I wrote the C# version of first, in that version there's a single Translate method that takes a Point.  Unfortunately, creating points in JavaScript is fairly awkward (and createFromXaml doesn't support <Point>), so easiest solution for me was to write separate translateX and translateY functions.  This ended up being the single biggest difference between the JavaScript and C# versions...

        function translateX(point, from, to, e)
        {
            var fromPoint = e.GetPosition(from);
            var toPoint = e.GetPosition(to);
            var delta = fromPoint.X - toPoint.X;
            var result = point - delta;
            return result;
        }

        function translateY(point, from, to, e)
        {
            var toPoint = e.GetPosition(to);
            var fromPoint = e.GetPosition(from);
            var delta = fromPoint.Y - toPoint.Y;
            var result = point - delta;
            return result;
        }

where e is any mouse eventargs...  It's an interesting little trick, although GetPosition was designed for getting the mouse position, with a couple extra lines you can turn it into a general-purpose coordinate transformation routine like the above.

dragdrop JavaScript and CS.zip

Comments

  • Anonymous
    June 27, 2007
    PingBack from http://blogs.msdn.com/nickkramer/archive/2007/06/27/drag-drop-with-feedback.aspx

  • Anonymous
    June 28, 2007
    Great stuff Nick...keep it coming. We will most of these techniques in an intranet based web application. Our users are greatly surprised with what we can do on the web now. Thanks for the article...

  • Anonymous
    June 30, 2007
    I put together a little Silverlight app demonstrating how to drag and drop between different areas of

  • Anonymous
    July 02, 2007
    Hi Nick, your little app may be great, but I can't see it on my Win2K system... I found out that there will be a Silverlight 1.1 version for Win2K, and I thought of that since you announced WPF/E. "Everywhere" should also include Win2K, shouldn't it? ;-) But what about desktop apps on Win2K? The decision that .NET 3.0 (including WPF) will run on WinXP SP2 was great, but because Win2K is still there on our customers workstations I would really need WPF on Win2K. I thought Silverlight could be the missing piece. I just downloaded the Silverlight 1.1 SDK and I only found some ASP.NET samples. So my question is: Are there any plans at Microsoft to support Silverlight in a WinForms app? That could be the missing piece to have the advantages of WPF in a desktop app on Win2K. By the way, we really want to get rid of the browsers controls that host an SVG viewer ;-) Would be great if you had a short answer for me, thanks.