Home > Articles > Programming > Windows Programming

  • Print
  • + Share This
This chapter is from the book

Learning by Example: A Forms-Based Drawing Application

In this example, we build upon what you've learned throughout the chapter to create a simple, forms-based drawing application. As an application, it is not very useful; the Paint application that ships with all copies of Windows has more features. However, as a learning tool, it should give you a nice test harness in which to experiment with writing your own code and to watch code execute.

Key Concepts Covered

The following represents the key concepts covered by this sample application:

  • Creating an MDI application

  • Managing a drawing surface

  • Maintaining drawing state with the GraphicsState class

  • Using the Color structure

  • Drawing, filling, and transforming shapes with the Graphics class

MDI Parent and Child Forms

To define the drawing surface in our application, we will use the multiple document interface (MDI) paradigm. This paradigm includes a parent, or container, form that provides the events and management of child forms. This is similar to Microsoft Word or Excel. The MDI form has a simple menu bar that provides the basic functionality for the form. Figure 9.6 shows the MDI form and its child form in their initial state.

Figure 9.6 System.Drawing MDI form.

Code Walkthrough

The code for the MDI form (see Listing 9.9) is rather basic. There is code to control the menu events, to load the form, and to reset the form when users click on the "New" menu item.

The code starts with form-level declarations and basic form building code.

Listing 9.9 The Code for the MDI Form

Public Class frmMDI

  Inherits System.Windows.Forms.Form

  Private WithEvents menuItemDraw As System.Windows.Forms.MenuItem
  Private WithEvents menuItemSurface As System.Windows.Forms.MenuItem
  Private WithEvents menuItemExit As System.Windows.Forms.MenuItem
  Private WithEvents menuItemNew As System.Windows.Forms.MenuItem

#Region " Windows Form Designer generated code "

  Public Sub New()
    MyBase.New()

    'This call is required by the Windows Form Designer.
    InitializeComponent()

    'Add any initialization after the InitializeComponent() call

  End Sub

  'Form overrides dispose to clean up the component list.
  Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
    If disposing Then
      If Not (components Is Nothing) Then
        components.Dispose()
      End If
    End If
    MyBase.Dispose(disposing)
  End Sub

  Private WithEvents menuItem1 As System.Windows.Forms.MenuItem
  Private WithEvents menuItem2 As System.Windows.Forms.MenuItem
  Private WithEvents menuItem3 As System.Windows.Forms.MenuItem
  Private WithEvents menuItem4 As System.Windows.Forms.MenuItem
  Private WithEvents mainMenu1 As System.Windows.Forms.MainMenu

  'Required by the Windows Form Designer
  Private components As System.ComponentModel.Container

  'NOTE: The following procedure is required by the Windows Form Designer
  'It can be modified using the Windows Form Designer. 
  'Do not modify it using the code editor.
  <System.Diagnostics.DebuggerStepThrough()> Private Sub _
    InitializeComponent()
    Me.mainMenu1 = New System.Windows.Forms.MainMenu()
    Me.menuItemSurface = New System.Windows.Forms.MenuItem()
    Me.menuItemExit = New System.Windows.Forms.MenuItem()
    Me.menuItemDraw = New System.Windows.Forms.MenuItem()
    Me.menuItemNew = New System.Windows.Forms.MenuItem()
    Me.mainMenu1.MenuItems.AddRange(New System.Windows.Forms.MenuItem() _
      {Me.menuItemNew, Me.menuItemSurface, Me.menuItemDraw, _
      Me.menuItemExit})
    Me.menuItemSurface.Index = 1
    Me.menuItemSurface.Text = "&Surface"
    Me.menuItemExit.Index = 3
    Me.menuItemExit.Text = "&Exit"
    Me.menuItemDraw.Index = 2
    Me.menuItemDraw.Text = "&Draw"
    Me.menuItemNew.Index = 0
    Me.menuItemNew.Text = "&New"
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
    Me.ClientSize = New System.Drawing.Size(395, 288)
    Me.IsMdiContainer = True
    Me.Menu = Me.mainMenu1
    Me.Text = "System.Drawing Example"

  End Sub

The form load event, formMDI_Load, is where we initialize the application and set a global reference to both the child form (m_myChild) and a Graphics object that references it (m_myGraphics). This allows us to maintain a reference to the drawing surface at all times.

  Private Sub frmMDI_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load

    'purpose: initialize the application, create the child form    

    'create a new instance of the child form
    m_myChild = New frmChild()

    'set the parent of the child form to the MDI form
    m_myChild.MdiParent = Me

    'show the form to the user
    m_myChild.Show()

    'set form to default values
    Call resetChildForm()

    'instantiate a graphics object with the child's handle
    m_myGraphics = Graphics.FromHwnd(m_myChild.Handle)

  End Sub

The resetChildForm procedure allows us to create new forms based on the default values for the application.

  Private Sub resetChildForm()

    'purpose: clear the child form

    'set the form back to default values
    m_myChild.Left = m_LeftPos
    m_myChild.Top = m_TopPos
    m_myChild.Width = m_Width
    m_myChild.Height = m_Height

    'set the background and foreground colors of the child form
    ' 	to their defaults
    m_myChild.BackColor = Color.FromName(m_ChildColor)
    m_myChild.ForeColor = Color.FromName(m_ChildColor)

    'refresh the form
    m_myChild.Refresh()

  End Sub

The following sub routines are the click events for the various menu items:

  • menuItemNew_Click creates a new drawing surface

  • menuItemExit_Click exits the application

  • menuSuface_Click loads the surface dialog

  • menuItemDraw_Click loads the drawing form

Private Sub menuItemNew_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles menuItemNew.Click
    'purpose: user clicks the NEW item on the menu bar to create a new
    '     drawing surface

    'note: no new form is actually created, we simply clear and reset
    '   current form

    'call the method used to reset the child form to its defaults
    Call resetChildForm()

  End Sub

  Private Sub menuItemExit_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles menuItemExit.Click

    'purpose: close the app. when the user clicks the EXIT menu item

    'kill the application
    End

  End Sub

  Private Sub menuSuface_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles menuItemSurface.Click

    'purpose:  show the surface dialog when the user clicks the 
    '      SURFACE menu item

    'local scope
    Dim mySurface As frmSuface

    'create new suface form
    mySurface = New frmSuface()

    'set the start position of the modal dialog to the center position 
    ' of its parent
    mySurface.StartPosition = FormStartPosition.CenterParent

    'show the form as modal
    mySurface.ShowDialog(Me)

  End Sub

  Private Sub menuItemDraw_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles menuItemDraw.Click

    'purpose:  show the draw dialog when the user clicks 
    '      the DRAW menu item

    'local scope
    Dim myDraw As frmDraw

    'create new suface form
    myDraw = New frmDraw()

    'set the start position of the modal dialog to the center 
    ' positions of its parent
    myDraw.StartPosition = FormStartPosition.CenterParent

    'show the form as modal
    myDraw.ShowDialog(Me)

  End Sub

#End Region

End Class 

Even though the parent form is of the type MDI, our code restricts the number of child windows to just one. We do not allow users to create more than one child form. This simplifies the example. Microsoft Paint has a similar design pattern. With very little additional effort, you can modify the code to manage multiple drawing surfaces.

The child form itself contains no additional code. Listing 9.10 shows the default code generated by Visual Studio .NET. The only items of interest are the form's property settings. The form's BorderStyle is set to None and the ShowInTaskBar property is set to False. This provides users with the illusion of working on a document, when in reality, all documents in Windows are simply versions of forms.

Listing 9.10 System.Drawing Child Form (formChild.vb)

Public Class formChild
  Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

  Public Sub New()
    MyBase.New()

    'This call is required by the Windows Form Designer.
    InitializeComponent()

    'Add any initialization after the InitializeComponent() call

  End Sub

  'Form overrides dispose to clean up the component list.
  Public Overrides Sub Dispose()
    MyBase.Dispose()
    If Not (components Is Nothing) Then
      components.Dispose()
    End If
  End Sub

  'Required by the Windows Form Designer
  Private components As System.ComponentModel.Container

  'NOTE: The following procedure is required by the Windows Form Designer
  'It can be modified using the Windows Form Designer. 
  'Do not modify it using the code editor.
  Private Sub <System.Diagnostics.DebuggerStepThrough()> _
    InitializeComponent()

    'the following are key properties of the child form that were
    ' changed to make the form look more like a painting surface
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
    Me.BackColor = System.Drawing.Color.White
    Me.BorderStyle = System.Windows.Forms.FormBorderStyle.None
    Me.MaximizeBox = False
    Me.MinimizeBox = False
    Me.ShowInTaskbar = False
    Me.Text = "formChild"

  End Sub

#End Region

End Class    

Surface Form

The surface form demonstrates the Color structure. It allows users to manage properties of the drawing surface. Users can change the background color of the child form and its height and width. Figure 9.7 is a screen shot of the surface dialog.

Code Walkthrough

Listing 9.11 is long for such a simple form, but much of the code is simply default property overrides for the form and its controls. The key procedures in this listing are the form load event, the Defaults button event, and the OK button event.

Figure 9.7 System.Drawing drawing surface form.

Listing 9.11 System.Drawing Surface Form (formSurface.vb)

The listing starts by defining the form.

Public Class frmSuface
  Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

  Public Sub New()
    MyBase.New()

    'This call is required by the Windows Form Designer.
    InitializeComponent()

    'Add any initialization after the InitializeComponent() call

  End Sub

  'Form overrides dispose to clean up the component list.
  Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
    If disposing Then
      If Not (components Is Nothing) Then
        components.Dispose()
      End If
    End If
    MyBase.Dispose(disposing)
  End Sub

  Private WithEvents groupBox1 As System.Windows.Forms.GroupBox
  Private WithEvents groupBox2 As System.Windows.Forms.GroupBox
  Private WithEvents label1 As System.Windows.Forms.Label
  Private WithEvents label2 As System.Windows.Forms.Label
  Private WithEvents label3 As System.Windows.Forms.Label
  Private WithEvents label4 As System.Windows.Forms.Label
  Private WithEvents comboBoxColors As System.Windows.Forms.ComboBox
  Private WithEvents buttonCancel As System.Windows.Forms.Button
  Private WithEvents textBoxWidth As System.Windows.Forms.TextBox
  Private WithEvents textBoxHeight As System.Windows.Forms.TextBox
  Private WithEvents buttonOk As System.Windows.Forms.Button
  Private WithEvents buttonDefaults As System.Windows.Forms.Button

  'Required by the Windows Form Designer
  Private components As System.ComponentModel.Container

  'NOTE: The following procedure is required by the Windows Form Designer
  'It can be modified using the Windows Form Designer. 
  'Do not modify it using the code editor.
  <System.Diagnostics.DebuggerStepThrough()> Private Sub _
    InitializeComponent()
    Me.groupBox2 = New System.Windows.Forms.GroupBox()
    Me.comboBoxColors = New System.Windows.Forms.ComboBox()
    Me.buttonCancel = New System.Windows.Forms.Button()
    Me.buttonDefaults = New System.Windows.Forms.Button()
    Me.textBoxHeight = New System.Windows.Forms.TextBox()
    Me.buttonOk = New System.Windows.Forms.Button()
    Me.label4 = New System.Windows.Forms.Label()
    Me.groupBox1 = New System.Windows.Forms.GroupBox()
    Me.textBoxWidth = New System.Windows.Forms.TextBox()
    Me.label1 = New System.Windows.Forms.Label()
    Me.label2 = New System.Windows.Forms.Label()
    Me.label3 = New System.Windows.Forms.Label()
    Me.groupBox2.Controls.AddRange(New System.Windows.Forms.Control() _
      {Me.comboBoxColors})
    Me.groupBox2.Location = New System.Drawing.Point(8, 12)
    Me.groupBox2.Size = New System.Drawing.Size(176, 68)
    Me.groupBox2.TabIndex = 0
    Me.groupBox2.TabStop = False
    Me.groupBox2.Text = "Background Color"
    Me.comboBoxColors.DropDownStyle = _
      System.Windows.Forms.ComboBoxStyle.DropDownList
    Me.comboBoxColors.DropDownWidth = 121
    Me.comboBoxColors.Location = New System.Drawing.Point(12, 28)
    Me.comboBoxColors.MaxDropDownItems = 10
    Me.comboBoxColors.Size = New System.Drawing.Size(156, 21)
    Me.comboBoxColors.Sorted = True
    Me.comboBoxColors.TabIndex = 1
    Me.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel
    Me.buttonCancel.Location = New System.Drawing.Point(196, 44)
    Me.buttonCancel.TabIndex = 5
    Me.buttonCancel.Text = "Cancel"
    Me.buttonDefaults.Location = New System.Drawing.Point(196, 76)
    Me.buttonDefaults.TabIndex = 6
    Me.buttonDefaults.Text = "Defaults"
    Me.textBoxHeight.Location = New System.Drawing.Point(56, 56)
    Me.textBoxHeight.MaxLength = 4
    Me.textBoxHeight.Size = New System.Drawing.Size(56, 20)
    Me.textBoxHeight.TabIndex = 3
    Me.textBoxHeight.Text = "textBoxHeight"
    Me.buttonOk.Location = New System.Drawing.Point(196, 12)
    Me.buttonOk.TabIndex = 4
    Me.buttonOk.Text = "Ok"
    Me.label4.Location = New System.Drawing.Point(116, 30)
    Me.label4.Size = New System.Drawing.Size(48, 16)
    Me.label4.TabIndex = 7
    Me.label4.Text = "pixels"
    Me.groupBox1.Controls.AddRange(New System.Windows.Forms.Control() _
      {Me.label4, Me.label3, Me.label2, Me.label1, Me.textBoxHeight, _
      Me.textBoxWidth})
    Me.groupBox1.Location = New System.Drawing.Point(8, 92)
    Me.groupBox1.Size = New System.Drawing.Size(176, 92)
    Me.groupBox1.TabIndex = 0
    Me.groupBox1.TabStop = False
    Me.groupBox1.Text = "Dimensions"
    Me.textBoxWidth.Location = New System.Drawing.Point(56, 24)
    Me.textBoxWidth.MaxLength = 4
    Me.textBoxWidth.Size = New System.Drawing.Size(56, 20)
    Me.textBoxWidth.TabIndex = 2
    Me.textBoxWidth.Text = "textBoxWidth"
    Me.label1.Location = New System.Drawing.Point(8, 28)
    Me.label1.Size = New System.Drawing.Size(48, 16)
    Me.label1.TabIndex = 6
    Me.label1.Text = "Width"
    Me.label2.Location = New System.Drawing.Point(8, 60)
    Me.label2.Size = New System.Drawing.Size(48, 16)
    Me.label2.TabIndex = 7
    Me.label2.Text = "Height"
    Me.label3.Location = New System.Drawing.Point(116, 62)
    Me.label3.Size = New System.Drawing.Size(48, 16)
    Me.label3.TabIndex = 7
    Me.label3.Text = "pixels"
    Me.AcceptButton = Me.buttonOk
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
    Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
    Me.CancelButton = Me.buttonCancel
    Me.ClientSize = New System.Drawing.Size(283, 192)
    Me.Controls.AddRange(New System.Windows.Forms.Control() _
      {Me.groupBox2, Me.buttonOk, Me.buttonCancel, Me.buttonDefaults, _
      Me.groupBox1})
    Me.MaximizeBox = False
    Me.MinimizeBox = False
    Me.ShowInTaskbar = False
    Me.Text = "Drawing Surface"

  End Sub

The form load event (formSurface_Load) uses the Reflection namespace to load a drop-down box with the names of the properties of the Color structure. The load event then initializes the remaining fields on the form to match the current state of the child form.

NOTE

The System.Reflection namespace is a very powerful set of classes. While they are beyond the scope of this book, you are encouraged to browse the MSDN reference to see what can be accomplished with this namespace.

  Private Sub frmSuface_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load

    'purpose:  load the drawing surface form whose purpose is 
    '      to set the properties of the drawing suface (child form)

    'local scope
    Dim myColor As System.Drawing.Color
    Dim myProps() As System.Reflection.PropertyInfo
    Dim myType As System.Type
    Dim count As Integer

    'return the type of the color structure
    myType = myColor.GetType()

    'return the property information of the structure
    myProps = myType.GetProperties()

    'iterate the properties and add to the combo box
    For count = 0 To UBound(myProps)

      'make sure we only get valid colors
      If myProps(count).PropertyType.ToString() = "System.Drawing.Color" _
        And myProps(count).Name <> "Transparent" Then

        'add the property name (color) to the combo box
        comboBoxColors().Items.Add(myProps(count).Name)

      End If
    Next

    'select the current child bg color in the properties dialog
    comboBoxColors().SelectedIndex = comboBoxColors().FindString( _
      m_myChild.BackColor.Name())

    'set the current values of the active drawing surface
    textBoxWidth().Text = CStr(m_myChild.Width())
    textBoxHeight().Text = CStr(m_myChild.Height())

    'set the ok button as the default button
    Me.AcceptButton = buttonOk()
    Me.CancelButton = buttonCancel()

  End Sub

The Cancel button click event closes the form without applying any updates.

  Private Sub buttonCancel_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles buttonCancel.Click

    'purpose:  respond the the cancel button's click event and close the 
    '      form without the applying the changes

    'kill the form
    Me.Close()

  End Sub

The Defaults button event simply loads the form fields with the application's default values.

  Private Sub buttonDefaults_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles buttonDefaults.Click

    'purpose:  set the properties of the drawing surface 
    '      equal to the application's default values

    'set drawing surface property boxes to their defaults
    textBoxWidth().Text = CStr(m_Width)
    textBoxHeight().Text = CStr(m_Height)

    'select the default color of white
    comboBoxColors().SelectedIndex = _
      comboBoxColors().FindString(m_ChildColor)

  End Sub

When users click the OK button, the properties of the child form are set to these new values. The key piece here is that after changing properties of the child form, we have to get a new reference to it for the Graphics object to function properly. If we do not rereference the form, the graphics object is unaware of the changes to the drawing surface, which results in shapes getting cut off at the window's old size and other undesirable behavior.

  Private Sub buttonOk_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles buttonOk.Click

    'purpose:  reset the properties of the drawing suface

    'validate the textBox myects befor submission
    If Not IsNumeric(textBoxWidth().Text) Or _
      Not IsNumeric(textBoxHeight().Text) Then

      'we would want to do more if this were a production app ...
      Beep()

    Else

      'set the dimensions of the drawing surface
      m_myChild.Width = CInt(textBoxWidth().Text)
      m_myChild.Height = CInt(textBoxHeight().Text)

      'set the background color
      m_SurfaceBackground = comboBoxColors().SelectedItem.ToString
      m_myChild.BackColor = Color.FromName(m_SurfaceBackground)
      m_myChild.Refresh()

      'settings applied, close form and return processing back to MDI 
      Me.Close()

      're-get the form to the graphics object
      m_myGraphics = Graphics.FromHwnd(m_myChild.Handle)

    End If

  End Sub

#End Region

End Class

Draw Form

The draw form allows users to draw shapes onto the child form. Obviously, this is not the ideal way to create graphics; mouse or pen-based input is much easier. Nevertheless, for the clarity of this example and in the interest of simplicity, we'll define shapes by text boxes and drop-downs.

The features of the draw form allow users to create basic shapes (line, rectangle, and ellipse). They can set the color and width of the shape outline. Shapes can be filled with a solid color, a blend, or a pattern. The form also allows users to rotate the shape prior to drawing it to the surface. The Apply and Clear buttons were added so that you could create multiple shapes onto the child form without leaving the draw dialog. Figure 9.8 is a screen capture of the draw form.

Figure 9.8 System.Drawing draw form.

Code Walkthrough

Listing 9.12 represents the code behind the draw form. Again, much of the code is form and control property settings.

Listing 9.12 System.Drawing Draw Form (formDraw)

Public Class frmDraw
  Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

  Public Sub New()
    MyBase.New()

    'This call is required by the Windows Form Designer.
    InitializeComponent()

    'Add any initialization after the InitializeComponent() call

  End Sub

  'Form overrides dispose to clean up the component list.
  Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
    If disposing Then
      If Not (components Is Nothing) Then
        components.Dispose()
      End If
    End If
    MyBase.Dispose(disposing)
  End Sub

  'form and control property assigments
  Private WithEvents groupBox1 As System.Windows.Forms.GroupBox
  Private WithEvents groupBox2 As System.Windows.Forms.GroupBox
  Private WithEvents groupBox3 As System.Windows.Forms.GroupBox
  Private WithEvents groupBox4 As System.Windows.Forms.GroupBox
  Private WithEvents comboBoxShape As System.Windows.Forms.ComboBox
  Private WithEvents label1 As System.Windows.Forms.Label
  Private WithEvents label2 As System.Windows.Forms.Label
  Private WithEvents label3 As System.Windows.Forms.Label
  Private WithEvents label4 As System.Windows.Forms.Label
  Private WithEvents label5 As System.Windows.Forms.Label
  Private WithEvents groupBox5 As System.Windows.Forms.GroupBox
  Private WithEvents label6 As System.Windows.Forms.Label
  Private WithEvents label7 As System.Windows.Forms.Label
  Private WithEvents textBoxHeight As System.Windows.Forms.TextBox
  Private WithEvents textBoxWidth As System.Windows.Forms.TextBox
  Private WithEvents textBoxX As System.Windows.Forms.TextBox
  Private WithEvents textBoxY As System.Windows.Forms.TextBox
  Private WithEvents label8 As System.Windows.Forms.Label
  Private WithEvents buttonCancel As System.Windows.Forms.Button
  Private WithEvents buttonOk As System.Windows.Forms.Button
  Private WithEvents label9 As System.Windows.Forms.Label
  Private WithEvents label10 As System.Windows.Forms.Label
  Private WithEvents comboBoxBlendTo As System.Windows.Forms.ComboBox
  Private WithEvents comboBoxBlendFrom As System.Windows.Forms.ComboBox
  Private WithEvents comboBoxPattern As System.Windows.Forms.ComboBox
  Private WithEvents comboBoxSolidColor As System.Windows.Forms.ComboBox
  Private WithEvents comboBoxOutlineColor As System.Windows.Forms.ComboBox
  Private WithEvents buttonApply As System.Windows.Forms.Button
  Private WithEvents numericUpDownWeight As _
    System.Windows.Forms.NumericUpDown
  Private WithEvents radioButtonSolid As System.Windows.Forms.RadioButton
  Private WithEvents radioButtonBlend As System.Windows.Forms.RadioButton
  Private WithEvents radioButtonNone As System.Windows.Forms.RadioButton
  Private WithEvents radioButtonPattern As System.Windows.Forms.RadioButton
  Private WithEvents label11 As System.Windows.Forms.Label
  Private WithEvents label12 As System.Windows.Forms.Label
  Private WithEvents comboBoxPattFore As System.Windows.Forms.ComboBox
  Private WithEvents comboBoxBackFore As System.Windows.Forms.ComboBox
  Private WithEvents label13 As System.Windows.Forms.Label
  Private WithEvents comboBoxBlendStyle As System.Windows.Forms.ComboBox
  Private WithEvents comboBoxPattBack As System.Windows.Forms.ComboBox
  Private WithEvents label14 As System.Windows.Forms.Label
  Private WithEvents numericUpDownRotate As _
    System.Windows.Forms.NumericUpDown
  Private WithEvents buttonClear As System.Windows.Forms.Button
  Private WithEvents label15 As System.Windows.Forms.Label

  'Required by the Windows Form Designer
  Private components As System.ComponentModel.Container

  'NOTE: The following procedure is required by the Windows Form Designer
  'It can be modified using the Windows Form Designer. 
  'Do not modify it using the code editor.
  <System.Diagnostics.DebuggerStepThrough()> Private Sub _
    InitializeComponent()
    Me.buttonApply = New System.Windows.Forms.Button()
    Me.radioButtonNone = New System.Windows.Forms.RadioButton()
    Me.numericUpDownRotate = New System.Windows.Forms.NumericUpDown()
    Me.textBoxHeight = New System.Windows.Forms.TextBox()
    Me.radioButtonSolid = New System.Windows.Forms.RadioButton()
    Me.comboBoxBlendTo = New System.Windows.Forms.ComboBox()
    Me.textBoxY = New System.Windows.Forms.TextBox()
    Me.textBoxWidth = New System.Windows.Forms.TextBox()
    Me.buttonClear = New System.Windows.Forms.Button()
    Me.radioButtonBlend = New System.Windows.Forms.RadioButton()
    Me.comboBoxShape = New System.Windows.Forms.ComboBox()
    Me.comboBoxPattern = New System.Windows.Forms.ComboBox()
    Me.comboBoxPattBack = New System.Windows.Forms.ComboBox()
    Me.comboBoxBlendStyle = New System.Windows.Forms.ComboBox()
    Me.label15 = New System.Windows.Forms.Label()
    Me.label14 = New System.Windows.Forms.Label()
    Me.label11 = New System.Windows.Forms.Label()
    Me.label10 = New System.Windows.Forms.Label()
    Me.label13 = New System.Windows.Forms.Label()
    Me.label12 = New System.Windows.Forms.Label()
    Me.comboBoxOutlineColor = New System.Windows.Forms.ComboBox()
    Me.buttonOk = New System.Windows.Forms.Button()
    Me.label8 = New System.Windows.Forms.Label()
    Me.label9 = New System.Windows.Forms.Label()
    Me.comboBoxSolidColor = New System.Windows.Forms.ComboBox()
    Me.buttonCancel = New System.Windows.Forms.Button()
    Me.label4 = New System.Windows.Forms.Label()
    Me.label5 = New System.Windows.Forms.Label()
    Me.label6 = New System.Windows.Forms.Label()
    Me.label7 = New System.Windows.Forms.Label()
    Me.label2 = New System.Windows.Forms.Label()
    Me.label3 = New System.Windows.Forms.Label()
    Me.comboBoxBlendFrom = New System.Windows.Forms.ComboBox()
    Me.radioButtonPattern = New System.Windows.Forms.RadioButton()
    Me.textBoxX = New System.Windows.Forms.TextBox()
    Me.comboBoxPattFore = New System.Windows.Forms.ComboBox()
    Me.groupBox1 = New System.Windows.Forms.GroupBox()
    Me.groupBox2 = New System.Windows.Forms.GroupBox()
    Me.groupBox3 = New System.Windows.Forms.GroupBox()
    Me.groupBox4 = New System.Windows.Forms.GroupBox()
    Me.groupBox5 = New System.Windows.Forms.GroupBox()
    Me.numericUpDownWeight = New System.Windows.Forms.NumericUpDown()
    Me.label1 = New System.Windows.Forms.Label()
    CType(Me.numericUpDownRotate, _
      System.ComponentModel.ISupportInitialize).BeginInit()
    CType(Me.numericUpDownWeight, _
      System.ComponentModel.ISupportInitialize).BeginInit()
    Me.buttonApply.Location = New System.Drawing.Point(336, 432)
    Me.buttonApply.TabIndex = 5
    Me.buttonApply.Text = "Apply"
    Me.radioButtonNone.Checked = True
    Me.radioButtonNone.Location = New System.Drawing.Point(12, 24)
    Me.radioButtonNone.TabIndex = 0
    Me.radioButtonNone.TabStop = True
    Me.radioButtonNone.Text = "No Fill"
    Me.numericUpDownRotate.Location = New System.Drawing.Point(56, 24) 
    Me.numericUpDownRotate.Maximum = New Decimal(New Integer() _
      {360, 0, 0, 0})
    Me.numericUpDownRotate.Minimum = New Decimal(New Integer() _
      {360, 0, 0, -2147483648})
    Me.numericUpDownRotate.Size = New System.Drawing.Size(52, 20)
    Me.numericUpDownRotate.TabIndex = 1
    Me.textBoxHeight.Location = New System.Drawing.Point(72, 88)
    Me.textBoxHeight.MaxLength = 4
    Me.textBoxHeight.Size = New System.Drawing.Size(48, 20)
    Me.textBoxHeight.TabIndex = 3
    Me.radioButtonSolid.Location = New System.Drawing.Point(12, 52)
    Me.radioButtonSolid.TabIndex = 1
    Me.radioButtonSolid.Text = "Solid"
    Me.comboBoxBlendTo.DropDownStyle = _
      System.Windows.Forms.ComboBoxStyle.DropDownList
    Me.comboBoxBlendTo.DropDownWidth = 132
    Me.comboBoxBlendTo.Location = New System.Drawing.Point(68, 136)
    Me.comboBoxBlendTo.Size = New System.Drawing.Size(132, 21)
    Me.comboBoxBlendTo.TabIndex = 8
    Me.textBoxY.Location = New System.Drawing.Point(32, 52)
    Me.textBoxY.MaxLength = 4
    Me.textBoxY.Size = New System.Drawing.Size(48, 20)
    Me.textBoxY.TabIndex = 5
    Me.textBoxWidth.Location = New System.Drawing.Point(72, 60)
    Me.textBoxWidth.MaxLength = 4
    Me.textBoxWidth.Size = New System.Drawing.Size(48, 20)
    Me.textBoxWidth.TabIndex = 2
    Me.buttonClear.Location = New System.Drawing.Point(12, 432)
    Me.buttonClear.TabIndex = 7
    Me.buttonClear.Text = "Clear"
    Me.radioButtonBlend.Location = New System.Drawing.Point(12, 80)
    Me.radioButtonBlend.TabIndex = 3
    Me.radioButtonBlend.Text = "Blend"
    Me.comboBoxShape.AllowDrop = True
    Me.comboBoxShape.DropDownStyle = _
      System.Windows.Forms.ComboBoxStyle.DropDownList
    Me.comboBoxShape.DropDownWidth = 121
    Me.comboBoxShape.Location = New System.Drawing.Point(68, 28)
    Me.comboBoxShape.Size = New System.Drawing.Size(121, 21)
    Me.comboBoxShape.TabIndex = 1
    Me.comboBoxPattern.DropDownStyle = _
      System.Windows.Forms.ComboBoxStyle.DropDownList
    Me.comboBoxPattern.DropDownWidth = 132
    Me.comboBoxPattern.Location = New System.Drawing.Point(308, 24)
    Me.comboBoxPattern.Size = New System.Drawing.Size(132, 21)
    Me.comboBoxPattern.TabIndex = 10
    Me.comboBoxPattBack.DropDownStyle = _
      System.Windows.Forms.ComboBoxStyle.DropDownList
    Me.comboBoxPattBack.DropDownWidth = 132
    Me.comboBoxPattBack.Location = New System.Drawing.Point(328, 80) 
    Me.comboBoxPattBack.Size = New System.Drawing.Size(132, 21)
    Me.comboBoxPattBack.TabIndex = 14
    Me.comboBoxBlendStyle.DropDownStyle = _
      System.Windows.Forms.ComboBoxStyle.DropDownList
    Me.comboBoxBlendStyle.DropDownWidth = 132
    Me.comboBoxBlendStyle.Location = New System.Drawing.Point(68, 80)
    Me.comboBoxBlendStyle.Size = New System.Drawing.Size(132, 21)
    Me.comboBoxBlendStyle.TabIndex = 4
    Me.label15.Location = New System.Drawing.Point(110, 26)
    Me.label15.TabIndex = 0
    Me.label15.Text = "degrees"
    Me.label14.Location = New System.Drawing.Point(12, 28)
    Me.label14.TabIndex = 0
    Me.label14.Text = "Rotate"
    Me.label11.Location = New System.Drawing.Point(256, 56)
    Me.label11.TabIndex = 11
    Me.label11.Text = "Foreground"
    Me.label10.Location = New System.Drawing.Point(12, 64)
    Me.label10.TabIndex = 1
    Me.label10.Text = "Color"
    Me.label13.Location = New System.Drawing.Point(28, 140)
    Me.label13.Size = New System.Drawing.Size(40, 23)
    Me.label13.TabIndex = 7
    Me.label13.Text = "To"
    Me.label12.Location = New System.Drawing.Point(256, 84)
    Me.label12.TabIndex = 13
    Me.label12.Text = "Background"
    Me.comboBoxOutlineColor.DropDownStyle = _
      System.Windows.Forms.ComboBoxStyle.DropDownList
    Me.comboBoxOutlineColor.DropDownWidth = 121
    Me.comboBoxOutlineColor.Location = New System.Drawing.Point(12, 80) 
    Me.comboBoxOutlineColor.Size = New System.Drawing.Size(148, 21)
    Me.comboBoxOutlineColor.TabIndex = 2
    Me.buttonOk.Location = New System.Drawing.Point(252, 432)
    Me.buttonOk.TabIndex = 4
    Me.buttonOk.Text = "Ok"
    Me.label8.Location = New System.Drawing.Point(28, 112)
    Me.label8.Size = New System.Drawing.Size(48, 23)
    Me.label8.TabIndex = 5
    Me.label8.Text = "From"
    Me.label9.Location = New System.Drawing.Point(12, 32)
    Me.label9.Size = New System.Drawing.Size(52, 16)
    Me.label9.TabIndex = 0
    Me.label9.Text = "Weight"
    Me.comboBoxSolidColor.DropDownStyle = _
      System.Windows.Forms.ComboBoxStyle.DropDownList
    Me.comboBoxSolidColor.DropDownWidth = 132
    Me.comboBoxSolidColor.Location = New System.Drawing.Point(68, 52)
    Me.comboBoxSolidColor.Size = New System.Drawing.Size(132, 21)
    Me.comboBoxSolidColor.TabIndex = 2
    Me.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel
    Me.buttonCancel.Location = New System.Drawing.Point(420, 432)
    Me.buttonCancel.TabIndex = 6
    Me.buttonCancel.Text = "Cancel"
    Me.label4.Location = New System.Drawing.Point(128, 64)
    Me.label4.Size = New System.Drawing.Size(52, 16)
    Me.label4.TabIndex = 2
    Me.label4.Text = "pixels"
    Me.label5.Location = New System.Drawing.Point(128, 92)
    Me.label5.Size = New System.Drawing.Size(52, 16)
    Me.label5.TabIndex = 2
    Me.label5.Text = "pixels"
    Me.label6.Location = New System.Drawing.Point(12, 28)
    Me.label6.Size = New System.Drawing.Size(52, 16)
    Me.label6.TabIndex = 2
    Me.label6.Text = "X"
    Me.label7.Location = New System.Drawing.Point(12, 56)
    Me.label7.Size = New System.Drawing.Size(52, 16)
    Me.label7.TabIndex = 2
    Me.label7.Text = "Y"
    Me.label2.Location = New System.Drawing.Point(12, 92)
    Me.label2.Size = New System.Drawing.Size(52, 16)
    Me.label2.TabIndex = 2
    Me.label2.Text = "Height"
    Me.label3.Location = New System.Drawing.Point(12, 64)
    Me.label3.Size = New System.Drawing.Size(52, 16)
    Me.label3.TabIndex = 2
    Me.label3.Text = "Width"
    Me.comboBoxBlendFrom.DropDownStyle = _
      System.Windows.Forms.ComboBoxStyle.DropDownList
    Me.comboBoxBlendFrom.DropDownWidth = 132
    Me.comboBoxBlendFrom.Location = New System.Drawing.Point(68, 108)
    Me.comboBoxBlendFrom.Size = New System.Drawing.Size(132, 21)
    Me.comboBoxBlendFrom.TabIndex = 6
    Me.radioButtonPattern.Location = New System.Drawing.Point(240, 24) 
    Me.radioButtonPattern.TabIndex = 9
    Me.radioButtonPattern.Text = "Pattern"
    Me.textBoxX.Location = New System.Drawing.Point(32, 24)
    Me.textBoxX.MaxLength = 4
    Me.textBoxX.Size = New System.Drawing.Size(48, 20)
    Me.textBoxX.TabIndex = 4
    Me.comboBoxPattFore.DropDownStyle = _
      System.Windows.Forms.ComboBoxStyle.DropDownList
    Me.comboBoxPattFore.DropDownWidth = 132
    Me.comboBoxPattFore.Location = New System.Drawing.Point(328, 52)
    Me.comboBoxPattFore.Size = New System.Drawing.Size(132, 21)
    Me.comboBoxPattFore.TabIndex = 12
    Me.groupBox1.Controls.AddRange(New System.Windows.Forms.Control() _
      {Me.groupBox5, Me.label5, Me.label4, Me.textBoxHeight, _
      Me.textBoxWidth, Me.label3, Me.label2, Me.label1, _
      Me.comboBoxShape})
    Me.groupBox1.Location = New System.Drawing.Point(12, 12)
    Me.groupBox1.Size = New System.Drawing.Size(304, 124)
    Me.groupBox1.TabIndex = 0
    Me.groupBox1.TabStop = False
    Me.groupBox1.Text = "Shape"
    Me.groupBox2.Controls.AddRange(New System.Windows.Forms.Control() _
      {Me.comboBoxBlendStyle, Me.label13, Me.comboBoxBlendTo, _
      Me.comboBoxBlendFrom, Me.comboBoxPattBack, Me.comboBoxPattFore, _
      Me.label12, Me.label11, Me.label8, Me.comboBoxPattern, _
      Me.comboBoxSolidColor, Me.radioButtonBlend, Me.radioButtonSolid, _
      Me.radioButtonPattern, Me.radioButtonNone})
    Me.groupBox2.Location = New System.Drawing.Point(12, 148)
    Me.groupBox2.Size = New System.Drawing.Size(484, 176)
    Me.groupBox2.TabIndex = 2
    Me.groupBox2.TabStop = False
    Me.groupBox2.Text = "Fill"
    Me.groupBox3.Controls.AddRange(New System.Windows.Forms.Control() _
      {Me.numericUpDownWeight, Me.comboBoxOutlineColor, Me.label10, _
      Me.label9})
    Me.groupBox3.Location = New System.Drawing.Point(328, 12)
    Me.groupBox3.Size = New System.Drawing.Size(168, 124)
    Me.groupBox3.TabIndex = 1
    Me.groupBox3.TabStop = False
    Me.groupBox3.Text = "Outline"
    Me.groupBox4.Controls.AddRange(New System.Windows.Forms.Control() _
      {Me.label15, Me.numericUpDownRotate, Me.label14})
    Me.groupBox4.Location = New System.Drawing.Point(12, 332)
    Me.groupBox4.Size = New System.Drawing.Size(484, 88)
    Me.groupBox4.TabIndex = 3
    Me.groupBox4.TabStop = False
    Me.groupBox4.Text = "Transform"
    Me.groupBox5.Controls.AddRange(New System.Windows.Forms.Control() _
      {Me.textBoxX, Me.textBoxY, Me.label7, Me.label6})
    Me.groupBox5.Location = New System.Drawing.Point(200, 24)
    Me.groupBox5.Size = New System.Drawing.Size(92, 84)
    Me.groupBox5.TabIndex = 4
    Me.groupBox5.TabStop = False
    Me.groupBox5.Text = "Position"
    Me.numericUpDownWeight.Location = New System.Drawing.Point(64, 28)
    Me.numericUpDownWeight.Maximum = New Decimal(New Integer() _
      {999, 0, 0, 0})
    Me.numericUpDownWeight.Size = New System.Drawing.Size(64, 20)
    Me.numericUpDownWeight.TabIndex = 1
    Me.label1.Location = New System.Drawing.Point(12, 32)
    Me.label1.Size = New System.Drawing.Size(52, 16)
    Me.label1.TabIndex = 2
    Me.label1.Text = "Shape"
    Me.AcceptButton = Me.buttonOk
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
    Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
    Me.CancelButton = Me.buttonCancel
    Me.ClientSize = New System.Drawing.Size(509, 462)
    Me.Controls.AddRange(New System.Windows.Forms.Control() _
      {Me.buttonClear, Me.buttonApply, Me.buttonCancel, Me.buttonOk, _
      Me.groupBox2, Me.groupBox3, Me.groupBox4, Me.groupBox1})
    Me.MaximizeBox = False
    Me.MinimizeBox = False
    Me.Text = "Draw"
    CType(Me.numericUpDownRotate, _
      System.ComponentModel.ISupportInitialize).EndInit()
    CType(Me.numericUpDownWeight, _
      System.ComponentModel.ISupportInitialize).EndInit()

  End Sub

The Cancel button click event simply closes the form without applying the current form values. Of course, if you've already pressed the Apply button, then the Cancel button just closes the form.

  Private Sub buttonCancel_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles buttonCancel.Click

    'purpose: close the form, cancel any pending actions

    'cancel the form
    Me.Close()

  End Sub

The formDraw_Load procedure initializes the controls on the form. In it, we fill the various combo boxes directly from enumeration and structure members using the Reflection namespace.

Note that at the end of the form load event, we set the AcceptButton and CancelButton properties of the form to the OK and Cancel buttons, respectively. The AcceptButton property indicates what button on the form should respond to the Enter key being pressed (default button). The CancelButton property indicates the button that is fired when users click the Escape key. This is new in .NET. In past versions of VB, in order to implement a button that responds to the Enter or Cancel keys you would set properties of the button; now, you set properties of the form.

  Private Sub frmDraw_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load

    'purpose:  initialize the form, set the initial values of controls

    'local scope
    Dim myColor As System.Drawing.Color
    Dim myProps() As System.Reflection.PropertyInfo
    Dim myType As System.Type
    Dim count As Integer
    Dim colorName As String
    Dim myHatchStyle As Drawing.Drawing2D.HatchStyle
    Dim myArray() As String
    Dim myLinGradMode As Drawing2D.LinearGradientMode

    'return the type of the color structure
    myType = myColor.GetType()

    'return the property information of the structure
    myProps = myType.GetProperties()

    'iterate the properties and add to the combo box
    For count = 0 To UBound(myProps)

      'make sure we only get valid colors
      If myProps(count).PropertyType.ToString() = "System.Drawing.Color" _
        And myProps(count).Name <> "Transparent" Then

        'get the color's name
        colorName = myProps(count).Name

        'add the property name (color) to the color combo boxes
        comboBoxOutlineColor().Items.Add(colorName)
        comboBoxBlendFrom().Items.Add(colorName)
        comboBoxBlendTo().Items.Add(colorName)
        comboBoxSolidColor().Items.Add(colorName)
        comboBoxPattFore().Items.Add(colorName)
        comboBoxPattBack().Items.Add(colorName)

      End If

    Next

    'get a type object that represents the hatchStyle enum
    myType = myHatchStyle.GetType()

    'fill an array with the hatchStyle's member names
    myArray = myHatchStyle.GetNames(myType)

    'loop through the array
    For count = 0 To UBound(myArray)

      'fill the pattern dialog
      comboBoxPattern().Items.Add(myArray(count))

    Next

    'get a type object based on the linear gradient enumeration
    myType = myLinGradMode.GetType()

    'fill an array with the linear gradient's member names
    myArray = myLinGradMode.GetNames(myType)

    'loop through the array
    For count = 0 To UBound(myArray)

      'fill the blend style drop-down
      comboBoxBlendStyle().Items.Add(myArray(count))

    Next

    'add some basic shape values for users to select from
    comboBoxShape().Items.Add("Line")
    comboBoxShape().Items.Add("Rectangle")
    comboBoxShape().Items.Add("Ellipse")

    'select first item in each list by default 
    '(saves enabling and disabling controls)
    comboBoxPattern().SelectedIndex = 0
    comboBoxShape().SelectedIndex = 0
    comboBoxOutlineColor().SelectedIndex = 0
    comboBoxBlendFrom().SelectedIndex = 0
    comboBoxBlendTo().SelectedIndex = 0
    comboBoxSolidColor().SelectedIndex = 0
    comboBoxPattFore().SelectedIndex = 0
    comboBoxPattBack().SelectedIndex = 0
    comboBoxBlendStyle().SelectedIndex = 0

    'set the ok button on the form to respond to the enter key
    Me.AcceptButton = buttonOk()

    'set the cancel button on the form to respond to the escape key
    Me.CancelButton = buttonCancel()

  End Sub
dialog

The Apply button's click event simply validates the form by calling validateForm. If no validation rules were broken, it calls the submitForm method to apply the shape to the child form.

  Private Sub buttonApply_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles buttonApply.Click

    'purpose:  allow users to draw items to the form without closing the 
    'dialog before drawing can happen the form fields must be validated
    If Not validateForm() Then

      'of course we would want to do more than beep ...
      'I assume a production application would capture user keystrokes
      ' and disallow invalid entries ...
      Beep()

    Else

      'call the centralized routine that call the drawing module
      Call submitForm()

    End If

  End Sub

The validateForm function checks for valid entries in our text boxes and returns either True to indicate all fields are valid or False to indicate one or more are invalid.

  Private Function validateForm() As Boolean

    'purpose:  quick check of field values (IsNumeric)

    'validate some of the form fields
    If Not IsNumeric(textBoxWidth().Text) Or _
      Not IsNumeric(textBoxHeight().Text) Or _
      Not IsNumeric(textBoxX().Text) Or _
      Not IsNumeric(textBoxY().Text) Then

      Return False

    Else

      Return True

    End If

  End Function

The submitForm method simply calls the draw method contained in our module. Of course, it passes all the form values as parameters.

  Private Sub submitForm()

    'purpose:  send the form values to the draw method of modDrawing

    'local scope
    Dim strFillType As String

    'set the fill pattern selected
    If radioButtonBlend().Checked = True Then
      strFillType = "BLEND"
    ElseIf radioButtonSolid().Checked = True Then
      strFillType = "SOLID"
    ElseIf radioButtonPattern().Checked = True Then
      strFillType = "PATTERN"
    Else
      strFillType = "NONE"
    End If

    'call the draw method
    Call draw(shape:=comboBoxShape().SelectedItem.ToString, _
      Width:=CInt(textBoxWidth().Text), _
      Height:=CInt(textBoxHeight().Text), _
      x:=CInt(textBoxX().Text), _
      y:=CInt(textBoxY().Text), _
      fillType:=strFillType, _
      outlineWeight:=CSng(numericUpDownWeight().Value), _
      outlineColor:=comboBoxOutlineColor().SelectedItem.ToString, _
      solidColor:=comboBoxSolidColor().SelectedItem.ToString, _
      blendFrom:=comboBoxBlendFrom().SelectedItem.ToString, _
      blendTo:=comboBoxBlendTo().SelectedItem.ToString, _
      pattern:=comboBoxPattern().SelectedItem.ToString, _
      patternForeColor:=comboBoxPattFore().SelectedItem.ToString, _
      patternBackColor:=comboBoxPattBack().SelectedItem.ToString, _
      blendStyle:=comboBoxBlendStyle().SelectedItem.ToString, _
      rotateAngle:=numericUpDownRotate().Value)

  End Sub

The OK button's click event checks the field entries for validity using validateForm. It then calls submitForm to apply the shape to the child form. Finally, it closes the dialog.

  Private Sub buttonOk_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles buttonOk.Click

    'purpose:  event triggered when users click the ok button
    '      draw the item and close the form

    'validate the form
    If Not validateForm() Then

      'of course we would want to do more than beep ...
      Beep()

    Else

      'call the centralized routine that call the drawing module
      Call submitForm()

      'close the form
      Me.Close()

    End If

  End Sub

  Private Sub buttonClear_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles buttonClear.Click

    'purpose: provide the illusion of clearing the form
    '     this allows users to keep drawing without closing the dialog

    'set the background color to the default
    m_myChild.BackColor = Color.FromName(m_SurfaceBackground)
    m_myChild.Refresh()

  End Sub


#End Region

End Class

Drawing Module

The drawing module contains the application's global variables and the actual draw procedure.

Code Walkthrough

We first set the application's default values including the drawing surface settings. We then declare the global objects that reference the child form and the associated graphics objects. The drawing module is presented in Listing 9.13.

Listing 9.13 Drawing Module (modDrawing)

Option Strict Off

Module modDrawing

  'set defaults for child form
  Public m_LeftPos As Integer = 5
  Public m_TopPos As Integer = 5
  Public m_Width As Integer = 256
  Public m_Height As Integer = 256
  Public m_ChildColor As String = "White"
  Public m_SurfaceBackground As String = "White"

  'set a reference to a graphics object to be used globally
  Public m_myGraphics As Graphics

  'set a reference to the child form to be used by the application
  Public m_myChild As frmChild

The draw method takes all of the necessary values from the draw form as parameters. It uses the Graphics class to render shapes onto the surface. You can see that most of the code is just simple logic to determine the type of shape to draw, the shape's outline, and its fill type. One interesting method call is myState = m_myGraphics.Save(), where myState is declared as Dim myState As Drawing2D.GraphicsState. The GraphicsState class allows you to save, or maintain, a copy of the Graphics object at a point in time. You do this by calling its save method.

This becomes important as you apply transforms to the Graphics object. Each call to a transform method sets the Graphics object's state to the given value. Therefore, subsequent calls to a transform method actually transform the already-transformed object. For instance, the line m_myGraphics.RotateTransform(angle:=rotateAngle), where rotateAngle is 5°, sets the graphics object to rotate shapes that it draws by 5°. A subsequent call to the RotateTranform property, where rotateAngle is 10°, actually results in a rotation of 15° from the base shape. You can see why the Save method is so important. Of course we could also call Graphics.ResetTransform. Finally, after the transforms are complete and the objects are drawn to the screen, you call m_myGraphics.Restore(myState). This restores the Graphics object to its original state.

  Public Sub draw( _
    ByVal shape As String, _
    ByVal width As Integer, _
    ByVal height As Integer, _
    ByVal x As Integer, _
    ByVal y As Integer, _
    ByVal fillType As String, _
    ByVal outlineWeight As Single, _
    ByVal outlineColor As String, _
    ByVal solidColor As String, _
    ByVal blendFrom As String, _
    ByVal blendTo As String, _
    ByVal pattern As String, _
    ByVal patternForeColor As String, _
    ByVal patternBackColor As String, _
    ByVal blendStyle As String, _
    ByVal rotateAngle As Single)

    'purpose: draw a shape to the drawing surface (frmChild)

    'local scope
    Dim myPen As Pen
    Dim myRectangle As Rectangle
    Dim myBrush As Brush
    Dim myHatchStyle As Drawing2D.HatchStyle
    Dim myType As System.Type
    Dim myBlendStyle As Drawing2D.LinearGradientMode
    Dim myState As Drawing2D.GraphicsState

    'get the current state of the graphics object (pre-tranform)
    myState = m_myGraphics.Save()

    'set the rotation transformation value
    m_myGraphics.RotateTransform(angle:=rotateAngle)

    'check what shape to draw
    Select Case shape
      Case "Line"

        'create a new pen instance
        myPen = New Pen(color.FromName(name:=outlineColor), _
          width:=outlineWeight)

        'draw the line to the surface
        m_myGraphics.DrawLine(pen:=myPen, x1:=x, y1:=y, _
          x2:=x + width, y2:=y + height)

      Case "Rectangle", "Ellipse"

        'note: the rectangle and ellipse are very similar 
        '   they can use the same code

        'create a new pen myect for the outline of the shape
        myPen = New Pen(color.FromName(name:=outlineColor), _
          width:=outlineWeight)

        'create the rectangle object
        myRectangle = New Rectangle(x:=x, y:=y, width:=width, _
          height:=height)

        'draw the rectangle to the surface
        If shape = "Ellipse" Then

          'draw the ellipse
          m_myGraphics.DrawEllipse(pen:=myPen, _
            rect:=myRectangle)

        Else

          'draw a rectangle
          m_myGraphics.DrawRectangle(pen:=myPen, _
            rect:=myRectangle)

        End If

        'fill the rectangle/ellipse
        If fillType <> "NONE" Then

          'determine brush type to create
          Select Case fillType
            Case "SOLID"

              'create a new solid brush
              myBrush = New SolidBrush( _
                color:=color.FromName(name:=solidColor))

            Case "PATTERN"

              'create the hatch brush
              'note: we use the type object and the parse 
              '    method of the enum to return the 
              '    value of the enum's member from its 
              '    name (string)
              myType = myHatchStyle.GetType()
              myBrush = New System.Drawing.Drawing2D.HatchBrush _
              (myHatchStyle.Parse(enumType:=myType, _
                value:=pattern), 
_foreColor:=Color.FromName(name:=patternForeColor), 
_backColor:=color.FromName(name:=patternBackColor))

            Case "BLEND"

              'create a blend brush
              'note: we use the type object and the parse 
              '    method of the enum to return the 
              '    value of the enum's member from its 
              '    name (string)
              myType = myBlendStyle.GetType
              myBrush = New _
                System.Drawing.Drawing2D.LinearGradientBrush( _
                rect:=myRectangle, _
                color1:=color.FromName(name:=blendFrom), _
                color2:=color.FromName(name:=blendTo), _
                LinearGradientMode:=myBlendStyle.Parse( _
                enumType:=myType, value:=blendStyle))

          End Select

          'fill the shape
          If shape = "Ellipse" Then

            'draw the ellipse with the correct brush
            m_myGraphics.FillEllipse(brush:=myBrush, _
              rect:=myRectangle)

          Else

            'fill the rectangle with the correct brush
            m_myGraphics.FillRectangle(brush:=myBrush, _
              rect:=myRectangle)

          End If

        End If

    End Select

    'reset the state of the graphics object
    m_myGraphics.Restore(myState)

  End Sub

End Module
  • + Share This
  • 🔖 Save To Your Account