WynApse Home Page
Home    Blog    About    Contact       
Latest Article:


My Tags:
My Sponsors:






Lifetime Member:

Silverlight With Java Script Tutorial 10 - Dragging Objects in a Canvas



Overview

Drag and Drop has been covered a bunch in the body of Silverlight articles. I've done two myself! From an introductory standpoint, however, I think there's yet room for more. For that reason, I'm going to break this into two pieces. This first one is a simple 'drag' showing how to drag objects around the canvas using the mouse. The next one will combine this 'drag' operation with the use of ZIndex to provide a more conventional aspect.

I had this ready to go with code that avoided being able to drag off the canvas, but thought that added complexity that violated my 'simple' rule, so I'm saving it for next time with ZIndex. Although it's possible to drag off the canvas this time, and I'm not doing any mouse-release things, they'll be in the next one.

As always, I'd like to give a reference to the Microsoft Silverlight SDK Online.


Drag and Drop in Silverlight

Drag and Drop is a misnomer in Silverlight. That's why I used only the word "Drag" in the title for this Tutorial. In a classic Windows Drag and Drop operation, you have a drag object and a drop target. Silverlight provides us a very handy way to drag any canvas object with the mouse, but there is no concept of drop target. What this means is that you cannot, for instance, drag a rectangle from one canvas to another canvas.

At the same time, it is entirely possible, if you don't program it just right, to drag something completely off the canvas and lose it! I'll show how to avoid that situation in the next tutorial.


The Grab

The first part of a 'Drag' operation is the act of 'Grabbing' an object with the mouse. You're probably not going to allow your users to move everything around the canvas, but that's certainly an option.

You give your users the visual clue that they can do something with an object by setting the cursor to "hand". We've all been conditioned to accept that indicator as telling us we can 'click' or 'grab' an item. All three of the rectangles on this canvas have Cursor="Hand" for this reason.

The rectangles also have MouseLeftButtonDown="OnMouseLeftBottonDown". If you look at the JavaScript for that function:

function OnMouseLeftButtonDown(sender, mouseEventArgs)
{
// Ensure this object is the only one receiving mouse events.
sender.captureMouse();

// Set the beginning position of the mouse.
beginX = mouseEventArgs.getPosition(null).x;
beginY = mouseEventArgs.getPosition(null).y;

isMouseDown = true;
}


The first thing that happens is sender.captureMouse(). This forces all mouse messages to be routed through sender until the capture is released. This means the MouseMove messages will be routed through the same sender that received the MouseDown so that you can track the movement.

To track movement, you need to capture the sender's position when the mouse was pressed, and that's the purpose of beginX and beginY. I also set a global flag "isMouseDown" to be true. Without the use of this flag, the mouse messages begin flying around before you get a chance to capture. You could do without the isMouseDown flag if we had the capability of doing something like sender.IsMouseCaptured(), or if you wanted to deal with multiple unique mouse handlers for each object.


The Drag

Now that the mouse is down and captured, your user begins moving the mouse. As the mouse moves, the MouseMove handler receives the messages and executes this code:

function OnMouseMove(sender, mouseEventArgs)
{
// Determine whether the mouse button is down.
// If so, move the object.
if (isMouseDown == true)
{
// Retrieve the current position of the mouse.
var currX = mouseEventArgs.getPosition(null).x;
var currY = mouseEventArgs.getPosition(null).y;

// Reset the location of the object.
sender["Canvas.Left"] += currX - beginX;
sender["Canvas.Top"] += currY - beginY;

// Update the beginning position of the mouse.
beginX = currX;
beginY = currY;
}
}


This is where you use "isMouseDown" to avoid problems prior to capture. If the mouse is captured, you retrieve the current mouse position from the function arguments and adjust the "Canvas.Left" and "Canvas.Top" for the object according to the delta change since the mouse was pressed down.

Then you simply update the beginX and beginY to the current mouse position at this point, the move will work just fine, and your user can drag the object all around the canvas.


MouseLeftButtonUp

When the left mouse is released, the MouseLeftButtonUp handler is executed, and that code is shown here:

function OnMouseLeftButtonUp(sender, mouseEventArgs)
{
isMouseDown = false;

sender.releaseMouseCapture();
}


Very simply, you reset the isMouseDown flag, and Release the mouse capture. This allows your user to move the mouse freely without any object Drag and opens the possibility of beginning the whole process once again.


Our XAML

Here is the XAML for the canvas above:

<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Loaded="OnCanvasLoaded">

<Rectangle Width="300"
Height="300"
StrokeThickness="1"
Stroke="Black"
/>

<Canvas Canvas.Top="129"
Canvas.Left="124"
Width="50"
Height="40"
>

<Rectangle Width="50"
Height="40"
StrokeThickness="1"
Stroke="Black"
Fill="Blue"
MouseLeftButtonDown="OnMouseLeftButtonDown"
MouseLeftButtonUp="OnMouseLeftButtonUp"
MouseMove="OnMouseMove"
Cursor="Hand"
/>
</Canvas>

<Canvas Canvas.Top="137"
Canvas.Left="132"
Width="50"
Height="40"
>

<Rectangle Width="50"
Height="40"
StrokeThickness="1"
Stroke="Black"
Fill="White"
MouseLeftButtonDown="OnMouseLeftButtonDown"
MouseLeftButtonUp="OnMouseLeftButtonUp"
MouseMove="OnMouseMove"
Cursor="Hand"
/>
</Canvas>

<Canvas Canvas.Top="145"
Canvas.Left="140"
Width="50"
Height="40"
>

<Rectangle Width="50"
Height="40"
StrokeThickness="1"
Stroke="Black"
Fill="Red"
MouseLeftButtonDown="OnMouseLeftButtonDown"
MouseLeftButtonUp="OnMouseLeftButtonUp"
MouseMove="OnMouseMove"
Cursor="Hand"
/>
</Canvas>

</Canvas>


Summary

The discussion here is much more involved than the actual code or concept, but I wanted to cover this in a little more detail since I see questions about this on the forums.

I also broke a bit with the previous tutorials in that the JavaScript is more complex and there is more of it. There's just no way to address the subject in Silverlight 1.0 without getting deeper into JavaScript.

In Tutorial 11, I'll deal with off-canvas issues and Canvas.ZIndex to give it a more 'normal' feel.

Stay in the 'Light!

Copyright © 2006-2017, WynApse