Текст книги "Программирование мобильных устройств на платформе .NET Compact Framework"
Автор книги: Иво Салмре
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 65 (всего у книги 69 страниц)
Case System.Xml.XmlNodeType.Element
Dim nodeName As String = xmlReader.Name
LoadHelper_NewElementEncountered(nodeName, _
currentReadLocation)
'–
'Здесь мы можем извлечь некоторый фактический текст
'и получить данные, которые пытаемся загрузить
'–
Case System.Xml.XmlNodeType.Text
Select Case currentReadLocation
Case ReadLocation.inFirstName
firstName = xmlReader.Value
Case ReadLocation.inLastName
lastName = xmlReader.Value
Case ReadLocation.inUserID
userId = CInt(xmlReader.Value)
End Select
'Конец оператора Case "System.Xml.XmlNodeType.Text"
'–
'Вызывается, когда встречается конец
'элемента
'
'Мы можем захотеть переключить состояние в зависимости
'от вида покидаемого узла, чтобы указать на то, что
'собираемся вернуться назад к его предку
'–
Case System.Xml.XmlNodeType.EndElement
Dim continueParsing As Boolean
continueParsing = LoadHelper_EndElementEncountered( _
currentReadLocation)
If (continueParsing = False) Then
GoTo finished_reading_wanted_data
End If
Case Else
'He страшно, если имеются XML-узлы других типов, но
'в нашем примере работы с XML-документом мы должны
'оповестить об этом факте
MsgBox( _
"Встретился непредвиденный XML-тип " + xmlReader.Name)
End Select 'Конец оператора Case, используемого для определения текущего
'типа XML-элeмeнтa, oбpaбaтывaeмoгo анализатором
'Перейти к следующему узлу
readSuccess = xmlReader.Read()
End While
'Если мы оказались в этом месте программы, не покинув
'XML-дескриптора UserInfo, то с XML-данными, которые мы считываем,
'что-то не так
Throw New Exception("He найден элемент UserInfo в XML-документе!")
finished reading_wanted_data:
'Закрыть файл, поскольку работа с ним закончена!
xmlReader.Close()
End Sub
'–
'Вспомогательный код, ответственный за принятие решения
'относительно того, в какое состояние необходимо перейти,
'когда встречается закрывающий дескриптор
'–
Private Shared Function LoadHelper_EndElementEncountered( _
ByRef currentReadLocation As ReadLocation) As Boolean
Select Case (currentReadLocation)
'Если мы покидаем узел Name, то должны вернуться
'обратно в узел UserInfo
Case ReadLocation.inName
currentReadLocation = ReadLocation.inUserInfo
'Если мы покидаем узел FirstName, то должны вернуться
'обратно в узел Name
Case ReadLocation.inFirstName
currentReadLocation = ReadLocation.inName
'Если мы покидаем узел LastName, то должны вернуться
'обратно в узел Name
Case ReadLocation.inLastName
currentReadLocation = ReadLocation.inName
'Если мы покидаем узел UserID, то должны вернуться
'обратно в узел UserInfo
Case ReadLocation.inUserID
currentReadLocation = ReadLocation.inUserInfo
'Если мы покидаем узел UserInfo, то мы только что
'закончили чтение данных в узлах UserID, FirstName
'и LastName
'
'Можно выйти из цикла, поскольку у нас уже есть вся
'информация, которую мы хотели получить!
Case ReadLocation.inUserInfo
Return False 'Анализ должен быть прекращен
End Select
Return True
'Продолжить анализ
End Function
Private Shared Sub LoadHelper_NewElementEncountered( _
ByVal nodeName As String, _
ByRef currentReadLocation As ReadLocation)
'–
'Мы вошли в новый элемент!
'В какое состояние переход возможен, зависит от того,
'в каком состоянии мы находимся в данный момент
'–
Select Case (currentReadLocation)
'Если мы находимся в узле AllMyData, то переход возможен
'в узлы, которые указаны ниже
Case (ReadLocation.inAllMyData)
If (nodeName = XML_USERINFO_TAG) Then
currentReadLocation = ReadLocation.inUserInfo
End If
'Если мы находимся в узле UserInfo, то переход возможен
'в узлы, которые указаны ниже
Case (ReadLocation.inUserInfo)
If (nodeName = XML_USERID_TAG) Then
currentReadLocation = ReadLocation.inUserID
ElseIf (nodeName = XML_NAMEINFO_TAG) Then
currentReadLocation = ReadLocation.inName
End If
'Если мы находимся в узле Name, то переход возможен
'в узлы, которые указаны ниже
Case (ReadLocation.inName)
If (nodeName = XML_FIRSTNAME_TAG) Then
currentReadLocation = ReadLocation.inFirstName
ElseIf (nodeName = XML LASTNAME_TAG) Then
currentReadLocation = ReadLocation.inLastName
End If
End Select
End Sub
End Class
Примеры к главе 11 (производительность и графика)
Листинг 11.1. Заполнение данными и очистка от них элементов управления TreeView с использованием альтернативных стратегий
'–
'Примечание #1: В этом примере используется класс PerformanceSampling,
' определённый ранее в данной книге. Убедитесь в том, что
' вы включили этот класс в свой проект.
'Примечание #2: Этот код необходимо включить в класс Form, содержащий элемент
' управления TreeView и кнопки Button, к которым подключены
' приведенные ниже функции xxx_Click.
'–
'Количество элементов, которые необходимо поместить в элемент
'управления TreeView
Const NUMBER_ITEMS As Integer = 800
'–
'Код для кнопки "Fill: Baseline"
'Использование неоптимизированного подхода для заполнения данными элемента
'управления TreeView
'–
Private Sub UnOptimizedFill_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles UnOptimizedFill.Click
'Очистить массив для создания одинаковых условий тестирования
If (TreeView1.Nodes.Count > 0) Then
TreeView1.BeginUpdate()
TreeView1.Nodes.Clear()
TreeView1.EndUpdate()
TreeView1.Update()
End If
'Для повышения корректности тестирования предварительно выполнить
'операцию сборки мусора. В реальных кодах этого делать не следует!
System.GC.Collect()
'Запустить таймер
PerformanceSampling.StartSample(0, "TreeViewPopulate")
'Заполнить данными элемент управления TreeView
Dim i As Integer
For i = 1 To NUMBER_ITEMS
TreeView1.Nodes.Add("TreeItem" + CStr(i))
Next
'Остановить таймер и отобразить результат
PerformanceSampling.StopSample(0)
MsgBox(PerformanceSampling.GetSampleDurationText(0))
End Sub
'–
'Код для кнопки "Clear: Baseline"
'Использование неоптимизированного подхода для заполнения данными элемента
'управления TreeView
'–
Private Sub UnOptimizedClear_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles UnOptimizedClear.Click
'Для повышения корректности тестирования предварительно выполнить
'операцию сборки мусора
System.GC.Collect()
'Запустить таймер
PerformanceSampling.StartSample(1, "TreeViewClear")
TreeView1.Nodes.Clear()
PerformanceSampling.StopSample(1)
MsgBox(PerformanceSampling.GetSampleDurationText(1))
End Sub
'–
'Код для кнопки "Fill: BeginUpdate"
'Подход, в котором используется метод BeginUpdate()
'–
Private Sub UseBeginEndUpdateForFill_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles UseBeginEndUpdateForFill.Click
'Очистить массив для создания одинаковых условий тестирования
If (TreeView1.Nodes.Count > 0) Then
TreeView1.BeginUpdate()
TreeView1.Nodes.Clear()
TreeView1.EndUpdate()
TreeView1.Update()
End If
'Для повышения корректности тестирования предварительно выполнить
'операцию сборки мусора. В РЕАЛЬНЫХ КОДАХ ЭТОГО ДЕЛАТЬ НЕ СЛЕДУЕТ!
System.GC.Collect()
'Запустить таймер
PerformanceSampling.StartSample(2, _
"Populate – Use BeginUpdate")
'Заполнить данными элемент управления TreeView
TreeView1.BeginUpdate()
Dim i As Integer
For i = 1 To NUMBER_ITEMS
TreeView1.Nodes.Add("TreeItem" + i.ToString())
Next
TreeView1.EndUpdate()
'Остановить таймер и отобразить результат
PerformanceSampling.StopSample(2)
MsgBox(PerformanceSampling.GetSampleDurationText(2))
End Sub
'–
'Код для кнопки "Clear: BeginUpdate"
'Подход, в котором используется метод BeginUpdate()
'–
Private Sub UseBeginEndUpdateForClear_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles UseBeginEndUpdateForClear.Click
'Для повышения корректности тестирования предварительно выполнить
'операцию сборки мусора. В РЕАЛЬНЫХ КОДАХ ЭТОГО ДЕЛАТЬ НЕ СЛЕДУЕТ!
System.GC.Collect()
'Запустить таймер
PerformanceSampling.StartSample(3, "Clear – Use BeginUpdate")
TreeView1.BeginUpdate()
TreeView1.Nodes.Clear()
TreeView1.EndUpdate()
'Остановить таймер и отобразить результат
PerformanceSampling.StopSample(3)
MsgBox(PerformanceSampling.GetSampleDurationText(3))
End Sub
'–
'Код для кнопки "Fill: Use Array"
'Подход, в котором используется массив
'–
Private Sub FillArrayBeforeAttachingToTree_Click(ByVal _
sender As System.Object, ByVal e As System.EventArgs) _
Handles FillArrayBeforeAttachingToTree.Click
'Очистить массив для создания одинаковых условий тестирования
If (TreeView1.Nodes.Count > 0) Then
TreeView1.BeginUpdate()
TreeView1.Nodes.Clear()
TreeView1.EndUpdate()
TreeView1.Update()
End If
'Для повышения корректности тестирования предварительно выполнить
'операцию сборки мусора. В РЕАЛЬНЫХ КОДАХ ЭТОГО ДЕЛАТЬ НЕ СЛЕДУЕТ!
System.GC.Collect()
'Запустить таймер
PerformanceSampling.StartSample(4, "Populate – Use Array")
'Распределить память для нашего массива узлов дерева
Dim newTreeNodes() As System.Windows.Forms.TreeNode
ReDim newTreeNodes(NUMBER_ITEMS – 1)
'Заполнить массив
Dim i As Integer
For i = 0 To NUMBER_ITEMS – 1
newTreeNodes(i) = _
New System.Windows.Forms.TreeNode("TreeItem" + _
i.ToString())
Next
'Связать массив с элементом управления TreeView
TreeView1.BeginUpdate()
TreeView1.Nodes.AddRange(newTreeNodes)
TreeView1.EndUpdate()
'Остановить таймер и отобразить результат
PerformanceSampling.StopSample(4)
MsgBox(PerformanceSampling.GetSampleDurationText(4))
End Sub
Листинг 11.2. Динамическое заполнение данными элемента управления TreeView
'Фиктивный текст для размещения в заполнителях дочерних узлов
Const dummy_node As String = "_dummynode"
'Метка, которую мы будем использовать для обозначения узла
Const node_needToBePopulated As String = "_populateMe"
'Текст, который мы будем использовать для наших узлов высшего уровня
Const nodeText_Neighborhoods As String = "Neighborhoods"
Const nodeText_Prices As String = "Prices"
Const nodeText_HouseType As String = "HouseTypes"
'–
'Обработчик события щелчка для кнопки
'Настраивает наш элемент управления TreeView для отображения процесса
'последовательного заполнения дерева
'–
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim tnNewNode As TreeNode
'Отключить обновление ПИ до тех пор, пока дерево не будет заполнено
TreeView1.BeginUpdate()
'Избавиться от устаревших данных
TreeView1.Nodes.Clear()
'–
'Узел "Neighborhoods"
'–
'Добавить узел "Neighborhoods" верхнего уровня.
tnNewNode = TreeView1.Nodes.Add("Neighborhoods")
'Установить для узла метку, указывающую на то, что узел
'будет заполняться динамически
tnNewNode.Tag = node_needToBePopulated
'Этот фиктивный дочерний узел существует лишь для того, чтобы
'узел имел, по крайней мере, один дочерний узел и поэтому
'был расширяемым.
tnNewNode.Nodes.Add(dummy_node)
'–
'Узел "Price"
'–
tnNewNode = TreeView1.Nodes.Add("Price")
'Установить для узла метку, указывающую на то, что узел
'будет заполняться динамически
tnNewNode.Tag = node_needToBePopulated
'Этот фиктивный дочерний узел существует лишь для того, чтобы
'узел имел, по крайней мере, один дочерний узел и поэтому
'был расширяемым
tnNewNode.Nodes.Add(dummy_node)
'–
'Узел "HouseType"
'–
tnNewNode = TreeView1.Nodes.Add("HouseType")
'Установить для узла метку, указывающую на то, что узел
'будет заполняться динамически
tnNewNode.Tag = node_needToBePopulated
'Этот фиктивный дочерний узел существует лишь для того, чтобы
'узел имел, по крайней мере, один дочерний узел и поэтому
'был расширяемым.
tnNewNode.Nodes.Add(dummy node)
'Восстанавливаем обновление ПИ
TreeView1.EndUpdate()
End Sub
''–
''Обработчик событий BeforeExpand для нашего элемента управления TreeView
''ПРИМЕЧАНИЕ: В отличие от C#, данный обработчик
'' НЕ требует от вас связываться дорабатывать код
'' "InitializeComponent()" (не делайте этого!)
'' Вы можете просто выбрать событие обычным путем
'' выпадающего списка событий в редакторах VB
''
''Вызывается при запросе пользователем расширения узла, у которого имеется,
''по крайней мере, один дочерний узел. Этот вызов осуществляется до отображения
''дочерних узлов данного узла и дает нам возможность динамически заполнить
''данными элемент управления TreeView.
''–
Private Sub TreeView1_BeforeExpand(ByVal sender As Object, _
ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) _
Handles TreeView1.BeforeExpand
'Получить узел, который будет расширяться
Dim tnExpanding As System.Windows.Forms.TreeNode
tnExpanding = e.Node
'Если узел не отмечен как "нуждающийся в заполнении данными",
'то он устраивает нас в том виде, "как он есть".
If Not (tnExpanding.Tag Is node needToBePopulated) Then
Return 'Разрешить беспрепятственное продолжение выполнения
End If
'Требуется динамическое заполнение дерева данными.
'Мы знаем, что узел должен быть заполнен данными; определить,
'что это за узел
If (tnExpanding.Text = nodeText_Neighborhoods) Then
PopulateTreeViewNeighborhoods(tnExpanding)
Return 'done adding items!
Else
'Проверить другие возможности для узлов дерева, которые мы должны
'добавить.
MsgBox("HE СДЕЛАНО: Добавьте код для динамического заполнения этого узла")
'Снять отметку с этого узла, чтобы мы не могли вновь выполнить
'этот код
tnExpanding.Tag = ""
End If
End Sub
'–
'Эта функция вызывается для динамического добавления дочерних узлов
'в узел "Neighborhood"
'–
Sub PopulateTreeViewNeighborhoods(ByVal tnAddTo As TreeNode)
Dim tvControl As TreeView
tvControl = tnAddTo.TreeView
tvControl.BeginUpdate()
'Очистить имеющийся фиктивный узел
tnAddTo.Nodes.Clear()
'Объявить четыре узла, которые мы хотим сделать дочерними узлами
'того узла, который был передан.
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")
'Добавить дочерние узлы в элемент управления TreeView
tnAddTo.Nodes.AddRange(newNeighborhoodNodes)
tvControl.EndUpdate()
End Sub
Листинг 11.3. Запуск обработчика событий при изменении содержимого элемента TextBox программным путем
Private m_eventTriggerCount As Integer
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Buttonl.Click
'Запускает событие TextChanged так же,
'как если бы текст был введен пользователем
TextBox1.Text = "Привет, мир"
End Sub
Private Sub TextBox1_TextChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles TextBox1.TextChanged
m_eventTriggerCount = m_eventTriggerCount + 1
'Обновить надпись для отображения количества событий
Label1.Text = "Событий: #" + CStr(m_eventTriggerCount)
'Внести каждое событие в список
ListBox1.Items.Add(m_eventTriggerCount.ToString() + TextBox1.Text)
End Sub
Листинг 11.4. Использование модели состояний для обновления интерфейса и контроль запуска событий с целью более глубокого изучения процесса обработки событий и управления им
'–
'Для активизации контроля запуска событий:
' #Const EVENTINSTRUMENTATION = 1
'Для отмены контроля запуска событий:
' #Const EVENTINSTRUMENTATION = 0
'–
#Const EVENTINSTRUMENTATION = 1
'–
'Флаг, указывающий обработчикам событий, должен ли из них осуществляться
'выход без выполнения каких-либо действий
'–
Private m_userInterfaceUpdateOccuring As Boolean
'Счетчики событий
Private m_radioButton1ChangeEventCount As Integer
Private m_textBox1ChangeEventCount As Integer
'–
'Код, который следует включать лишь в том случае, если приложение
'выполняется в режиме контроля запуска событий. Этот код характеризуется
'относительно высокими накладными расходами, и его следует компилировать и
'выполнять только тогда, когда выполняется диагностика.
'–
#If EVENTINSTRUMENTATION <> 0 Then
Private m_instrumentedEventLog As System.Collections.ArrayList
'–
'Заносит записи о возникновении событий в массив, который мы
'можем просмотреть
'Примечание: Не делается никаких попыток ограничить размерность массива
' регистрационных записей, поэтому, чем дольше выполняется приложение,
' тем больше становится размер массива
'–
Private Sub instrumented_logEventOccurrence(ByVal eventData _
As String)
'Создать журнал событий, если он еще не был создан
If (m_instrumentedEventLog Is Nothing) Then
m_instrumentedEventLog = _
New System.Collections.ArrayList
End If
'Зарегистрировать событие
m_instrumentedEventLog.Add(eventData)
End Sub
'–
'Отобразить список возникших событий
'Примечание: Этот вариант реализации довольно груб.
' Целесообразнее отображать список событий
' в отдельном диалоговом окне, которое специально выводится
' для этого на экран.
'–
Private Sub instrumentation_ShowEventLog() Dim listItems As _
System.Windows.Forms.ListBox.ObjectCollection
listItems = listBoxEventLog.Items
'Очистить список элементов
listItems.Clear()
'При отсутствии событий – выход
If (m instrumentedEventLog Is Nothing) Then
listItems.Add("0 событий")
Return
End If
'Отобразить поверх списка общее количество
'подсчитанных нами событий
listItems.Add(m_instrumentedEventLog.Count.ToString() + _
" событий")
'Перечислить элементы списка в обратном порядке, чтобы
'первыми отображались самые последние из них
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
'–
Private Sub RadioButton1_CheckedChanged(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles RadioButton1.CheckedChanged
'Если обновление данных в пользовательском интерфейсе осуществляется
'приложением, то мы не хотим обрабатывать его так же, как если бы
'это событие было запущено пользователем. Если это именно так,
'то осуществить выход из функции без выполнения каких-либо действий.
If (m userInterfaceUpdateOccuring = True) Then
Return
End If
'Подсчитать, сколько раз выполнена обработка данного события
m_radioButtonlChangeEventCount = _
m_radioButtonlChangeEventCount + 1
#If (EVENTINSTRUMENTATION <> 0) Then
'Зарегистрировать наступление события
instrumented_logEventOccurrence("radioButton1.Change:" + _
m_radioButton1ChangeEventCount.ToString() + ":" + _
RadioButton1.Checked.ToString()) 'value
#End If
End Sub
'–
'Событие щелчка на кнопке Button1
'Имитирует обновление пользовательского интерфейса программным
'кодом, что может приводить к запуску обработчика события
'–
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'Указать на то, что мы не хотим, чтобы обработчики сразу же
'обрабатывали события, поскольку мы обновляем
'пользовательский интерфейс.
'm_userInterfaceUpdateOccuring = true;
RadioButton1.Checked = True
TextBox1.Text = "Hello World"
'Обновление пользовательского интерфейса закончено
m_userInterfaceUpdateOccuring = False
End Sub
'–
'Обработчик события изменения состояния элемента управления TextBox
'–
Private Sub TextBox1_TextChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles TextBox1.TextChanged
'Если обновление данных в пользовательском интерфейсе осуществляется
'приложением, то мы не хотим обрабатывать его так же, как если бы
'это событие было запущено пользователем. Если это именно так,
'то осуществить выход из функции без выполнения каких-либо действий.
If (m_userInterfaceUpdateOccuring = True) Then
Return
End If
'Подсчитать, сколько раз выполнена обработка данного события
m_textBox1ChangeEventCount = m_textBox1ChangeEventCount + 1
#If EVENTINSTRUMENTATION <> 0 Then
'Занести событие в журнал
instrumented_logEventOccurrence("textBox1.Change:" + _
m_textBoxlChangeEventCount.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
Листинг 11.5. Вызов метода Update() элемента управления для отображения пояснительного текста, информирующего о ходе выполнения задачи
'–
'Этот код принадлежит форме, содержащей по одному элементу управления
'Button (button1) и Label (label1)
'–
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'Отобразить курсор ожидания
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..."
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Чтобы отобразить информацию о процессе обновления,!
'удалите символы комментария в строке ниже !
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Label1.Update()
testString = ""
Dim loop2 As Integer
For loop2 = 1 To 1000
testString = testString + "тест"
Next
Next
Label1.Text = "Готово!"
'Удалить курсор ожидания
System.Windows.Forms.Cursor.Current = _
System.Windows Forms.Cursors.Default
End Sub
Листинг 11.6. Создание изображения на внеэкранной растровой поверхности и передача его в элемент управления PictureBox
'–
'Создать рисунок на растровой поверхности. Переслать его в PictureBox
'–
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'Создать новую битовую карту
Dim myBitmap As System.Drawing.Bitmap
myBitmap = New System.Drawing.Bitmap(PictureBox1.Width, _
PictureBox1.Height)
'–
'Создать объект Graphics, чтобы иметь возможность рисовать на битовой карте
'–
Dim myGfx As System.Drawing.Graphics
myGfx = System.Drawing.Graphics.FromImage(myBitmap)
'Закрасить нашу битовую карту желтым цветом
myGfx.Clear(System.Drawing.Color.Yellow)
'Создать перо
Dim myPen As System.Drawing.Pen
rayPen = New System.Drawing.Pen(System.Drawing.Color.Blue)
'–
'Нарисовать эллипс
'–
myGfx.DrawEllipse(myPen, 0, 0, myBitmap.Width – 1, _
myBitmap.Height – 1)
'Создать сплошную кисть
Dim myBrush As System.Drawing.Brush
'–
'Нарисовать текст кистью
'–
myBrush = New System.Drawing.SolidBrush( _
System.Drawing.Color.Black)
'Примечание: мы используем объект Font из формы
myGfx.DrawString("Привет!", Me.Font, myBrush, 2, 10)
'–
'Важно! Очистить все после себя
'–
myGfx.Dispose()
myPen.Dispose()
myBrush.Dispose()
'–
'Указать объекту pictureBox, на необходимость отображения растрового
'изображения, которое мы только что создали и нарисовали.
'–
PictureBox1.Image = myBitmap
End Sub
Листинг 11.7. Создание объекта Graphics для формы
'–
'Создает объект Graphics для формы и осуществляет рисование
'–
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'Создать объект Graphics для формы
Dim myGfx As System.Drawing.Graphics
myGfx = Me.CreateGraphics()
'Создать кисть
Dim myBrush As System.Drawing.Brush
myBrush = New System.Drawing.SolidBrush( _
System.Drawing.Color.DarkGreen)
'Заполнить прямоугольник
myGfx.FillRectangle(myBrush, 4, 2, 60, 20)
'–
'Важно: Выполнить очистку!
'–
myBrush.Dispose()
myGfx.Dispose()
End Sub
Листинг 11.8. Подключение к функции Paint формы
'Кисти, которые мы хотим кэшировать, чтобы избавить себя от необходимости
'все время создавать их и уничтожать
Private m_brushBlue As System.Drawing.Brush
Private m_brushYellow As System.Drawing.Brush
'Ради интереса подсчитаем, сколько раз осуществлялся вызов
Private m_paintCount As Integer
'–
'Мы перекрываем обработчики событий Paint наших базовых классов. Это означает,
'что каждый раз, когда форма вызывается для перерисовки самой себя, будет
'вызываться эта функция.
'–
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
'ВАЖНО: Вызвать базовый класс и дать ему возможность
'выполнить всю необходимую работу по рисованию
MyBase.OnPaint(e)
'Увеличить на 1 значение счетчика вызовов
m_paintCount = m_paintCount + 1
'–
'Важно:
'Вместо того чтобы создавать объект Graphics, мы получаем его
'на время данного вызова. Это означает, что освобождать память путем вызова
'метода .Dispose() объекта – не наша забота
'–
Dim myGfx As System.Drawing.Graphics
myGfx = e.Graphics
'–
'Поскольку эту операцию рисования необходимо выполнить быстро,
'кэшируем кисти, чтобы избавить себя от необходимости создавать их и
'уничтожать при каждом вызове
'–
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
'–
'Выполнить рисование
'–
myGfx.FillRectangle(m_brushBlue, 2, 2, 100, 100)
myGfx.DrawString("PaintCount: " + CStr(m_paintCount), _
Me.Font, m_brushYellow, 3, 3)
'Выход: Объекты, для которых мы должны были бы вызывать метод
'.Dispose(), отсутствуют.
End Sub
Листинг 11.9. Простой пользовательский элемент управления, который изменяет цвета и запускает событие, определяемое пользователем
'Простейший пользовательский элемент управления
Public Class myButton
Inherits System.Windows.Forms.Control
'–
'Объекты, необходимые нам для рисования
'–
Private m_RectangleBrush As System.Drawing.Brush
Private m_TextBrush As System.Drawing.Brush
Private m_RectangleColor As System.Drawing.Color
'–
'Событие, которое мы хотим предоставить на обработку. Это – общедоступный
'делегат.
'–
Public Event EventButtonTurningBlue(ByVal sender As Object, _
ByVal e As System.EventArgs)
'Конструктор
Public Sub New()
MyBase.New()
'ПРИМЕЧАНИЕ: Мы должны написать функцию "Dispose()" и
'деструктор, который освобождает память от этих объектов
'Создать необходимые кисти
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
'–
'Внутренним откликом на щелчок является
'повторение трех различных цветов кнопки в цикле
'–
Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
'–
'Важно: Вызвать базовую реализацию. Это
'обеспечит возможность вызова любого обработчика событий,
'подключенного к данному элементу управления
'–
MyBase.OnClick (e)
'–
'Выбрать цвет новой кисти, исходя из цвета старой кисти
'–
If (m_RectangleColor.Equals(System.Drawing.Color.Black)) Then
m_RectangleColor = System.Drawing.Color.Blue
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Запустить событие!
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Возбудить событие без передачи аргумента
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
'–
'Освободить старую кисть
'–
m_RectangleBrush.Dispose()
'–
'Создать новую кисть, которую мы собираемся использовать для фона
'–
m_RectangleBrush = _
New System.Drawing.SolidBrush(m_RectangleColor)
'–
'Сообщить операционной системе, что наш элемент управления
'должен быть перерисован, как только представится возможность
'–
Me.Invalidate()
End Sub
'–
'Ради интереса подсчитаем, сколько раз осуществлялась перерисовка
'–
Private m_paintCount As Integer
Protected Overrides Sub OnPaint( _
ByVal e As System.Windows.Forms.PaintEventArgs)
'–
'ВАЖНО: Вызвать базовый класс и позволить ему
'выполнить работу по рисованию
'–
MyBase.OnPaint(e)
'Увеличить на единицу значение счетчика вызовов
m_paintCount = m_paintCount + 1
'–
'Важно:
'Вместо того чтобы создавать объект Graphics, мы получаем его
'на время данного вызова. Это означает, что освобождать память путем вызова
'метода .Dispose() объекта – не наша забота
'–
Dim myGfx As System.Drawing.Graphics
myGfx = e.Graphics
'Нарисовать прямоугольник
myGfx.FillRectangle(m_RectangleBrush, 0, 0, _
Me.Width, Me.Height)
'Нарисовать текст
myGfx.DrawString("Button! Paint: " + m_paintCount.ToString(), _
Me.Parent.Font, m_TextBrush, 0, 0)
End Sub
End Class
Листинг 11.10. Код, который должен быть помещен в форму для создания экземпляра пользовательского элемента управления
'Наша новая кнопка
Private m_newControl As myButton
'–
'Этот код будет подключен в качестве нашего обработчика событий
'–
Private Sub CallWhenButtonTurningBlue(ByVal sender As Object, _
ByVal e As System.EventArgs)
MsgBox("Кнопка становится синей!")
End Sub
'–
'Эта функция подключается для обработки событий
'щелчка на кнопке Button1
'–
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
'–
'Для простоты мы допускаем существование только
'одного экземпляра элемента управления,
'–
If Not (m_newControl Is Nothing) Then Return
'Создать экземпляр нашей кнопки
m_newControl = New myButton
'Указать ему его местоположение внутри родительского объекта
m_newControl.Bounds = New Rectangle(10, 10, 150, 40)
'–
'Присоединить обработчик событий
'–
AddHandler m_newControl.EventButtonTurningBlue, _
AddressOf CallWhenButtonTurningBlue
'Добавить его в список элементов управления данной формы.
'Это сделает его видимым
Me.Controls.Add(m_newControl)
End Sub
Листинг 11.11. Три полезных способа кэширования графических ресурсов
Imports System
Imports System.Drawing
Friend Class GraphicsGlobals
'=======================================================================
'Подход 1: Создать ресурс по требованию
' и кэшировать его для последующего использования.
'
'Внешний код получает доступ к общедоступным свойствам для их просмотра,
'но сами переменные остаются внутренними переменными класса
'=======================================================================
Private Shared s_bluePen As Pen
Public Shared ReadOnly Property globalBluePen() As Pen
Get
'Если перо еще не было создано
If (s_bluePen Is Nothing) Then
s_bluePen = New System.Drawing.Pen( _
System.Drawing.Color.Blue)







