Blue Theme Orange Theme Green Theme Red Theme
 
Discover the top 5 tips for understanding .NET Interop
Home | Forums | Videos | Advertise | Certifications | Downloads | Blogs | Interviews | Jobs | Beginners | Training
 | Consulting  
Submit an Article Submit a Blog 
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
Team Foundation Server Hosting
Search :       Advanced Search »
Home » GDI+ & Graphics » Flickerless Drag and Drop of Graphic Primitives Using .Net GDI

Flickerless Drag and Drop of Graphic Primitives Using .Net GDI

This article shows you how dragging and dropping graphic primitives smoothly onto the screen without flicker is easily achieved using .Net's GDI.

Page Views : 35771
Downloads : 2295
Rating :
 Rate it
Level : Intermediate
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
DrawImages.zip
 
 
Nevron Chart
Become a Sponsor
Team Foundation Server Hosting
Become a Sponsor
 Tag Cloud
 Latest Jobs
More ... 
 Latest Interview Questions
More ... 

Introduction

This article shows you how dragging and dropping graphic primitives smoothly onto the screen without flicker is easily achieved using .Net's GDI. 

The hurdles to over come are as follows:

  1. How to select a shape on the screen.
  2. How to move a shape without having to re-draw all of the other shapes.
  3. How to avoid screen flicker as the object is moved.

Implementation

Using an object-oriented design almost always simplifies the coding.  We know we are going to have shapes that need to be drawn and that we will need something to manage these shapes.   So I created an IShape interface and a User Control to manage a collection of shape objects.  A concrete ShapeRectangular class inherits the IShape interface.  I've given the Shape object the responsibility of drawing its self and determining if the mouse cursor is over it.   It also has a property to indicate that it's in the process of being dragged.  If we are dealing with just rectangles the collision detection is already done for us mostly by using .Net's Rectangle's IntersectsWith method. Our ShapeRectangular class only needs to create a Rectangle object to define its dimension.  Anytime we want to test to see if the mouse cursor is over it then we just pass the mouse cursor's point to an instance of the ShapeRectangular and create a small rectangle where there mouse's point is and then pass this to the Rectangle's IntersectsWith method.  Listing 1 shows a couple of the significant methods in the Shape object.  Figure 1 shows the complete class diagram for the Shapes.  Note that A Shape class has also been created to contain the common properties and methods to avoid duplicating code.

Listing 1 - Methods in the ShapeRectangle class

/// <summary>

/// Draw the rectangle to the graphics

/// </summary>

/// <param name="pGraphics"></param>

void IShape.Draw(Graphics pGraphics)

{

          pGraphics.FillRectangle(_brush, _rectRegion);

}

 

/// <summary>

/// returns true if the mouse pointer is inside the shape

/// </summary>

/// <param name="pPoint"></param>

/// <returns></returns>

bool IShape.IsCollision(Point pPoint)

{

          // determine type of collision

          /*

          * For now just see if there is any kind of collision

          * with the mouse cursor

          */

          Rectangle lrectCursorRect=new Rectangle(pPoint,new Size(2,2));

          if (this._rectRegion.IntersectsWith(lrectCursorRect))

          {

                   this._cursor = Cursors.SizeAll;

                   return true;

          }

          return false;

}

Figure 1 - Class Diagram for Shapes

 

That doesn't leave that much for the User control to do.  Its main responsibility will be to manage the canvas.  To avoid flicker we'll use double buffering which means we will first draw our shapes onto a bitmap and then draw our bitmap to the control's surface.  This will easily allow us to avoid re-drawing all of the other shapes during the drag process as well.   We do this by when someone clicks the mouse to select the drag shape.  We will put code in the mouse click event to draw all the shapes not being dragged onto a persisted bitmap.  Then cloning the persisted bitmap will create a temporary bitmap on which we will draw the shape being dragged.  At the end of the method we'll draw this bitmap to the control's surface.  Then on the mouse move event we will just clone the persisted bitmap having all the shapes not being dragged and draw only the shape being dragged as the user moves the mouse.  The temporary bitmap will again be drawn to the control's surface at the end of the mouse move event.  Listing 2 shows some of the significant methods in the User Control.  Both lBitmapdrawingArea and lBitmapOriginalDrawingArea are persisted class variables.  The lBitmapOriginalDrawingArea is a blank canvas where as the lBitmapdrawingArea will always have the non-dragged shapes drawn on it.   The lBitMapTemp is a local method variable and is created by cloning the lBitmapdrawingArea and then drawing the Shapes being dragged onto it.  Figure 2 shows the class diagram for the User Control. 

Listing 2 - Code in the User Control to manage the Canvas

/// <summary>

/// On mouse down either switch a dragged shape to a static

/// shape

/// or change a static shape to a dragged shape.

/// </summary>

private void DragImages_MouseDown(object sender, MouseEventArgs e)

{

          int lX = e.X ;

          int lY = e.Y ;

          Point lpntMouseLocation = new Point(lX, lY);

          lBitmapdrawingArea =(Bitmap)lBitmapOriginalDrawingArea.Clone();

          Graphics lGraphics = Graphics.FromImage(lBitmapdrawingArea);

 

          //Draw out the non dragged shapes and non selected on

          // the Canvas to be saved;

          foreach (IShape pShape in this.lListShapes)

          {

                   /*

                   * If we are not a dragged shape but our mouse is contained in our

                   * shape then we want to become a dragged shape

                   */

                   if (!pShape.IsDragged && pShape.IsCollision(lpntMouseLocation))

                   {

                             Point lpntOffset = new Point(pShape.Location.X -

                                      lpntMouseLocation.X,pShape.Location.Y -

                                                   lpntMouseLocation.Y);

                             pShape.MouseOffset = lpntOffset;

                             pShape.IsDragged = true;

                             continue;

                   }

                   else

                   {

                             // we are just a static shape that needs to

                             //be drawn to the canvas

                             pShape.IsDragged = false;

                             pShape.Brush = Brushes.Red;

                             this.Cursor = this.DefaultCursor;

                             pShape.Draw(lGraphics);

                   }

          }

          lGraphics.Dispose();

          // Draw out the dragged shapes on the Cavas not to save;

          Bitmap lBitMapTemp = (Bitmap)lBitmapdrawingArea.Clone();

          lGraphics = Graphics.FromImage(lBitMapTemp);

          foreach (IShape pShape in this.lListShapes)

          {

                    if (pShape.IsDragged)

                    {

                             pShape.Location = lpntMouseLocation;

                             pShape.Brush = Brushes.Blue;

                             pShape.Draw(lGraphics);

                    }

          }

          lGraphics.Dispose();

          // draw the canvas to the control's surface

          Graphics lGraphicsForm = this.CreateGraphics();

          lGraphicsForm.DrawImage(lBitMapTemp, new Point(0, 0));

          lGraphicsForm.Dispose();

}

 

/// <summary>

/// Draw our dragged images as the user moves the mouse

/// Change the mouse cursor when over a static shape if nothing

/// is being dragged

/// </summary>

private void DragImages_MouseMove(object sender, MouseEventArgs e)

{

          Bitmap lBitmap = (Bitmap)lBitmapdrawingArea.Clone();

          Graphics lGraphics = Graphics.FromImage(lBitmap);

          int lX = e.X ;

          int lY = e.Y;

          Point lLocation = new Point(lX, lY);

          Cursor lCursor = null;

          foreach (IShape pShape in this.lListShapes)

          {

                   //if the mouse cursor is over a shpae then change it.

                   if (pShape.IsCollision(new Point(lX, lY)))

                   {

                             lCursor = pShape.GetCursor();

                   }

                   // if dragged then change its location

                   if (pShape.IsDragged)

                   {

                             pShape.Location = lLocation;

                             pShape.Draw(lGraphics);

                   }

          }

 

          // change the cursor if over a shape

          if (lCursor != null)

          {

                   this.Cursor = lCursor;

          }

          else

          {

                   this.Cursor = this.DefaultCursor;

          }

          lGraphics.Dispose();

 

          // draw the bitmap canvas to the control's surface

          Graphics lGraphicsForm = this.CreateGraphics();

          lGraphicsForm.DrawImage(lBitmap, new Point(0, 0));

          lGraphicsForm.Dispose();

} 

}

Figure 2 - User Control Class Diagram

Conclusion

This article shows how easy it is to do double buffering and implementing drag and drop with graphic primitives in .Net.  The sample code is written generically enough so that it could be extended to drag other shapes. For example for fun you could modify the code to drag and drop poker chips in a Black Jack game or maybe even something useful.

Comment Request!
Thank you for reading this post. Please post your feedback, question, or comments about this post Here.
Login to add your contents and source code to this article
 [Top] Rate this article
 
 About the author
 
Mike Clark
Mike is a software consultant with 10+ development experience who works on IT projects mainly for Microsoft and Boeing. Most of these projects have been intranet based web sites connected to a SQL database. In the last 6 years, Mike has focused on C# and SQL Server development.
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Discover the top 5 tips for understanding .NET
Ricky Leeks presents the top 5 tips for understanding .NET Interoperability. Learn more.
Nevron Chart for .NET 2010.1 Now Available
The leading .NET charting control now features PDF, Flash and Silverlight export, visualization of large datasets and more. Deliver true charting functionality to your BI, Scorecard, Presentation or Scientific apps. Download evaluation now.
ASP.NET 4 Hosting
Get 2 Months Free of ASP.NET Hosting for Only $4.95/month! Receive FREE MS SQL and MySQL Databases Including ASP.NET 4/3.5, MVC 3.0, Silverlight 4, Windows 2008/IIS 7.0 Plus FREE IIS 7 Modules. Host UNLIMITED ASP.NET Web Sites – Click Here!
 
 Post a Feedback, Comment, or Question about this article
Subject:
Comment:
Nevron Chart
Become a Sponsor
 Comments
Great artcle by Mahesh On August 15, 2006
Good article Mike. Keep up the good work.
Reply | Email | Modify 
Are complete sources available? by Miki On September 20, 2007
Can you please post the complete source and project files for this example? I'm having trouble implementing this. Thanks
Reply | Email | Modify 
Runs out of memory by dumbass On January 20, 2009
If you place several rectangles, move them around a bunch and click on them a bunch, you run out of ram. A first chance exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll An unhandled exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll Additional information: Out of memory.
Reply | Email | Modify 
That's what i'm looking for!! by Chris On October 10, 2009
Awesome!!!
Reply | Email | Modify 
Memory Leak Solution by raptor00 On May 9, 2011
To get rid of the memory leak add lBitmap.Dispose(); at the end of DragImages.DragImages_MouseMove
Reply | Email | Modify 
changes by nac On August 16, 2011
hi mike, this is very useful article. i am electronics engineer and learning c# programming and interested in game programming. i wanted make some changes in your code but i was not successful. i am some what weak with interfaces may be that's why. i want to make two changes and want your tips for that. 1) i want the rectangles should not cross the control, i want the rectangle should fully visible it should cross the borders. 2) one rectangle should not overlap the another. i will be very thankful to you if you give me tips for changes above in code or full code with changes. thanks
Reply | Email | Modify 
DevExpress Free UI Controls
 © 2012  contents copyright of their authors. Rest everything copyright Mindcracker. All rights reserved.