Click here to Skip to main content
15,868,019 members
Articles / Programming Languages / C#
Article

Combining GDI and GDI+ to Draw Rubber Band Rectangles

Rate me:
Please Sign up or sign in to vote.
4.75/5 (24 votes)
9 Sep 20036 min read 269.5K   3.8K   68   52
Demonstrates drawng rubber band rectangles using GDI in a .NET GDI app

Introduction

I have been frustrated, like many others apparently, by the lack of XOR drawing capabilities in the .NET Framework. This is of particular interest when it’s needed to outline a selected Region of Interest in a graphic. I’ve seen several approaches to solve this problem that have, for the most part, stayed within the .NET Framework context, but they were fairly complex. Thus, I was inspired when I saw the article "Using GDI and GDI+ mixed drawing rubber band lines" by sedatkurt in the MFC/C++ >> GDI >> Unedited Reader Contributions in The CodeProject. I was sure it could be extended to rectangles as well.

Background

Anyone who has used a graphics program has probably also used a selection rectangle to act on only a portion of the displayed image. In historical Windows® applications, this selection rectangle is normally drawn by the mouse from a starting corner with the left mouse button depressed until the button is released. The mouse position at each move event is the second corner of the drawn rectangle. Along the way, the rectangle is therefore drawn and erased many times until the final version is drawn at mouse button up. If the drawing mode is set to an XOR raster operation (ROP), the rectangle is usually completely visible since all bits of the pixels are XORed with the color of the drawing pen. More importantly, redrawing in XOR mode along the same rectangle with the same pen automatically restores the original pixel colors, effectively erasing the rectangle. Unfortunately, the .NET Framework Team did not include ROP drawing control in GDI+.

The Core Code

While I adopted the basic concept that sedatkurt showed in his code, I was primarily interested in developing a strictly rubber band rectangle drawing capability. Consequently, all and only the rubber band rectangle related code is concentrated into the RubberbandRectangle class in the RubberbandRects namespace in the file RubberbandRects.cs. This means hardcoding the pen style to draw the rectangle border as a dotted line (PS_DOT), an XOR drawing mode for the pen (R2_XORPEN), a pen width of 1 pixel (doesn't have to be pretty, just visible), and a brush style to NOT fill in the rectangle (NULL_BRUSH) so the underlying graphics are still visible. The resultant public API of RubberbandRectangle is quite compact:

C#
public class RubberbandRectangle
{
  public RubberbandRectangle();
  public int PenStyle { get/set }
  public void DrawXORRectangle( Graphics grp,
              int X1, int Y1,
              int X2, int Y2 );
}

The default constructor sets the pen style to the default value of PS_DOT. I was not originally going to include a capability to reset this value, but I relented and made the pen style a property with get/set capability. The pen color is fixed internally in the class as a predefined BLACK_PEN in the CreatePen call, although I kept sedatkurt's RGB conversion macro in case.

The process of drawing the XORed rectangle begins with extracting the Win32 GDI device context from the GDI+ Graphics object passed to the function. A black dotted pen is created one pixel wide. The ROP drawing mode is then set to XOR and the new pen selected into the device context; the old pen's handle is saved for replacement later (always clean up resources when you're finished with them). A stock NULL_BRUSH is created and simultaneously selected into the device context, again saving the old brush handle for later. The drawing is now performed, the old brush and pen put back into the device context, and the new pen deleted. Note that stock resources do not need to be deleted since they're only borrowed anyway. The device context is released and the function is finished.

Using the Code

The demo application is in the file MainForm.cs. It is a simple WindowsForm with a number of rectangles painted on its client area in the MainForm_Paint event handler. However, I've also added a call to DrawXORRectangle() if a rubber band rectangle was present (the flag haveRect is set) so that the dotted rectangle is also then redrawn.

The rectangle drawing functionality is nearly the same as in sedatkurt's code. The rubber banding operation is initiated in MainForm_MouseDown where the mouseDown flag is set to indicate that the mouse button is down. The initial point of the mouse down event is stored in XDown and YDown. All of this presumes that it was the Left mouse button that is being pressed. I use a Right mouse button press to initiate an operation to clear the rubber band rectangle from the screen (clear the haveRect flag and call Invalidate() ).

The stage is now set for the actual drawing, which takes place in the MainForm_MouseMove event handler. Drawing will actually only occur if the mouse button is down. This prevents an attempt at drawing when the mouse is simply run across the app. If the mouse is down and moving, a rectangle has already been drawn and must be erased with a call to DrawXORRectangle(). The rectangle is then redrawn through a call to DrawXORRectangle() with one corner at the new mouse coordinates. The new coordinates are saved and the moving flag set.

The final part of the rubber band rectangle drawing occurs in the MainForm_MouseMove event handler. The mouseDown and mouseMove flags are cleared and the haveRect flag is set. We now have a dotted rectangle in the client area of the app window.

Points of Interest

This code was developed in SharpDevelop, an IDE available for free at http://www.icsharpcode.net. It is an evolving, beta level project written in C# and comes with full source code. While it still has warts, it is improving by the month and the price is right. For anything but large team, production code development, it works fine. I used version 0.96 which compiles with .NET Framework v1.1, so you will need the latest .NET version to run the demo app. There is no apparent reason it won't compile with Visual Studio 2003 or any other Visual Studio / .NET Framework pair as well.

What's Next

The code in the RubberbandRectangle class is a stripped down version I had evolved keeping a lot of the enums and functions implied by sedatkurt's code and adding the enums and functions appropriate for the rectangle (or rounded rectangle or ellipse or polygon) drawing cases. In the end, I stripped it all out since I had no interest in demonstrating it and the extension of what's left is fairly straight forward. A possible extension in the same vein as rubber banding is the ability to move bitmaps around the client area with the mouse. A brush made from the bitmap should be as effecting in XOR drawing mode as a dotted pen. At any rate, enjoy.

History

  • New code - no changes.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionI want to get permission to use your Code Pin
Member 139991158-Jul-19 16:57
Member 139991158-Jul-19 16:57 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey26-Feb-12 21:56
professionalManoj Kumar Choubey26-Feb-12 21:56 
QuestionHow to avoid/reduce flickering Pin
TheMperor24-Feb-12 9:50
TheMperor24-Feb-12 9:50 
QuestionHow do I draw a general polygon with xor? Pin
phojholt1-Jan-11 23:12
phojholt1-Jan-11 23:12 
Thank you for a very nice article.

I need to invert a general polygon shape using the Polygon function.
In Win32 gdi the Polygon function is defined as:

BOOL Polygon(__in HDC hdc,__in const POINT *lpPoints,__in int nCount);

But how do I handle the second parameter from C#?

And do you know a good brief introduction on how to make calls to the Win32 gdi from c#? Rose | [Rose]
GeneralVery good Pin
bluealert4556-Jul-09 15:24
bluealert4556-Jul-09 15:24 
GeneralProblem removing the frame Pin
buyValu23-Aug-08 6:13
buyValu23-Aug-08 6:13 
QuestionHow to Select Multiple Controls at Runtime Pin
BLekha14-Mar-07 21:40
BLekha14-Mar-07 21:40 
GeneralAlternative approach Pin
zanderxo26-Dec-06 18:43
zanderxo26-Dec-06 18:43 
NewsControlPaint.DrawReversibleFrame() Pin
Sergey Arhipenko25-Sep-06 22:27
Sergey Arhipenko25-Sep-06 22:27 
GeneralBug when using code in a scrollable panel Pin
Frédéric BEAULIEU12-May-06 3:52
Frédéric BEAULIEU12-May-06 3:52 
QuestionNeed to draw a rubberband rectangle and capture coordinates Pin
sowjanya_d7-Dec-05 19:19
sowjanya_d7-Dec-05 19:19 
GeneralDifferent way Pin
T.D.Brown25-Sep-05 17:01
T.D.Brown25-Sep-05 17:01 
GeneralRe: Different way Pin
gabegabe12-Apr-07 20:12
gabegabe12-Apr-07 20:12 
GeneralRe: Different way - no, not really. Pin
hughd23-Jul-07 12:28
hughd23-Jul-07 12:28 
GeneralDrwaing on top of web browser control Pin
Balaji Vishnumolakala25-Aug-05 11:37
Balaji Vishnumolakala25-Aug-05 11:37 
GeneralRe: Drwaing on top of web browser control Pin
cthomas26-Aug-05 13:56
cthomas26-Aug-05 13:56 
Generalchange background of Rubber Band Rectangle Pin
is_vlb507-Jun-05 8:35
is_vlb507-Jun-05 8:35 
GeneralRe: change background of Rubber Band Rectangle Pin
cthomas7-Jun-05 16:35
cthomas7-Jun-05 16:35 
GeneralRe: change background of Rubber Band Rectangle Pin
is_vlb507-Jun-05 21:22
is_vlb507-Jun-05 21:22 
GeneralRe: change background of Rubber Band Rectangle Pin
cthomas8-Jun-05 17:40
cthomas8-Jun-05 17:40 
QuestionWhere did those constants come from? Pin
Qwertie25628-Apr-05 7:12
Qwertie25628-Apr-05 7:12 
AnswerRe: Where did those constants come from? Pin
Qwertie25628-Apr-05 7:21
Qwertie25628-Apr-05 7:21 
GeneralRe: Where did those constants come from? Pin
Qwertie25628-Apr-05 7:34
Qwertie25628-Apr-05 7:34 
GeneralRe: Where did those constants come from? Pin
cthomas26-Aug-05 13:51
cthomas26-Aug-05 13:51 
GeneralBetter get a license for this... Pin
Mike Stevenson19-Apr-04 13:18
Mike Stevenson19-Apr-04 13:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.