Building a Silverlight Game: Part 9: Dropping Dragged Rectangles

by brad 10. February 2010 15:22

Introduction and Review

This is the eight post in a series on building a Silverlight game.  In the last post, we coded up all the steps to drag rectangles around the canvas.  This left us with a very simple program where we could pick up and drag the top-most rectangle on a stack.

image

The obvious thing that are code from last week couldn’t do though, is drop the rectangle when you let go of the mouse.  We’ll write the code for dropping the dragged rectangle in this post.

Wiring up a mouse event

Like the other features that we’ve implemented so far, we first need to pick the mouse event to write into. 

  • When checking if dragging was a valid thing to do, we attached to MouseEnter
  • When setting up a rectangle to be dragged, we attached to MouseLeftButtonDown
  • When dragging a rectangle, we attached to MouseMove
  • And finally, for dropping a rectangle, we will attach to MouseLeftButtonUp

In Blend, setting this up is just a matter of selecting the orange rectangle, bringing up the properties tab, switching to the event view, and double-clicking in the textbox next to MouseLeftButtonUp

image

This will generate our method stub and the XAML below.

   1: <Rectangle Height="50" Width="100" Fill="Orange" 
   2:            MouseEnter="Rectangle_MouseEnter" 
   3:            MouseLeftButtonDown="Rectangle_MouseLeftButtonDown" 
   4:            MouseMove="Rectangle_MouseMove" 
   5:            MouseLeftButtonUp="Rectangle_MouseLeftButtonUp" />

   1: private void Rectangle_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
   2: {
   3:     // TODO: Add event handler implementation here.
   4: }

Just like our previous features, we’re going to defer anything related to dragging or dropping to our DragAndDrop class.  Let’s add the call to the event handler.

   1: private void Rectangle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
   2: {
   3:     dragAndDrop.Drop(
   4:         sender as Rectangle,
   5:         e.GetPosition(null));
   6: }

The Drop() Method

Here’s the new Drop() method for DragAndDrop.cs

   1: public void Drop(Rectangle rectangle, Point position)
   2: {
   3:     dragging = false;
   4:     Panel panel = main.GetDropPanel(position);
   5:     RectangleDropper.DropRectangle(
   6:         draggingCanvas,
   7:         panel,
   8:         originalParent,
   9:         rectangle,
  10:         position);
  11:     rectangle.ReleaseMouseCapture();
  12:     rectangle.Opacity = 1;
  13: }

On line 3, we set our dragging boolean to false.  This boolean is checked by our Drag() method, so setting it to false stops the rectangle from moving around with the mouse.

On line 4, we get the panel to drop the rectangle into.  For now, this line won’t compile.  More on it later.

On line 5, we drop the rectangle into the appropriate panel.  This line will also not compile at the moment since we don’t have a RectangleDropper class yet.

Lines 11 and 12 reverse the UI stuff that we did when we started to drag.  We release the mouse capture and set the opacity of the rectangle back to 1 so that it’s not see through anymore.

Now about those lines that are throwing compile errors – lines 5 and 6.

Getting the panel to drop the rectangle into

When the user released the mouse, the rectangle needs to drop neatly into the panel that they mouse is currently over.  The problem that we have is that the DragAndDrop helper doesn’t know about the panels and shouldn’t.  They are a concern of the main control.

So let’s put a method on our main control that the DragAndDrop helper can call to find out about panels.  We’ll call it GetDropPanel() and it will look this:

   1: public StackPanel GetDropPanel(Point position)
   2: {
   3:     if (position.X > Canvas.GetLeft(RightBorder))
   4:     {
   5:         return RightPanel;
   6:     }
   7:     if (position.X > Canvas.GetLeft(MiddleBorder))
   8:     {
   9:         return MiddlePanel;
  10:     }
  11:     return LeftPanel;
  12: }

Remember that this method belongs to our main control, not the DragAndDrop helper.  It takes the position of the mouse, provided by the DragAndDrop helper, and gives the panel that lives at that location.

To use this code from the DragAndDrop helper, we need to make sure that the DragAndDrop helper has a reference back to the main control.  We’ll add it as a constructor dependency for the DragAndDrop helper class.

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

Now, when the main control creates an instance of the DragAndDrop class, it will have to pass itself in.  A more object-oriented approach would be to have the main control implement an interface and pass that in, but this is just a simple game, so we’re not going to bother.

Here’s the code for the main control to instantiate an instance of DragAndDrop

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

Dropping the rectangle

The last piece of the puzzle to drop the rectangle is the RectangleDropper class.  It looks like this.

   1: public class RectangleDropper
   2: {
   3:     public static void DropRectangle(
   4:         Canvas canvas, 
   5:         Panel panel, 
   6:         Panel oldParent,
   7:         Rectangle rectangle,
   8:         Point position)
   9:     {
  10:         if (RectangleHasMoved(rectangle, panel))
  11:         {
  12:             if (RectangleIsBiggerThanRectangleOnTop(rectangle, panel))
  13:             {
  14:                 panel = oldParent;
  15:             }
  16:             canvas.Children.Remove(rectangle);
  17:             panel.Children.Insert(0, rectangle);
  18:         }
  19:     }
  20:  
  21:     private static bool RectangleHasMoved(Rectangle rectangle, Panel panel)
  22:     {
  23:         return !panel.Children.Contains(rectangle);
  24:     }
  25:  
  26:     private static bool RectangleIsBiggerThanRectangleOnTop(Rectangle rectangle, Panel panel)
  27:     {
  28:         if (panel.Children.Count.Equals(0))
  29:         {
  30:             return false;
  31:         }    
  32:         Rectangle topRectangleInPanel = panel.Children[0] as Rectangle;
  33:         return topRectangleInPanel.ActualWidth < rectangle.ActualWidth;
  34:     }
  35: }

It has one public method, DropRectangle(), that defers to a couple of private methods to make sure that the basic rules for dropping are followed.

Download source code so far

Tags: , ,

Comments are closed

About Brad

Brad Tutterow lives in Illinois and works in Missouri. He has 12 years of experience developing web sites and Windows applications using a variety of technologies and is most excited currently about Silverlight, Windows Phone 7, Halo Reach, and Visual Studio 2010.