Mike Taulty's Blog
Bits and Bytes from Microsoft UK
Silverlight V1.1: Trying to build a simple drag-and-drop capability
Mike Taulty's Blog

Mike's Badges

Follow on Twitter
View mike's profile on slideshare
Add to Technorati Favorites
CW Blog Awards

I wanted a very simple kind of drag and drop ability in Silverlight V1.1 and I struggled to build it.

Basic procedure might be to build a Canvas that does something like;

  1. Capture mouse down on a FrameworkElement
  2. Watch for mouse drag.
  3. As the mouse drags, create a graphical representation of the FrameworkElement being dragged
  4. Watch for mouse up.
  5. Take the original Visual and either move it within its original container or perhaps reparent it if it got dragged into the bounds of some new container.

I got fairly stuck at point (3).

If I had a VisualBrush then this would perhaps be relatively easy in that I could create a brush from the original FrameworkElement, create a rectangle of similar size and paint it with that brush.

However, there's no VisualBrush.

So, I just figure I'll put a requirement onto whatever FrameworkElement that wants to be draggable so that it implement ICloneable so that I can ask it to copy itself and I'll drag a copy of it around.

However...a control copying itself becomes a bit tricky. As far as I know, in Silverlight, you have to be careful right now what you try and share around.

For instance, if I have 2 canvases and I programmatically set their background to the same Brush then I get failures ( the dreaded "Value does not fall within the expected range" error ) and, as far as I know, this is because you can't share resources like this in multiple places in the Alpha bits right now.

What does that mean? It means that if I use ICloneable to clone the control that's being dragged then it's unlikely that the control writer is going to be able to clone themselves easily because if they use things like brushes (which is pretty likely) then they'll have to clone those brushes and they'll struggle to do that (first problem will be for them to determine what type of Brush they have and the second problem will be trying to then clone it :-().

I can't think of any other way around it just now so I stuck with the ICloneable way of doing this.

I had a few stabs at trying to build something here in the following order;

  1. A top down approach of adding a special canvas that knew how to "drag and drop" controls within it. The canvas would capture the mouse events and try and handle controls without them needing to do much (other than perhaps implement a "marker" interface so that the canvas could find them). This didn't really work too well because the canvas has to work out which controls to drag-and-drop and to do that it has to hit-test them. I struggled with this (walking a tree made up of Canvas and Control got tricky) and decided it was a bad plan (also, asked around and various people said "bad plan").
  2. A more bottom-up approach. Adding a new base class as a control which likes to be "dragged" and another which likes to accept drags and then a co-ordinating Canvas that brings those 2 things together.

I went with something more like (2) in the end and it works ok(ish) - needs a bit of refinement.

The way in which I went about it was to build new classes derived from Control. I built a DraggableControl and a DroppableControl and essentially added a bit of code such that they hook MouseEnter/MouseLeave events and when those happen they walk up the tree of content (Parent.Parent.Parent and so on) looking for a DragDropCanvas and, if they find one, they say "Hey, I'm being dragged" or "Hey, I'm available for drop".

From there on in there's a bit of a slightly simplistic hand-shake between the two to either copy a control from draggable to droppable or to move a control from a draggable to a droppable. At the moment, the control itself says whether it wants to be copied or moved and I'm not sure that's quite right but it worked for what I wanted.

Once again, I've uploaded the project bits along with a bit of a test sample. Once again, this is very rough-and-ready but perhaps it might help a little bit with something you're doing.

image

You'll notice that if you run it then the Red circle is a "copy-drag-and-drop" whereas the blue rectangle is a "move-drag-and-drop".

You might also notice that there's some odd mouse-handling in that when you perform a drag, you'll get the dragged copy of the object you're dragging to the top and left of the mouse pointer. Why is that? Essentially, it's because I found that if I drag around an object under the mouse pointer then I didn't get MouseEnter/MouseLeave events on my other controls. I don't know if that's by design or not but it's the way it worked out for me and so I moved the dragged control to be "offset" slightly from the mouse pointer to avoid this.

Here's the project bits.


Posted Fri, Jul 27 2007 8:50 AM by mtaulty
(C) Mike Taulty, 2009. All rights reserved. The information in this weblog is provided "AS IS" with no warranties, and confers no rights. This weblog does not represent the thoughts, intentions, plans or strategies of my employer. It is solely my opinion. Inappropriate comments will be deleted at the authors discretion. All code samples are provided "AS IS" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems