ProblemYou want to add " rubber-band selection" to your graphics, giving the user the ability to click and drag with the mouse to select a rectangular region of an image. SolutionSample code folder: Chapter 09\RubberBand Use the RubberBand class presented here to use one of three different-appearing rubber-band selection algorithms. DiscussionYou've probably seen rubber-band selection in action when cropping images or working with screen-grabbing programs, paint programs, and so on. The RubberBand class presented here can be included in any project in which you want to let the user select a rectangular area of an image in this way. The complete code for the class is presented below. The RubberBandStyle enumeration and the public Style property work together to let you set the RubberBand object's appearance while in operation. While the user drags the mouse, the selected area is outlined with either a dashed-line rectangle (as in Figure 9-37, below), a solid line with inverted colors, or a solid-filled box with inverted colors. There are two overloaded constructors in this class, which let you instantiate a RubberBand object in three different ways. (The plan was to have only one constructor with two optional arguments, but Visual Basic does not permit structure objectsColor, in this caseto be optional.) You can set the RubberBand's Style and BackColor properties when you create the object, or you can set these properties later. You do need to indicate the control on which the RubberBand is to operate, so the painting on the screen can coordinate with the surface of the control. The Start(), Stretch(), and Finish() methods are called from the program that creates the RubberBand object to update the rectangular selection. Once "rubberbanding" is complete, the Rectangle property returns the results. These methods are demonstrated in the calling code presented later. Here's the code for the RubberBand class: Public Class RubberBand ' ----- The three types of rubber bands. Public Enum RubberBandStyle DashedLine ThickLine SolidBox End Enum ' ----- The current drawing state. Public Enum RubberBandState Inactive FirstTime Active End Enum ' ----- Class-level variables. Private BasePoint As Point Private ExtentPoint As Point Private CurrentState As RubberBandState Private BaseControl As Control Public Style As RubberBandStyle Public BackColor As Color Public Sub New(ByVal useControl As Control, _ Optional ByVal useStyle As RubberBandStyle = _ RubberBandStyle.DashedLine) ' ----- Constructor with one or two parameters. BaseControl = useControl Style = useStyle BackColor = Color.Black End Sub Public Sub New(ByVal useControl As Control, _ ByVal useStyle As RubberBandStyle, _ ByVal useColor As Color) ' ----- Constructor with three parameters. BaseControl = useControl Style = useStyle BackColor = useColor End Sub Public ReadOnly Property Rectangle() As Rectangle Get ' ----- Return the bounds of the rubber-band area. Dim result As Rectangle ' ----- Ensure the coordinates go left to ' right, top to bottom. result.X = IIf(BasePoint.X < ExtentPoint.X, _ BasePoint.X, ExtentPoint.X) result.Y = IIf(BasePoint.Y < ExtentPoint.Y, _ BasePoint.Y, ExtentPoint.Y) result.Width = Math.Abs(ExtentPoint.X - BasePoint.X) result.Height = Math.Abs(ExtentPoint.Y - BasePoint.Y) Return result End Get End Property Public Sub Start(ByVal x As Integer, ByVal y As Integer) ' ----- Start drawing the rubber band. The user must ' call Stretch() to actually draw the first ' band image. BasePoint.X = x BasePoint.Y = y ExtentPoint.X = x ExtentPoint.Y = y Normalize(BasePoint) CurrentState = RubberBandState.FirstTime End Sub Public Sub Stretch(ByVal x As Integer, ByVal y As Integer) ' ----- Change the size of the rubber band. Dim newPoint As Point ' ----- Prepare the new stretch point. newPoint.X = x newPoint.Y = y Normalize(newPoint) Select Case CurrentState Case RubberBandState.Inactive ' ----- Rubber band not in use. Return Case RubberBandState.FirstTime ' ----- Draw the initial rubber band. ExtentPoint = newPoint DrawTheRectangle() CurrentState = RubberBandState.Active Case RubberBandState.Active ' ----- Undraw the previous band, then ' draw the new one. DrawTheRectangle() ExtentPoint = newPoint DrawTheRectangle() End Select End Sub Public Sub Finish() ' ----- Stop drawing the rubber band. DrawTheRectangle() CurrentState = 0 End Sub Private Sub Normalize(ByRef whichPoint As Point) ' ----- Don't let the rubber band go outside the view. If (whichPoint.X < 0) Then whichPoint.X = 0 If (whichPoint.X >= BaseControl.ClientSize.Width) _ Then whichPoint.X = BaseControl.ClientSize.Width - 1 If (whichPoint.Y < 0) Then whichPoint.Y = 0 If (whichPoint.Y >= BaseControl.ClientSize.Height) _ Then whichPoint.Y = BaseControl.ClientSize.Height - 1 End Sub Private Sub DrawTheRectangle() ' ----- Draw the rectangle on the control or ' form surface. Dim drawArea As Rectangle Dim screenStart, screenEnd As Point ' ----- Get the square that is the rubber-band area. screenStart = BaseControl.PointToScreen(BasePoint) screenEnd = BaseControl.PointToScreen(ExtentPoint) drawArea.X = screenStart.X drawArea.Y = screenStart.Y drawArea.Width = (screenEnd.X - screenStart.X) drawArea.Height = (screenEnd.Y - screenStart.Y) ' ----- Draw using the user-selected style. Select Case Style Case RubberBandStyle.DashedLine ControlPaint.DrawReversibleFrame( _ drawArea, Color.Black, FrameStyle.Dashed) Case RubberBandStyle.ThickLine ControlPaint.DrawReversibleFrame( _ drawArea, Color.Black, FrameStyle.Thick) Case RubberBandStyle.SolidBox ControlPaint.FillReversibleRectangle( _ drawArea, BackColor) End Select End Sub End Class To demonstrate the RubberBand class, the following code creates an instance and calls its Start(), Stretch(), and Finish() methods based on the user's mouse activities. When the mouse button is first depressed, the code calls the Start() method. As the mouse is moved, the Stretch() method is called to continuously update the visible selection rectangle. When the mouse button is released, the Finish() method completes the selection process. At this point, the read-only Rectangle property returns a complete description of the selected area: Public Class Form1 ' ----- Adust the second and third arguments to ' see different methods. Dim SelectionArea As RubberBand = New RubberBand(Me, _ RubberBand.RubberBandStyle.DashedLine, Color.Gray) Private Sub Form1_MouseDown(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles MyBase.MouseDown ' ----- Start rubber-band tracking. SelectionArea.Start(e.X, e.Y) End Sub Private Sub Form1_MouseMove(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles MyBase.MouseMove ' ----- Update the rubber-band display area. SelectionArea.Stretch(e.X, e.Y) End Sub Private Sub Form1_MouseUp(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles MyBase.MouseUp ' ----- Finished with the selection. SelectionArea.Finish() Me.Refresh() End Sub Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) _ Handles MyBase.Paint ' ----- Add some interest to the form surface. Dim canvas As Graphics = e.Graphics Dim polygonPoints() As Point = {New Point(300, 150), _ New Point(200, 300), New Point(400, 300)} ' ----- Draw some shapes and text. canvas.FillEllipse(New SolidBrush(Color.Red), _ 10, 20, 200, 150) canvas.FillRectangle(New SolidBrush(Color.Blue), _ 100, 100, 250, 100) canvas.FillPolygon(New SolidBrush(Color.Green), _ polygonPoints) canvas.DrawString( SelectionArea.Rectangle.ToString, _ New Font("Arial", 12), Brushes.Black, 0, 0) End Sub End Class Figure 9-37 shows the results of running this demonstration code to select a rectangular area on the form. In this case the mouse was dragged down and to the right to select the area, but the code compensates for dragging in any direction and returns a proper rectangle. Figure 9-37. The RubberBand class lets you select rectangular areas of any graphics area |