Welcome to Smips.com Sign in | Join | Help
Building a Silverlight Game : Part 8 : Writing Code to Drag Rectangles

Introduction and Review

This is the eight post in a series on building a Silverlight game.  In the last post, we talked about all the steps that we need to take to drag a rectangle around.  In this post, we’ll write the code to satisfy all those steps and let us drag our rectangles around the canvas.

For the sake of review, remember that in Part 6 we wrote code to prepare a rectangle for dragging.  This included

  1. Setting a flag that tells us a rectangle is being dragged
  2. Adjusting the opacity of the rectangle to provide a nice visual effect
  3. Capturing the mouse so the rectangle will be sure to respond to mouse events
   1: public void StartDrag(Rectangle rectangle)
   2: {
   3:     if (rectangle.Cursor.Equals(Cursors.Hand))
   4:     {
   5:         dragging = true;
   6:         rectangle.CaptureMouse();
   7:         rectangle.Opacity = 0.6;
   8:     }
   9: }

That will set up a rectangle for dragging.  The code that will drag the rectangle around (Part 7) needs to do these things:

  1. Check the flag to make sure it’s true
  2. Remove the rectangle from the stack panel
  3. Place the rectangle on the canvas so we can drag it around
  4. Bring the rectangle forward
  5. Move the rectangle to the current position of the mouse

Wiring up the proper event

The first thing to do is to decide when we want to do all these things.  In other words, what event of what object?  The answer (for now) is that we want to do these things when the MouseMove event of the topmost rectangle fires off.  To get things started in blend, just select the top-most rectangle in the object explorer, switch your properties tab to the event view, and double-click inside the MouseMove event’s textbox.  This will auto-generate an event handler method and wire it up in the XAML. 

image

If you’re not using Blend, the same event can be wired up manually in Visual Studio.

   1: <StackPanel x:Name="LeftPanel" Orientation="Vertical" 
   2:     Canvas.ZIndex="1" VerticalAlignment="Bottom">
   3:     <Rectangle Height="50" Width="100" Fill="Orange" 
   4:                MouseEnter="Rectangle_MouseEnter" 
   5:                MouseLeftButtonDown="Rectangle_MouseLeftButtonDown" 
   6:                MouseMove="Rectangle_MouseMove" />
   7:     <Rectangle Height="50" Width="110" Fill="Blue" />
   8:     <Rectangle Height="50" Width="120" Fill="Green" />
   9:     <Rectangle Height="5" Width="120" Fill="Black" />
  10: </StackPanel>

Just like we’ve done with all of our other rectangle events so far, we are going to delegate the actual work to the DragAndDrop.cs class.

   1: private void Rectangle_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
   2: {
   3:     dragAndDrop.Drag(
   4:         sender as Rectangle,
   5:         e.GetPosition(null));
   6: }

The Drag() method

You can see that we’re calling a Drag method on the instance of DragAndDrop.  This method wants to know the rectangle to drag and the current position of the mouse.  Now we just need to write this method.

First things first, here’s the entire Drag() method. 

   1: public void Drag(Rectangle rectangle, Point position)
   2: {
   3:     if (dragging)
   4:     {
   5:         StackPanel panel = rectangle.Parent as StackPanel;
   6:         if (panel != null)
   7:         {
   8:             originalParent = panel;
   9:             panel.Children.Remove(rectangle);
  10:             draggingCanvas.Children.Add(rectangle);
  11:             Canvas.SetZIndex(rectangle, 5);
  12:         }
  13:         mover.Move(rectangle, position);
  14:     }
  15: }

Let’s step through this one line at a time. 

On line 3, we’re checking our dragging flag.  Since this method will be called whenever the rectangle’s MouseMove event fires, we want to make sure that we really should be dragging before we start moving things all over the screen.

On line 5, we put the rectangle’s parent in a local variable.  This is so we can put the rectangle back where it belongs in things go awry.

On line 6, we check to see if the rectangle’s parent is null.  If it is, then that means the cast to StackPanel failed (line 5).  And that means that the parent is not a stack panel.  And THAT means that rectangle is on the canvas.  And if the rectangle is on the canvas, then we skip lines 8 – 11, since those lines put the rectangle on the canvas.  Remember that this is being called over and over as the mouse moves across the screen.

On line 8, we keep track of the original parent.  On line 9, we remove the rectangle from it’s parent stack panel.  On line 10, we place the rectangle on the dragging canvas.  And on line 11, we bring the rectangle to the front of the screen.

Finally, one line 13, we call out to another class that will actually move the rectangle.  We haven’t written the code for this class yet.

If you paste the code for the Drag() method into your copy of DragAndDrop.cs, you’ll notice it doesn’t compile.  We just have to make a handful of changes to get things all wired up and working.

The first problem is that we don’t have a field variable for the original parent.  The next problem is that we don’t have a reference to a dragging canvas.

DragAndDrop’s new constructor

Both of these problems are resolved by changing our field declarations and constructor definition.

   1: private Canvas draggingCanvas;
   2:  
   3: private bool dragging = false;
   4: private Panel originalParent;
   5:  
   6: public DragAndDrop(Canvas draggingCanvas)
   7: {
   8:     this.draggingCanvas = draggingCanvas;
   9: }

Now we have fields for the dragging canvas and the original parent.  Notice also that we added the dragging canvas as a parameter for the constructor.  We’re telling anyone that wants to create an instance of a DragAndDrop helper that they better provide a canvas to use.

If you remember, it’s MainPage.xaml.cs that is currently creating an instance of the DragAndDrop helper.  So we need to change the code there to pass in the dragging canvas.  In this case, the dragging canvas is the layout root for the main page.

   1: public MainPage()
   2: {
   3:     InitializeComponent();
   4:     dragAndDrop = new DragAndDrop(this.LayoutRoot);
   5: }

The RectangleMover class

At this point, the only thing that’s preventing our code from compiling is line 13 of the Drag() method above. 

   1: mover.Move(rectangle, position);

To get this working, we need a class that move a rectangle around the canvas … a mover.  This class will be very dumb.  While the DragAndDrop class has all the logic associated with dragging a rectangle around, the mover will just know how to move a rectangle within the bounds of a canvas.

The code for this class looks like this:

   1: public class RectangleMover
   2: {
   3:     private double heightLimit;
   4:     private double widthLimit;
   5:  
   6:     public RectangleMover(double heightLimit, double widthLimit)
   7:     {
   8:         this.heightLimit = heightLimit;
   9:         this.widthLimit = widthLimit;
  10:     }
  11:  
  12:     public void Move(Rectangle rectangle, Point position)
  13:     {
  14:         double left = CalculateRectangleLeft(rectangle, position);
  15:         double top = CalculateRectangleTop(rectangle, position);
  16:         Canvas.SetLeft(rectangle, left);
  17:         Canvas.SetTop(rectangle, top);
  18:     }
  19:  
  20:     private double CalculateRectangleTop(Rectangle rectangle, Point position)
  21:     {
  22:         double top = position.Y - (rectangle.ActualHeight / 2);
  23:         double maxTop = this.heightLimit - rectangle.ActualHeight;
  24:         double minTop = 0;
  25:         if (top >= maxTop)
  26:         {
  27:             return maxTop;
  28:         }
  29:         if (top < minTop)
  30:         {
  31:             return minTop;
  32:         }
  33:         return top;
  34:     }
  35:  
  36:     private double CalculateRectangleLeft(Rectangle rectangle, Point position)
  37:     {
  38:         double left = position.X - (rectangle.ActualWidth / 2);
  39:         double maxLeft = this.widthLimit - rectangle.ActualWidth;
  40:         double minLeft = 0;
  41:         if (left >= maxLeft)
  42:         {
  43:             return maxLeft;
  44:         }
  45:         if (left < minLeft)
  46:         {
  47:             return minLeft;
  48:         }
  49:         return left;
  50:     }
  51: }

This class can look a bit intimidating, but it doesn’t really do anything interesting.  It has one public method that takes a rectangle and a position.  It moves the rectangle to that position.  Feel free to explore the class on your own, but we’ll leave it at that for our purposes here.

Now we just need to instantiate an instance of RectangleMover when we instantiate our DragAndDropClass, so DragAndDrop gets a new constructor a new instance field.

   1: private Canvas draggingCanvas;
   2: private RectangleMover mover;
   3:  
   4: private bool dragging = false;
   5: private Panel originalParent;
   6:  
   7: public DragAndDrop(Canvas draggingCanvas)
   8: {
   9:     this.draggingCanvas = draggingCanvas;
  10:     mover = new RectangleMover(
  11:         draggingCanvas.Height,
  12:         draggingCanvas.Width);
  13: }

Running the game

If we run the game now, we can now drag the top rectangle around the canvas.  But we can’t drop it yet.  That’s for next time!

image

Download the source code so far

Share this post: Email it! | bookmark it! | digg it! | reddit!
Posted: Thursday, January 21, 2010 12:28 PM by brad

Comments

No Comments

Leave a Comment

(required) 

(required) 

(optional)

(required) 

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS