Writing Mobile Code Essential Software Engineering for Building Mobile Applications [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Writing Mobile Code Essential Software Engineering for Building Mobile Applications [Electronic resources] - نسخه متنی

Ivo Salmre

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید











Chapter 11 (Performance and Graphics) Samples

Listing 11.1. Populating and Clearing a TreeView Control Using Alternative Strategies



'------------------------------------------------------------
'Note #1: This sample uses the PerformanceSampling class
' defined earlier in this book. Make sure this class
' is included in your project.
'Note #2: This code need to be inserted into a Form class that
' has a TreeView control and buttons hooked up to the
' xxx_Click functions below.
'------------------------------------------------------------
'Number of items to place into the tree view
Const NUMBER_ITEMS As Integer = 800
'--------------------------------------------
'Code for: "Fill: Baseline" Button
'
'"Unoptimized" Approach to filling a TreeView
'--------------------------------------------
Private Sub UnOptimizedFill_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles UnOptimizedFill.Click
'To make sure we're testing the same thing, make sure
'the array is clear
If (TreeView1.Nodes.Count > 0) Then
TreeView1.BeginUpdate()
TreeView1.Nodes.Clear()
TreeView1.EndUpdate()
TreeView1.Update()
End If
'For more consistent measurement, collect the garbage
'before running. Do not do this in production code!
System.GC.Collect()
'Start the test timer
PerformanceSampling.StartSample(0, "TreeViewPopulate")
'Fill the TreeView
Dim i As Integer
For i = 1 To NUMBER_ITEMS
TreeView1.Nodes.Add("TreeItem" + CStr(i))
Next
'Stop the test timer and show the results
PerformanceSampling.StopSample(0)
MsgBox(PerformanceSampling.GetSampleDurationText(0))
End Sub
'--------------------------------------------
'Code for: "Clear: Baseline" Button
'
'"Unoptimized" Approach to filling a TreeView
'--------------------------------------------
Private Sub UnOptimizedClear_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles UnOptimizedClear.Click
'For more consistent measurement, collect the garbage
' before running
System.GC.Collect()
'Start the test timer
PerformanceSampling.StartSample(1, "TreeViewClear")
TreeView1.Nodes.Clear()
PerformanceSampling.StopSample(1)
MsgBox(PerformanceSampling.GetSampleDurationText(1))
End Sub
'--------------------------------------------
'Code for: "Fill: BeginUpdate" Button
'
'"Using BeginUpdate()" Approach
'--------------------------------------------
Private Sub UseBeginEndUpdateForFill_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles UseBeginEndUpdateForFill.Click
'To make sure we're testing the same thing, make sure
'the array is clear
If (TreeView1.Nodes.Count > 0) Then
TreeView1.BeginUpdate()
TreeView1.Nodes.Clear()
TreeView1.EndUpdate()
TreeView1.Update()
End If
'For more consistent measurement, collect the garbage
' before running. DO NOT DO THIS IN PRODUCTION CODE!
System.GC.Collect()
'Start the test timer
PerformanceSampling.StartSample(2, _
"Populate - Use BeginUpdate")
'Fill the TreeView
TreeView1.BeginUpdate()
Dim i As Integer
For i = 1 To NUMBER_ITEMS
TreeView1.Nodes.Add("TreeItem" + i.ToString())
Next
TreeView1.EndUpdate()
'Stop the test timer and show the results
PerformanceSampling.StopSample(2)
MsgBox(PerformanceSampling.GetSampleDurationText(2))
End Sub
'--------------------------------------------
'Code for: "Clear: BeginUpdate" Button
'
'"Using BeginUpdate()" Approach
'--------------------------------------------
Private Sub UseBeginEndUpdateForClear_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles UseBeginEndUpdateForClear.Click
'For more consistent measurement, collect the garbage
' before running. DO NOT DO THIS IN PRODUCTION CODE!
System.GC.Collect()
'Start the test timer
PerformanceSampling.StartSample(3, "Clear - Use BeginUpdate")
TreeView1.BeginUpdate()
TreeView1.Nodes.Clear()
TreeView1.EndUpdate()
'Stop the test timer and show the results
PerformanceSampling.StopSample(3)
MsgBox(PerformanceSampling.GetSampleDurationText(3))
End Sub
'--------------------------------------------
'Code for: "Fill: Use Array" Button
'
'"Using Array" Approach
'--------------------------------------------
Private Sub FillArrayBeforeAttachingToTree_Click(ByVal _
sender As System.Object, ByVal e As System.EventArgs) _
Handles FillArrayBeforeAttachingToTree.Click
'To make sure we're testing the same thing, make sure
' the array is clear
If (TreeView1.Nodes.Count > 0) Then
TreeView1.BeginUpdate()
TreeView1.Nodes.Clear()
TreeView1.EndUpdate()
TreeView1.Update()
End If
'For more consistent measurement, collect the garbage before
'running. DO NOT DO THIS IN PRODUCTION CODE!
System.GC.Collect()
'Start the test timer
PerformanceSampling.StartSample(4, "Populate - Use Array")
'Allocate space for our array of tree nodes
Dim newTreeNodes() As System.Windows.Forms.TreeNode
ReDim newTreeNodes(NUMBER_ITEMS - 1)
'Fill up the array
Dim i As Integer
For i = 0 To NUMBER_ITEMS - 1
newTreeNodes(i) = _
New System.Windows.Forms.TreeNode("TreeItem" + _
i.ToString())
Next
'Connect the array to the TreeView
TreeView1.BeginUpdate()
TreeView1.Nodes.AddRange(newTreeNodes)
TreeView1.EndUpdate()
'Stop the test timer and show the results
PerformanceSampling.StopSample(4)
MsgBox(PerformanceSampling.GetSampleDurationText(4))
End Sub

Listing 11.2. Dynamic Population of a TreeView Control



'Dummy text to put in the placeholder child nodes
Const dummy_node As String = "_dummynode"
'Tag we will use to indicate a node
Const node_needToBePopulated As String = "_populateMe"
'Text we will use for our top-level nodes
Const nodeText_Neighborhoods As String = "Neighborhoods"
Const nodeText_Prices As String = "Prices"
Const nodeText_HouseType As String = "HouseTypes"
'-------------------------------------------------------------
'Click event handler for our button
'
'Sets up our TreeView to show incremental filling of the
'tree
'-------------------------------------------------------------
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim tnNewNode As TreeNode
'Turn off UI updates before we fill in the tree
TreeView1.BeginUpdate()
'Throw out any old data
TreeView1.Nodes.Clear()
'-----------------------------
''Neighborhoods' node
'-----------------------------
'Add the top-level 'Neighborhoods' node.
tnNewNode = TreeView1.Nodes.Add("Neighborhoods")
'Set a tag on the node that indicates that we will
'dynamically fill in the node
tnNewNode.Tag = node_needToBePopulated
'This dummy child node only exists so that the node has
'at least one child node and therefore the tree node is
'expandable.
tnNewNode.Nodes.Add(dummy_node)
'-----------------------------
''Price' node
'-----------------------------
tnNewNode = TreeView1.Nodes.Add("Price")
'Set a tag on the node that indicates that we will
'dynamically fill in the node
tnNewNode.Tag = node_needToBePopulated
'This dummy child node only exists so that the node has
'at least one child node and therefore the tree node is
'expandable.
tnNewNode.Nodes.Add(dummy_node)
'-----------------------------
''HouseType' node
'-----------------------------
tnNewNode = TreeView1.Nodes.Add("HouseType")
'Set a tag on the node that indicates that we will
'dynamically fill in the node
tnNewNode.Tag = node_needToBePopulated
'This dummy child node only exists so that the node has
'at least one child node and therefore the tree node is
'expandable.
tnNewNode.Nodes.Add(dummy_node)
'Resume the UI updates
TreeView1.EndUpdate()
End Sub
''------------------------------------------------------
''BeforeExpand event handler for our TreeView
''NOTE: Unlike with C#, This event handler
'' DOES NOT require you to tinker with the code in
'' "InitializeComponent()" (don't do this!)
'' You can just choose the event the regular way
'' via the VB editors event drop-down list
''
''Called when a user asks to expand a node that has at least
''one child node. This is called before the node's children
''are shown and gives us a chance to dynamically populate the
''TreeView control.
''------------------------------------------------------
Private Sub TreeView1_BeforeExpand(ByVal sender As Object, _
ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) _
Handles TreeView1.BeforeExpand
'Get the node that is about to be expanded
Dim tnExpanding As System.Windows.Forms.TreeNode
tnExpanding = e.Node
'If the node is not marked 'need to be populated' the
'node is fine 'as is.'
If Not (tnExpanding.Tag Is node_needToBePopulated) Then
Return 'Allow things to contine without hinderance
End If
'--------------------------------------------------------
'Dynamic tree population required.
'We know the node needs to be populated, figure out which
'node it is
'--------------------------------------------------------
If (tnExpanding.Text = nodeText_Neighborhoods) Then
PopulateTreeViewNeighborhoods(tnExpanding)
Return 'done adding items!
Else
'Check other possibilities for tree nodes we need to add.
MsgBox("UN-DONE: Add code to dynamically populate this node")
'Remove the tag from the node so we don't run this
'code again
tnExpanding.Tag = "
End If
End Sub
'------------------------------------------------------
'This function is called to dynamically add child nodes
'To the "Neighborhood" node
'------------------------------------------------------
Sub PopulateTreeViewNeighborhoods(ByVal tnAddTo As TreeNode)
Dim tvControl As TreeView
tvControl = tnAddTo.TreeView
tvControl.BeginUpdate()
'Clear the dummy sub-node we have in there
tnAddTo.Nodes.Clear()
'Declare four nodes we want to make children
'of the node that was passed in.
Dim newNeighborhoodNodes() As TreeNode
ReDim newNeighborhoodNodes(3)
newNeighborhoodNodes(0) = New TreeNode("Capitol Hill")
newNeighborhoodNodes(1) = New TreeNode("Chelsea")
newNeighborhoodNodes(2) = New TreeNode("Downtown")
newNeighborhoodNodes(3) = New TreeNode("South Bay")
'Add the child nodes to the tree view
tnAddTo.Nodes.AddRange(newNeighborhoodNodes)
tvControl.EndUpdate()
End Sub

Listing 11.3. Programmatic TextBox Update Causes Event Code to Be Run



Private m_eventTriggerCount As Integer
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'This triggers a TextChanged event
'same as if the user typed in text
TextBox1.Text = "Hello World"
End Sub
Private Sub TextBox1_TextChanged(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles TextBox1.TextChanged
m_eventTriggerCount = m_eventTriggerCount + 1
'Update a label to show the number of events
Label1.Text = "Events: #" + CStr(m_eventTriggerCount)
'List each of the events
ListBox1.Items.Add(m_eventTriggerCount.ToString() + _
TextBox1.Text)
End Sub

Listing 11.4. Using a State Model for Updates and Instrumentation to Better Understand and Control Event Processing



'-------------------------------------------------------------
'To enable event instrumentation:
' #Const EVENTINSTRUMENTATION = 1
'
'To disable event instrumentation:
' #Const EVENTINSTRUMENTATION = 0
'-------------------------------------------------------------
#Const EVENTINSTRUMENTATION = 1
'-------------------------------------------------------------
'A flag that tells control event handlers if they should
'exit without doing any work
'-------------------------------------------------------------
Private m_userInterfaceUpdateOccuring As Boolean
'Counters for event occurrences
Private m_radioButton1ChangeEventCount As Integer
Private m_textBox1ChangeEventCount As Integer
'-------------------------------------------------------------
'Code we only want to include if we are running in an
'instrumented mode. This code has relatively high execution
'overhead and we only want to compile it in and run it if
'we are doing diagnostics.
'-------------------------------------------------------------
#If EVENTINSTRUMENTATION <> 0 Then
Private m_instrumentedEventLog As System.Collections.ArrayList
'-------------------------------------------------------------
'Logs the occurrence of an event into an array we can inspect
'
'Note: No attempt is made to keep the size of the
' logging array bounded, so the longer the application
' runs the larger this array will become
'-------------------------------------------------------------
Private Sub instrumented_logEventOccurrence(ByVal eventData _
As String)
'Create the event log if it has not already been created
If (m_instrumentedEventLog Is Nothing) Then
m_instrumentedEventLog = _
New System.Collections.ArrayList
End If
'Log the event
m_instrumentedEventLog.Add(eventData)
End Sub
'-------------------------------------------------------------
'Show the list of events that have occurred
'Note: This implementation is pretty crude.
' You may want instead to show the events
' list in a separate dialog that pops up for the
' purpose.
'-------------------------------------------------------------
Private Sub instrumentation_ShowEventLog()
Dim listItems As _
System.Windows.Forms.ListBox.ObjectCollection
listItems = listBoxEventLog.Items
'Clear the items in the list
listItems.Clear()
'If there are no events, exit
If (m_instrumentedEventLog Is Nothing) Then
listItems.Add("0 Events")
Return
End If
'At the top of the list show the total of events we
'have counted
listItems.Add(m_instrumentedEventLog.Count.ToString() + _
" Events")
'List the items in reverse order, so the most recent are
'displayed first
Dim logItem As String
Dim listIdx As Integer
For listIdx = _
m_instrumentedEventLog.Count - 1 To 0 Step -1
logItem = CStr(m_instrumentedEventLog(listIdx))
listItems.Add(logItem)
Next
End Sub
#End If
'-------------------------------------------------------------
'RadioButton1 Changed event
'-------------------------------------------------------------
Private Sub RadioButton1_CheckedChanged(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles RadioButton1.CheckedChanged
'If our application is updating the data in the
'user interface we do not want to treat this as
'a user triggered event. If this is the case
'exit and do nothing.
If (m_userInterfaceUpdateOccuring = True) Then
Return
End If
'Count the number of times this event has been called
m_radioButton1ChangeEventCount = _
m_radioButton1ChangeEventCount + 1
#If (EVENTINSTRUMENTATION <> 0) Then
'Log the occurrence of the event
instrumented_logEventOccurrence("radioButton1.Change:" + _
m_radioButton1ChangeEventCount.ToString() + ":" + _
RadioButton1.Checked.ToString()) 'value
#End If
End Sub
'-------------------------------------------------------------
'Button1 click event
'Simulates a case where code updates the user interface
'potentially causes event code to be run
'-------------------------------------------------------------
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'Indicate that we do not want the event handlers
'to process events right now because we are updating
'the user interface.
'
'm_userInterfaceUpdateOccuring = true
RadioButton1.Checked = True
TextBox1.Text = "Hello World"
'We are done updating the user interface
m_userInterfaceUpdateOccuring = False
End Sub
'-------------------------------------------------------------
'TextBox changed event handler
'-------------------------------------------------------------
Private Sub TextBox1_TextChanged(ByVal sender As System.Object _
, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
'If our application is updating the data in the
'user interface we do not want to treat this as
'a user triggered event. If this is the case
'exit and do nothing.
If (m_userInterfaceUpdateOccuring = True) Then
Return
End If
'Count the number of times we execute this event
m_textBox1ChangeEventCount = m_textBox1ChangeEventCount + 1
#If EVENTINSTRUMENTATION <> 0 Then
'Log the occurrence of the event
instrumented_logEventOccurrence("textBox1.Change:" + _
m_textBox1ChangeEventCount.ToString() + ":" + _
TextBox1.Text.ToString()) 'Value
#End If
End Sub
Private Sub buttonShowEventLog_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles buttonShowEventLog.Click
#If EVENTINSTRUMENTATION <> 0 Then
instrumentation_ShowEventLog()
#End If
End Sub

Listing 11.5. Calling a Controls Update() Method to Show Progress Text



'---------------------------------------------------
'This code belongs in a Form containing a single
'Button (button1) and a Label (label1)
'---------------------------------------------------
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'Show a wait cursor
System.Windows.Forms.Cursor.Current = _
System.Windows.Forms.Cursors.WaitCursor
Dim testString As String
Dim loop3 As Integer
For loop3 = 1 To 100 Step 10
Label1.Text = loop3.ToString() + "% Done..."
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Uncomment the line below to show progress updates!
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Label1.Update()
testString = "
Dim loop2 As Integer
For loop2 = 1 To 1000
testString = testString + "test"
Next
Next
Label1.Text = "Done!"
'Remove the wait cursor
System.Windows.Forms.Cursor.Current = _
System.Windows.Forms.Cursors.Default
End Sub

Listing 11.6. Drawing into an Off-Screen Bitmap and Sending It to a Picture Box



'-------------------------------------------------------------
'Draw into a bitmap. Send the bitmap to a PictureBox
'-------------------------------------------------------------
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'Create a new bitmap
Dim myBitmap As System.Drawing.Bitmap
myBitmap = New System.Drawing.Bitmap(PictureBox1.Width, _
PictureBox1.Height)
'-----------------------------------------------------
'Create a graphics object so we can draw in the bitmap
'-----------------------------------------------------
Dim myGfx As System.Drawing.Graphics
myGfx = System.Drawing.Graphics.FromImage(myBitmap)
'Paint our bitmap all yellow
myGfx.Clear(System.Drawing.Color.Yellow)
'Create a pen
Dim myPen As System.Drawing.Pen
myPen = New System.Drawing.Pen(System.Drawing.Color.Blue)
'-----------------------------------------------------
'Draw an ellipse
'-----------------------------------------------------
myGfx.DrawEllipse(myPen, 0, 0, myBitmap.Width - 1, _
myBitmap.Height - 1)
'Create a solid brush
Dim myBrush As System.Drawing.Brush
'-----------------------------------------------------
'Draw the text with the brush
'-----------------------------------------------------
myBrush = New System.Drawing.SolidBrush( _
System.Drawing.Color.Black)
'Note: We are useing the Font object from the Form
myGfx.DrawString("Hello!", Me.Font, myBrush, 2, 10)
'-----------------------------------------------------
'Important! Clean up after ourselves
'-----------------------------------------------------
myGfx.Dispose()
myPen.Dispose()
myBrush.Dispose()
'-----------------------------------------------------
'Tell the picture box that it should display the
'bitmap we just created and drew on.
'-----------------------------------------------------
PictureBox1.Image = myBitmap
End Sub

Listing 11.7. Creating a Graphics Object for a Form



'-------------------------------------------------------------
'Creates a Graphics object for a Form and draws onto it
'-------------------------------------------------------------
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'Create a Graphics object for the Form
Dim myGfx As System.Drawing.Graphics
myGfx = Me.CreateGraphics()
'Create a Brush
Dim myBrush As System.Drawing.Brush
myBrush = New System.Drawing.SolidBrush( _
System.Drawing.Color.DarkGreen)
'Fill the rectangle
myGfx.FillRectangle(myBrush, 4, 2, 60, 20)
'--------------------------------------
'Important: Clean up!
'--------------------------------------
myBrush.Dispose()
myGfx.Dispose()
End Sub

Listing 11.8. Hooking into the Paint Function for a Form



'Brushes we want to cache, so we don't need to create/dispose them
'all the time
Private m_brushBlue As System.Drawing.Brush
Private m_brushYellow As System.Drawing.Brush
'Just for fun, lets count the number of times we are called
Private m_paintCount As Integer
'--------------------------------------------------------------
'We are overriding our base classes 'Paint' event. This means
'every time the Form gets called to paint itself, this
'function will get called.
'--------------------------------------------------------------
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
'Important: Call the base class and allow it to do its
'paint work
MyBase.OnPaint(e)
'Up the count of the number of times we have been called
m_paintCount = m_paintCount + 1
'------------------------------------------------------------
'Important:
'Instead of creating a graphics object, we are being lent one
'for the duration of this call. This means that it is not
'our job to .Dispose() of the object
'------------------------------------------------------------
Dim myGfx As System.Drawing.Graphics
myGfx = e.Graphics
'------------------------------------------------------------
'Because this painting needs to occur quickly, let's cache the
'brushes so we don't need to create/dispose them every time
'we are called
'------------------------------------------------------------
If (m_brushBlue Is Nothing) Then
m_brushBlue = New System.Drawing.SolidBrush( _
System.Drawing.Color.Blue)
End If
If (m_brushYellow Is Nothing) Then
m_brushYellow = New System.Drawing.SolidBrush( _
System.Drawing.Color.Yellow)
End If
'------------------------------------------------------------
'Do the drawing
'------------------------------------------------------------
myGfx.FillRectangle(m_brushBlue, 2, 2, 100, 100)
myGfx.DrawString("PaintCount: " + CStr(m_paintCount), _
Me.Font, m_brushYellow, 3, 3)
'Exit: Nothing we want to call .Dispose() on.
End Sub

Listing 11.9. A Simple Custom Control That Changes Colors and Fires a Custom Event



'A very simple custom control
Public Class myButton
Inherits System.Windows.Forms.Control
'-------------------------------------------------------
'Objects we need for drawing
'-------------------------------------------------------
Private m_RectangleBrush As System.Drawing.Brush
Private m_TextBrush As System.Drawing.Brush
Private m_RectangleColor As System.Drawing.Color
'-------------------------------------------------------
'The event we want to expose. This is a public delegate.
'-------------------------------------------------------
Public Event EventButtonTurningBlue(ByVal sender As Object, _
ByVal e As System.EventArgs)
'The constructor
Public Sub New()
MyBase.New()
'Note: We should write a "Dispose()" function and
'destructor that clean up these objects
'Create the brushes we will need
m_RectangleColor = System.Drawing.Color.Black
m_RectangleBrush = New System.Drawing.SolidBrush( _
m_RectangleColor)
m_TextBrush = New System.Drawing.SolidBrush( _
System.Drawing.Color.White)
End Sub
'-------------------------------------------------------
'Internal response to being clicked is to cycle
'through three different button colors
'-------------------------------------------------------
Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
'------------------------------------------------
'Important: Call the base implementation. This
'will allow any event handlers hooked up to this
'control to be called
'------------------------------------------------
MyBase.OnClick(e)
'--------------------------------------------------------
'Select our new brush color based on the last brush color
'--------------------------------------------------------
If (m_RectangleColor.Equals(System.Drawing.Color.Black)) Then
m_RectangleColor = System.Drawing.Color.Blue
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Trigger an event!
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Call the event, pass no event arguments
RaiseEvent EventButtonTurningBlue(Me, Nothing)
ElseIf (m_RectangleColor.Equals(System.Drawing.Color.Blue)) _
Then
m_RectangleColor = System.Drawing.Color.Red
Else
m_RectangleColor = System.Drawing.Color.Black
End If
'------------------------------------------------
'Release the old brush
'------------------------------------------------
m_RectangleBrush.Dispose()
'---------------------------------------------------------
'Create the new brush we want to draw the background with
'---------------------------------------------------------
m_RectangleBrush = _
New System.Drawing.SolidBrush(m_RectangleColor)
'---------------------------------------------------------
'Tell the operating system that our control needs to be
'redrawn as soon as reasonable
'---------------------------------------------------------
Me.Invalidate()
End Sub
'---------------------------------------------------------
'Just for fun let's count how many times we get painted
'---------------------------------------------------------
Private m_paintCount As Integer
Protected Overrides Sub OnPaint( _
ByVal e As System.Windows.Forms.PaintEventArgs)
'---------------------------------------------------------
'Important: Call the base class and allow it to do its
'paint work
'---------------------------------------------------------
MyBase.OnPaint(e)
'Up the count of the number of times we have been called
m_paintCount = m_paintCount + 1
'------------------------------------------------------------
'Important:
'Instead of creating a graphics object, we are being lent one
'for the duration of this call. This means that it is not
'our job to .Dispose() of the object
'------------------------------------------------------------
Dim myGfx As System.Drawing.Graphics
myGfx = e.Graphics
'Draw the rectangle
myGfx.FillRectangle(m_RectangleBrush, 0, 0, _
Me.Width, Me.Height)
'Draw the text
myGfx.DrawString("Button! Paint: " + m_paintCount.ToString(), _
Me.Parent.Font, m_TextBrush, 0, 0)
End Sub
End Class

Listing 11.10. Code to Place inside a Form to Create an Instance of the Custom Control



'Our new button
Private m_newControl As myButton
'---------------------------------------------------
'This code will get hooked up as our event handler
'---------------------------------------------------
Private Sub CallWhenButtonTurningBlue(ByVal sender As Object, _
ByVal e As System.EventArgs)
MsgBox("Button is about to turn blue!")
End Sub
'---------------------------------------------------
'This function is to be hooked up to the click event
'of Button1
'---------------------------------------------------
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'--------------------------------------------------
'To keep things simple, allow only one instance of
'the control.
'--------------------------------------------------
If Not (m_newControl Is Nothing) Then Return
'Create an instance of our button
m_newControl = New myButton
'Tell it where it should be located inside its parent
m_newControl.Bounds = New Rectangle(10, 10, 150, 40)
'--------------------------------------
'Connect up an event handler
'--------------------------------------
AddHandler m_newControl.EventButtonTurningBlue, _
AddressOf CallWhenButtonTurningBlue
'Add it to the list of controls in this Form.
'This will make it visible
Me.Controls.Add(m_newControl)
End Sub

Listing 11.11. Three Useful Ways to Cache Graphical Resources



Imports System
Imports System.Drawing
Friend Class GraphicsGlobals
'=========================================================
'Approach 1: Create the resource on demand
' and keep cached afterward.
'
'External code gets access view the public properties, but
'the variable itself is internal to the class
'=========================================================
Private Shared s_bluePen As Pen
Public Shared ReadOnly Property globalBluePen() As Pen
Get
'If we have not created a
If (s_bluePen Is Nothing) Then
s_bluePen = New System.Drawing.Pen( _
System.Drawing.Color.Blue)
End If
Return s_bluePen
End Get
End Property
'========================================================
'Approach 2:
'Globally load and cache a bunch of commonly
'used Pens, ImageAttributes, Fonts, and brushes
'
'External code gets access to the public members,
'no accessors functions needed.
'========================================================
Public Shared g_blackPen As Pen
Public Shared g_whitePen As Pen
Public Shared g_ImageAttribute As Imaging.ImageAttributes
Private Shared s_alreadyInitialized As Boolean
Public Shared g_boldFont As Font
Public Shared g_smallTextFont As Font
Public Shared g_greenBrush As Brush
Public Shared g_yellowBrush As Brush
Public Shared g_redBrush As Brush
Public Shared g_blackBrush As Brush
'===========================================================
'Needs to be called before anyone accesses the globals above
'===========================================================
Public Shared Sub InitializeGlobals()
If (s_alreadyInitialized = True) Then Return
g_blackPen = New System.Drawing.Pen(Color.Black)
g_whitePen = New System.Drawing.Pen(Color.White)
g_ImageAttribute = New _
System.Drawing.Imaging.ImageAttributes
g_ImageAttribute.SetColorKey(Color.White, Color.White)
g_boldFont = New Font(FontFamily.GenericSerif, _
10, FontStyle.Bold)
g_smallTextFont = New Font(FontFamily.GenericSansSerif, _
8, FontStyle.Regular)
g_blackBrush = New SolidBrush(System.Drawing.Color.Black)
g_greenBrush = New SolidBrush( _
System.Drawing.Color.LightGreen)
g_yellowBrush = New SolidBrush(System.Drawing.Color.Yellow)
g_redBrush = New SolidBrush(System.Drawing.Color.Red)
s_alreadyInitialized = True
End Sub
'========================================================
'Approach 3: Return an array of related resources.
' Cache the resources locally so that multiple
' requests do not load duplicate (wasteful)
' versions
'
'========================================================
Private Shared m_CaveMan_Bitmap1 As Bitmap
Private Shared m_CaveMan_Bitmap2 As Bitmap
Private Shared m_CaveMan_Bitmap3 As Bitmap
Private Shared m_CaveMan_Bitmap4 As Bitmap
Private Shared m_colCaveManBitmaps As _
System.Collections.ArrayList
'----------------------------------------------------------
'Create and load an array of images for a sprite
'----------------------------------------------------------
Public Shared Function g_CaveManPictureCollection() As _
System.Collections.ArrayList
'Only load the bitmaps if we do not have them loaded yet
If (m_CaveMan_Bitmap1 Is Nothing) Then
'-------------------------------------------------------
'Load the bitmaps. These bitmaps are stored as embedded
'resources in our binary application
'
'Loading the images from external files would be similar
'but slightly simpler (we could just specify the file
'name in the bitmaps constructor).
'-------------------------------------------------------
'Get a reference to our binary assembly
dim thisAssembly as System.Reflection.Assembly = _
System.Reflection.Assembly.GetExecutingAssembly()
'Get the name of the assembly
Dim thisAssemblyName As System.Reflection.AssemblyName = _
thisAssembly.GetName()
Dim assemblyName As String = thisAssemblyName.Name
'Load the bitmaps as binary streams from our assembly
m_CaveMan_Bitmap1 = New System.Drawing.Bitmap( _
thisAssembly.GetManifestResourceStream( _
assemblyName + ".Hank_RightRun1.bmp"))
m_CaveMan_Bitmap2 = New System.Drawing.Bitmap( _
thisAssembly.GetManifestResourceStream( _
assemblyName + ".Hank_RightRun2.bmp"))
m_CaveMan_Bitmap3 = New System.Drawing.Bitmap( _
thisAssembly.GetManifestResourceStream( _
assemblyName + ".Hank_LeftRun1.bmp"))
m_CaveMan_Bitmap4 = New System.Drawing.Bitmap( _
thisAssembly.GetManifestResourceStream( _
assemblyName + ".Hank_LeftRun2.bmp"))
'Add them to the collection
m_colCaveManBitmaps = New System.Collections.ArrayList
m_colCaveManBitmaps.Add(m_CaveMan_Bitmap1)
m_colCaveManBitmaps.Add(m_CaveMan_Bitmap2)
m_colCaveManBitmaps.Add(m_CaveMan_Bitmap3)
m_colCaveManBitmaps.Add(m_CaveMan_Bitmap4)
End If
'Return the collection
Return m_colCaveManBitmaps
End Function
End Class


/ 159