- Key Classes Related to Drawing
- Drawing with the .NET Namespaces
- Drawing Basics
- Drawing Basic Shapes
- Filling Shapes
- Collections of Shapes
- Working with Images
- Transformations
- Learning by Example: A Forms-Based Drawing Application
- Summary
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