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.
Leave a Reply to Johnny Cancel reply