Today I've run some tests on drag-n-drop support available for the alpha version. It ended up being really easy to add this kind of behavior to a silverlight element. Lets start by defining some elements in XAML, which are placed inside a top canvas:
<Rectangle x:Name="r" Width="66" Height="80" Canvas.Left="41" Canvas.Top="40.537"
<Rectangle x:Name="r2" Width="66" Height="80" Canvas.Left="200" Canvas.Top="40.537"
<Rectangle x:Name="r3" Width="66" Height="80" Canvas.Left="300" Canvas.Top="40.537"
The idea is to let the user reorganize the rectangles on the canvas by dragging them around. The user must also be able to cancel the current drag operation by pressing the ESC key (I've used the KeyUp event to handle the ESC key press).
We start by hooking up several events of the rectangles during the Load event of the top canvas element:
r.MouseLeftButtonDown += new MouseEventHandler(down);
r2.MouseLeftButtonDown += new MouseEventHandler(down);
r3.MouseLeftButtonDown += new MouseEventHandler(down);
r.MouseLeftButtonUp += new MouseEventHandler(up);
r2.MouseLeftButtonUp += new MouseEventHandler(up);
r3.MouseLeftButtonUp += new MouseEventHandler(up);
r.MouseMove += new MouseEventHandler(move);
r2.MouseMove += new MouseEventHandler(move);
r3.MouseMove += new MouseEventHandler(move);
this.KeyUp += new KeyboardEventHandler(keyUp);
As you can see, we need to handle several mouse events. We also need to handle a key press because the ESC key must cancel the current drag operation! - that is, if there's one being done.
When the user clicks over a rectangle, we need to save the rectangle's current position and Z-Index because we must return the rectangle to its initial position when the user hits the ESC key during the dragging. So, we start by adding the following fields to the Canvas class:
bool _mouseCaptured = false;
double _currentX = 0.0;
double _currentY = 0.0;
int _initialZIndex = 0;
Rectangle _current = null;
And then we can proceed to the down method, that handles the MouseLeftButtonDown:
private void down(object sender, MouseEventArgs e)
_current = (Rectangle)sender;
_currentX = (double)((Rectangle)sender).GetValue(Canvas.LeftProperty);
_currentY = (double)((Rectangle)sender).GetValue(Canvas.TopProperty);
_mouseCaptured = true;
_initialZIndex = Convert.ToInt32( ((Rectangle)sender).GetValue( Canvas.ZIndexProperty ) );
Since this method marks the beginning of a drag operation, we must initialize our auxiliary fields. Notice the CaptureMouse method call...it's really important and it's saying that all mouse events should be redirected to the current Rectangle. The z-index value is changed to a "safe" value so that the dragged rectangle remains on the top of the other elements that exist on the page (for instance, in this example, if i didn't change the z-index and i started dragging the 1st rectangle, when this rectangle was over the second rectangle, it would be placed below that second rectangle because both have were declared with the same Z-order and the second was added after the first - not what we want in this case).
A drag operation needs to handle the mouse move (only if a drag is taking place). Here's the code to do that:
void move(object sender, MouseEventArgs e)
if (!_mouseCaptured) return;
When the user moves the cursor and the dragging operation has started (_mouseCaptured = true), we only need to update the Left and Top properties of the Rectangle (note how we're passing this in order to use the top canvas as the reference for the X and Y coordinates). When the user releases the mouse button, it's time to clear the auxiliary fields and to release the mouse capture:
private void up(object sender, EventArgs e)
_mouseCaptured = false;
The only thing that is left is cancelling a dragging operation, which is done by the keyUp method:
void keyUp(object sender, KeyboardEventArgs e)
if (!_mouseCaptured || e.PlatformKeyCode != 27 ) return;
_current = null;
_mouseCaptured = false;
Again, nothing too complicated...we start by checking if we're on a dragging operation and if the user hit the ESC key. If any of those conditions isn't true, we don't do anything. If they are both true, we must put the rectangle at its original place (ie, we must put it where it was before starting the dragging operation).