Day #5: Silverlight Drag and Drop

This is the fifth day of 31 Days of Silverlight. Today’s post is on enabling Drag & Drop in a Silverlight application.

Why Drag & Drop?

Drag & Drop is an important feature to think about, because it’s one of those gestures that we use all the time on our computers, and never even think about it. Moving files to the Trash, moving text from one area in a document to another, there’s plenty of applications for it. It’s not generally a gesture we associate with the web, which is yet another reason to try and change that perception.

Create a few XAML objects

For simplicity, I am creating two Rectangles and an Ellipse. We will have a circle, square, and rectangle. Each one will be a different color. Here’s the XAML:

<UserControl x:Class="SilverlightDragAndDrop.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<
Canvas x:Name="LayoutRoot" Background="White">
<
Ellipse x:Name="CircleRed" Width="100" Height="100" Fill="Red" Stroke="Black" StrokeThickness="2" Canvas.Top="150" Canvas.Left="50" />
<
Rectangle x:Name="SquareBlue" Width="100" Height="100" Fill="Blue" Stroke="Black" StrokeThickness="2" />
<
Rectangle x:Name="RectangleGreen" Width="200" Height="100" Fill="Green" Stroke="Black" StrokeThickness="2" Canvas.Top="50" Canvas.Left="150" />
</
Canvas>
</
UserControl>

Create some event handlers

The rest of this post will take place in the code-behind for our Page.xaml file. The first step is to add some event handlers to our shapes. We can do this in our startup method, but because we’re going to have several lines of code here, I prefer to extract this into its own method. Here’s what my startup and new InitializeEvents() methods look like:

        public Page()
{
InitializeComponent();
InitializeEvents();
}

public void InitializeEvents()
{
CircleRed.MouseLeftButtonDown += new MouseButtonEventHandler(Shape_MouseLeftButtonDown);
CircleRed.MouseMove += new MouseEventHandler(Shape_MouseMove);
CircleRed.MouseLeftButtonUp += new MouseButtonEventHandler(Shape_MouseLeftButtonUp);

SquareBlue.MouseLeftButtonDown += new MouseButtonEventHandler(Shape_MouseLeftButtonDown);
SquareBlue.MouseMove += new MouseEventHandler(Shape_MouseMove);
SquareBlue.MouseLeftButtonUp += new MouseButtonEventHandler(Shape_MouseLeftButtonUp);

RectangleGreen.MouseLeftButtonDown += new MouseButtonEventHandler(Shape_MouseLeftButtonDown);
RectangleGreen.MouseMove += new MouseEventHandler(Shape_MouseMove);
RectangleGreen.MouseLeftButtonUp += new MouseButtonEventHandler(Shape_MouseLeftButtonUp);
}

Shape_MouseLeftButtonDown

In our MouseLeftButtonDown event method, we need to determine a few things. First, we’re going to need the current mouse position. This will be vital. We’re also going to have to capture the mouse movements going forward. Here’s what we need to do:

        public void Remote_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{

mouseY = e.GetPosition(null).Y;
mouseX = e.GetPosition(null).X;
isMouseCaptured = true;
Remote.CaptureMouse();
}

You’ll notice that I am also setting a boolean value called isMouseCaptured. This is because in our MouseMove method, we’re going to want to be able to check and see if the mouse movement is being captured. All of our movement action will depend on this. Make sure to define this boolean value (as well as the mouseX and mouseY variables) as properties of our class.

Shape_MouseMove

This is our “meat and potatoes” method. This is where the bulk of our work will be done. We’ll need to calculate the offset of the mouse from the position of the shape, and set the new top and left values of our shape in relation to the mouse’s position. Here’s the code:

        void Shape_MouseMove(object sender, MouseEventArgs e)
{
if (isMouseCaptured)
{
Shape s = sender as Shape;
double deltaY = e.GetPosition(null).Y - mouseY;
double deltaX = e.GetPosition(null).X - mouseX;
double newTop = deltaY + (double)s.GetValue(Canvas.TopProperty);
double newLeft = deltaX + (double)s.GetValue(Canvas.LeftProperty);

s.SetValue(Canvas.TopProperty, newTop);
s.SetValue(Canvas.LeftProperty, newLeft);

mouseY = e.GetPosition(null).Y;
mouseX = e.GetPosition(null).X;
}
}

The deltaX and deltaY values are calculating the difference between where the mouse WAS, and where the mouse IS. We use that data in conjunction with the current position of the shape to move it to its next position. The two s.SetValue statements are setting the new Top and Left properties of the current shape. Finally, we record where our mouse is, so we have something to compare to the next time we come through this method.

Shape_MouseLeftMouseButtonUp

This is more of a cleanup method than anything else. We set our boolean value to false, release the mouse capture, and devalue the mouseX and mouseY values. Here’s what it looks like:

        void Shape_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Shape s = sender as Shape;
isMouseCaptured = false;
s.ReleaseMouseCapture();
mouseY = -1;
mouseX = -1;
}

It works, but we still have a problem.

At this point, you should have a working drag & drop example in Silverlight. However, you might notice, for example, that while we can drag the circle around, it stays behind the other shapes. In a true drag & drop example, we’d want the object we are dragging to be on top of everything else. To do this, we need to modify the z-index of the shape as we grab it. That means we’ll do it in the Shape_MouseLeftButtonDown method. I have created another global variable called zIndex, not surprisingly, and each time you click on Shape, we increment the zIndex value of that Shape, and add one more to the variable. Here’s the two lines of code to get it done in our method:

            s.SetValue(Canvas.ZIndexProperty, zIndex);
zIndex++;

Sample Code

You should now have a fully functional drag & drop example. If you’d like to see my entire code in a working state, you can download my Silverlight Drag & Drop example here. This example should also be running below this paragraph. If it isn’t, you can click here to see a working example of Silverlight Drag & Drop.

5 thoughts on “Day #5: Silverlight Drag and Drop

  1. Update on my question. I realised that the Remote obj in Remote.CaptureMouse() is the sender obj that is being dragged.

  2. Hey Jeff,Thank you once again for another great tutorial 🙂

  3. WOW!!! This is really easy to follow and understand. Thank you so much!m.campos

  4. Hey Jeff, Just a comment on the code, I see you use two double values to capture the point pressed. It might be more clearifying if the class Point would have been used to hold the mousepoint pressed, like Point p = e.GetPosition(shape);Great tutorials however, keep up the good work!

  5. Hi Jeff;The very first time you move the blue box, it goes under the green. If you change the following code, it will fix it:zIndex++; s.SetValue(Canvas.ZIndexProperty, zIndex);..Ben

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s