• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Restoring raster graphics and rubberbands to .net
 

Restoring raster graphics and rubberbands to .net

on

  • 870 views

Described the ins and outs of GDI32 Raster Graphics under .NET. It also demonstrates how to perform rubberband selection of objects on a form. Additional demonstrations show how to use GDI+ to draw ...

Described the ins and outs of GDI32 Raster Graphics under .NET. It also demonstrates how to perform rubberband selection of objects on a form. Additional demonstrations show how to use GDI+ to draw selection rectangles. The first GDI+ example demonstrates the DrawReverseRectangle() method, and explains its shortcomings. It also demonstrates how to cleaning draw a GDI+ selection rectangle with a translucent background, as is standard on Vista and Windows 7.

Statistics

Views

Total Views
870
Views on SlideShare
870
Embed Views
0

Actions

Likes
0
Downloads
8
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Restoring raster graphics and rubberbands to .net Restoring raster graphics and rubberbands to .net Document Transcript

    • Restoring Raster Graphics and Rubber Bands to .NETBy David Ross GobenCopyright © 2011 by David Ross GobenAll rights reserved.Last Update: Wednesday, June 08, 2011This is a sample excerpt from the free PDF e-book, Enhancing Visual Basic .NET Far Beyond the Scope of Visual Basic 6.0, by DavidRoss Goben. Download this e-book, and its free companion, Navigating Your Way Through Visual Basic 6.0 Upgrades to Visual Basic.NET, also by David Ross Goben, at www.slideshare.net/davidrossgoben. They are also available on Google Docs athttps://docs.google.com/leaf?id=0B_Dj_dKazINlN2JlY2EwMmEtNGUyMy00NzQzLTliN2QtMDhlZTc5NDUzY2E5&sort=name&layout=list&num=50. The Google site also contains the source code in a file named Restore Raster Graphics and Rubberbands Source.zip.Table of ContentsRestoring Raster Graphics and Rubber Bands to .NET ................................................................................223 Getting an Image Graphics Object Without Going Through the Paint() Event.........................................................224 Restoring Raster Operations to .NET ......................................................................................................................224 Getting One’s Hands Dirty................................................................................................................................... 225 The GDI32 Class ................................................................................................................................................ 227 Emulating a Selection Rubber Band under GDI.......................................................................................................233 Emulating a Selection Rubber Band under GDI+.....................................................................................................234 Using the GDI+ DrawReversibleFrame() Method.................................................................................................. 234 Using the GDI+ DrawRectangle() Method and a Translucent Brush ...................................................................... 235About the Author ............................................................................................................................................ 237
    • Restoring Raster Graphics and Rubber Bands to .NETYou may be quick to notice that VB.NET does not support general drawing commands as a part of itsintrinsic language repertoire, as had been the case under VB6. I say thank goodness! I still cannot believethat the very primitive DOS-BASIC-style drawing commands had managed to make their way all the wayinto VB6, and especially as a part of the fundamental language, itself. I had been very fearful that the broadand largely non-professional, albeit fiercely devoted user-base would force VB.NET to end up suffering asimilar fate during its development cycle. Just look at the damage hoards of die-hard fans had forced onbitwise and logical operations, changing them from innovative and more clearly defined to just archaic andso-so (“Mongo fears change,” to quote Mongo from Mel Brook’s classic comedy, Blazing Saddles).Under .NET, any specialized features, such as drawing, are grouped under a separate namespace. This is avery good thing. By placing them as members in a separate static class, we can use the methods in that classto draw on graphical objects, or, later, when more advanced graphical capabilities are added to .NET, we caninstead use the members of the namespace devoted to that paradigm. In the meantime we are not clutteringup the language, as was sadly done to VB6. For example, suppose someone had developed a class supportingOpenGL or Direct3D drawing that often used “standard” drawing commands. If we forget to specify orimport their namespace, we would find ourselves drawing using standard features instead of enhancedfeatures, and we would then have to track down the bugs by finding where we forgot to reference ourgraphical class. By keeping all these things in different namespaces, we have simplified the language andallowed for easy expansion of its capabilities, and there is also less likelihood for method name collisions.However, as easy and better as all this is, drawing is accordingly different under VB.NET than under VB6.Granted, some stuff is easy, like changing the VB6 Cls command to Clear under VB.NET.Others are easy to adapt to, such as using the System.Drawing.Graphics object to draw shapes, lines,arcs, circles, etc. to a PictureBox. Indeed, these are now much easier to use, and are also a lot faster.Some others do not exist any more, because it is much easier to provide and maintain these features withinyour own code; they no longer eat up valuable resource space if you do not use them, which is the case inmost situations. Cases in point are the CurrentX and CurrentY last drawing location properties. Thesefeatures are easy enough to compute and update locally without eating control resources to store them forevery object, when only a few of them would actually need to make use of them.Others are not obvious. For instance, the VB6 Point command, now GetPixel(), to get a point on animage, must be accessed through a Bitmap object, which, logically, is where such a command should be.And regarding Bitmap objects – most examples tell you to create a new Bitmap object and fill it will theimage from the PictureBox, but this is a waste of time and resources if we just need to access the image orquickly draw to it. Indeed, the only time we should really need to do something like that is if we will bemaking numerous changes to the image, but we want to update the actual displayed image only once, toeliminate flicker. Otherwise, instead of chewing up resources and valuable time doing something like this:Dim Bmp As New Bitmap(Me.PictureBox1.Image) instantiate a new copy of the image as a bitmap (useful for numerous changes)We can instead simply do the following (note that a Bitmap and an Image are identical in structure):Dim Bmp As Bitmap = DirectCast(Me.PictureBox1.Image, Bitmap) define a reference to the image without instantiating a new imageCertainly, the only times we would need to create a separate bitmap is when, as previously stated, we need tomake numerous changes, or if the image object might be set to Nothing, which a blank PictureBox or a formwith no background image may possess. In that case, we can declare them like this:Dim Bmp As New Bitmap(Me.Width, Me.Height, Me.CreateGraphics) create blank Form bitmapor...Dim Bmp As New Bitmap(Me.PictureBox1.Width, Me.PictureBox1.Height, Me.PictureBox1.CreateGraphics) create blank PictureBox1 bitmapRegardless of how we define it, we can then get the color at a specific point using a command like this:Dim Clr As Color = Bmp.GetPixel(intX, intY) Get an ARGB color value at a specified X,Y point (duplicates the VB6 Point() method) Page –223–
    • To set a single point, we are very often advised to draw a circle with a radius of 1, like this:e.Graphics.DrawEllipse(Pens.Black, 100, 100, 1, 1) using Elimpse to draw a point at 100x, 100y with width and height of 1NOTE: Rather than using e.Graphics, as shown here, which is provided by a Paint event, we can instead easily create ourown graphical interface to the object, as will be demonstrated shortly, and use it outside of Paint events.Though slothful, it is not as wasteful as it might appear, because .NET will see the width and height areset to 1, and so it will just draw a dot. Even so, the SetPixel() P/Invoke under gdiplus.dll is muchfaster. And because of this, I find the above ellipse advice odd, because VB.NET does in fact support amuch faster SetPixel() method under the Bitmap object, complementing its GetPixel() method,which does in fact provide an interface to the fore-mentioned SetPixel() P/Invoke, as shown here:Bmp.SetPixel(100, 100, Color.Black) set a single point on the image (duplicates the VB6 graphical Set command)Getting an Image Graphics Object Without Going Through the Paint() EventThe main problem many new users to VB.NET run into is when they try to draw shapes at will to somethinglike a PictureBox. It may be easy to draw within its Paint() event (as demonstrated earlier in this document– see the article, Easy Ways to Draw Lines and Shapes, and to Paint in VB.NET, on page 143, for instance),where the PaintEventArgs “e” parameter provides us with access to a convenient Graphics object that is pre-tied to the PictureBox. However, they seem to get a bit stuck when they want to simply draw a unique line,shape, or text to the PictureBox, such as a drawing program might use. In fact, I have seen code where, out offrustration, people have placed specialized hooks into a Paint() event that passes back the event argumentobject, or more often, saving it off somewhere, implementing it in a customized method so that they couldapply those non-typical drawing features. But this leads only to exception errors.But accessing this graphical interface is really not complicated. We need to understand is that thegraphical interface provided by the Paint() event is just a simple System.Drawing.Graphics object thatis tied to the PictureBox, and this is something that is quite easy to generate within our code.For example, suppose we want to draw to PictureBox1. To access a graphical interface to it using anobject named eg (reminding us of e.graphics), we could use code like this:generate a graphical interface to a picture box image (same as e.Graphics provided by a System.Windows.Forms.PaintEventArgs object)Dim eg As System.Drawing.Graphics = Me.PictureBox1.CreateGraphicsYou may also have seen such objects defined like the following, which is also valid:Dim egf As System.Drawing.Graphics = System.Drawing.Graphics.FromHwnd(Me.PictureBox1.Handle) a graphical interface to a picture box.Dim egi As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(Me.PictureBox1.Image) useful for drawing text or copying images.NOTE: If you are drawing shapes, use the egf definition for the control, rather than egi for the image on the control;otherwise the drawn shapes may not display. The first alters the displayed image, the second alters its stored image.Suppose we want to draw a simple white cross through an image when the user clicks on it. Try this:react to user clicking on picturePrivate Sub PictureBox1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PictureBox1.Click Try Dim eg As System.Drawing.Graphics = Me.PictureBox1.CreateGraphics generate a graphical interface to a picture box image With Me.PictureBox1 eg.DrawLine(Pens.White, 0, 0, .Width, .Height) draw a white line from top-left to bottom-right eg.DrawLine(Pens.White, 0, .Height, .Width, 0) draw a white line from bottom-left to top-right End With Catch ignore errors (usually happens when no image data is loaded in a PictureBox, so the Image object is set to Nothing) End TryEnd SubRestoring Raster Operations to .NETOther graphical features seem to be lost in limbo, such as the VB6 DrawMode property, which allowedyou to specify how a pen object was to be drawn upon the background. It could be normal(R2_CopyPen), R2_XOrPen, R2_NotXOrPen, R2_MergePen, R2_MergePenNot, or whatever rasterdrawing operation you would want to perform. The reason for this is that Raster operations are notsupported by GDI+ under .NET (regardless of the fact that these are in fact simple processes). Page –224–
    • GDI+, introduced in 2001 with WinXP, is faster and more powerful than standard GDI, and this isbecause GDI+ is linked directly to the operations available to graphics card though its firmware(software burned into a board-based read-only memory chip (ROM)). It therefore naturally lacks supportfor Raster Operations (memory-mapped operations) because the card’s firmware lacks it.What this means to most people is that the raster-generated selection rectangle that is used so often inWindows is not supported by GDI+ under .NET. Indeed, GDI+ was primarily meant to be used byC/C++ developers, but it was quite naturally incorporated into .NET.For example, under GDI+, the System.Drawing.Graphics.CompositingMode property, which is theclosest thing it has to the GDI Raster Operations property, has only 2 enumerated settings: Member name Description SourceOver Specifies that when a color is rendered, it is blended with the background color. The blend is determined by the alpha component of the color being rendered. GDI has no similar command. SourceCopy Specifies that when a color is rendered, it overwrites the background color (Default). This is just like the Raster R2_CopyPen command.Conversely, GDI Raster operations implemented 17 settings, as defined below: Member name Description R2_Black Specifies black pen color. R2_CopyPen Specifies the pen color. The pen over-writes anything it draws over (Default). R2_MakePenNot Specifies a combination of the colors are common to both the pen and the inverse of the display. R2_MaskNotPen Specifies a combination of the colors are common to the background color and the inverse of the pen. R2_MaskPen Specifies a combination of the colors common to both the pen and the display. R2_MergeNotPen Specifies a combination of the display color and the inverse of the pen color. R2_MergePen Specifies a combination of the pen color and the display color. R2_MergePenNot Specifies a combination of the pen color and the inverse of the display color. R2_NoOperation Specifies no operation; the output remains unchanged. R2_Not Specifies the inverse of the display color. R2_NotCopyPen Specifies the inverse of R2_CopyPen. R2_NotMaskPen Specifies the inverse of R2_MaskPen. R2_NotMergePen Specifies the inverse of R2_MergePen. R2_NotXOrPen Specifies an inverse of R2_XOrPen. R2_White Specifies a white pen color. R2_XOrPen Specifies a combination of the colors in the pen and in the display color, but not in both.As you can see, even with all this functionality, there is no alpha-channel-blending support provided byGDI, such as the GDI+ SourceOver setting specified. The other GDI+ setting, SourceCopy, is duplicatedby the GDI R2_CopyPen setting. No other correlations between these two platforms are yet supported.Getting One’s Hands DirtySo, if you want to implement GDI Raster operations under .NET, you need to get your hands dirty andperform GDI operations in much the same way as they are done under C/C++. This involves obtaining thedevice context (DC) of the object we want to draw to, creating pens and brushes to draw with, select theminto the DC, perform the drawing operation, and finally release these created resources.Of course, even though this can seem to get a bit involved, it all boils down to a uniform series of simple,repeated processes that can be performed for you in an automated way, so you can focus entirely on theactual drawing tasks. What this would actually involve are two support methods that can be invoked “behindthe curtain”, out of sight and mind; the first will automatically set up for a drawing task you want to perform,and the second will automatically close down from it after your drawing task.The first method, which we will name InitPenAndBrush(), will get the Graphical interface and the Devicecontext of the target object, create the foreground drawing pen with a selected color and width, create thebackground brush (the fill) with its color and pattern, select each of them into the object we want to draw on,save the pen and brush that were previously assigned, and select the type of Raster Operation we need.Next, just like we would do under GDI+, we perform the actual drawing task, such as drawing a line.After we perform the drawing task, the other method is invoked, which we will nameDisposeResources(), that will select back in the previous pen and brush to the target, and finallyrelease the resources of our new pen, brush, graphical interface, and device context. Page –225–
    • Of course, to make this even easier, we can create a class that performs all this added work for each ofthe drawing functions for us, so all we have to do is concern ourselves with invoking its public methodsand properties; we will simply define the pen we want to draw with, as needed, the background brush, ifwe need it, the type of Raster Operation, and, naturally, to invoke any required drawing operations.The GDI32 class, listed on the next page, is derived from various C++/C# sources, and also from a lot ofmy own current and previous work (actually, one of the anonymous C++/C# postings in fact credited asadly unremembered VB.NET author who had designed an ordered structure to the class and defined itsnaming conventions – if anyone recognizes it; please let me know). Note that any additional features youneed can also be added quite easily.Once you invoke an instance of this class, you can set the drawing pen’s color and width, the brush’scolor and hatch style (HS_SOLID is the default), if you need to change them (they are saved within theclass properties, so you do not need to set them with every drawing command), and invoke any of therequired drawing operations: SetPoint(), GetPoint(), DrawLine(), DrawCircle(), DrawEllipse(),DrawArc(), DrawRectangle(), DrawRoundRect(), and DrawObround().To implement this class into your own code is very simple. Somewhere within your code, perhaps aspublicly as possible, you would declare an instance of the GDI32 class:Friend m_GDI As New GDI32 instantiate an instace of the GDI32 classThen, when you are ready to draw, be sure your Pen color (foreground), Brush color (background), andthe Raster operation are as you need them to be. By default, the Pen color is White, the Pen style isPS_Solid, the Brush color is Transparent, the Brush hatch pattern is HS_Solid (no pattern), and theRaster operation is R2_CopyPen. This will draw using a solid white pen with an invisible brush (nobackground color for the pen will be painted over the target surface; typical for ellipses and rectangles).NOTE: If the Pen color is Transparent, then the drawing operation will use a stock Null Pen (invisible). If the Brush coloris Transparent, then the drawing operation will use a stock Null Brush.You set the properties for the drawing operation by change the properties of the class. For example:m_GDI.PenColor = Color.LightBlue set pen color to Light Blue (default is Color.White)m_GDI.PenStyle = PenStyles.PS_DASH alternate dashes with dots (default is PenStyles.PS_SOLID)m_GDI.PenWidth = 3 Set the pen width to 3 pixels (Default is 1)m_GDI.BrushHatch = HatchStyle.HS_CROSS set the brush style to Crosses (default is HatchStyle.HS_SOLID)m_GDI.BrushColor = Color.Blue set the brush color to Blue (default is Color.Transparent)m_GDI.RasterOp = RasterOps.R2_XOrPen merge the pen and background colors (default is RasterOps.R2_CopyPen)NOTE: GDI requires RGB colors, not the ARGB colors that are part of the standard Color Palette and GDI+. As such, wemust convert them from ARGB to RGB. Most gurus will tell you to simply perform a Clr.ToArgb And &HFFFFFF operation,but this is not enough. ARGB is stored internally as AARRGGBB, for Alpha, Red, Green, and Blue, where each letterrepresents a Hexadecimal digit. However, RGB is stored as 00BBGGRR. As such, the Red and Blue color values must alsobe swapped. I do this for you automatically within the class using the ARGBtoRGB() function, defined below:************************************************************************************************************* Function: ARGBtoRGB Helper function to covert Alpha Color ARGB value (AARRGGBB) to RGB (00BBGGRR)*************************************************************************************************************Private Function ARGBtoRGB(ByVal clr As Color) As Integer Dim vARGB As Integer = clr.ToArgb convert color value to AARRGGBB Return RGB((vARGB >> 16) And &HFF, (vARGB >> 8) And &HFF, vARGB And &HFF) return RGB colorEnd FunctionYou then perform the desired drawing operation. Because several of the methods, such as DrawArc(),DrawEllipse(), DrawRectangle(), DrawRoundRect(), and DrawObround() have overloaded methods, you canspecify them by providing Point objects to define their top-left and bottom-right bounds, or by providing abounding Rectangle object. Also, you must provide a graphical interface to the target object you want todraw upon. For example:draw a rectangle from PictureBox coordinates 25,25 (top-left) to 100,100 (bottom-right)m_GDI.DrawRectangle(Me.PictureBox1.CreateGraphics, New Point(25, 25), New Point(100, 100))Now draw as you see fit. You need to change colors and Raster operations only as needed, not every time. Page –226–
    • The GDI32 ClassWhat follows is the GDI32 class:Option Strict OnOption Explicit On************************************************************************************** GDI32 - GDI Support for .NET**************************************************************************************#Region "GDI32 Enumerations"***************************************************************************************************************************** Enumerations***************************************************************************************************************************** Pen Styles (how lines are drawn)Public Enum PenStyles As Integer PS_SOLID = &H0 A pen style that is a solid color. ────── PS_DASH = &H1 A pen style that is dashed. ------ PS_DOT = &H2 A pen style that is dotted. ●●●●●● PS_DASHDOT = &H3 A pen style that consists of alternating dashes and dots. -●-●-● PS_DASHDOTDOT = &H4 A pen style that consists of dashes and double dots. -●●-●● PS_NULL = &H5 A pen style that is invisible. PS_INSIDEFRAME = &H6 A pen style that is a solid color. When this style is specified in a drawing record that takes a bounding rectangle, the dimensions of the figure are shrunk so that it fits entirely in the bounding rectangle, taking into account the width of the pen.End EnumType of Raster operation (how drawing interacts with the background)Public Enum RasterOps As Integer R2_Black = 1 Specifies black pen color. R2_NotMergePen = 2 Specifies the inverse of MergePen. R2_MaskNotPen = 3 Specifies a combination of the colors are common to the background color and the inverse of the pen. R2_NotCopyPen = 4 Specifies the inverse of CopyPen. R2_MakePenNot = 5 Specifies a combination of the colors are common to both the pen and the inverse of the display. R2_Not = 6 Specifies the inverse of the display color. R2_XOrPen = 7 Specifies a combination of the colors in the pen and in the display color, but not in both. R2_NotMaskPen = 8 Specifies the inverse of MaskPen. R2_MaskPen = 9 Specifies a combination of the colors common to both the pen and the display. R2_NotXOrPen = 10 Specifies an inverse of XOrPen. R2_NoOperation = 11 Specifies no operation; the output remains unchanged. R2_MergeNotPen = 12 Specifies a combination of the display color and the inverse of the pen color. R2_CopyPen = 13 Specifies the pen color. R2_MergePenNot = 14 Specifies a combination of the pen color and the inverse of the display color. R2_MergePen = 15 Specifies a combination of the pen color and the display color. R2_White = 16 Specifies a white pen color.End EnumEnd EnumHatch StylesPublic Enum HatchStyle As Integer HS_HORIZONTAL = 0 ----- A horizontal hatch. HS_VERTICAL = 1 ||||| A vertical hatch. HS_FDIAGONAL = 2 A 45-degree downward, left-to-right hatch. HS_BDIAGONAL = 3 ///// A 45-degree upward, left-to-right hatch. HS_CROSS = 4 +++++ A horizontal and vertical cross-hatch. HS_DIAGCROSS = 5 xxxxx A 45-degree crosshatch. HS_FDIAGONAL1 = 6 HS_BDIAGONAL1 = 7 HS_SOLID = 8 HS_DENSE1 = 9 HS_DENSE2 = 10 HS_DENSE3 = 11 HS_DENSE4 = 12 HS_DENSE5 = 13 HS_DENSE6 = 14 HS_DENSE7 = 15 HS_DENSE8 = 16 HS_NOSHADE = 17 HS_HALFTONE = 18 HS_SOLIDCLR = 19 HS_DITHEREDCLR = 20 HS_SOLIDTEXTCLR = 21 HS_DITHEREDTEXTCLR = 22 HS_SOLIDBKCLR = 23 HS_DITHEREDBKCLR = 24 HS_API_MAX = 25 End EnumStock ObjectsEnum StockObjects As Integer WHITE_BRUSH = 0 White brush. LTGRAY_BRUSH = 1 Light gray brush. GRAY_BRUSH = 2 Gray brush. DKGRAY_BRUSH = 3 Dark gray brush. BLACK_BRUSH = 4 Black brush. NULL_BRUSH = 5 Null Brush (equivalen to HOLLOW_BRUSH). HOLLOW_BRUSH = NULL_BRUSH Hollow brush (equivalent to NULL_BRUSH) WHITE_PEN = 6 White pen. BLACK_PEN = 7 Black pen. NULL_PEN = 8 Null Pen. The null pen draws nothing. OEM_FIXED_FONT = 10 Original Equiptment Manufacturer (OEM) dependent fixed-pitch (monospace) font. ANSI_FIXED_FONT = 11 Windows fixed-pitch (monospace) system font. ANSI_VAR_FONT = 12 Windows variable-pitch (proportional space) system font. SYSTEM_FONT = 13 System font. By default, the system uses the system font to draw menus, dialog box controls, and text. DEVICE_DEFAULT_FONT = 14 WinNT/Win2K/XP: Device-dependent font. DEFAULT_PALETTE = 15 Default palette. This palette consists of the static colors in the system palette. SYSTEM_FIXED_FONT = 16 Fixed-pitch (monospace) system font. This stock object is provided only for compatibility with 16-bit Windows versions earlier than 3.0. DEFAULT_GUI_FONT = 17 Default font for user interface objects such as menus and dialog boxes. This is MS Sans Serif. Compare this with SYSTEM_FONT. DC_BRUSH = 18 Win2K/XP: Solid color brush. The default color is white. The color can be changed by using the SetDCBrushColor() function. DC_PEN = 19 Win2K/XP: Solid pen color. The default color is white. The color can be changed by using the SetDCPenColor() function.End Enum#End Region************************************************************************************** GDI Class to support GDI operations not supported by GDI+ GDI+ (gdiplus.dll), a Graphical Design Interface used to take advantage of Graphic card hardware and software. It was introduced with Windows XP. It was designed for use by C/C++ users, but naturally was incorporated into the .NET platform. GDI+ offers faster operations that those provided by GDI, yet Raster operations, such as displaying rubberband lines was lost.**************************************************************************************Friend Class GDI32 Page –227–
    • #Region "GDI32 Protected Fields" ***************************************************************************************************************************** Protected Fields ***************************************************************************************************************************** Protected m_hdc As IntPtr handle to drawing context Protected m_gdiPen As IntPtr handle to pen Protected m_oldPen As IntPtr hold original pen Protected m_penColor As Color = Color.White Default drawing color Protected m_penStyle As PenStyles = PenStyles.PS_SOLID init pen to solid Protected m_penWidth As Integer = 1 pixel width of pen (line) Protected m_rasterOp As RasterOps = RasterOps.R2_CopyPen Init raster operation to normal Protected m_gdiBrush As IntPtr handle to new brush Protected m_oldBrush As IntPtr hold original brush Protected m_brushColor As Color = Color.Transparent default brush color (fill color). Protected m_brushHatch As HatchStyle = HatchStyle.HS_SOLID hatch style. None = use Solid Brush#End Region#Region "GDI32 P/Invoke Declarations" ***************************************************************************************************************************** INTEROP P/INVOKE DECLARATIONS ***************************************************************************************************************************** ----------------------------------------------------------------------------------------------------------------------------- Function: CreateSolidBrush The CreateSolidBrush function creates a logical brush that has the specified solid color. ----------------------------------------------------------------------------------------------------------------------------- Private Declare Function CreateSolidBrush Lib "gdi32.DLL" Alias "CreateSolidBrush" ( _ ByVal crColor As Integer) As IntPtr ----------------------------------------------------------------------------------------------------------------------------- Function: CreateHatchBrush The CreateHatchBrush function creates a logical brush that has the specified hatch pattern and color. ----------------------------------------------------------------------------------------------------------------------------- Private Declare Function CreateHatchBrush Lib "gdi32.DLL" Alias "CreateHatchBrush" ( _ ByVal Style As HatchStyle, _ ByVal crColor As Integer) As IntPtr ----------------------------------------------------------------------------------------------------------------------------- Function: CreatePatternBrush The CreatePatternBrush function creates a logical brush with the specified bitmap pattern. The bitmap can be a DIB section bitmap, which is created by the CreateDIBSection function, or it can be a device-dependent bitmap. ----------------------------------------------------------------------------------------------------------------------------- Private Declare Function CreatePatternBrush Lib "gdi32.DLL" Alias "CreatePatternBrush" ( _ ByVal hBitmap As IntPtr) As IntPtr ----------------------------------------------------------------------------------------------------------------------------- Function: GetStockObject The GetStockObject function retrieves a handle to one of the stock pens, brushes, fonts, or palettes. ----------------------------------------------------------------------------------------------------------------------------- Private Declare Function GetStockObject Lib "gdi32.DLL" Alias "GetStockObject" ( _ ByVal nIndex As StockObjects) As IntPtr ----------------------------------------------------------------------------------------------------------------------------- Function: CreatePen The CreatePen function creates a logical pen that has the specified style, width, and color. The pen can subsequently be selected into a device context and used to draw lines and curves. ----------------------------------------------------------------------------------------------------------------------------- Private Declare Function CreatePen Lib "gdi32.DLL" Alias "CreatePen" ( _ ByVal nPenStyle As PenStyles, _ ByVal nWidth As Integer, _ ByVal crColor As Integer) As IntPtr ----------------------------------------------------------------------------------------------------------------------------- Function: SelectObject The SelectObject function selects an object into the specified device context (DC). The new object replaces the previous object of the same type. ----------------------------------------------------------------------------------------------------------------------------- Private Declare Function SelectObject Lib "gdi32.DLL" Alias "SelectObject" ( _ ByVal hdc As IntPtr, _ ByVal hObject As IntPtr) As IntPtr ----------------------------------------------------------------------------------------------------------------------------- Function: DeleteObject The DeleteObject function deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object. After the object is deleted, the specified handle is no longer valid. ----------------------------------------------------------------------------------------------------------------------------- Private Declare Function DeleteObject Lib "gdi32.DLL" Alias "DeleteObject" ( _ ByVal hObject As IntPtr) As IntPtr ----------------------------------------------------------------------------------------------------------------------------- Function: SetROP2 The SetROP2 function sets the current foreground mix mode. GDI uses the foreground mix mode to combine pens and interiors of filled objects with the colors already on the screen. The foreground mix mode defines how colors from the brush or pen and the colors in the existing image are to be combined. If the function succeeds, the return value specifies the previous mix mode. If the function fails, the return value is zero. ----------------------------------------------------------------------------------------------------------------------------- Private Declare Function SetROP2 Lib "gdi32.DLL" Alias "SetROP2" ( _ ByVal hdc As IntPtr, _ ByVal nDrawMode As RasterOps) As RasterOps ----------------------------------------------------------------------------------------------------------------------------- Function: GetROP2 The GetROP2 function retrieves the foreground mix mode of the specified device context. The mix mode specifies how the pen or interior color and the color already on the screen are combined to yield a new color. If the function succeeds, the return value specifies the previous mix mode. If the function fails, the return value is zero. ----------------------------------------------------------------------------------------------------------------------------- Private Declare Function GetROP2 Lib "gdi32.DLL" Alias "GetROP2" ( _ ByVal hdc As IntPtr) As Integer structure used by MoveToEx. It will contain the previous current position Public Structure POINTAPI Dim x As Integer Dim y As Integer End Structure Page –228–
    • ----------------------------------------------------------------------------------------------------------------------------- Function: MoveToEx The MoveToEx function updates the current position to the specified point and optionally returns the previous position.-----------------------------------------------------------------------------------------------------------------------------hdc: Handle to a device context.X: Specifies the x-coordinate, in logical units, of the new position, in logical units.Y: Specifies the y-coordinate, in logical units, of the new position, in logical units.lpPoint: Pointer to a POINT structure that receives the previous current position. If this parameter is a NULL pointer, the previous position is not returned.-----------------------------------------------------------------------------------------------------------------------------Private Declare Function MoveToEx Lib "gdi32.DLL" Alias "MoveToEx" ( _ ByVal hdc As IntPtr, _ ByVal x As Integer, _ ByVal y As Integer, _ ByVal lpPoint As POINTAPI) As Boolean----------------------------------------------------------------------------------------------------------------------------- Function: MoveToEx Description: The MoveToEx function updates the current position to the specified point and optionally returns the previous position.-----------------------------------------------------------------------------------------------------------------------------hdc: Handle to a device context.X: Specifies the x-coordinate, in logical units, of the new position, in logical units.Y: Specifies the y-coordinate, in logical units, of the new position, in logical units.lpPoint: Pointer to a POINT structure that receives the previous current position. If this parameter is a NULL pointer, the previous position is not returned.-----------------------------------------------------------------------------------------------------------------------------Private Declare Function MoveToEx Lib "gdi32.DLL" Alias "MoveToEx" ( _ ByVal hdc As IntPtr, _ ByVal x As Integer, _ ByVal y As Integer, _ ByVal lpPoint As IntPtr) As Boolean----------------------------------------------------------------------------------------------------------------------------- Function: LineTo The LineTo function draws a line from the current position up to, but not including, the specified point.-----------------------------------------------------------------------------------------------------------------------------hdc: Handle to a device context.nXEnd: Specifies the x-coordinate, in logical units, of the lines ending point.nYEnd: Specifies the y-coordinate, in logical units, of the lines ending point.-----------------------------------------------------------------------------------------------------------------------------Private Declare Function LineTo Lib "gdi32.DLL" Alias "LineTo" ( _ ByVal hdc As IntPtr, _ ByVal nXEnd As Integer, _ ByVal nYEnd As Integer) As Boolean----------------------------------------------------------------------------------------------------------------------------- Function: Ellipse The Ellipse function draws an ellipse. The center of the ellipse is the center of the specified bounding rectangle. The ellipse is outlined by using the current pen and is filled by using the current brush.-----------------------------------------------------------------------------------------------------------------------------hdc: A handle to the device context.nLeftRect: The x-coordinate, in logical coordinates, of the upper-left corner of the bounding rectangle.nTopRect: The y-coordinate, in logical coordinates, of the upper-left corner of the bounding rectangle.nRightRect: The x-coordinate, in logical coordinates, of the lower-right corner of the bounding rectangle.nBottomRect: The y-coordinate, in logical coordinates, of the lower-right corner of the bounding rectangle.-----------------------------------------------------------------------------------------------------------------------------Private Declare Function Ellipse Lib "gdi32.DLL" Alias "Ellipse" ( _ ByVal hdc As IntPtr, _ ByVal nLeftRect As Integer, _ ByVal nTopRect As Integer, _ ByVal nRightRect As Integer, _ ByVal nBottomRect As Integer) As Boolean----------------------------------------------------------------------------------------------------------------------------- Function: Rectangle The Rectangle function draws a rectangle. The rectangle is outlined by using the current pen and filled by using the current brush.-----------------------------------------------------------------------------------------------------------------------------hdc: A handle to the device context.nLeftRect: The x-coordinate, in logical coordinates, of the upper-left corner of the rectangle.nTopRect: The y-coordinate, in logical coordinates, of the upper-left corner of the rectangle.nRightRect: The x-coordinate, in logical coordinates, of the lower-right corner of the rectangle.nBottomRect: The y-coordinate, in logical coordinates, of the lower-right corner of the rectangle.-----------------------------------------------------------------------------------------------------------------------------Private Declare Function Rectangle Lib "gdi32.DLL" Alias "Rectangle" ( _ ByVal hdc As IntPtr, _ ByVal nLeftRect As Integer, _ ByVal nTopRect As Integer, _ ByVal nRightRect As Integer, _ ByVal nBottomRect As Integer) As Boolean----------------------------------------------------------------------------------------------------------------------------- Function: RoundRect The RoundRect function draws a rectangle with rounded corners. The rectangle is outlined by using the current pen and filled by using the current brush.-----------------------------------------------------------------------------------------------------------------------------hdc: A handle to the device context.nLeftRect: The x-coordinate, in logical coordinates, of the upper-left corner of the rectangle.nTopRect: The y-coordinate, in logical coordinates, of the upper-left corner of the rectangle.nRightRect: The x-coordinate, in logical coordinates, of the lower-right corner of the rectangle.nBottomRect: The y-coordinate, in logical coordinates, of the lower-right corner of the rectangle.nWidth: The width, in logical coordinates, of the ellipse used to draw the rounded corners.nHeight: The height, in logical coordinates, of the ellipse used to draw the rounded corners.-----------------------------------------------------------------------------------------------------------------------------Private Declare Function RoundRect Lib "gdi32.DLL" Alias "RoundRect" ( _ ByVal hdc As IntPtr, _ ByVal nLeftRect As Integer, _ ByVal nTopRect As Integer, _ ByVal nRightRect As Integer, _ ByVal nBottomRect As Integer, _ ByVal nWidth As Integer, _ ByVal nHeight As Integer) As Boolean----------------------------------------------------------------------------------------------------------------------------- Function: Arc The Arc function draws an elliptical arc.----------------------------------------------------------------------------------------------------------------------------- hdc: A handle to the device context where drawing takes place. nLeftRect: The x-coordinate, in logical units, of the upper-left corner of the bounding rectangle. nTopRect: The y-coordinate, in logical units, of the upper-left corner of the bounding rectangle. nRightRect: The x-coordinate, in logical units, of the lower-right corner of the bounding rectangle. nBottomRect: The y-coordinate, in logical units, of the lower-right corner of the bounding rectangle. nXStartArc: The x-coordinate, in logical units, of the ending point of the radial line defining the starting point of the arc. nYStartArc: The y-coordinate, in logical units, of the ending point of the radial line defining the starting point of the arc. nXEndArc: The x-coordinate, in logical units, of the ending point of the radial line defining the ending point of the arc. nYEndArc: The y-coordinate, in logical units, of the ending point of the radial line defining the ending point of the arc.----------------------------------------------------------------------------------------------------------------------------- Page –229–
    • The points (nLeftRect, nTopRect) and (nRightRect, nBottomRect) specify the bounding rectangle. An ellipse formed by the specified bounding rectangle defines the curve of the arc. The arc extends in the current drawing direction from the point where it intersects the radial from the center of the bounding rectangle to the (nXStartArc, nYStartArc) point. The arc ends where it intersects the radial from the center of the bounding rectangle to the (nXEndArc, nYEndArc) point. If the starting point and ending point are the same, a complete ellipse is drawn. The arc is drawn using the current pen; it is not filled. ----------------------------------------------------------------------------------------------------------------------------- Private Declare Function Arc Lib "gdi32.DLL" Alias "Arc" ( _ ByVal hdc As IntPtr, _ ByVal nLeftRect As Integer, _ ByVal nTopRect As Integer, _ ByVal nRightRect As Integer, _ ByVal nBottomRect As Integer, _ ByVal nXStartArc As Integer, _ ByVal nYStartArc As Integer, _ ByVal nXEndArc As Integer, _ ByVal nYEndArc As Integer) As Boolean#End Region#Region "GDI32 Properties" ***************************************************************************************************************************** Properties BrushColor: Get/Set brush color. Default is Black. Also be sure this is black for XOR operations and you want a "Rubberband" effect. BrushHatch: Get/Set optional Brush Hatch Style. Default is HS_SOLID, which specifies a solid brush. PenColor: Get/Set pen color. Default is black. PenStyle: Get/Set pen style. Default is PS_SOLID. PenWidth: Get/Set pen pixel width. Default is 1. RasterOp: Get/Set type of raster operation to perform. Default is R2_CopyPen (overwrite) ***************************************************************************************************************************** Get/Set brush color Public Property BrushColor() As Color Get Return Me.m_brushColor End Get Set(ByVal value As Color) Me.m_brushColor = value End Set End Property Get/Set Brush Hatch Style Public Property BrushHatch() As HatchStyle Get Return Me.m_brushHatch End Get Set(ByVal value As HatchStyle) Me.m_brushHatch = value End Set End Property Get/Set pen color Public Property PenColor() As Color Get Return Me.m_penColor End Get Set(ByVal value As Color) Me.m_penColor = value End Set End Property Get/Set pen style (pattern) Public Property PenStyle() As PenStyles Get Return Me.m_penStyle End Get Set(ByVal value As PenStyles) Me.m_penStyle = value End Set End Property Get/Set pen width in pixels Public Property PenWidth() As Integer Get Return Me.m_penWidth End Get Set(ByVal value As Integer) Me.m_penWidth = value End Set End Property Get/Set Raster Operation Public Property RasterOp() As RasterOps Get Return m_rasterOp End Get Set(ByVal value As RasterOps) m_rasterOp = value End Set End Property#End Region#Region "GDI32 Protected Support Methods" ***************************************************************************************************************************** Protected Support Methods ***************************************************************************************************************************** ************************************************************************************************************* Function: ARGBtoRGB Helper function to covert Alpha Color ARGB value (AARRGGBB) to RGB (00BBGGRR) ************************************************************************************************************* Private Function ARGBtoRGB(ByVal clr As Color) As Integer Dim vARGB As Integer = clr.ToArgb convert color value to AARRGGBB Return RGB((vARGB >> 16) And &HFF, (vARGB >> 8) And &HFF, vARGB And &HFF) return RGB color End Function Page –230–
    • ----------------------------------------------------------------------------------------------------------------------------- Subrouine: InitPenAndBrush Initialize pen annd brush ----------------------------------------------------------------------------------------------------------------------------- Protected Sub InitPenAndBrush(ByVal g As Graphics) Me.m_hdc = g.GetHdc save handle to device context process brush options If Me.m_brushColor = Color.Transparent Then Me.m_gdiBrush = GetStockObject(StockObjects.NULL_BRUSH) hide brush if brush color is transparent ElseIf Me.m_brushHatch = HatchStyle.HS_SOLID Then Me.m_gdiBrush = GDI32.CreateSolidBrush(Me.ARGBtoRGB(Me.m_brushColor)) set solid brush style and fill color) Else Me.m_gdiBrush = GDI32.CreateHatchBrush(Me.m_brushHatch, Me.ARGBtoRGB(Me.m_brushColor)) create hatch brush style End If process pen options If Me.m_penColor = Color.Transparent Then Me.m_gdiPen = GetStockObject(StockObjects.NULL_PEN) hide pen if it is transparent Else Me.m_gdiPen = GDI32.CreatePen(Me.PenStyle, Me.m_penWidth, Me.ARGBtoRGB(Me.PenColor)) set pen pattern, style, and width End If GDI32.SetROP2(Me.m_hdc, m_rasterOp) set raster operation Me.m_oldPen = GDI32.SelectObject(Me.m_hdc, Me.m_gdiPen) set new pen, save old Me.m_oldBrush = GDI32.SelectObject(Me.m_hdc, Me.m_gdiBrush) set new background brush, save old End Sub ----------------------------------------------------------------------------------------------------------------------------- Subrouine: DisposeResources Dispose of created data and reset old data ----------------------------------------------------------------------------------------------------------------------------- Protected Sub DisposeResources(ByVal g As Graphics) GDI32.DeleteObject(GDI32.SelectObject(Me.m_hdc, Me.m_oldBrush)) reset old brush, delete replaced one GDI32.DeleteObject(GDI32.SelectObject(Me.m_hdc, Me.m_oldPen)) reset old pen, delete replaced one g.ReleaseHdc(Me.m_hdc) release device context g.Dispose() End Sub#End Region#Region "GDI32 Public Methods" ***************************************************************************************************************************** Public Methods ***************************************************************************************************************************** ----------------------------------------------------------------------------------------------------------------------------- Function: DrawCircle draw a circle with a uniform radius from a center point ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawCircle(ByVal g As Graphics, ByVal p1 As Point, ByVal Radius As Integer) p1 is center of circle Me.InitPenAndBrush(g) init pen and brush GDI32.Ellipse(Me.m_hdc, p1.X - Radius, p1.Y - Radius, p1.X + Radius, p1.Y + Radius) draw circle Me.DisposeResources(g) disposes of resources End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawEllipse Draw circles and ellipses from a bounds defined by a top-left point and a bottom-right point ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawEllipse(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point) p1 is top-left of bounds, p2 is bottom-right Me.InitPenAndBrush(g) init pen and brush GDI32.Ellipse(Me.m_hdc, p1.X, p1.Y, p2.X, p2.Y) draw ellipse Me.DisposeResources(g) disposes of resources End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawEllipse Draw ellipses defined within a bounding rectangle ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawEllipse(ByVal g As Graphics, ByVal Bnds As Rectangle) p1 is top-left of bounds, p2 is bottom-right Me.InitPenAndBrush(g) init pen and brush With Bnds GDI32.Ellipse(Me.m_hdc, .Left, .Top, .Right, .Bottom) draw ellipse within the rectangle End With Me.DisposeResources(g) disposes of resources End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawArc Draw an arc within a bounding rectangle. Point ArcStart defines a point from center along start line, ArcEnd is a point along end line ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawArc(ByVal g As Graphics, ByVal Bnds As Rectangle, ByVal ArcStart As Point, ByVal ArcEnd As Point) Me.InitPenAndBrush(g) init pen and brush With Bnds GDI32.Arc(Me.m_hdc, .Left, .Top, .Right, .Bottom, ArcStart.X, ArcStart.Y, ArcEnd.X, ArcEnd.Y) draw arc End With Me.DisposeResources(g) disposes of resources End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawArc Draw an arc within a rectangle specifyed by p1 (top-left) and p2(bottom-right). Point ArcStart defines a point from center along start line, ArcEnd is a point along end line ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawArc(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point, ByVal ArcStart As Point, ByVal ArcEnd As Point) Me.InitPenAndBrush(g) init pen and brush GDI32.Arc(Me.m_hdc, p1.X, p1.Y, p2.X, p2.Y, ArcStart.X, ArcStart.Y, ArcEnd.X, ArcEnd.Y) draw arc Me.DisposeResources(g) disposes of resources End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawLine Draw a line from point p1 to point p2 ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawLine(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point) Me.InitPenAndBrush(g) init pen and brush GDI32.MoveToEx(Me.m_hdc, p1.X, p1.Y, IntPtr.Zero) move to starting point GDI32.LineTo(Me.m_hdc, p2.X, p2.Y) draw line from start point to end point Me.DisposeResources(g) disposes of resources End Sub Page –231–
    • ----------------------------------------------------------------------------------------------------------------------------- Function: DrawPolygon Draw a polygon from an array of points ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawPolygon(ByVal g As Graphics, ByRef PointsArray() As Point) If PointsArray Is Nothing Then Return if nothing to process End If Dim NumPoints As Integer = UBound(PointsArray) get upper bounds of array Me.InitPenAndBrush(g) init pen and brush GDI32.MoveToEx(Me.m_hdc, PointsArray(0).X, PointsArray(0).Y, IntPtr.Zero) move to starting point If CBool(NumPoints) Then if more than 1 point For Idx As Integer = 1 To NumPoints process each point as a sequence in a chain GDI32.LineTo(Me.m_hdc, PointsArray(Idx).X, PointsArray(Idx).Y) draw a line from previous point to current Next If Not PointsArray(0).Equals(PointsArray(NumPoints)) Then close polygon if start and last not the same GDI32.LineTo(Me.m_hdc, PointsArray(NumPoints).X, PointsArray(NumPoints).Y) draw a line from previous point to current End If End If Me.DisposeResources(g) disposes of resources End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawRectangle Draw a rectangle or square from a top-left point to a bottom-right point P1 is the top-left corner, P2 is the bottom Right corner ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawRectangle(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point) Me.InitPenAndBrush(g) init pen and brush GDI32.Rectangle(Me.m_hdc, p1.X, p1.Y, p2.X, p2.Y) draw rectangle from point1 to point2 Me.DisposeResources(g) disposes of resources End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawRectangle Draw a rectangle or square from a bounding rectangle ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawRectangle(ByVal g As Graphics, ByVal Bnds As Rectangle) Me.InitPenAndBrush(g) init pen and brush With Bnds GDI32.Rectangle(Me.m_hdc, .Left, .Top, .Right, .Bottom) draw rectangle within bounds End With Me.DisposeResources(g) disposes of resources End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawRoundRect Draw a rounded rectangle (obround). Make CornerWidth and CornerHeight equal for symetrical corners P1 is the top-left corner, P2 is the bottom Right corner ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawRoundRect(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point, ByVal CornerWidth As Integer, ByVal CornerHeight As Integer) if corner radius out of range If CornerWidth <= 0 OrElse CornerHeight <= 0 OrElse CornerWidth >= Math.Abs(p1.X - p2.X) OrElse CornerHeight >= Math.Abs(p1.Y - p2.Y) Then DrawRectangle(g, p1, p2) just draw a rectangle Else Me.InitPenAndBrush(g) init pen and brush GDI32.RoundRect(Me.m_hdc, p1.X, p1.Y, p2.X, p2.Y, CornerWidth, CornerHeight) draw rounded rectangle from point1 to point2 Me.DisposeResources(g) disposes of resources End If End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawRoundRect Draw a rounded rectangle (obround) from a bounding rectangle. Make CornerWidth and CornerHeight equal for symetrical corners ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawRoundRect(ByVal g As Graphics, ByVal Bnds As Rectangle, ByVal CornerWidth As Integer, ByVal CornerHeight As Integer) if corner radius out of range With Bnds If CornerWidth <= 0 OrElse CornerHeight <= 0 OrElse CornerWidth >= Math.Abs(.Left - .Right) OrElse CornerHeight >= Math.Abs(.Top - .Bottom) Then DrawRectangle(g, Bnds) just draw a rectangle Else Me.InitPenAndBrush(g) init pen and brush GDI32.RoundRect(Me.m_hdc, .Left, .Top, .Right, .Bottom, CornerWidth, CornerHeight) draw rounded rectangle from point1 to point2 Me.DisposeResources(g) disposes of resources End If End With End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawObRound Draw an ObRound, allowing one to specify a uniform corner radius by specifying just a single corner radius property P1 is the top-left corner, P2 is the bottom Right corner ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawObRound(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point, ByVal CornerRadius As Integer) if corner radius out of range If CornerRadius <= 0 OrElse CornerRadius >= Math.Abs(p1.X - p2.X) OrElse CornerRadius >= Math.Abs(p1.Y - p2.Y) Then DrawRectangle(g, p1, p2) just draw a rectangle Else Me.InitPenAndBrush(g) init pen and brush GDI32.RoundRect(Me.m_hdc, p1.X, p1.Y, p2.X, p2.Y, CornerRadius, CornerRadius) draw rounded rectangle from point1 to point2 Me.DisposeResources(g) disposes of resources End If End Sub ----------------------------------------------------------------------------------------------------------------------------- Function: DrawObRound Draw an ObRound from a bounding rectangle, allowing one to specify a uniform corner radius by specifying just a single corner radius property ----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawObRound(ByVal g As Graphics, ByVal Bnds As Rectangle, ByVal CornerRadius As Integer) if corner radius out of range With Bnds If CornerRadius <= 0 OrElse CornerRadius >= Math.Abs(.Left - .Right) OrElse CornerRadius >= Math.Abs(.Top - .Bottom) Then DrawRectangle(g, Bnds) just draw a rectangle Else Me.InitPenAndBrush(g) init pen and brush GDI32.RoundRect(Me.m_hdc, .Left, .Top, .Right, .Bottom, CornerRadius, CornerRadius) draw rounded rectangle from point1 to point2 Me.DisposeResources(g) disposes of resources End If End With End Sub#End RegionEnd Class Page –232–
    • Emulating a Selection Rubber Band under GDISuppose you wanted to define a Selection Rubber Band on an image, such as a background image on aform. You can do this quite easily with the GDI32 class. The code required to support this is very simple(I keep forcing myself to avoid the pun of saying it is very basic).Create a new VB Windows project. Add a background image to it, if you wish. Also, if you choose to,add some controls to dress it up (unlike all other rubber band examples you may have seen, this oneactually works exactly like you would expect of normal rubber band operations, where the rubber bandwill also cover any controls it passes over on the form). Then, in the Form1 code, add the following:Public Class Form1 Friend m_GDI As New GDI32 instantiate an instace of the GDI class Friend m_StartPoint As Point keep track of starting point Friend m_LastPoint As Point keep track of last mouse location ************************************************************************************** Subroutine: Form1_MouseDown When the mouse select button is down, init the rectangle start and end points ************************************************************************************** Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown m_GDI.PenColor = Color.White set color to white (black will be invisible in an XOR operation) m_GDI.BrushColor = Color.Transparent hide the brush m_GDI.RasterOp = RasterOps.R2_XOrPen use XOR to emulate rubberbanding m_StartPoint = e.Location set the start and end locations to the current mouse position m_LastPoint = e.Location End Sub ************************************************************************************** Subroutine: Form1_MouseMove If the mouse button is down, erase the old rubberband, update the location, then draw the new rubberband. ************************************************************************************** Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove If e.Button = Windows.Forms.MouseButtons.Left Then if the mouse select button is down DrawRubberBand() erase the old rectangle at the old location m_LastPoint = e.Location update the location DrawRubberBand() draw the new rectange to the new location End If End Sub ************************************************************************************** Subroutine: Form1_MouseUp Erase the selection rubber band, report selection data ************************************************************************************** Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp DrawRubberBand() erase the old rectangle Dim Ctls As New Collections.Generic.List(Of Control) list to collect controls selected Dim Msg As String = "The selection rectangle was from (" & m_StartPoint.X.ToString & "," & m_StartPoint.Y.ToString & ") to (" & _ m_LastPoint.X.ToString & "," & m_LastPoint.Y.ToString & ")" & vbCrLf & vbCrLf define a new rectangle to compare against any controls on the form Dim Rect As Rectangle = NormalizeSelectRectangle() find which controls were selected For Each Ctl As Control In Controls If Ctl.Visible AndAlso Ctl.Bounds.IntersectsWith(Rect) Then a control intersection was found Ctls.Add(Ctl) End If Next If Ctls.Count = 0 Then Msg &= "There were no controls selected" if no controls were selected Else Msg &= "The follows controls were also selected:" & vbCrLf else list the controls selected For Each Ctl As Control In Ctls Msg &= " " & Ctl.Name & vbCrLf Next End If report results MsgBox(Msg, MsgBoxStyle.OkOnly Or MsgBoxStyle.Information, "Selection Result") End Sub ************************************************************************************** Function: NormalizeSelectRectangle Defind the selection rectangle and normalize its definition, where the start point is always upper-left, and the ending point is always lower-right ************************************************************************************** Private Function NormalizeSelectRectangle() As Rectangle define the final selection rectangle Dim X As Integer = m_StartPoint.X get start point Dim Y As Integer = m_StartPoint.Y If X > m_LastPoint.X Then X = m_LastPoint.X NORMALIZE COORDINATES if inverted in any way If Y > m_LastPoint.Y Then Y = m_LastPoint.Y define a new rectangle to draw our current rectangle, and compare against any controls on the form Return New Rectangle(X, Y, Math.Abs(m_LastPoint.X - m_StartPoint.X), Math.Abs(m_LastPoint.Y - m_StartPoint.Y)) End Function ************************************************************************************** Subroutine: DrawRubberBand Draw or erase the rubberband, taking advantage of XOR Raster method. Also draw/erase over controls that may be placed on the form. ************************************************************************************** Private Sub DrawRubberBand() define a new rectangle to draw our current rectangle, and compare against any controls on the form Dim Rect As Rectangle = NormalizeSelectRectangle() m_GDI.DrawRectangle(Me.CreateGraphics, Rect) erase the old rectangle or draw the new one For Each Ctl As Control In Controls also process any controls it intersects with If Ctl.Visible AndAlso Ctl.Bounds.IntersectsWith(Rect) Then a control intersection was found erase the old rectangle or draw the new one over control, offsetting the rubberband as needed m_GDI.DrawRectangle(Ctl.CreateGraphics, New Rectangle(Rect.Left - Ctl.Left, Rect.Top - Ctl.Top, Rect.Width, Rect.Height)) End If Next End SubEnd Class Page –233–
    • NOTE: Unlike any other GDI example on the web, this is the first one to demonstrate a feature that many gurus havetypically thought was too difficult – the above DrawRubberBand() method draws the selection rubber band over any controlsit crosses. Most gurus considered this control-inclusive technique to be too complicated and deemed it an advanced topicthat cannot be covered in a single article. But, as you can see, my solution was unbelievably simple (actually, the solutionbecame simple due to techniques I had already developed to easily emulate VB6 image controls with transparentbackgrounds on a form – see the article, Emulating VB6 Image Control Features in VB.NET on page 58).Now run the project (once you have added the GDI32 class to it). Itemulates the rubber band function very smoothly. Because it isdrawing to the form’s surface, any controls you place on the formwill be in front of the rubber band, unless we reflect it to the surfaceof each control as well. The NormalizeSelectRectangle()method normalizes coordinates, so that no matter how you makeyour selection, the coordinate addressing will be standardized. Thedark-shaded code in the DrawRubberBand() method shows youhow simple it is to actually reflect the rubber band to the surface ofeach control that it intersects with.Emulating a Selection Rubber Band under GDI+Suppose you wanted to simply emulate a Selection Rubber Band under the graphics support that isalready built into .NET, using GDI+. We can do that as well, though if you are not very careful in theway you write it, I think it can also look much less professional than the GDI method (and I have seensome very bad examples on the web at some blog and support sites – but these folks are just trying to getpeople pointed in the right direction to solving their own problems). For instance, with some examples, Ioften see the selection rectangle disappear after about one second, if you hold it still. But if it is renderedand handled properly, as I will show you, below, it will work great and it will look clean.Using the GDI+ DrawReversibleFrame() MethodThe typical technique developers have been using to perform this taskunder GDI+ (and the technique I like the least) is by implementing theControlPaint.DrawReversibleFrame() method. Unlike the GDI method,this method draws to the screen, over the top of everything, which isuseful for also conveniently covering form controls without the need foradditional code. But, because it draws to the whole screen, we musttranslate our rectangle’s start coordinates to coordinates relative to thescreen, which we can do using the PointToScreen() method, as you willsee in the new DrawRubberBand() method, below. Unlike the previousexample, this example does not require the GDI32 class.NOTE: This method can look goofy and lose its luster if you move the selection outside the target form, where it will leavegraphical artifacts on the screen, though no real damage is caused and it cleans easily. As such, I will check for this withinthe form code and automatically correct for that. But regardless, it can also at times leave tiny graphical artifacts on certaincontrols on the form as well. I do not like this method for this reason, and consider it to be the least professional method touse. But we will cover it here because so many online examples feature it, and people are constantly asking me about it.Like the GDI Rubber Band example, create a new VB Windows project. Add a background image to itif you wish. Also add some controls to dress it up if you choose. And, to avoid window flashing, be sureto also set the form’s DoubleBluffered parameter to True. Then, in the Form1 code, add the following:Public Class Form1 Friend m_StartPoint As Point keep track of starting point Friend m_LastPoint As Point keep track of last mouse location ************************************************************************************** Subroutine: Form1_MouseDown When the mouse select button is down, init the rectangle start and end points ************************************************************************************** Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown m_StartPoint = e.Location set the start and end locations to the current mouse position m_LastPoint = e.Location End Sub Page –234–
    • ************************************************************************************** Subroutine: Form1_MouseMove If the mouse button is down, erase the old rubberband, update the location, then draw the new rubberband. ************************************************************************************** Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove If e.Button = Windows.Forms.MouseButtons.Left Then if the mouse select button is down Dim Rect As Rectangle = NormalizeSelectRectangle() get current rectangle Rect.Width += 1 bump 1 for width/height because drawing edge is one higher than rectangle Rect.Height += 1 Me.Invalidate(Rect) invalidate that region Me.Update() update display of that region m_LastPoint = e.Location update the location DrawRubberBand() draw the new rectange to the new location End If End Sub ************************************************************************************** Subroutine: Form1_MouseUp Erase the selection rubber band, report selection data ************************************************************************************** Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp Me.Refresh() refresh form and controls, erasing rectangles Dim Ctls As New Collections.Generic.List(Of Control) list to collect controls selected Dim Msg As String = "The selection rectangle was from (" & m_StartPoint.X.ToString & "," & m_StartPoint.Y.ToString & ") to (" & _ m_LastPoint.X.ToString & "," & m_LastPoint.Y.ToString & ")" & vbCrLf & vbCrLf define a new rectangle to compare against any controls on the form Dim Rect As Rectangle = NormalizeSelectRectangle() find which controls were selected For Each Ctl As Control In Controls If Ctl.Visible AndAlso Ctl.Bounds.IntersectsWith(Rect) Then a control intersection was found Ctls.Add(Ctl) End If Next If Ctls.Count = 0 Then Msg &= "There were no controls selected" if no controls were selected Else Msg &= "The follows controls were also selected:" & vbCrLf else list the controls selected For Each Ctl As Control In Ctls Msg &= " " & Ctl.Name & vbCrLf Next End If report results MsgBox(Msg, MsgBoxStyle.OkOnly Or MsgBoxStyle.Information, "Selection Result") End Sub ************************************************************************************** Function: NormalizeSelectRectangle Defind the selection rectangle and normalize its definition, where the start point is always upper-left, and the ending point is always lower-right ************************************************************************************** Private Function NormalizeSelectRectangle() As Rectangle define the final selection rectangle Dim X As Integer = m_StartPoint.X get start point Dim Y As Integer = m_StartPoint.Y If m_LastPoint.X < 0 Then m_LastPoint.X = -1 prevent selection from goung outside top-left of frame If m_LastPoint.Y < 0 Then m_LastPoint.Y = -1 If X > m_LastPoint.X Then X = m_LastPoint.X NORMALIZE COORDINATES if inverted in any way If Y > m_LastPoint.Y Then Y = m_LastPoint.Y define a new rectangle to draw our current rectangle, and compare against any controls on the form Return New Rectangle(X, Y, Math.Abs(m_LastPoint.X - m_StartPoint.X), Math.Abs(m_LastPoint.Y - m_StartPoint.Y)) End Function ************************************************************************************** Subroutine: DrawRubberBand Draw or erase the rubberband, taking advantage of XOR Raster method. Also draw/erase over controls that may be placed on the form. ************************************************************************************** Private Sub DrawRubberBand() define a new rectangle to draw our current rectangle Dim Rect As Rectangle = NormalizeSelectRectangle() With Me.ClientRectangle If Rect.Left + Rect.Width > .Width Then make sure selection, which is actually drown to the screen, not Rect.Width = .Width - Rect.Left + 1 to the form, will not exceed the bounds of the forms client End If area, otherwise the selection rectangle will be reflected to the If Rect.Top + Rect.Height > .Height Then screen area not covered by the form, and will look very Rect.Height = .Height - Rect.Top + 1 unprofessional. These two checks for eliminate that problem. End If End With draw the selection rectangle to the actual screen, not to the form and its controls. ControlPaint.DrawReversibleFrame(New Rectangle(PointToScreen(Rect.Location), Rect.Size), Color.White, FrameStyle.Dashed) End SubEnd ClassUsing the GDI+ DrawRectangle() Method and a Translucent BrushIf you want to emulate the selection rectangle that Vista and Windows7 uses, this is also very easy to do. In fact, I was not able to invent thismethod until I figured out how to make the rectangle for theDrawReversibleFrame() method more stable. Only then did I beginto wonder about how Vista and Windows 7 made their translucentselection rectangle. Granted, I could have simply used the GDI+DrawRectangle() method to emulate the previous examples, once Idiscovered that the Invalidate(), Refresh(), and Update()methods could be used to make the GDI+ DrawReversibleFrame()method appear more professional-looking. However, because it was now so stable, especially with theform’s DoubleBluffered property set to True, I felt that I had to figure it out. Page –235–
    • NOTE: Setting the DoubleBuffered property for a form to True is important to cut down on screen flicker. With thisproperty set, the display will wait until it is between display refresh cycles to update the screen, so display updates whiledrawing is going on will be minimized.The only real problem I was running into was finding out how to create a translucent brush, and no oneseemed to have any answers on the internet – only questions on how to create one.That is, until I happened upon an obscure example for using .NET’s SolidBrush() method whilestudying brush creation in MSDN.Creating a translucent brush simply involves assigning a color value to a brush. The color value is one ofthe common ARGB colors that are typical to the WinXP+ operating systems. Unfortunately, mostpeople seem to be at a loss to as to how to do that with an alpha blended color value. Worse, mostexamples of the SolidBrush() method specify just a single color parameter, and leave it at that.The trick, though, is to realize that a color can be multiply defined. Consider the following examples:Dim Clr As Color = Color.Navy assign Clr variable the color NavyClr = Color.FromName("Navy") assign Clr variable the color NavyClr = RGB(0, 0, 80) assign Clr variable the color Navy (Red, Green, Blue)Clr = Color.FromArgb(Color.Navy.ToArgb) assign Clr variable the integer value of NavyClr = Color.FromArgb(128, 0, 0, 80) Assign Clr variable an alpha blend value of 128 (1/2 opaque) for the color Green (A,R,G,B)Clr = Color.FromArgb(0, 0, 80) Assign Clr variable the color Navy ([Alpha=255; fully opaque], Red=0, Green=0, Blue=80)Clr = Color.FromArgb(64, Color.Navy) Assign Clr variable an alpha blend value of 64 (1/4 opaque, 3/4 transparent) for the color NavyWith this, we can create a brush with a ½ opaque (½ transparent) Light Blue color using the following:Dim Brsh As New SolidBrush(Color.FromArgb(128, Color.LightBlue)) define translucent brush with 50% translucencyThen to use it, we need only fill a drawn rectangle with the brush:Me.CreateGraphics.FillRectangle(Brsh, Rect) do fill with translucient brush (we can also define the new brush here)Hence, replace the DrawRubberBand() method in the previous example with the following two methods:************************************************************************************** Subroutine: DrawRubberBand Draw the rubberband. Also draw over controls that may be placed on the form.**************************************************************************************Private Sub DrawRubberBand() define a new rectangle to draw our current rectangle, and compare against any controls on the form Dim Rect As Rectangle = NormalizeSelectRectangle() DrawBand(Me.CreateGraphics(), Rect) draw selection rectangle for form For Each Ctl As Control In Controls also process any controls it intersects with If Ctl.Visible Then if control can be seen Ctl.Refresh() always refresh control surface, even if not affected (otherwise artifacts may be left behind) If Ctl.Bounds.IntersectsWith(Rect) Then a control intersection was found erase the old rectangle or draw the new one over control, offsetting the rubberband as needed DrawBand(Ctl.CreateGraphics, New Rectangle(Rect.Left - Ctl.Left, Rect.Top - Ctl.Top, Rect.Width, Rect.Height)) End If End If NextEnd Sub************************************************************************************** Subroutine: DrawBand Draw using a Vista/Win7-style selection rubberband**************************************************************************************Private Sub DrawBand(ByVal eg As System.Drawing.Graphics, ByVal Rect As Rectangle) Dim VistaClr As Color = Color.FromArgb(255, 51, 153, 255) use Vista/Win7 Selector color (Alpha=255: fully opaque) eg.DrawRectangle(New Pen(VistaClr), Rect) draw out edge of rectangle eg.FillRectangle(New SolidBrush(Color.FromArgb(64, VistaClr)), Rect) do fill with translucient brush (Alpha=64: 1/4 opaque, 3/4 transparent)End SubYou now have Vista-style or Windows 7-style rubber band selection.NOTE: Some people have reported that they have this type of selection rectangle under Windows XP. My XP platform usesjust a rectangle outline. Perhaps they have an enhancement patch installed, or have an option set that I have disabled,because I thought that this translucent selection rectangle was introduced with Windows 6.0 (Vista. By the way, Windows 7is actually Windows 6.1 – there goes the Microsoft marketing department, again).David Ross GobenLast update: Wednesday, June 08, 2011, 3:33:16 PMContact: david.ross.goben@gmail.com Page –236–
    • About the AuthorDavid Ross Goben has been a professional software engineer, a writer, and an obsessiveresearcher. Of Jewish descent, he has extensively explored Biblical history, ancientcultural thinking, and ancient slang for over three decades, which has resulted in hisseminal work: A Gnostic Cycle: Exploring the Origin of Christianity. He has writtennumerous books, manuals, and magazine articles, many uncredited, or authored underpen names. He has been involved in computing since long before personal computerswere available even in kit form, he was a contributing Editor to 80 Micro Magazine, andthere, co-wrote the Feedback Loop column with Beve Woodbury, under the pen name, Mercedes Silver. Hisinterests include Cosmology, Quantum Physics, human-machine interaction, the Global Warming Myth, TheElectric Universe Theory, Perpetual Energy Technology, Quartz Technology, Dream Walking, and the study ofthe bio-mechanical origins of life.He is currently exploring the possibility that Albert Einstein got his Theory of Special Relativity wrong. Just abouteveryone is familiar with his equation, E=MC2, Energy (E) equals the Mass (M) times the speed of light (C)squared. But this Mass to Energy conversion is only half of the Special Relativity expression.The other half of Special Relativity involves Velocity (V), and is the calculation of Time Displacement (T):Einstein calculated E = MC2 / T, which would mean that at the speed of light, an object would attain infinitevolume and infinite mass. Correct me if I am wrong, but even an ordinary mortal like me sees this as ridiculous,because this would mean that we would attain several quintillion times the volume and mass that exists in theentire universe (and even that may an infinite understatement). All you have to do is simply ponder it for amoment.However, reverse the order, where E = T / MC2, and all calculations yield the same results, save one, and that isthat instead of attaining infinite volume and infinite mass at the speed of light, one instead moves into an alternateuniverse. What most people do not realize is that the very atoms of our body, at this very moment, are alreadyvibrating at just under the speed of light. Just a tiny push and we would become multidimensional beings. I thinkthis is why Quantum Physics, which is still nowhere near being a perfect model for planck-scale physics, will,even so, discover the Far World, or what the ancients called the Underworld or the Hidden World, or mosteveryone today refers to as the Afterlife or Heaven. Quantum Physics has already demonstrated that nothing in theuniverse has ever existed without Consciousness. So, where was Consciousness when the Universe was createdout of literally nothing during a causal Singularity Event? Evidence shows that it existed. And that has been thecrux of great debates throughout history.There is an easy experiment that can prove that Einstein’s Theory of Special Relativity is inverted. Weigh a veryheavy object, and then drop it onto a solid base, such as a concrete floor. Now weight the object again. It willweigh less… for about 20 minutes; at which time its full weight will finally return. Where did the missing mass goto during those 20 minutes? If Einstein’s calculation had been correct, the object would have actually weighedmore after being dropped, not less. Page –237–
    • The following Visual Basic documents are publicly available at: http://www.slideshare.net/DavidRossGoben, and at Google Docs at:http://docs.google.com/leaf?id=0B_Dj_dKazINlN2JlY2EwMmEtNGUyMy00NzQzLTliN2QtMDhlZTc5NDUzY2E5&sort=name&layout=list&num=50. Page –238–