ПОНЯТНО О Visual Basic NET (том 3)

         

Строка как объект Свойства и методы класса String


Строка – это не просто строка. Это объект. Объект класса String пространства имен System. Здесь та же ситуация, что и с массивами (15.6). Мы можем и не подозревать, что строка – объект, и тем не менее, успешно со строками работать.

Не путайте класс String с модулем Strings. Любая переменная (или литерал) строкового типа является специфическим экземпляром класса String, и чтобы воспользоваться его свойством или методом, вы просто пишете после имени переменной или литерала точку и за ней название свойства или метода, как мы делали это раньше.

Итак, строка обладает свойствами и методами своего класса.  Разберем их.

Прежде всего, строка представляет собой последовательность символов, пронумерованных, начиная с 0. Вы можете легко обращаться к каждому символу по его номеру (индексу):

Debug.WriteLine("Гастроном".Chars(3))             'Символ с индексом 3 в строке (т)

'Ищется первое вхождение символа "о" в  строку и находится его индекс (5):

Debug.WriteLine("Гастроном".IndexOf("о"))

'Ищется последнее вхождение символа "о" в  строку и находится его индекс (7):

Debug.WriteLine("Гастроном".LastIndexOf("о"))

Debug.WriteLine("Гастроном".Length)                       'Длина строки (9)

Вот что напечатает этот фрагмент:

т

5

7

9

Строкой легко заполнить символьный массив:

Dim Буквы() As Char                              'Объявляем массив символов

Буквы = "Гастроном".ToCharArray       'Заполняем его буквами слова "Гастроном"

Debug.WriteLine(Буквы(6))                     'Печатается буква  н

Вот еще несколько методов:



'Часть строки длиной 4, начиная с символа с индексом 3 (трон):

Debug.WriteLine("Гастроном".Substring(3, 4))

Debug.WriteLine("Гастроном".StartsWith("Га"))      'Правда ли, что "Гастроном" начинается с "Га"

Debug.WriteLine("Гастроном".EndsWith("ном"))    'Правда ли, что "Гастроном" заканчивается на "ном"


Вот что напечатает этот фрагмент:

трон

True

True

Вот методы, видоизменяющие строку:

Debug.WriteLine("Гастроном".Remove(2, 6))          'Убрать из строки 6 символов, начиная с номера 2

'Вставить в строку перед символом номер 2 другую строку ("лактический а"):

Debug.WriteLine("Гастроном".Insert(2, "лактический а"))

Debug.WriteLine("Победа".Replace("бед", "годк"))   'Заменить в строке одну часть ("бед")  на другую ("годк")

Вот что напечатает этот фрагмент:

Гам

Галактический астроном

Погодка

Если строка состоит из частей, разделенных пробелами, запятыми или другими символами, этими частями легко можно заполнить массив строк:

Dim Предложение As String = "Маша ела кашу"

Dim Слова_в_предложении() As String        'Объявляем массив строк

'Заполняем его частями строки Предложение, разделенными пробелами (" "):

Слова_в_предложении = Предложение.Split(" ")

Dim Список_покупок As String = "хлеб,молоко,кофе"

Dim Покупки() As String                                 'Объявляем массив строк

'Заполняем его частями строки Список_покупок, разделенными запятыми (","):

Покупки = Список_покупок.Split(",")

После выполнения этого фрагмента массив Слова_в_предложении будет состоять из строк  "Маша", "ела", "кашу", а массив Покупки будет состоять из строк  "хлеб", "молоко", "кофе".

Кроме рассмотренных методов у класса String есть еще методы, которые дублируют работу функций модуля Strings.

Задание 8.    

Определите без компьютера, что напечатает оператор

        Debug.WriteLine(ChrW(AscW("Ю") + 1))

Задание 9.    

«Детская шифровка». Среди детей встречается игра, заключающаяся в зашифровке своей речи «для секретности» за счет вставки в произносимые слова какого-нибудь звукосочетания, например, «быр». Тогда вместо слова «корова» будет произнесено «кобырробырвабыр». Составьте программу, которая распечатывает любую строку из 6 букв, после каждой второй буквы вставляя «быр». Если получилось, то решите эту задачу для строки произвольной длины.



Задание 10.          

Давайте поставим задачу шифрования текста более серьезно. Имеется строка текста. Требуется написать программу, которая зашифровывала бы ее в другую строку. Способов шифровки вы можете придумать сколько угодно. Попробуйте такой – заменять каждый символ текста символом, следующим по порядку в алфавите (точнее – в последовательности Unicode символов). Тогда изречение Козьмы Пруткова

Взирая на солнце, прищурь глаза свои, и ты смело разглядишь в нем пятна.

должно превратиться вот во что:

Гийсб?!об!тпмочж-!рсйъфсэ!дмбиб!тгпй-!й!уь!тнжмп!сбидм?ейщэ!г!ожн!р?уоб/

Составьте, пожалуйста, и программу дешифровки. Когда вы познакомитесь с файлами, вы сможете уже зашифровывать и дешифровывать не отдельные строки, а целые тексты. В том числе и ваши программы.

Совет: Если задача требует сложных преобразований строки и ее символов, то обычно начинают с того, что объявляют массив символов и заполняют его символами исходной строки. Теперь с ними удобней работать. В конце же из нужным образом преобразованного массива снова собирают строку. В нашем случае сложностей нет, поэтому в ответе эта задача решается другим, более коротким способом: работа идет непосредственно над символами строки.


Строка состояния (StatusBar)


Строка состояния имеется во многих приложениях Windows. Вот пример строки состояния редактора Microsoft Word (Рис. 20.21):

Рис. 20.21

Строка состояния всегда присутствует в окне приложения (обычно, прилепившись к  его нижнему краю) и применяется поэтому для отображения информации, которую всегда удобно держать перед глазами. Например, всем ясно, что сейчас пользователь работает со 158-й страницей документа Word. Лично мне строки состояния редко сообщали что-нибудь нужное (за исключением строки состояния Internet Explorer), поэтому я на них никогда не гляжу и втайне считаю, что они только занимают дефицитное место на экране J.



Строки и символы


Начнем со строк. Со строковым типом String мы познакомились в 5.6. Какие интересные задачи связаны со строками? Шифровка-дешифровка информации. Поиск в длинном тексте (например, в словаре или в инструкции по игре) нужного слова, которое просто так, глазами, искать очень долго. Автоматическое исправление орфографических ошибок в диктанте по русскому языку. Бездна задач по ведению деловой документации. И так далее.

Что мы умеем делать со строками? Мы знаем только операцию слияния строк:

Операция

Результат

Пояснение

"Мото"  +  "роллер"

Мотороллер

Операция  +  над строками просто соединяет строки в одну. VB может ее спутать со сложением чисел

"Мото"  &  "рол" &  "лер"

Мотороллер

Операция  &  тоже соединяет строки в одну. Ее рекомендуется всегда применять вместо +, так как со сложением ее не спутаешь

Для решения поставленных выше задач нам мало одного слияния, мы должны уметь «влезать внутрь строк», то есть анализировать и преобразовывать строки. VB предоставляет для этого богатый набор возможностей.



Строки Методы модуля Strings


 Полезные средства для работы со строками унаследованы от Visual Basic 6.0. Вы можете воспользоваться ими, как методами модуля Strings пространства имен Microsoft. VisualBasic. Эти методы представлены функциями. Вот основные из них:

Функция

Результат

Пояснение

Len ("Чук и Гек")

9

Длина строки, включая пробелы

GetChar ("Чук и Гек", 5)

и

5-й символ в строке

Mid ("Астроном" , 3, 4)

трон

Часть строки длиной 4, начиная с 3-го символа

Strings.Left ("Победа", 2)

По

2 левых символа в строке

Strings.Right ("Победа", 3)

еда

3 правых символа в строке

Мне пришлось написать Strings.Left, потому что функция Left встречается не только в модуле Strings. То же относится и к Strings.Right.

При помощи функции GetChar вы можете добраться до каждой буквы в тексте. Следующий фрагмент распечатывает в столбик слово «Телепортация»:

        Dim s As String = "Телепортация"

        Dim i As Integer

        For i = 1 To Len(s)

            Debug.WriteLine (GetChar(s, i))                                'Это i-я буква в строке

        Next

Следующая функция позволяет искать в тексте нужное слово:

InStr ("Астроном", "трон")

3

Позиция (номер символа), начиная с которой строка "трон" находится в строке "Астроном"

InStr ("Астроном", "Трон")

0

Строка "Трон" не найдена в строке "Астроном"

Следующие функции занимаются заглавными и строчными буквами:

Ucase ("астРОнОм")

АСТРОНОМ

Все символы строки переводятся в верхний регистр

Lcase ("астРОнОм")

астроном

Все символы строки переводятся в нижний регистр

В 3.3 (Калькулятор) жизнь заставила нас познакомиться с функцией Val, которая преобразует строку в число. Напомню ее вам:

Val (“20 груш и 8 яблок”)

20

Функция читает строку слева направо, пока не натолкнется на символы, никакого отношения к числам не имеющие

Val (“  -    1       0груш”)

-10

На пробелы при этом  внимание не обращается

3  *  Val ( "2"  &  "0" )

60

Выражение  "2" & "0"   равняется строке  "20", ну а  Val("20")  равняется числу 20

<
Существует функция Str, которая наоборот – преобразует число в строку.

Str (5 * 5)

25

Число 25 преобразуется в строку "25". Хотя, надо сказать, что VB при работе с данными во многих случаях сам, безо всякого вмешательства, услужливо преобразовывает данные к удобному с его точки зрения типу.

Когда мы вводим текст в текстовое окно, мы часто не замечаем, что лишний раз нажали на клавишу пробела, тем более, что лишние пробелы, особенно в самом начале и в самом конце строки, заметить трудно. Мы не всегда заботимся о том, чтобы избавиться от них. А зачем? А затем, что компьютер пробелы видит не хуже любой буквы и считает их полноправными символами. Мы склонны считать строки   "Африка"  и  "Африка "  вполне одинаковыми. Компьютер же не может позволить себе такой вольности, он прекрасно видит, что во второй строке в конце стоит пробел. Значит строки не равны и это может привести к неожиданным для нас результатам. Сколько раз в моей практике ученик при подключении к локальной сети вводил свое имя с лишним пробелом, а потом негодовал, что компьютер-сервер не пускает его в сеть, потому что он такого имени не знает.

Следующие три функции позволяют нам справиться с невнимательностью:

Функция

Результат

Пояснение

"Ж" &  LTrim("    Бутевни  матлны   ")  &  "Ж"

ЖБутевни  матлны     Ж

Функция LTrim отсекает ведущие слева пробелы

"Ж" &  RTrim("    Бутевни  матлны   ")  &  "Ж"

Ж     Бутевни  матлныЖ

Функция RTrim отсекает волочащиеся справа пробелы

"Ж" &  Trim  ("    Бутевни  матлны   ")  &  "Ж"

ЖБутевни  матлныЖ

Функция Trim отсекает пробелы и слева и справа


Структура проекта и решения Окно Solution Explorer


Посмотрим, что полезного и интересного мы сможем извлечь из окна Solution Explorer.



Связь компьютеров между собой Сети, модем, сетевая карта


Для переноса информации с одного компьютера на другой не обязательно использовать съемные носители. Если два компьютера расположены рядом на соседних столах, то в простейшем случае их достаточно соединить коротким проводом, и при помощи простенькой программы любая информация с винчестера одного компьютера по проводу небыстро переписывается на винчестер другого. Такое соединение позволяет и играть на двух компьютерах в одну игру.

Теперь поговорим о соединении нескольких компьютеров. Группу компьютеров, постоянно соединенных друг с другом каким-нибудь способом для обмена информацией, называют компьютерной сетью.

Когда эти компьютеры расположены в пределах одного здания, их соединяют специальным кабелем и при помощи мощной операционной системы они могут удобно и с высокой скоростью обмениваться между собой любой информацией. Такая компьютерная сеть называется локальной. Для возможности осуществления такой быстрой связи каждый компьютер снабжается специальным электронным устройством – сетевой картой.

Когда соединяемые компьютеры находятся в разных концах города или даже земного шара, то говорят, что они образуют глобальную компьютерную сеть. Правда, протягивать специальные кабели на большие расстояния дорого. Поэтому для дальней связи компьютеров часто используют обычную телефонную сеть. Люди, чтобы переговариваться по телефонной сети, используют телефоны; компьютеры же – специальные устройства – модемы. Самый известный пример всемирной глобальной компьютерной сети – Internet. Телефонная сеть изначально не была предназначена для передачи компьютерной информации, поэтому Интернет при связи по телефонному проводу работает гораздо медленнее локальной сети. Поэтому от связи по телефонным линиям с помощью модема в Интернете постепенно отказываются и переходят к более дорогой, но гораздо более быстрой связи по выделенным каналам, которая осуществляется в основном с помощью отдельного подводимого к компьютеру кабеля и сетевой карты. Используют также специальную аппаратуру для скоростной связи по телефонным проводам.



Свойства, события и методы списков


Простейшие и одновременно наиболее популярные свойства, события и методы во многом одинаковы для всех рассмотренных типов списков. Я перечислил их в процедуре, приведенной ниже.

Основным содержанием списка является его свойство Items. Оно представляет собой коллекцию, состоящую изо всех элементов списка. Элементы списков нумеруются с нуля (а не с 1).

Все свойства и методы, рассмотренные в нижеприведенной процедуре применительно к списку ComboBox, относятся также и к остальным типам списков. Исключение – свойство Text списка ListBox.

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

              'Напечатать значение выделенного элемента в списке:

        Debug.WriteLine(ComboBox2.SelectedItem)

              'Напечатать текст из текстового поля списка:

        Debug.WriteLine(ComboBox2.Text)

              'Напечатать номер (индекс) выделенного элемента в списке:

        Debug.WriteLine(ComboBox2.SelectedIndex)

               'Заменить значение элемента под номером 3:

        ComboBox2.Items(3) = "Энергия"

              'Напечатать количество элементов в списке:

        Debug.WriteLine(ComboBox2.Items.Count)

              'Добавить в конец списка новый элемент "Flamengo":

        ComboBox2.Items.Add("Flamengo")

              'Вставить в  список под номером 2 новый элемент "Monaco". Нижележащие элементы сдвинутся:

        ComboBox2.Items.Insert(2, "Monaco")

              'Исключить из списка  элемент "Спартак":

        ComboBox2.Items.Remove("Спартак")

              'Исключить из списка  элемент под номером 9:

        ComboBox2.Items.RemoveAt(9)

              'Напечатать, правда ли, что в списке содержится элемент "Monaco":

        Debug.WriteLine(ComboBox2.Items.Contains("Monaco"))

              'Напечатать, под каким номером в списке значится элемент "Flamengo":

        Debug.WriteLine(ComboBox2.Items.IndexOf("Flamengo"))


End Sub
Если ни один элемент в списке не выделен, значение SelectedIndex равно  -1.
Если вы хотите очистить список, напишите:
        ComboBox2.Items.Clear()
Если вы хотите, чтобы элементы списка были отсортированы по алфавиту, то установите в True свойство Sorted. Не забывайте, что сортировка – текстовая, а не числовая, поэтому если ваш список состоит из числовых строк, то 28 будет стоять выше, чем 5.
Среди разнообразных событий списка упомяну событие SelectedIndexChanged, которое происходит в момент выбора очередного элемента списка.
Удобная работа со списком. В большинстве реальных проектов вам нужно не просто что-то выбрать из списка, но и иметь возможности для удобного изменения списка: добавления, удаления, изменения и перестановки его элементов.
Создадим проект. Разместим на форме простой или раскрывающийся ComboBox и 4 кнопки для разнообразной работы с его списком. Вот функции кнопок:
Кнопка «Удаление» удаляет выбранный элемент из списка
Кнопка «Добавить» добавляет содержимое текстового поля списка в список (удобно для быстрого внесения дополнений в список)
Кнопка «Переставить в конец» переставляет выбранный элемент в конец списка (удобно для произвольной пересортировки  списка)
Кнопка «Исправить» заменяет выбранный элемент содержимым текстового поля списка (удобно для исправлений в написании элемента)
Попробуйте написать код самостоятельно. Это не очень легко. Если не получится, то вот он:
Dim Номер As Integer     'Номер выбранного элемента в списке
Private Sub Удаление(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Удаление.Click
        ComboBox1.Items.Remove(ComboBox1.SelectedItem)
End Sub
Private Sub Добавить(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Добавить.Click
        If ComboBox1.Text <> "" Then ComboBox1.Items.Add(ComboBox1.Text)     'Если в поле не пусто, то ...
End Sub
Private Sub Переставить(ByVal sender As System.Object, ByVal e As System.EventArgs)  _


Handles Переставить.Click
        ComboBox1.Items.Add(ComboBox1.Text)                             'Сначала добавляем в конец,
        ComboBox1.Items.Remove(ComboBox1.SelectedItem)  'а затем удаляем с прежнего места
End Sub
Private Sub Исправить(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Исправить.Click
        ComboBox1.Items.RemoveAt(Номер)                              'Сначала удаляем старый элемент,
        ComboBox1.Items.Insert(Номер, ComboBox1.Text)        ' а затем на его место вставляем текст из поля
End Sub
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)  _
Handles ComboBox1.SelectedIndexChanged
        Номер = ComboBox1.SelectedIndex
End Sub
Пояснения: Поскольку при редактировании текстового поля выделение элемента списка исчезает, я решил узнать номер (индекс) исправляемого элемента заранее, еще до начала редактирования, для чего использовал событие SelectedIndexChanged.
Замечу, что я, чтобы не утонуть в подробностях, не предохранил проект от неосторожных «нештатных» действий пользователя. Например, кнопка «Переставить в конец» ставит в конец списка, к сожалению, не сам элемент, а содержимое текстового поля, которое вы вполне могли по неосторожности и изменить.
Задание 4.    
«Англо-русский словарь». Поместите на форму два раскрывающихся списка. В левый запишите в алфавитном порядке несколько десятков английских слов. В правый запишите в том же порядке переводы этих слов на русский. При выборе слова в левом списке в правом должен появляться перевод.

Таймер и общая механика работы проекта


Это уже наш третий солидный проект после проектов «Будильник» и «Гонки». Давайте продумаем общую механику его работы. Она, в общем, напоминает механику работы первых двух проектов. Если вы ее подзабыли, обязательно перечитайте 13.5.11 и 14.4.4. Там я писал, что важную роль в моделировании на компьютере согласованной работы разных механизмов играет таймер, который своими импульсами синхронизирует работу этих механизмов (объектов). Применительно к нашей задаче можно сказать, что очередной импульс таймера «будит» по очереди объекты (шары и ловца), каждый из которых выполняет весь цикл своей жизнедеятельности, то есть все свои действия, которые положено выполнять при приходе импульса, после чего «засыпает» до следующего импульса. На следующем импульсе таймера все повторяется снова. Поскольку импульсы следуют друг за другом с большой частотой, создается впечатление непрерывной и одновременной работы объектов.

Посмотрим, как применить эту идеологию к нашему проекту. В игре 11 объектов: ловец и 10 шаров. Имеется 1 таймер. Он непрерывно на протяжении всей игры посылает импульсы с частотой 100 импульсов в секунду. Вот игра началась. От таймера пошел 1-й импульс. Оживает 1-й шар, «осознает себя, как объект», заглядывает внутрь себя, запускает всю свою механику и выполняет все, что ему положено в данной обстановке. В начале игры это означает, что он сдвигается на некоторый шаг от точки старта. Выполнив все свои дела, он «засыпает» и «просыпается» 2-й шар, который тоже делает все, что ему положено (сдвигается), и «засыпает» и т.д. Когда засыпает 10-й шар, просыпается ловец и тоже выполняет все, что диктует ему его механизм, в частности смотрит, не нажата ли клавиша на клавиатуре, и в зависимости от этого сдвигается или остается на месте. После чего засыпает. Все. Все объекты сделали свои дела и спят до прихода 2-го импульса от таймера. Вот пришел 2-й импульс. Просыпается 1-й шар, все делает (сдвигается еще на шаг в том же направлении) и засыпает, просыпается 2-й шар и так далее…

Обратите внимание, что в каждый момент времени не спит, работает и движется только один объект. Остальные неподвижны и спят. Однако, поскольку импульсы от таймера следуют друг за другом с большой частотой, создается впечатление непрерывной и одновременной работы объектов: шары летают, ловец ловит (несмотря на то, что все они «бодрствуют» по очереди).

Обратите внимание, что таймер ничего не должен говорить объектам о том, что они должны делать, они должны знать это сами, так как это запрограммировано в их процедурах. Таймер всего лишь будит их через равные промежутки времени.

Кроме шаров и ловца импульсам таймера у нас в проекте подчиняется счетчик времени на форме. Но его делать объектом мы не будем, он слишком прост. Вот если бы мы захотели его усложнить, заставив вести себя по-разному в зависимости, например, от собственных показаний (мигать, менять цвет, выдавать сообщения), тогда дело другое.



Типизированные файлы «Старый» доступ к файлам


Кто разбирался в Задание 136 про жителей города и Задание 137 про мэрию, тот понимает, что исправление какой-то информации в текстовом файле, содержащем базу данных из 10000 записей, дело долгое, так как, чтобы исправить запись № 9001, нужно прочесть все 10000 записей, а затем вновь записать все 10000 записей. Поэтому никто не хранит базы данных в текстовых файлах. Для этого удобно применять так называемые типизированные файлы. Рассмотрим пример.

Задание. В стране спортом занимаются 4 миллиона спортсменов (мы для простоты возьмем 4 спортсмена). Про каждого спортсмена известны:

Фамилия

Дата рождения

Пол

Вес

Требуется:

Создать файл с пронумерованными записями этой информации обо всех спортсменах страны

Процедуру чтения всех записей из файла

Процедуру чтения записи с указанным номером

Процедуру исправления записи с указанным номером

Создадим структуру

Structure типСпортсмен

        Dim Фамилия As String

        Dim Дата As DateTime

        Dim Мужчина As Boolean

        Dim Вес As Integer

End Structure

Поле Мужчина равно True если спортсмен мужчина, и False – если женщина.

Создадим массив структур для всех спортсменов страны:

Dim Сп(4) As типСпортсмен

Разместите на форме 4 кнопки.

Создаем файл с записями обо всех спортсменах страны:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        'Создаем в памяти компьютера информацию о всех спортсменах:

        Сп(1).Фамилия = "Волков"             : Сп(1).Дата = #12/30/1974#     : Сп(1).Мужчина = True       : Сп(1).Вес = 72

        Сп(2).Фамилия = "Медведев" : Сп(2).Дата = #1/4/1942#   : Сп(2).Мужчина = True    : Сп(2).Вес = 96

        Сп(3).Фамилия = "Лисицина"   : Сп(3).Дата = #10/14/1981#     : Сп(3).Мужчина = False            : Сп(3).Вес = 60

        Сп(4).Фамилия = "Зайцев"             : Сп(4).Дата = #11/29/1978#     : Сп(4).Мужчина = True       : Сп(4).Вес = 48

        'Записываем эту информацию в файл:


После завершения работы с файлом его нужно закрыть процедурой FileClose. Ее параметр – номер закрываемого файла.

Итак, после выполнения процедуры мы будем иметь файл, в котором друг за другом идут 4 записи:  Сп(1),  Сп(2),  Сп(3),  Сп(4). Компьютер способен определять их порядковые номера в файле, несмотря на то, что они нигде физически в файле не записаны. Внутри каждой записи присутствует информация о конкретном спортсмене страны. Никаких символов возврата каретки в файле нет и чтобы понять, где кончается одна запись и начинается следующая, компьютер пользуется другими критериями.

Процедура FilePut (как и нижеописанная FileGet) может применяться для работы не только со структурами, но и с простыми типами данных, например, с числами конкретного типа или строками. В этом случае файл будет представлять собой цепочку чисел одного типа или строк.

Файлы, представляющие собой цепочку записей одинаковой структуры или простых данных одного типа, называются  типизированными файлами.

Читаем все записи. Вот процедура чтения всех записей из файла:

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

        FileOpen(1, "E:\Папка\База.gor", OpenMode.Random)

        Dim i As Integer

        For i = 1 To 4

            FileGet(1, Сп(i))

        Next

        FileClose(1)

End Sub

Пояснения: Осуществляет чтение информации из файла с произвольным доступом процедура FileGet. Первый ее параметр – номер файла, а второй – переменная, в которую посылается значение, считанное из файла. В нашем случае это Сп(i) – структура информации об очередном спортсмене. Мы можем указать и третий параметр – номер записи в файле, которую хотим считать, но у нас он не указан, поэтому записи будут считываться подряд одна за другой, как при считывании информации из текстового файла.

Читаем одну запись. Поместим на форму 5 текстовых полей. В текстовое поле 1 мы будем вводить номер интересующей нас записи в файле. Остальные 4 поля предназначены соответственно для фамилии, даты рождения, пола и веса спортсмена.



Вот процедура чтения из файла одной нужной нам записи с указанным номером:

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click

        Dim Спортсмен As типСпортсмен

        Dim Номер As Integer = CInt(TextBox1.Text)

        'Считываем из файла информацию об одном спортсмене:

        FileOpen(1, "E:\Папка\База.gor", OpenMode.Random)

        FileGet(1, Спортсмен, Номер)

        FileClose(1)

        'Отображаем информацию об одном спортсмене:

        TextBox2.Text = Спортсмен.Фамилия

        TextBox3.Text = Спортсмен.Дата

        TextBox4.Text = Спортсмен.Мужчина

        TextBox5.Text = Спортсмен.Вес

    End Sub

Пояснения: Создаем рабочую переменную Спортсмен типа структуры типСпортсмен. Пусть нас интересует информация, хранящаяся в файле в записи №3. Мы вводим число 3 в поле TextBox1. Функция CInt на всякий случай преобразует то, что мы ввели, в тип Integer. Результат присваивается переменной Номер. В процедуре FileGet мы указали параметр Номер, поэтому она считывает из файла в переменную Спортсмен запись с номером 3. Обратите внимание – записи 1 и 2 не считываются, время на них не тратится, компьютер сразу «прыгает» к записи 3. В текстовых полях появляется информация о спортсмене №3.

Исправляем одну запись. Вот процедура исправления записи с указанным номером:

    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click

        Dim Спортсмен As типСпортсмен

        Dim Номер As Integer = CInt(TextBox1.Text)

        'Создаем в памяти компьютера информацию об одном спортсмене:

        Спортсмен.Фамилия = TextBox2.Text

        Спортсмен.Дата = CDate(TextBox3.Text)

        Спортсмен.Мужчина = CBool(TextBox4.Text)

        Спортсмен.Вес = CInt(TextBox5.Text)

        'Записываем эту  информацию в файл:

        FileOpen(1, "E:\Папка\База.gor", OpenMode.Random)

        FilePut(1, Спортсмен, Номер)

        FileClose(1)



    End Sub

Пояснения: Пусть мы хотим исправить в файле запись №2.  Вводим число 2 в поле TextBox1. В остальные четыре текстовые поля вводим вручную информацию о спортсмене, затем нажимаем кнопку – и информация оказывается в файле в записи №2.

Функции  CInt,  CDate,  CBool  на всякий случай преобразуют то, что мы ввели, в нужные типы. В процедуре FilePut мы указали третий параметр – Номер, поэтому она записывает значение  переменной Спортсмен в файл в запись с номером 2. Обратите внимание – на запись 1 время не тратится, компьютер сразу «прыгает» к записи 2. То, что было записано в файле в записи 2, стирается, и на это место пишется новая информация.

Совет: Удобнее исправлять запись о конкретном спортсмене так: Пусть спортсмен №2 похудел и нам нужно исправить в файле только его вес.  Вводим число 2 в поле TextBox1. Щелкаем по 3 кнопке. В текстовых полях появляется информация о спортсмене №2. Исправляем только вес и щелкаем по 4 кнопке. Дело сделано.

Файлы с произвольным доступом. Мы видим, что информация в типизированных файлах записывается упорядоченно и регулярно, благодаря чему к ней возможен более гибкий и быстрый доступ, чем в текстовых файлах. Эти файлы хорошо приспособлены для хранения массивов простых данных и структур. Здесь уже нет необходимости читать вхолостую начало файла для того, чтобы прочесть что-то в его середине или перезаписывать весь файл, если нужно исправить его маленькую частицу. Поэтому в отличие от файлов с последовательным доступом типизированные файлы причисляют к файлам с произвольным доступом.

Методы «старого» доступа. Вы вполне можете столкнуться с необходимостью прочесть код, в котором для работы с файлами любого вида используются методы модуля FileSystem пространства имен Microsoft.VisualBasic. Поэтому я для справки привожу примеры обращения к этим методам:

FileOpen (1, файл, OpenMode.Random)

Открыть файл для типизированного доступа

FileOpen (1, файл, OpenMode.Binary)

Открыть файл для нетипизированного доступа (о нем ниже)

FileOpen (1, файл, OpenMode.Input)

Открыть текстовый файл для чтения

FileOpen (1, файл, OpenMode.Output)

Открыть текстовый файл для записи

FileOpen (1, файл, OpenMode.Append)

Открыть текстовый файл для дозаписи

FileClose (1)

Закрыть файл, открытый методом FileOpen

FilePut (1, переменная, номер)

Записать переменную в файл с произвольным доступом на место, указанное номером

FileGet (1, переменная, номер)

Прочесть переменную из файла с произвольным доступом с места, указанного номером

Seek (1)

На какой позиции в файле я нахожусь?

Seek (1, позиция)

Переместить головку в файле на указанную позицию

Write (1, a, b, c, …)

Записывает в текстовый файл значения переменных a, b, c и так далее. Переменные эти могут иметь разные простые типы. Интересно, что в файле они тоже отделяются друг от друга запятыми, а строковые данные, например, берутся в двойные кавычки.

WriteLine (1, a, b, c, …)

То же, что Write, но следующая запись начнется уже с новой строки

Input (1, a)

Обычно используется для правильного чтения из текстового файла того, что туда понаписали Write и WriteLine. За один раз читает одно очередное данное и сдвигает головку до следующей запятой или символа возврата каретки..

Print (1, a, b, c, …)

Записывает в текстовый файл значения переменных a, b, c и так далее. Переменные эти могут иметь разные простые типы.

PrintLine (1, a, b, c, …)

То же, что Print, но следующая запись начнется уже с новой строки

LineInput (1)

Считывает из текстового файла очередную строку, какие бы и сколько бы данных в этой строке ни находилось. Обычно используется для чтения того, что записано методами Print и PrintLine.

InputString (1, число символов)

Считывает из файла указанное количество символов

EOF (1)

Правда ли, что достигнут конец текстового или типизированного файла?

LOF (1)

Длина открытого файла в байтах

Если хотите, можете попробовать, используя приведенные в таблице примеры, поработать с текстовыми файлами. Но имейте в виду, что о многих тонкостях обращения к файлам и об ограничениях на использование перечисленных методов я не упоминал.


TreeView


Мне этот раздел очень интересен. Но если вы не собираетесь пользоваться элементом управления TreeView, то можете прочесть его «по диагонали».

Я уже говорил, что TreeView – это левая панель Проводника Windows. Если у вас нет опыта работы с Проводником, то см. Приложение 2. Научитесь уверенно ходить в Проводнике по дереву папок, нажимая сознательно на плюсики и минусики.

Задание. Создадим TreeView следующего содержания (см. Рис. 20.16).

Рис. 20.16

Это не что иное, как родословная человечества, начиная с Адама (как я ее вычитал на первых страницах Библии). У Адама было три сына: Каин, Авель и Сиф. У Каина был сын Енох. Про сыновей Авеля я ничего там не нашел (возможно, он просто не успел обзавестись семьей). У Сифа родился Енос, у Еноса – еще кто-то … Через несколько поколений родился потомок Еноса Ной (тот, что пережил Всемирный потоп). У Ноя было три сына: Сим, Хам и Иафет. И так далее.



Усовершенствованный текстовый редактор


Поставим задачу создать на основе поля RichTextBox усовершенствованный текстовый редактор. Внешний вид его вы можете видеть на Рис. 20.6.

Рис. 20.6

Большое прямоугольное поле на форме – это и есть RichTextBox. Вы вводите в него вручную любой текст и форматируете его, то есть изменяете его внешний вид. Чтобы отформатировать фрагмент текста, вы его, как принято в текстовых редакторах, сначала выделяете, а затем нажимаете одну из шести кнопок в окне редактора, а именно:

Кнопка Настроить шрифт вызывает диалоговое окно настройки шрифта, после чего предварительно выделенный в текстовом поле фрагмент текста форматируется выбранным шрифтом.

Смысл кнопок Налево, По центру, Направо ясен из текста в поле RichTextBox на рисунке.

Кнопки Поставить и Снять ставят и снимают маркеры перед строкой. У нас на рисунке маркеры – это, круглые пятна перед «яблоками и мандаринами».

Кнопки Открыть документ и Сохранить документ вызывают диалоговые окна открытия и сохранения файла.

Счетчик Масштаб управляет приближением содержимого текстового поля к нашим глазам.

Кнопка Кнопка будет объяснена чуть позже.

Конечно, в текстовых редакторах элементов управления побольше, но для понимания работы RichTextBox хватит и этих. Конечно, все эти кнопки просятся в меню или на панель инструментов (о которой речь позже), но я сделал как попроще. Меню вы можете сделать и сами.

Создайте проект. Поместите на форму нужные кнопки, метки, счетчик NumericUpDown. Поместите  на форму элемент управления RichTextBox и для краткости переименуйте его в RTB. Поместите в проект диалоговые окна открытия, сохранения файла, настройки шрифта.

Вот программа:

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

        RTB.HideSelection

= False        'Чтобы выделение не снималось с фрагмента при уходе фокуса с поля     

End Sub

Private Sub Открыть_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Открыть.Click

        OpenFileDialog1.Filter = "RTF-файлы|*.RTF"


        If OpenFileDialog1.ShowDialog() = DialogResult.Cancel Then Exit Sub

        Dim Файл As String = OpenFileDialog1.FileName

        RTB.LoadFile(Файл)

End Sub

Private Sub Сохран_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Сохран.Click

        SaveFileDialog1.Filter = "RTF-файлы|*.RTF"

        If SaveFileDialog1.ShowDialog() = DialogResult.Cancel Then Exit Sub

        Dim Файл As String = SaveFileDialog1.FileName

        RTB.SaveFile(Файл)

End Sub

Private Sub Настр_шр(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Настр_шр.Click

        FontDialog1.ShowColor = True    'Предоставить возможность выбирать цвет шрифта

        FontDialog1.ShowDialog()            'Показать диалоговое окно настройки  шрифта

        ' Присвоить выделенному фрагменту  шрифт, выбранный в диалоговом окне:

        RTB.SelectionFont = FontDialog1.Font

        'Присвоить выделенному фрагменту цвет шрифта, выбранный  в диалоговом окне:

        RTB.SelectionColor = FontDialog1.Color

End Sub

Private Sub Налево_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Налево.Click

        RTB.SelectionAlignment = HorizontalAlignment.Left

End Sub

Private Sub По_центру(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles По_центру.Click

        RTB.SelectionAlignment

= HorizontalAlignment.Center

End Sub

Private Sub Направо_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Направо.Click

        RTB.SelectionAlignment

= HorizontalAlignment.Right

End Sub

Private Sub Поставить(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Поставить.Click

        RTB.SelectionBullet = True

End Sub

Private Sub Снять_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Снять.Click

        RTB.SelectionBullet

= False

End Sub

Private Sub NumericUpDown1_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)  _



Handles NumericUpDown1.ValueChanged

        RTB.ZoomFactor

= NumericUpDown1.Value

End Sub

Пояснения: Рассмотрим процедуры по порядку сверху вниз.

Объект RichTextBox обладает большим количеством свойств и методов, которые и обуславливают богатство его возможностей. Начнем со свойства HideSelection, которое устанавливается в процедуре Form1_Load: Когда мы выделяем в поле темным цветом фрагмент текста, чтобы отформатировать его, а затем уходим из поля к соответствующей нашим желаниям кнопке, с уходом фокуса из поля исчезает и темный цвет выделения. Само выделение, правда, не исчезает и форматирование проходит нормально, но вот что именно было выделено, мы не видим. Это непривычно и неудобно. Чтобы темный цвет не исчезал, мы пишем в процедуре Form1_Load:

        RTB.HideSelection = False        'Чтобы выделение не снималось с фрагмента при уходе фокуса с поля     

Перейдем к процедурам открытия и сохранения документа. RichTextBox сохраняет свой текст в так называемом RTF-файле, который не является обычным текстовым файлом, поэтому работать с ним мы не умеем. Однако, у объекта RichTextBox имеются методы LoadFile и SaveFile. Первый целиком загружает документ из файла в поле RichTextBox, а второй целиком сохраняет содержимое поля RichTextBox в файле. А нам больше и не надо.

Перейдем к следующим 6 процедурам, которые соответствуют 6 кнопкам форматирования в нашем проекте. Все они имеют дело со свойствами объекта RichTextBox, начинающимися на Selection…. При изменении значения этих свойств мы меняем формат (вид) выделенного фрагмента текста. Например, изменение свойства SelectionColor приводит к изменению цвета выделенного фрагмента текста.

В последней процедуре мы при помощи счетчика NumericUpDown меняем значение свойства ZoomFactor. Это свойство «приближает к глазам» содержимое поля RichTextBox или «удаляет его от глаз». Чем больше значение счетчика, тем ближе к глазам. В Microsoft Word этим занимается инструмент «Масштаб». Но наш простой инструмент работает хуже, потому что «заворачивает» текст, «уткнувшийся» в правую границу поля, на следующую строку.


Установка соединения с базой данных


Первое, что вам требуется, чтобы добраться до файла базы данных, это установить с ним соединение (Connection). Ведь и по телефону не поговоришь, не соединившись с абонентом. Для этого щелкните правой клавишей мыши по Data Connections и в контекстном меню выберите Add Connection (добавить соединение). Перед вами возникнет окно свойств соединения. Зайдите в нем в закладку Providers (провайдеры – Рис. 24.13). Под провайдером здесь понимается не фирма, обеспечивающая соединения, а тип, формат базы данных, до которой мы хотим добраться. Его мы должны знать заранее.

Рис. 24.13

Провайдером баз данных, совместимых с Access, является

Microsoft Jet 4.0 OLEDB Provider.

Его мы и выбираем, после чего нажимаем Next. Перед нами возникает следующая закладка этого окна – Connection (Рис. 24.14).

Рис. 24.14

В верхнем поле вы должны написать или выбрать при помощи кнопки с троеточием адрес файла базы данных на вашем компьютере или даже на другом. Да, если ваш компьютер соединен сетью с другими компьютерами, то вы можете указать файл базы данных на другом компьютере и работать с ним, не вставая со своего.

Нажмите на кнопку Test Connection, чтобы протестировать соединение, и затем – ОК. В окне Server Explorer появится значок соединения (ACCESS…….). Соединение установлено. Щелкните по плюсику у этого значка, а затем по плюсикам у возникших значков Tables и Книги. Окно Server Explorer примет такой вид, как на Рис. 24.15. Это привычное нам окно элемента управления TreeView.

Рис. 24.15

Если вы захотите впоследствии удалить соединение, щелкните правой клавишей мыши по значку соединения и в контекстном меню выберите Delete (удалить).



Устройства ввода


Первые три устройства предназначены для ввода информации в компьютер непосредственно от пальцев человека.

1. Клавиатура.

2. Мышь.

3. Джойстик.

4. Сканер. Пусть вы литературовед и хотите поручить компьютеру проанализировать текст попавшего к вам в руки тысячестраничного древнего манускрипта. Однако тут возникает трудность: чтобы компьютер мог это сделать, текст манускрипта должен оказаться у него в памяти. Для этого вы должны весь текст набрать на клавиатуре – работа на несколько месяцев. Есть способ быстро ввести печатный текст с листа в компьютер. Для этого используется сканер – прибор, главной составной частью которого является специальное считывающее устройство, передающее изображение листа бумаги с текстом в компьютер.

Итак, изображение текста находится в памяти компьютера. Однако, это не значит, что компьютер «знает», что записано у него в памяти. Ведь компьютер сам не умеет по изображению буквы различить, что это за буква. Нужна специальная программа, которая различает между собой печатные буквы различных шрифтов.

Вы спросите, а как же компьютер различает буквы, вводимые с клавиатуры? Ответ: А он различает совсем не буквы, а нажимаемые клавиши.

Сканером можно вводить с листа не только текст, но и картинки. Но для распознавания картинок нужны программы еще более сложные, чем для распознавания текста. Так, в мире пока не существует программы, которая бы могла по фотографии отличить собаку от кошки.

5. Цифровой фотоаппарат. Того же результата, что со сканером, вы можете достичь, сфотографировав манускрипт или кошку на цифровом фотоаппарате и введя фотографию в компьютер.

6. Микрофон. Предназначен для того, чтобы вы могли что-нибудь приказать компьютеру или записать свой голос на диск или попытаться продиктовать текст текстовому редактору. Для его работы нужна звуковая карта (см. ниже).

7. Ввод с дискеты и с других носителей. Вы уже знаете, что с дискеты и других носителей информации можно вводить в компьютер программы, музыку и другую информацию. Более подробно с этими носителями вы познакомитесь чуть ниже.



Устройства вывода


1. Монитор (дисплей). Компьютер не умеет рисовать на экране монитора ничего, кроме светящихся точек (пикселей). Однако, расположив несколько светящихся точек в ряд вплотную друг к другу, получим линию, а сплошь заполнив ими некоторую область экрана, получим изображение фигуры. Пиксели расположены на экране стройными рядами. Каждый пиксель по указанию программы может быть потухшим или гореть заданным цветом (см. Рис. П2).

Рис. П2

На рисунке видно, что на экране умещается 640 столбцов и 480 строк пикселей. Общее количество пикселей получается равным 640х480=307200. Много это или мало? Вглядевшись в нарисованную фигуру, мы видим, что контуры ее несколько угловаты и грубоваты, изображение не гладкое, а зернистое. То же самое будет, если вы посмотрите в увеличительное стекло на поверхность экрана работающего монитора. Происходит это от того, что пиксели слишком крупные. Действительно, высота экрана современного монитора где-то около 24 см. Делим на 480, получаем размер пикселя – полмиллиметра. Для глаза вполне различимо. Это нехорошо.

Давайте увеличим число строк и столбцов пикселей на экране. Тогда пикселей на том же экране станет больше, они станут меньше размером и изображение станет менее зернистым, оно будет более тонко очерченным и правдоподобным. Общим количеством столбцов и строк пикселей на экране (разрешением) управляете вы сами через специальную электронную схему, находящуюся в системном блоке – видеоадаптер (видеокарту). Сейчас в ходу разрешения 1280х960 и выше.

Если у вас в компьютере мощная видеокарта, она позволяет устанавливать разрешения 2048х1536 и выше. Размер пикселя в этом случае 0,15 мм и меньше. Очень хорошо, но так не делают. Причин две. Первая. Пиксель – понятие, связанное не с монитором, а с видеоадаптером. У монитора есть другое понятие – зерно. Монитор собран из зерен, как крыша из черепицы или мозаика из стеклышек. Если пиксель может менять свой размер под управлением электронной схемы видеокарты, то зерно сделано из вещества и размер свой, естественно, не меняет. Каждое зерно может гореть заданным светом или быть потухшим. Деталей меньших, чем размер зерна, монитор в принципе не способен воспроизводить, какой бы мелкий пиксель не задавала видеокарта. Размер зерна современных мониторов – где-то около 0,2 мм. Поэтому, если видеокарта разобьет экран на пиксели размером 0,1 мм, то толку от этого не будет – все равно самые мелкие детали будут иметь размер 0,2 мм. Качество изображения не улучшится. В еще большей степени это относится к плоским экранам. Вторая причина: Чем больше пикселей, тем больше требуется ресурсов компьютера, чтобы с ними справиться, ведь каждый пиксель – это информация, а ее надо обрабатывать.


Количество цветов, на которое вы можете настроить видеоадаптер, тоже разное – от 256 до миллионов.

2.Принтер. Если мы хотим, чтобы числа, текст, рисунки, полученные компьютером, оказались не на экране, а на листе бумаги, то пользуемся готовой программой печати их на принтере. Принтер – это устройство, подключенное к компьютеру и печатающее текст, рисунки и фотографии по командам программы. Изображение на листе получается из отдельных точек по тому же принципу, что и изображение на экране. По физическому способу получения точек на листе принтеры делятся в основном на матричные, струйные и лазерные. Матричный принтер получает черные точки на листе ударами маленьких штырьков (игл) по красящей ленте, которая в результате соприкасается с бумагой и оставляет на ней след. Матричные принтеры – самые дешевые и цветными быть не могут. Они уже устаревают. Струйный принтер впрыскивает на лист мельчайшие капельки разноцветных чернил из множества специальных шприцев (сопел), поэтому изображение на листе может быть цветным. Лазерный принтер при помощи лазерного луча снимает с предварительно заряженного специального барабана в тех точках, где должно быть изображение, электрический заряд, после чего барабан входит в контакт с красящим порошком. Порошок пристает к барабану только там, где его не отталкивает электрический заряд, в результате чего на барабане  получается изображение. Затем барабан прокатывается по листу бумаги и отдает изображение ему. Лазерные принтеры бывают и цветными.

3.Вывод звука. Компьютеры могут воспроизводить на наушники или звуковые колонки любые звуки. Вы можете услышать хоть целый симфонический оркестр. Весь звук создает электронное устройство, которое называется звуковой адаптер (звуковая карта). Но если вы наблюдали, как работает персональный компьютер, то обратили внимание, что во время загрузки, когда звуковая карта еще не работает, компьютер все же издает какие-то звуки. Это чаще всего отдельные редкие попискивания. Их генерирует простенькое устройство, которым снабжаются все компьютеры и которое называется PC Speaker.

4.Вывод на дискету и другие носители. Вы знаете, что на дискеты и другие носители информации можно записывать из компьютера программы, музыку, картинки и другую информацию. Более подробно с этим вы познакомитесь чуть ниже.


Устройство и работа компьютера


Для того, чтобы водить автомобиль, совсем не обязательно знать, отчего крутятся колеса. Но чтобы быть хорошим водителем, устройство и взаимодействие основных частей автомобиля представлять все же необходимо. То же самое верно и о компьютере: можно на нем работать, используя готовые программы, и даже самому программировать, не зная внутреннего устройства компьютера. Однако квалифицированным пользователем и программистом не станешь, не изучив устройство и взаимодействие основных его частей.



Устройство оперативной памяти


Представьте себе тетрадный листок в клеточку. В каждую клетку вы имеете право записать карандашом какую-нибудь букву или цифру, или знак препинания, или вообще любой символ, который можно найти на клавиатуре. А можете и стереть ластиком и записать другой символ. Много ли букв можно записать на листе? Ровно столько, сколько на нем клеток.

Оперативная память компьютера устроена аналогично этому листу. Только физический размер ее гораздо меньше, чем у тетрадного листа, а клеточек гораздо больше. Каждая клеточка называется байтом. Для запоминания слова КОШКА понадобится 5 байтов. На странице вашего учебника около 1000 букв и других символов (включая запятые, точки и пробелы), значит, для запоминания страницы текста нужно 1000 байтов. Вы можете сами подсчитать, сколько страниц текста может запомнить современный компьютер, если я скажу, что его память не бывает меньше 16 миллионов байтов.

Оперативная память компьютера электронная. Информация хранится в ней в виде электрических зарядов или потенциалов в миниатюрных электронных схемах и передается из одного места в другое со скоростью близкой к скорости света. Запись, стирание, считывание информации из нее осуществляются по приказам процессора в соответствии с программой меньше чем за стомиллионную долю секунды.



Волшебные кнопки отмены и возврата


Иногда так бывает, что в результате неосторожного нажатия на какую-нибудь кнопку в окне редактора ВСЕ СТАНОВИТСЯ ОЧЕНЬ ПЛОХО! То есть, или текст программы безнадежно испорчен, или большой фрагмент этого текста вообще пропал неизвестно куда, или произошло что-нибудь еще катастрофическое. И вы не знаете, как помочь этому горю. В этом случае просто щелкните один или несколько раз по кнопке отмены ваших действий

 на панели инструментов. Все вернется на свои места. Для интереса щелкайте по этой клавише, пока щелкается. Вы увидите, что все ваши действия в редакторе отменяются одно за другим. Чтобы вернуть их, пощелкайте по кнопке возврата 
.



«Все модули в одном»


До сих пор мы создавали классы и стандартные модули при помощи меню: Project ® Add New Item. При этом для каждого стандартного модуля или класса автоматически создавались свой файл и свое окно кода. Оказывается, все стандартные модули и классы можно создавать в одном-единственном окне кода и в одном файле, причем даже в окне кода и файле формы. Например, создайте проект. Запишите в окне кода формы такой текст:

Public Class Form1

    Inherits System.Windows.Forms.Form

Windows Form Designer generated code

    'Сюда пишем код формы

End Class

Public Class Класс

    'Сюда пишем код класса

End Class

Public Module Модуль

    'Сюда пишем код модуля

End Module

Public Structure Структура

    'Сюда пишем код структуры

End Structure

Впервые мы пишем что-то не внутри класса формы, а ниже. Вы видите в окне кода четыре равноправных модуля: два класса, один стандартный модуль и одну структуру. Как видите, VB не возражает. Отныне вы можете делать так всегда, а когда модули короткие, это даже удобно. Для этого подходит окно кода не только формы, но и класса, и стандартного модуля. Причем ведут себя классы и стандартные модули совершенно так, как если бы были созданы в собственных файлах и имели собственное окно кода.

Теперь о структуре. Раньше она была для нас только вместилищем разнотипных данных. Оказывается, она может включать в себя и процедуры, и функции, и все другие элементы, вхожие в класс и стандартный модуль. Раньше мы писали ее только внутри других модулей. Оказывается (и теперь мы видим, что это только справедливо), ее можно писать в окне кода не внутри, а рядом с другими модулями. На том, какая от этого выгода, мы не будем останавливаться.

Теперь мы будем называть модулем не только то, что в проекте имеет свой файл и окно кода, а просто класс, стандартный модуль и структуру, где бы они не были записаны. Ну и форму, конечно, мы тоже будем называть модулем.

О строении кода в проекте. Структуру, как мы знаем, можно писать и внутри модулей. То же самое относится и к классу. Структуру и класс можно писать внутри любых модулей, а именно, классов, стандартных модулей и структур. А вот стандартный модуль нельзя писать внутри других модулей.

Имейте также в виду, что внутрь любого модуля могут входить процедуры и функции, а вот внутрь процедур и функций никакому модулю входить нельзя.

На том, почему одним можно, а другим нельзя, я не буду останавливаться. Всю эту экзотическую пока для вас информацию я дал не для того, чтобы вы пускались тут же вставлять классы в структуры и наоборот, а для того, чтобы лучше поняли материал следующего раздела. Более точно о строении кода в проекте вы узнаете в 27.1.



Всплывающая подсказка (ToolTip)


Вы уже привыкли к подсказкам, всплывающим в VS над ошибочными фрагментами кода. Такие же подсказки возникают, если мы поместим мышку над каким-нибудь инструментом из любой панели инструментов VS или, скажем, Microsoft Word или любого другого солидного приложения Windows.

Проекты, которые мы создаем – не что иное, как приложения Windows. Нам тоже нужно научиться подсказывать. Поместите на форму кнопку и текстовое поле. Создадим для каждого из этих элементов всплывающую подсказку (см. Рис. 18.15).

Рис. 18.15

Для этого возьмем из Toolbox элемент управления ToolTip и поместим в проект. Он разместится в нижней части проекта, подобно таймеру, под именем ToolTip1. Это будет наша подсказка для кнопки. Аналогично поместим в проект элемент ToolTip2. Это будет наша подсказка для текстового поля.

Подсказка – объект. Чтобы она заработала, нужно выполнить ее метод SetToolTip. У этого метода – два параметра: над каким элементом управления должна возникать подсказка и каков должен быть ее текст. Поместим обращение к этим методам в подходящую процедуру, например, в Form1_Load:

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

        ToolTip1.SetToolTip(Button1, "Рисуется круг диаметра, указанного в текстовом поле.")

        ToolTip2.SetToolTip(TextBox1, "Введите диаметр круга.")

End Sub

Запустите проект. Проверьте работу обеих подсказок.

Когда мы помещаем мышку над объектом, подсказка всплывает не сразу и видна не вечно. Время задержки перед появлением в миллисекундах определяет свойство InitialDelay, а время видимости – свойство AutoPopDelay.



Второй способ обработки событий: Оператор AddHandler


Создайте проект без кнопок. Введите такой код:

Dim b As New Button

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

        Me.Controls.Add(b)     'Добавляем новую кнопку в коллекцию

        AddHandler

 

b.Click,  AddressOf  Обработчик_нажатия_на_кнопку

End Sub

Private Sub Обработчик_нажатия_на_кнопку(ByVal На_кого_нажали As Object,  ByVal e As EventArgs)

        На_кого_нажали.Text = "Нажали"

        На_кого_нажали.Left = На_кого_нажали.Left + 30

End Sub

Поведение кнопки после запуска точно такое же, как и при 1 способе.

Пояснения: Вместо слова WithEvents  и фрагмента Handles b.Click мы пишем один оператор:

        AddHandler  b.Click,  AddressOf  Обработчик_нажатия_на_кнопку

Слово AddHandler  можно перевести как «Добавить обработчик». Слово AddressOf  указывает на процедуру-обработчик события, иначе обработчик невозможно было бы найти, ведь в заголовке самого обработчика ничего теперь не указывает на то, какое событие он должен обрабатывать. В целом оператор можно перевести так: «Добавить обработчик события b.Click, находящийся в процедуре Обработчик_нажатия_на_кнопку».

В общем, оператор AddHandler связывает событие с обработчиком. Обратите внимание, что выполняется оператор в процессе выполнения кода проекта и пока он не выполнился, обработка невозможна, то есть нажатие на кнопку ни к чему не будет приводить. Более того, связь эту можно в любое время убрать аналогичным оператором RemoveHandler, после чего кнопка прекратит реагировать на нажатия.  

К сожалению, никто нам здесь не предлагал автоматически создать заготовку обработчика.

Несколько обработчиков у одного события. Почему «Добавить» обработчик, а не «Создать»? Потому что обработчиков у одного события может быть много. Если вы хотите, чтобы при наступлении события выполнялась еще одна процедура-обработчик, просто напишите для нее еще один оператор AddHandler:

        AddHandler  b.Click,  AddressOf  Еще_один_обработчик_нажатия_на_кнопку


Один обработчик для нескольких событий. И при 2 способе одним обработчиком можно обрабатывать разные события и у разных объектов:

Dim b1, b2 As New Button

Dim t As New TextBox

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

        Me.Controls.Add(b1)

        Me.Controls.Add(b2)

        Me.Controls.Add(t)

        AddHandler b1.Click, AddressOf Обработчик_события_с_элементом_упр

        AddHandler b2.MouseEnter, AddressOf Обработчик_события_с_элементом_упр

        AddHandler t.Click, AddressOf Обработчик_события_с_элементом_упр

End Sub

Private Sub Обработчик_события_с_элементом_упр(ByVal С_кем_произошло_событие As Object,  _

ByVal Информация As EventArgs)

        С_кем_произошло_событие.Left = С_кем_произошло_событие.Left + 30

End Sub

Здесь путешествовать по форме будут две кнопки и одно текстовое поле, причем первая кнопка и текстовое поле при щелчке, а вторая кнопка – при наведении мыши.

 Слуга ста господ. Приятно, что при 2 способе можно обрабатывать события массива объектов, чего не позволяет 1 способ. Вспомним задачу о 40 графических полях с рожицами (15.8). Давайте сделаем так, чтобы щелчок по любому графическому полю заставлял его прыгнуть вверх на 10 пикселей. Вот программа:

Dim Рожица(40) As PictureBox

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

        Dim i As Integer

        For i = 1 To 40

            Рожица(i) = New PictureBox             'Создаем очередной объект

            Рожица(i).SizeMode = PictureBoxSizeMode.AutoSize

            Рожица(i).Image = Image.FromFile("FACE02.ICO")

            Рожица(i).Top = Me.Height - 50

            Рожица(i).Left = 20 * i

            Me.Controls.Add(Рожица(i))     'Добавляем новую рожицу в коллекцию формы

            AddHandler  Рожица(i).Click,  AddressOf Обработчик

        Next

End Sub

Private Sub Обработчик(ByVal На_кого_нажали As Object, ByVal e As EventArgs)

        На_кого_нажали.Top = На_кого_нажали.Top - 10

End Sub


Второй способ создания и обработки событий: AddHandler


Для иллюстрации этого способа дополним предыдущую задачу двумя компонентами. Первый. Пусть Господь наградил вас бдительной супругой, которая при вашей попытке потратить больше, чем имеете, выдает сообщение «Куда ты хочешь потратить наши последние деньги?!». Создадим для этого класс clsСупруга. Второй. Пусть в самом банке при наступлении такого прискорбного события на стол руководства банка ложится сообщение «Этот клиент ненадежен». Сообщение это будет порождать уже имеющийся класс clsСчет. Для краткости откажемся от сообщения в метке на форме. Вот код:

Форма:

Private Счет As New clsСчет

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Счет.Снятие_со_счета(Val(TextBox1.Text))

End Sub

Класс clsСчет:

Public Class clsСчет

    Private Супруга As New clsСупруга

    Private Сумма_на_счете As Decimal = 1000

    Private Event Не_хватает_денег()

    Public Sub New()

        AddHandler

Не_хватает_денег, AddressOf Обработчик_в_банке

        AddHandler

Не_хватает_денег, AddressOf Супруга.Обработчик_дома

    End Sub

    Public Sub Снятие_со_счета(ByVal Сумма_к_снятию As Decimal)

        If Сумма_на_счете - Сумма_к_снятию >= 0 Then

            Сумма_на_счете = Сумма_на_счете - Сумма_к_снятию

        Else

            RaiseEvent

Не_хватает_денег()

        End If

    End Sub

    Private Sub Обработчик_в_банке()

        MsgBox("Этот клиент ненадежен")

    End Sub

End Class

Класс clsСупруга:

Public Class clsСупруга

    Public Sub Обработчик_дома()

        MsgBox("Куда ты хочешь потратить наши последние деньги?!")

    End Sub

End Class

Пояснения: Здесь, как и при первом способе, программист сначала объявляет событие:

    Private Event

Не_хватает_денег()

а затем вставляет во все нужные места программы оператор RaiseEvent.

Дальше начинаются различия. Мы объявили наше событие как Private. Это значит, мы не хотим, чтобы его обрабатывали все, кому не попало. Теперь не получится объявлять объект класса clsСчет словом WithEvents. Обработчиков для нашего события мы назначаем сами при помощи операторов AddHandler. Эти обработчики могут находиться как в clsСчет, так и в других классах. В других классах их приходится объявлять Public, так как иначе их нельзя будет упомянуть в операторе AddHandler.

Заключение. В этом разделе я дал только основные наметки работы с событиями. Более квалифицированная работа включает в себя освоение понятия делегата и другие премудрости.



Ввод текста в текстовом редакторе Visual Studio NET


Ввод вашей программы в компьютер производится при помощи специальной программы, которая называется текстовым редактором и входит в состав VS. Программа на VB – это обычный текст, поэтому вам достаточно знать работу в текстовом редакторе с обычным текстом. Приемы работы в текстовом редакторе VS практически ничем не отличаются от приемов работы в других текстовых редакторах.

Чтобы научиться вводить текст, нужно потренироваться. Тренироваться мы будем, естественно, в VS.

Запустите VS и создайте новый проект, как это описано в 1.3.1.

Вводить текст мы будем не в окно кода, так как оно не очень удобно для тренировок, а в окно текстового файла. Откройте это окно. Для этого в главном меню выберите Project ® Add New Item ® в возникшем окне Add New Item выбираете Text File, даете ему имя, затем ® Open. Перед вами в окне проекта возникает большое пустое окно текстового редактора (Рис. П8)

Рис. П8

Это – наш лист, и на нем мы будем писать. Начнем!



Взаимодействие форм, модулей и классов в проекте


Несмотря на то, что до конкретного и солидного применения модулей и классов мы еще не добрались, необходимо рассмотреть взаимодействие, взаимные различия и сходство форм, модулей и классов. Без этого и применять их как следует мы не сможем.

Вникать будем на примере. Создайте проект из двух форм, двух модулей и класса. На первую форму поместите кнопку. В пять окон кода введите такой текст:

Первая форма:

Public Class Form1

    Inherits System.Windows.Forms.Form

Windows Form Designer generated code

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Debug.WriteLine(M2)

        Debug.WriteLine(Объект.C1)

        Debug.WriteLine(Форма2.F2)

        Процедура_модуля2()

        Объект.Процедура_класса()

        Форма2.Процедура_формы2()

        Форма2.Show()

    End Sub

End Class

Первый модуль:

Module Module1

    Public Форма2 As New Form2

    Public Объект As New Класс

End Module

Вторая форма:

Public Class Form2

    Inherits System.Windows.Forms.Form

Windows Form Designer generated code

    Public F2 As String = "F2"

    Public Sub Процедура_формы2()

        Объект.C1 = "Форма 2 изменила переменную С1 из Объекта"

        Debug.WriteLine(Объект.C1)

    End Sub

End Class

Второй модуль:

Module Module2

    Public M2 As String = "M2"

    Public Sub Процедура_модуля2()

        Форма2.F2 = "Модуль 2 изменил переменную F2 из формы 2"

        Debug.WriteLine(Форма2.F2)

    End Sub

End Module

Класс:

Public Class Класс

    Public C1 As String = "C1"

    Public Sub Процедура_класса()

        M2 = "Объект  изменил переменную M2 из модуля 2"

        Debug.WriteLine(M2)

    End Sub

End Class

Запустите проект, нажмите кнопку, проект напечатает:

M2

C1

F2

Модуль 2 изменил переменную F2 из формы 2

Объект изменил переменную M2 из модуля 2

Форма 2 изменила переменную С1 из Объекта

Сходство и различия будем прослеживать у трех модулей, а именно: второго стандартного модуля, второй формы и класса. Вы видите, что их код я написал почти одинаковым, если не считать, конечно, начальные строки кода у формы. Код всех этих трех модулей содержит объявление переменной и объявление процедуры. И та и другая объявлены, как Public, чтобы и той и другой можно было пользоваться из любого модуля проекта.


Когда форма, модуль и класс могут начать работу.

Если форма – стартовый объект, VB сам, без нашего вмешательства, и загружает ее и показывает на экране.

Стандартные модули тоже безо всяких забот программиста готовы к работе и использованию сразу после запуска проекта.

Это значит, что форма Form1 и оба стандартных модуля готовы к работе и использованию сразу после запуска проекта.

Что же касается класса и нестартовой формы, здесь дело другое. Кто их разбудит? В нашем проекте забота о том, чтобы они пришли в рабочее состояние, лежит на первом стандартном модуле. Взгляните на скромные две строки его кода.

Если мы хотим использовать класс как объект, то чтобы класс заработал, нам нужно из него создать объект,

что и делает оператор

    Public Объект As New Класс

Теперь о форме. Форма, оказывается, тоже класс. Чтобы убедиться в этом, достаточно взглянуть на первую строку окна ее кода. А раз так, то чтобы она заработала в проекте, из нее тоже должен быть создан объект и показан на экране. Если форма – стартовый объект, эти заботы незаметно для нас берет на себя VB,  а вот

если форма – не стартовый объект, программист должен сам создать ее как объект из ее класса, а затем показать на экране.

Первое делает оператор

    Public Форма2 As New Form2

а второе – оператор

        Форма2.Show()

Строки стандартного модуля, объявляющие переменные, выполняются автоматически,

поэтому нам не нужно опасаться, будут ли созданы объекты Объект и Форма2 к тому моменту, когда в них возникнет нужда. Будут. Я потому и записал две строки, их создающие, именно в стандартный модуль, потому что он сразу же после запуска проекта готов к работе, чего не скажешь о классе и форме (если она не стартовая, конечно).

Взаимные различия и сходство формы и класса. Когда вы в режиме проектирования работаете над формой Form2, добавляете в нее элементы управления, пишете код в окно ее кода, вы создаете и видите перед собой класс

(не объект) Form2. Когда же вы запускаете проект и выполняете оператор



    Public Форма2 As New Form2

вы порождаете из этого класса объект

Форма2, как экземпляр класса Form2. Форма (объект, а не класс) порождена и вы можете пользоваться ее переменными и процедурами. Но на экране она не видна. Чтобы ее увидеть и пользоваться ее элементами управления, вы должны выполнить оператор

        Форма2.Show()

Аналогично, когда вы в режиме проектирования пишете код в окне модуля класса, вы создаете класс, а когда  вы запускаете проект и выполняете оператор

    Public Объект As New Класс

вы порождаете из этого класса объект Объект, как экземпляр класса Класс.

В чем же отличие формы от класса? В том, что создатели VB для удобства программистов снабдили формы механизмом визуализации (см. 6.1.4). Это значит, что как в режиме проектирования, так и в режиме работы мы форму на экране видим. Специальные, скрытые от программиста методы рисуют и перерисовывают как форму, так и все ее элементы управления.

Класс же лишен механизма визуализации. Поэтому объекты, порожденные из него, невидимы. У них не может быть ни кнопок, ни меток, ни других элементов управления. Они, как серые кардиналы, действуют невидимо за кулисами и выполняют «умственную» работу. Когда же им захочется сделать что-то «видимое», они «дергают за ниточки» форму и ее элементы управления, а также рисуют на их поверхности то, что им заблагорассудится (подобно тому, как привидения вызывают полтергейст.)

Грамматика обращения к переменным и процедурам. Ниже под модулем я понимаю или форму, или класс, или стандартный модуль.

Если переменная или процедура объявлены с помощью слова Public, то они видны и доступны не только из своего модуля, но и из других модулей проекта.

Внимательно просмотрите код процедуры Button1_Click первой формы и коды процедур в остальных модулях и обратите внимание на то, как грамматически происходит обращение к переменным и процедурам разных модулей. Вы установите следующее:

Чтобы обратиться к переменной или процедуре, объявленным в стандартном модуле, достаточно написать имя переменной или процедуры без указания имени модуля.



Чтобы обратиться к переменной или процедуре, объявленным в классе, нужно перед их именем писать имя объекта этого класса с точкой.

Чтобы обратиться к переменной или процедуре, объявленным в форме, нужно перед их именем писать имя экземпляра класса этой формы с точкой.

Перед именем переменной или процедуры, объявленной в стандартном модуле, имя модуля писать не запрещено. Это бывает даже необходимо, когда в нескольких стандартных модулях объявлены переменные или процедуры с одинаковыми именами. Иначе их не различишь.

Все вышесказанное касается случая, когда нужно обратиться к переменной или процедуре, объявленной в чужом модуле. Когда же нужно обратиться к переменной или процедуре, объявленной в своем модуле, все просто и привычно: пишите имя переменной или процедуры и все. Иногда, правда, бывает удобно или даже необходимо указать имя хозяина или Me. Это не возбраняется (правда, в случае стандартного модуля слово Me писать нельзя).

Ниже для примера приведены разные способы обращения к собственной переменной, дающие один и тот же результат:

Вторая форма:

    Public F2 As String = "F2"

    Public Sub Процедура_формы2()

        Debug.WriteLine(F2)

        Debug.WriteLine(Me.F2)

        Debug.WriteLine(Форма2.F2)

    End Sub

Второй модуль:

    Public M2 As String = "M2"

    Public Sub Процедура_модуля2()

        Debug.WriteLine(M2)

        Debug.WriteLine(Module2.M2)

    End Sub

Класс:

    Public C1 As String = "C1"

    Public Sub Процедура_класса()

        Debug.WriteLine(C1)

        Debug.WriteLine(Me.C1)

        Debug.WriteLine(Объект.C1)

    End Sub

Из текста процедуры Button1_Click становится также ясно, что с переменными и процедурами формы можно работать еще до того, как форма появилась на экране.


Взаимодействие программ


Важно помнить, что компьютер работает по программе не только тогда, когда выполняет нашу только что разобранную программу умножения, но и до и после этого. Спрашивается, по какой же программе он работает, когда не выполняет нашу. Рассмотрим упрощенно, что происходит в период между моментом включения компьютера и  моментом начала выполнения нашей программы.

Внутри компьютера в специальном постоянном запоминающем устройстве находится программа самопроверки компьютера. Как только вы включаете компьютер, он всегда начинает выполнять именно ее. Если в результате ее выполнения компьютер решит, что его здоровье и умственные способности в порядке, он продолжает работу и первым делом обязательно переписывает (загружает) в память с жесткого диска (о котором подробнее – позже) основную часть так называемой операционной системы (ОС) – комплекса служебных программ, предназначенного для того, чтобы обеспечить человеку и созданным им программам нормальную работу на компьютере. На вашем компьютере ОС – это Windows. Загрузив Windows, компьютер сразу же переходит к ее выполнению. Сделав свои маленькие дела, Windows останавливается и ждет указаний от человека, что ей делать дальше. Вы решаете, например, что вам нужно работать с VB, и несколькими щелчками мыши приказываете Windows запустить VS в работу. После этого процессор переходит к выполнению тех команд Windows, которые «осознают» ваше указание и выполняют его, в результате чего загружают большую комплексную программу, которой является VS, с жесткого диска в память и запускают эту программу на выполнение.

Важно понимать, что запуск на выполнение целой программы – VS явился результатом выполнения очередной команды другой программы – Windows (говорят – Windows вызывает VS или управление передается программе VS). От того, что начала выполняться VS, Windows не ушла в небытие, она осталась в памяти, притаилась и ждет, когда VS, как и положено каждой порядочной программе, решив поставленные человеком задачи, закончит свою работу и уступит место. В этот момент Windows как ни в чем не бывало продолжит свою работу (говорят - управление возвращается к Windows). Выполнив несколько следующих своих команд и поделав маленькие свои дела, Windows снова приказывает компьютеру ждать указаний от человека, что ему делать дальше. На этот раз человек может пожелать поиграть в какую-нибудь игру. Windows загружает с винчестера в память и затем запускает программу этой игры. После окончания игры управление снова возвращается к Windows и т.д. Так и проходит с утра до вечера работа на компьютере: после выполнения очередного желания человека Windows получает управление, выполняет некоторую подготовительную работу (чистит память и т.п.) и снова ждет от человека новых пожеланий.


А теперь рассмотрим подробнее период между запуском программы VS и завершением ее работы. VS берет пример с Windows. Получив управление, она выполняет некоторые подготовительные действия и останавливается, ожидая действий человека. Вы вводите с клавиатуры на языке VB свою программу умножения, а VS отправляет ее в память. VS все время настроена мгновенно реагировать на действия человека. Вы в любой момент можете пожелать исправить свою программу, запустить ее на выполнение, сохранить на диске и т.д. Предположим, вы приказываете выполнить программу. Тогда VS, проанализировав ваш приказ, выполняет вашу программу, то есть происходит примерно то, что я подробно описал ранее.

Обратите внимание на то, сколько программ находится в оперативной памяти во время выполнения вашей программы умножения. Во-первых, это Windows, которая ждет, когда вам надоест работать в VS. Во-вторых, это VS, которая выполняет вашу программу умножения, а выполнив, будет ждать от вас дальнейших приказов. И в-третьих, это сама ваша программа умножения.

Таким образом, в памяти одновременно находится как минимум три программы. На самом деле их там находится несколько десятков, так как я не упомянул о множестве служебных программ, которые входят в состав Windows и запускаются вместе с ней. К тому же, Windows является многозадачной ОС. Это значит, что вы можете запустить и одновременно работать с несколькими программами. Например, вдобавок к VS вы можете запустить Word и пару игр, расположить их окна на экране и, работая в Word, краем глаза наблюдать, как сражаются герои игр.

Это обычная практика работы большинства ОС: в памяти одновременно находится большое число программ. Во многих из них есть команды, которые передают управление другим программам, а затем получают его обратно. Такая передача управления происходит постоянно и зачастую автоматически, без ведома человека. Представьте себе детей, играющих в мяч и перекидывающих его друг другу. Дети – программы, мяч – компьютер. Каждый ребенок может делать с мячом, что хочет – бросить другому ребенку, погладить, проткнуть гвоздем. Отсюда вывод – не связывайтесь с подозрительными программами!

Начинающий программист может ничего этого и не знать. Ему достаточно знать те несколько клавиш, которые он должен нажать, чтобы добраться до VS и производить там элементарные действия – ввод программы, ее исправление, запуск и т.п.


Взаимодействие устройств компьютера Работа процессора


Рассмотрим порядок, в котором обычно обмениваются информацией устройства компьютера во время выполнения программы. Пусть мы только-что придумали программу для перемножения двух чисел, одно из которых находится на дискете, а другое должно вводиться с клавиатуры. Вот алгоритм программы:

Ввести число с дискеты

Ввести число с клавиатуры

Перемножить эти числа

Показать результат на мониторе

Пусть мы придумали эту программу на языке VB и теперь хотим ее выполнить на компьютере. Для этого Visual Studio .NET должна уже быть установлена на жестком диске нашего компьютера. (Напомню, что для краткости я обозначаю Visual Studio .NET как VS.) Мы включаем компьютер, он несколько секунд готовится к работе, после чего мы нажимаем несколько клавиш. VS с жесткого диска через шину переписывается (загружается) в память. После этого VS дает нам понять, что готова принимать от нас программу, и мы эту программу на языке VB набираем на клавиатуре.

Все компьютеры могут выполнять программу только тогда, когда она находится в оперативной памяти.

В соответствии с этим требованием наша программа автоматически по мере ввода с клавиатуры отправляется в память и там запоминается. Примерно в это же время мы вставляем в дисковод дискету с одним из двух перемножаемых чисел. Как только вся программа введена, мы нажатием клавиши приказываем компьютеру ее выполнить. И вот что в общих чертах происходит дальше.

Программа компилируется. Допустим для простоты и для соответствия с алгоритмом, что в нашем случае после компиляции программа состоит из 4 команд на машинном языке, смысл которых соответствует алгоритму.

После этого программа на машинном языке выполняется. Начинается выполнение с того, что процессор приказывает памяти послать ему по шине первую команду программы.  После того, как эта команда (ввод числа с дискеты) пришла в процессор, он «осознает» ее и отдает приказы устройствам компьютера на ее выполнение в соответствии с тем, как он ее осознал. В нашем случае он отдает приказ дисководу прочесть с дискеты число (пусть это было число 3) и направить его по шине в оперативную память, а оперативной памяти приказывает принять это число и запомнить. Как только число запомнилось, процессор считает команду выполненной и приказывает памяти послать ему вторую команду (которой оказался ввод числа с клавиатуры). Осознав ее, он приказывает компьютеру остановиться и ждать, когда человек введет с клавиатуры какое-нибудь число. Введенное число (пусть это будет  -0.25), процессор тоже приказывает направить в память. После этого он принимает из памяти третью команду (умножение). Внутри процессора имеется арифметическое устройство – своеобразный автоматический карманный калькулятор, способный выполнять четыре действия арифметики. Процессор приказывает памяти послать ему по шине оба числа, перемножает их, после чего запоминает результат сам или отправляет его в память (предположим, он выбрал память). Наконец, он получает из памяти последнюю команду, согласно которой приказывает памяти же отправить результат (-0.75) на так называемую видеокарту, а той – принять результат и изобразить его на мониторе. На этом выполнение программы заканчивается, VS останавливается и ждет от человека ввода новой программы или исправления старой.

Итак, мы видим, что работа процессора состоит в том, чтобы считывать из памяти по порядку команды программы, осознавать их, после чего выполнять их самому или приказывать выполнить другим устройствам.


Работа оперативной памяти состоит в том, чтобы хранить программу во время ее выполнения, а также принимать от любых устройств, запоминать и отправлять в любые устройства любую информацию, с которой работает программа и которую укажет процессор. Такая информация, в отличие от программы, называется данными. В нашем случае данными являются числа 3 и -0.25 (это исходные данные решения задачи), а также ?0.75 (это данное является результатом). Программа – это предписание того, что нужно делать с исходными данными, чтобы получить результат, а данные - это информация, над которой производит действия программа и которая зачастую в программе не содержится. Так. в нашей программе нигде не заданы значения перемножаемых чисел. Оба они находятся совсем в другом месте – одно на дискете, другое – в голове человека.

Если программа предназначена для сложения 10000 чисел, записанных на дискете, то данными будут эти 10000 чисел. Если программа предназначена для подсчета количества слов в тексте рассказа, вводимого с клавиатуры, то данными будет этот текст. Если программа предназначена для распечатки на принтере изображения с экрана монитора, то данными будет изображение. Если программа предназначена для распознавания речи, вводимой в компьютер с микрофона, то данными будет звук. В подавляющем большинстве случаев данные во время их обработки хранятся в оперативной памяти.

Взаимодействие различных устройств компьютера можно уподобить взаимодействию нескольких заводов, стоящих вдоль скоростного шоссе (шины) и производящих друг для друга различную продукцию (информацию). При этом память – это не завод, а большой перевалочный склад. А на заводах собственных складов нет или они маленькие. Пусть сегодня один завод произвел для другого большое количество деталей, которое другой завод будет использовать в течение целого месяца. Целиком все детали этот второй завод сегодня забирать не будет, потому что ему складывать их некуда. Первому заводу их тоже негде хранить. Тогда первый завод везет все детали на перевалочный склад, откуда второй завод будет их понемножку забирать по мере надобности.



Windows API


Классы библиотеки классов .NET Framework обладают множеством методов, пользуясь которыми программист выполняет те или иные действия над элементами проектируемого приложения. Операционная система Windows тоже обладает множеством функций, пользуясь которыми программист может выполнять действия над элементами Windows. Это множество функций Windows называется Windows API. Многие функции Windows API позволяют выполнять действия, недоступные методам библиотеки классов .NET Framework. Эти функции относятся скорее к операционной системе Windows, а не к VB. Например, это перезагрузка компьютера или установка фона рабочего стола. VB предоставляет возможность прямо из проекта пользоваться функциями Windows API. Однако делать это не так просто, как использовать функции библиотеки классов .NET Framework.

Функции Windows API расположены в библиотеках динамической компоновки – файлах с расширением dll.

Использование в VB функций Windows API я проиллюстрирую на двух примерах.

Beep. В Windows API имеется функция MessageBeep, действие которой состоит в генерации нескольких стандартных звуков Windows. Один из них – это наш знакомый Beep. Несмотря на то, что мы умеем его генерировать стандартным методом библиотеки классов .NET Framework, покажу для иллюстрации грамматики вызова, как это делать через Windows API.

Создайте проект с кнопкой. Вот код:

Declare  Function  Звук  Lib  "User32"  Alias  "MessageBeep" (ByVal A As Long)  As Long

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Звук(-1)

End Sub

Запустите проект. Нажмите кнопку – раздастся наш знакомый Beep.

Пояснения: Первая строка кода объявляет внешнюю

для VB функцию из библиотеки динамической компоновки. Об этом говорит ключевое слово Declare. Мы должны заранее знать точное имя этой функции и указать его в двойных кавычках после ключевого слова Alias. Мы должны заранее знать имя библиотеки динамической компоновки, в которую входит нужная нам функция, и указать его в двойных кавычках после ключевого слова Lib. Мы можем для собственного удобства придумать другое имя для этой функции, которым и будем пользоваться в проекте (я придумал Звук). Мы должны знать заранее смысл и тип параметров функции и указать их в объявлении. У нас это единственный параметр типа Long, который определяет конкретный звук. Для звука Beep этот параметр равен  -1.


Итак, как видите, многое нужно знать заранее. А откуда эти знания взять?

Определяем свободное место на диске. В библиотеке kernel32.dll  Windows API имеется функция GetDiskFreeSpaceA, при помощи которой можно определить свободное место на диске. Создайте проект с кнопкой. Вот код:

Declare Function Свободное_место Lib "kernel32.dll" Alias "GetDiskFreeSpaceA" _

             (ByVal Диск As String, ByRef Секторов_в_кластере As Integer, ByRef Байтов_в_секторе As Integer, _

             ByRef Свободно_кластеров As Integer, ByRef Всего_кластеров As Integer) As Integer

Private Sub Button9_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button9.Click

    Dim Секторов_в_кластере, Байтов_в_секторе, Свободно_кластеров, Всего_кластеров As Integer

    Свободное_место("E:\", Секторов_в_кластере, Байтов_в_секторе, Свободно_кластеров, Всего_кластеров)

    Debug.WriteLine(Секторов_в_кластере)

    Debug.WriteLine(Байтов_в_секторе)

    Debug.WriteLine(Свободно_кластеров)

    Debug.WriteLine(Всего_кластеров)

    Debug.WriteLine("Свободно байтов - " &   _

CLng(Свободно_кластеров) * Секторов_в_кластере * Байтов_в_секторе)

End Sub

Запустите проект. Нажмите кнопку. VB напечатает:

8

512

6313557

10004470

Свободно байтов - 25860329472

Пояснения: На физическом уровне место под информацию на дисках отводится в так называемых кластерах. Кластеры делятся на сектора, сектора – на байты. Кластер может быть занят информацией или свободен. Чтобы узнать, сколько на диске имеется свободных байтов, нужно, как вы уже догадались, умножить число свободных кластеров на число секторов в кластере и на число байтов в секторе.

Функция имеет 5 параметров. Само значение функции нас никак не интересует, а интересуют некоторые параметры. Первый параметр указывает имя интересующего нас диска, смысл остальных параметров ясен из их имен.

В этом примере я обращаюсь к функции Свободное_место, чтобы узнать количество свободных байтов на диске E. Поскольку их слишком много, я, чтобы не было переполнения типа Integer, преобразую один из сомножителей в тип Long.

Итоги. Просуммирую кратко те трудности, с которыми мы столкнулись при использовании функций Windows API. Нужно знать название нужной вам функции Windows API и название библиотеки dll, куда она входит. Нужно хорошо представлять работу функции, смысл и тип ее параметров. Для этого нужно иметь представление о программировании в Windows и держать перед глазами соответствующие учебники и справочники. Вы можете найти информацию об этих функциях и в системе Help, если установили VS в полном объеме. Правда, информация эта приведена для языка Си и предполагает, что вы уже достаточно знаете о программировании в Windows.


Задаем ширину и названия столбцов DataGrid


Каждый раз после запуска проекта все столбцы DataGrid имеют одинаковую ширину и нам приходится ее подправлять, что утомительно. Исправить положение нам поможет такой код, задающий ширину столбцов и их названия:

        Dim Стиль As New DataGridTableStyle

        Стиль.MappingName = DataSet11.Книги.TableName

        DataGrid1.TableStyles.Add(Стиль)

        DataGrid1.TableStyles(0).GridColumnStyles("Код").Width = 0

        DataGrid1.TableStyles(0).GridColumnStyles("Nazvanie").Width

= 200

        DataGrid1.TableStyles(0).GridColumnStyles("Nazvanie").HeaderText

= "Название"

        DataGrid1.TableStyles(0).GridColumnStyles("Kol_str").HeaderText

= "Число стр."

В результате сетка будет выглядеть, как на Рис. 24.28.

Рис. 24.28

Пояснения: Настройка названий, ширины столбцов и других параметров сетки DataGrid осуществляется в объекте класса DataGridTableStyle. Поскольку одну сетку мы можем использовать последовательно для показа нескольких таблиц, то и объектов таких может быть несколько. Они объединяются в принадлежащую сетке коллекцию TableStyles. В каждом таком объекте есть коллекция стилей столбцов GridColumnStyles, где и осуществляются настройки конкретных столбцов.

Первая строка фрагмента создает стиль – объект класса DataGridTableStyle. Вторая указывает имя (TableName) таблицы, к которой следует относить этот стиль, для чего присваивает его свойству MappingName данного стиля. Третья строка добавляет созданный стиль в коллекцию стилей нашей сетки DataGrid1. Остальные четыре строки устанавливают ширину и названия некоторым столбцам сетки. Установив столбцу Код нулевую ширину, мы скрыли его из вида.



Задание на справочную систему


Сначала создадим игрушечный проект приложения Windows. Он нам нужен только для того, чтобы создать для него справочную систему. Весь проект состоит из кнопки на форме, а работа его заключается в том, что при нажатии на кнопку раздается сигнал Beep.

Из этого проекта должна вызываться справочная система, внешний вид которой вы видите на Рис. 26.1 и ниже.

Рис. 26.1

Слева вы видите Оглавление (Contents), справа – содержание любого выделенного в оглавлении пункта. В оглавлении ниже выделенного пункта «Предварительные замечания» вы видите раздел «Работа с программой», который делится на два пункта: «Как извлечь звук» и «Как выйти из программы». Вот их содержание:

Как получить звук

Чтобы сгенерировать звук, нажмите кнопку Button1.

Как выйти из программы

Чтобы выйти из программы, нажмите крестик в верхнем правом углу окна программы.

Заканчивается оглавление пунктом «Заключение»:

Заключительные замечания

Я надеюсь, что наша справка помогла вам узнать, как генерировать звук и выходить из программы.

Рядом с закладкой Contents находится закладка Index. Это алфавитный указатель.



Запись в текстовый файл


Задача 1: Требуется записать слово, число и дату в текстовый файл с именем Filimon.txt, располагающийся в какой-нибудь папке на каком-нибудь диске вашего компьютера (я выбрал папку VB на диске E:).

Решение: Прежде всего заметьте, что файла Filimon.txt в вашей папке скорее всего нет. Ну и не надо. Не создавайте его. Он будет создан автоматически. Вот программа:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        'Создаем объект для записи информации в текстовый файл  E:\VB\Filimon.txt:

        Dim Запись As New System.IO.StreamWriter("E:\VB\Filimon.txt")

        'Записываем в файл 3 строки:

        Запись.WriteLine("Азия")

        Запись.WriteLine(5.27)

        Запись.WriteLine(#3/29/2005 9:30:00 PM#)

        'Записываем в файл 4-ю строку:

        Dim a As Integer = 10

        Запись.WriteLine(a + 2)

        Запись.Close()                                                    'Закрываем файл

End Sub

Пояснения: Прежде чем начать работать с файлом, нужно взять в руки подходящий инструмент. Для нашей задачи как нельзя лучше подойдет объект класса StreamWriter, обеспечивающий удобную запись в текстовые файлы. Мы его и создаем первым оператором нашей процедуры. Мы придумали объекту имя Запись, а в качестве параметра конструктора указали адрес файла. Впрочем, вариантов конструктора довольно много и можно было указать другие параметры. Но на них мы останавливаться не будем.

Говорят, что при выполнении этого оператора файл открывается для записи. При этом компьютер выполняет определенные подготовительные действия для работы с файлом. Если файл раньше не существовал, то он создается. Если файл открывается для записи, то магнитная головка перемещается в начало файла. Если в файле было раньше что-то записано, то все стирается. Открывать можно одновременно несколько файлов.

Следующие 3 оператора процедуры записывают в файл 3 строки. Запись осуществляется методом WriteLine объекта Запись. Параметрами методов в VB могут быть не только литералы, но и переменные, и выражения. Чтобы напомнить вам этот общеизвестный факт, я записал в файл еще и 4-ю строку.

Информация в наш файл может записываться только по порядку, последовательно. Мы не можем записать что-то сперва в начало файла, потом в конец, потом в середину. То же самое относится и к считыванию, о котором вскоре пойдет речь.

После завершения записи в файл его нужно закрыть методом Close.

Заглянем в текстовый файл. Давайте убедимся, что все действительно правильно записалось. Для этого выйдем из VS в Windows, найдем нужную папку и обнаружим, что там действительно находится файл Filimon.txt. Чтобы заглянуть в него, щелкните по нему дважды мышкой, он откроется программой Notepad (Блокнот) и вы увидите, что он содержит 4 строки:

Азия

5,27

29.03.2005 21:30:00

12

Обратите внимание, что в компьютере формат данных был преобразован применительно к российским стандартам. В таком виде информация и была записана в строки файла.



Заполняем TreeView в коде


Предоставим пользователю нашего приложения возможность, запустив проект, самому заполнять произвольное дерево, причем так, как это делается в Редакторе вершин дерева. Для этого достаточно запрограммировать работу 4 кнопок, которые вы видите на Рис. 20.16. Все кнопки действуют по отношению к любой выделенной вершине и смысл их ясен без объяснений. Текстовое поле служит для ввода названия вершины.

Но предварительно мы должны кое-что о дереве узнать. Сначала о терминологии.

Вершина – человек. От человека тянутся вниз и направо ветки к его сыновьям (так называемые дочерние вершины – Child). По отношению к сыновьям родитель является родительской вершиной (Parent). Адам – корень. Нажимая в режиме проектирования кнопку Add Root, мы можем добавить к дереву сколько угодно корней. Братья по отношению друг к другу называются Sibling.

Свойства и методы дерева и вершин. Каждая вершина является объектом класса TreeNode. Каждая вершина обладает свойством – коллекцией Nodes – это дочерние вершины (только дочерние, без внуков, правнуков и более отдаленных потомков). Сколько вершин на дереве – столько и коллекций. Но некоторые коллекции – пустые (у кого-то нет детей). Каждый элемент коллекции – это тоже объект класса TreeNode, то есть вершина. Вот такая, довольно сложная, но стройная картина получается.

У каждой вершины есть метод Remove – он уничтожает вершину со всеми ее потомками. Есть свойство Parent – это родительская вершина. Каждую вершину можно выделить своим цветом фона (свойство BackColor) и цветом шрифта (свойство ForeColor). Метод Expand осуществляет нажатие на плюсик у вершины, то есть показывает ее дочерние вершины. Метод ExpandAll показывает не только дочерние вершины, но всех потомков.

Также у вершины есть метод GetNodeCount, который сообщает нам количество вершин-потомков данной вершины. У метода есть булевский параметр: True указывает, что считать нужно всех потомков, False – только дочерние вершины.

Методы Expand и ExpandAll, а также GetNodeCount есть и у дерева TreeView. Здесь True указывает, что считать нужно все вершины дерева, False – только корни.


У дерева TreeView есть также свойство SelectedNode. Оно имеет своим значением выделенную вершину.

Простой вариант программы. Создайте проект с TreeView, 4 кнопками и текстовым полем. TreeView не заполняйте, он должен до запуска проекта быть пустым. Введите в окно кода следующие 5 процедур. Сразу же оговорюсь, что текст процедур данного варианта максимально упрощен. Я жертвовал целесообразностью и надежностью ради понятности:

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

        TV.Nodes.Add("Адам")

End Sub

Private Sub Создай_сына_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Создай_сына.Click

        Dim Вершина As New TreeNode(TextBox1.Text)

        TV.SelectedNode.Nodes.Add(Вершина)

End Sub

Private Sub Создай_брата_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Создай_брата.Click

        Dim Вершина As New TreeNode(TextBox1.Text)

        TV.SelectedNode.Parent.Nodes.Add(Вершина)

End Sub

Private Sub Переименуй_Click(ByVal sender As Object, ByVal e As EventArgs)  Handles Переименуй.Click

        TV.SelectedNode.Text = TextBox1.Text

End Sub

Private Sub Удали_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Удали.Click

        TV.SelectedNode.Remove()

End Sub

Пояснения: У коллекции Nodes, как и у всякой коллекции, есть метод Add, который добавляет в коллекцию элемент – объект класса TreeNode. В качестве параметра метода мы можем указать свойство Text

этого объекта (в процедуре Form1_Load мы указали "Адам") или заранее созданный объект класса TreeNode (так мы делаем в других процедурах проекта).

Первой выполняется процедура Form1_Load. В пустом дереве она создает вершину Адам. Смысла в этом нет, но мне так проще объяснять.

Не надо сразу же нажимать на кнопку Удали. Поскольку кроме Адама других вершин на дереве нет, именно она и будет выбранной (выделенной темным цветом), а значит будет удалена процедурой Удали_Click. Дерево опустеет и дальше нам не с чем будет работать.



Переименуйте эту вершину, набрав текст в текстовом поле и нажав кнопку Переименуй. Поскольку кроме Адама других вершин на дереве нет, именно она и будет выбранной, а значит будет переименована процедурой Переименуй_Click.

Щелкните по кнопке Создай сына. Первым выполнится оператор

        Dim Вершина As New TreeNode(TextBox1.Text)

Он создает объект Вершина класса TreeNode. Свойство Text

этого объекта определяется значением параметра. У нас это текст в текстовом поле.

Вторым выполнится оператор

        TV.SelectedNode.Nodes.Add(Вершина)

Поскольку выделен Адам, то к коллекции Nodes его сыновей (пустой пока), добавляется только что созданная вершина, что вы и увидите сразу же на экране, щелкнув по появившемуся плюсику. Щелкните по кнопке Создай сына несколько раз. На экране вы увидите несколько сыновей Адама.

Выделите одного из сыновей и щелкните по кнопке Создай сына. Теперь выделен сын и поэтому вершина будет добавлена к коллекции Nodes сына, а не Адама. На экране появится внук.

Выделяя того или иного из сыновей или внуков, щелкайте по кнопке Создай сына, предварительно вводя разный текст в текстовое поле. Вы увидите, как на дереве появляются внуки, правнуки и более отдаленные потомки.

Кнопка Создай брата вроде бы ни к чему, и без нее все работает. Но она мне пригодилась для объяснения свойства Parent. Выделите одну из вершин (но не корень) и нажмите кнопку. В операторе

        TV.SelectedNode.Parent.Nodes.Add(Вершина)

мы видим вот что:

TV.SelectedNode

Выбранная вершина

TV.SelectedNode.Parent

Родитель выбранной вершины

TV.SelectedNode.Parent.Nodes

Коллекция сыновей родителя выбранной вершины, то есть братья выбранной вершины

Надежный вариант программы. Наша программа делает далеко не все, что нужно, и совсем не предохранена от «неправильных» щелчков по кнопкам. Так, нам не удастся создать брата Адаму. Удалив Адама, мы окажемся не у дел. И так далее. Пишем надежный вариант программы:

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



        Dim Вершина As TreeNode

        For Each Вершина In TV.Nodes

            Вершина.Remove()

        Next

End Sub

Private Sub Создай_сына_Click( ByVal sender As Object, ByVal e As EventArgs)  Handles Создай_сына.Click

        Dim Вершина As New TreeNode(TextBox1.Text)

        If TV.GetNodeCount(True) = 0 Then

            TV.Nodes.Add(Вершина)

            TV.Focus()

        ElseIf TV.SelectedNode Is Nothing Then

            MsgBox("Выберите вершину")

        Else

            TV.SelectedNode.Nodes.Add(Вершина)

            Вершина.Parent.Expand()

        End If

End Sub

Private Sub Создай_брата_Click(ByVal sender As Object, ByVal e As EventArgs)  Handles Создай_брата.Click

        Dim Вершина As New TreeNode(TextBox1.Text)

        If TV.GetNodeCount(True) = 0 Then

            TV.Nodes.Add(Вершина)

            TV.Focus()

        ElseIf TV.SelectedNode Is Nothing Then

            MsgBox("Выберите вершину")

        ElseIf TV.SelectedNode.Parent Is Nothing Then

            TV.Nodes.Add(Вершина)

        Else

            TV.SelectedNode.Parent.Nodes.Add(Вершина)

        End If

End Sub

Private Sub Переименуй_Click(ByVal sender As Object, ByVal e As EventArgs)  Handles Переименуй.Click

        If TV.GetNodeCount(True) = 0 Then

            MsgBox("Дерево пусто")

        ElseIf TV.SelectedNode Is Nothing Then

            MsgBox("Выберите вершину")

        Else

            TV.SelectedNode.Text = TextBox1.Text

        End If

End Sub

Private Sub Удали_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Удали.Click

        If TV.GetNodeCount(True) = 0 Or TV.SelectedNode Is Nothing Then Exit Sub

        TV.SelectedNode.Remove()

End Sub

Пояснения: Процедуре Form1_Load я придумал другое дело – теперь она очищает дерево, если вы его нечаянно заполнили в режиме проектирования. Поскольку метод Remove уничтожает вершину со всеми ее потомками, достаточно уничтожить все корни дерева, то есть элементы коллекции TV.Nodes, что и делается.



Теперь разберем процедуру для кнопки Переименуй. Ее работа – ключ к пониманию работы других процедур. Переименовывать имеет смысл только тогда, когда дерево не пусто и когда на дереве имеется выделенная вершина. Оператор If в этой процедуре создан для обработки именно такой ситуации, и его можно перевести так:

      Если общее количество вершин дерева = 0, то

             Сообщай, что дерево пусто

      Иначе если не существует выбранной вершины, то

             Сообщай, что надо бы выбрать вершину

      Иначе

             Переименовывай вершину.

Если объект не существует, то в VB говорят, что он «есть ничто»,  по-английски –

Is  Nothing

Поэтому выражение

TV.SelectedNode Is Nothing

можно перевести, как «выбранная вершина есть ничто» или «выбранная вершина не существует».

Логика остальных кнопок ясна из их кода. Кнопки Создай сына и Создай брата при пустом дереве добавляют корень, как я и хотел. Строка

            TV.Focus()

возвращает фокус на дерево, что удобно, но не обязательно.

В процедуре для кнопки Создай брата строка

        ElseIf TV.SelectedNode.Parent Is Nothing Then

обрабатывает следующую ситуацию: Если у выбранной вершины не существует родителя, значит это корень, значит брат ему – тоже корень, значит создавать этого брата придется строкой

            TV.Nodes.Add(Вершина)

В процедуре для кнопки Создай сына строка

            Вершина.Parent.Expand()

избавляет от необходимости жать плюсик на родителе, чтобы увидеть только что созданного сына.

Прочее. Теперь было бы неплохо, чтобы работа пользователя по созданию дерева не пропала даром, а для этого нужно дать ему возможность сохранить созданное дерево, а потом загрузить. Но это нетривиальная задача. Отложите ее на будущее. Я ее разбирать не буду, но идею ее решения вы можете почерпнуть в следующем подразделе.


Запросы к базе данных


Посмотрим, как находить в базе данных нужную информацию. Удобнее всего применять для этого так называемые запросы.

Простой запрос. Пусть нам нужно в таблице «Книги» найти все книжки толще 200 страниц. Для этого в окне базы (Рис. 24.1) в списке Объекты выберите Запросы, а затем выберите Создание запроса в режиме конструктора. Появившееся окно (Рис. 24.8) спрашивает, к какому источнику данных делается запрос.

Рис. 24.8

У нас нет других источников, кроме таблицы «Книги», поэтому выбираем его и нажимаем кнопки Добавить, а затем Закрыть. Мы видим перед собой окно конструктора запроса (Рис. 24.9).

Рис. 24.9

В строке Поле мы выбираем те поля, которые хотим видеть в запросе. Как видите, мы не пожелали утомлять глаза лицезрением полей Код и Data. В строке Условие отбора мы указываем условие (критерий) отбора записей в запросе. В нашем случае мы пишем под полем  Kol_str  условие  >200.

Закрываем окно крестиком. Access предложит вам сохраниться. Соглашайтесь. В открывшемся окне введите имя запроса, например, «Толстые книги» и нажмите ОК. Запрос создан. В знак этого в Запросах появится значок этого запроса. Сделайте по нему двойной щелчок и можете наблюдать в окне запроса результаты (Рис. 24.10).

Рис. 24.10

Вы видите, что после того, как запрос выполнен, он представляет нам искомую информацию в табличном виде. Но запрос – это не таблица. Таблица – это кладовая исходной информации, а запрос –удобный вид на нужную нам часть этой информации, это производная информация.

Закройте запрос. Снова перейдите в Таблицы и зайдите в таблицу «Книги». Добавьте какую-нибудь толстую книгу, закройте таблицу и снова откройте запрос. Он изменился, включив в себя новую книжку. Таким образом, запросы автоматически пересоздаются при их открытии.

Помните, что при закрытии таблицы она сохраняется автоматически, не выдавая запроса на сохранение.

Обратите внимание, что в окне запроса вы можете менять информацию, удалять и добавлять записи, как вы это делали в исходной таблице. Изменения тут же отразятся в исходной таблице. Это удобно, хотя и заставляет быть внимательным.


Из запроса вы можете переходить в его конструктор и обратно подобно тому, как вы переходили из таблицы в ее конструктор и обратно.

Еще один запрос. Создайте еще один запрос – «Старые книги». Его конструктор вы видите на Рис. 24.11.



Рис. 24.11

Условие приказывает отобразить в запросе все книги, изданные раньше 1960 года. Обратите внимание, что в строке Сортировка  в поле Data я выбрал сортировку По убыванию, поэтому в окне запроса самые старые книги будут внизу.

Указав в новом конструкторе запроса в поле Data условие #30.09.1848#, мы получим в результате в окне запроса единственную книжку – «Белые ночи» Достоевского.

Указав в новом конструкторе запроса в поле Avtor условие "Стругацкие", мы получим в результате в окне запроса две книжки Стругацких.

Сложный запрос. Условие отбора может быть сложным, включать в себя знаки сравнения, логические операции, функции языка Visual Basic for Applications и имена полей в квадратных скобках.

Условия можно писать одновременно под несколькими полями. В этом случае они считаются соединенными логической операцией And. Так, если в новом запросе мы в поле Data напишем условие   >#01.01.1960#,  а в поле  Kol_str   условие  <200,  то этот запрос вполне можно будет назвать «Новые тонкие книжки».

В строке Поле мы можем вместо имен полей писать выражения, например, [Kol_str]+2.  В этом случае в столбце запроса мы увидим не значение поля, а значение вычисленного выражения. На таблице это не отразится.

Прочее. Кроме пунктов Таблицы и Запросы в списке объектов окна базы данных есть еще другие пункты, посвященные важным сторонам работы с базами данных. Так, пункт Отчеты посвящен представлению сведений из таблиц и запросов в виде, удобном для распечатки. Но объем книги не позволяет нам на этом останавливаться.


Запросы на выборку из таблицы, создаваемые в режиме проектирования


Адаптер, который был автоматически создан в нашем проекте, по умолчанию настроен на перенос из исходной базы данных в  DataSet  всей таблицы целиком. Мы можем изменить эту ситуацию, заставив его переносить из базы данных в  DataSet  не всю таблицу, а только часть ее, согласно созданному нами оператору SELECT. Это и будет решением задачи на выборку из базы данных. Решать ее можно вручную, в режиме проектирования. Можно и программным способом, в режиме работы, то есть не останавливая проекта для создания нового запроса на выборку. В этом подразделе мы рассмотрим создание запроса на выборку в режиме проектирования.

Создадим еще раз проект из 24.5.1. Создавать его будем в точности так же, как и тогда, но с одним отличием. Начните, как и раньше, соединив среду VS с базой данных «Контакт» при помощи Server Explorer и перетащив мышкой значок таблицы «Книги» из окна Server Explorer на форму вашего проекта, вследствие чего под формой появятся значки OleDbConnection1 и OleDbDataAdapter1.

Дальше начинаются отличия. Мы будем адаптер настраивать. Щелкните правой клавишей мыши по значку адаптера под формой и в контекстном меню выберите Configure Data Adapter. Вы увидите окно мастера настройки адаптера (Рис. 24.29).

Рис. 24.29

Нажмите Next в этом окне и в следующих двух окнах тоже, пока не появится окно создания оператора SQL (Рис. 24.30).

Рис. 24.30

В белом поле мы видим оператор SELECT, которым будет руководствоваться адаптер, выбирая данные из таблицы. Так вот кто виноват в том, что порядок полей был перепутан! Вручную подредактируйте этот оператор, чтобы порядок полей был удобным для вас:

SELECT  Код,  Avtor,  Nazvanie,  Data,  Kol_str  FROM  Книги

Вы можете выкинуть некоторые поля, но не выкидывайте ключевое поле.

Нажмите Next, а в следующем окне – Finish. Адаптер настроен. Дальше продолжайте, как раньше: создавайте DataSet и DataGrid, пока проект с двумя кнопками не будет создан и запущен.

Обратите внимание, что SQL-оператор записан в одну строку, а не в несколько, как на Рис. 24.19. Это допустимо.

Вы уже догадались, что в белом поле вы можете написать какой угодно оператор SELECT, после чего будете наблюдать в сетке DataGrid нужную вам выборку. Для этого достаточно снова запустить мастер настройки адаптера и переписать оператор.

В процессе создания запроса на выборку вы можете нажать кнопку Query Builder (Рис. 24.19), после чего увидите очень удобное и привычное вам окно (Рис. 24.31), которое поможет вам сконструировать запрос, не затрудняясь написанием SQL-оператора.

Рис. 24.31


Задача создания выборки решена. Однако, если вы в процессе работы с приложением выполняете несколько выборок с разными условиями выбора, вам вряд ли захочется каждый раз прерывать работу проекта для того, чтобы в режиме проектирования заново настроить адаптер. К тому же, если вы создаете независимое приложение Windows, то оно обязано работать без запуска среды VS, а значит режим проектирования в этом случае просто недоступен.

Создадим проект, который выглядит так, как на Рис. 24.32.

Рис. 24.32

Сверху мы видим привычные нам две кнопки и таблицу, целиком загруженную из базы данных в сетку DataGrid1 при помощи верхней кнопки. В этой сетке мы вручную изменяем данные, удаляем и добавляем строки и сохраняем эти изменения в исходной базе при помощи второй кнопки.

Ниже мы видим кнопку, текстовое поле и еще одну сетку – DataGrid2. В текстовом поле мы пишем SQL-запрос на выборку, после чего нажимаем кнопку и в сетке DataGrid2 наблюдаем результаты запроса.

Начнем создавать проект. Верхняя часть его создается абсолютно так же, как в 24.5.1, но не забудьте переставить поля в адаптере и задать ширину и названия столбцов сетки.

Проверив в работе верхнюю часть проекта, приступим к созданию нижней, поместив на форму кнопку, текстовое поле и еще одну сетку. А теперь еще раз перетащим мышкой значок таблицы «Книги» из окна Server Explorer на форму вашего проекта. Вы увидите, что в ваш проект добавился еще один значок адаптера – OleDbDataAdapter2. Таким образом, данные из одной таблицы можно перекачивать в проект несколькими адаптерами, настроив каждый по-своему. Но нам нет нужды как-то его особенно настраивать. Задайте ему тот же порядок полей, что и у адаптера OleDbDataAdapter1.

А теперь пора для нового адаптера создавать DataSet. Щелкните правой клавишей мыши по форме и в контекстном меню выберите Generate Dataset. Вы увидите диалоговое окно. Настройте его, как показано на Рис. 24.33.

Рис. 24.33

Как видите, мы выбрали создание нового объекта DataSet2, включающего в себя таблицу «Книги», поставляемую адаптером OleDbDataAdapter2 (но не адаптером OleDbDataAdapter1).




Щелкните ОК. Вы увидите, что в ваш проект добавился еще один значок – DataSet21. Установим значение свойства DataSource второй сетки в DataSet21.Книги. Теперь DataGrid2 будет автоматически отражать содержимое таблицы Книги, поставляемое в  DataSet2 адаптером OleDbDataAdapter2. Не перепутайте все эти единицы и двойки.

Для задания названий и ширины столбцов второй сетки напишите код, аналогичный коду, написанному для первой.

Для обеспечения работы нижней части проекта достаточно написать короткую процедуру обработки нажатия на третью кнопку:

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click

        DataSet21.Книги.Clear()

        Dim Команда As New OleDb.OleDbCommand(TextBox1.Text, OleDbConnection1)

        OleDbDataAdapter2.SelectCommand = Команда

        OleDbDataAdapter2.Fill(DataSet21)

End Sub

Пояснения: Первая строка процедуры очищает DataSet2 (а значит и DataGrid2) от прошлых данных, освобождая место для данных требуемой выборки.

Вторая строка создает объект Команда класса OleDbCommand.  Это не что иное, как команда на выполнение некоторого действия над базой данных. Первый параметр конструктора (TextBox1.Text) указывает, откуда брать текст команды (у нас это SQL-оператор, заранее записанный нами в текстовом поле TextBox1). Второй параметр конструктора (OleDbConnection1) указывает, куда направлять команду, чтобы она делала там свое дело.

Третья строка процедуры указывает уже персонально адаптеру, какой командой (Команда) ему пользоваться для выборки (SelectCommand) данных из исходной базы данных.

Настроенный таким образом адаптер в четвертой строке заполняет DataSet2 из базы данных.

Запустите проект. Введите текст SQL-оператора в текстовое поле, нажмите кнопку и наблюдайте результаты запроса. Чуть измените оператор, снова нажмите кнопку и снова наблюдайте результаты запроса. И так далее. Проект готов.

Вам может показаться не очень «круто» – заставлять пользователя самому записывать текст SQL-оператора. Ну что ж, вы можете поступить, как поступили создатели Access или Excel, или хотя бы Query Builder (см. Рис. 24.31), которые предлагают пользователю выбирать параметры выборки из разворачивающихся списков или записывать простейшие логические выражения фильтрации. Вы тоже можете организовать массивы списков и маленьких текстовых полей по числу столбцов таблицы. Вам останется только сконструировать согласно выбору пользователя текст SQL-оператора, как это делает Query Builder.



Еще об SQL. Следующая таблица иллюстрирует некоторые необязательные, но удобные возможности оператора SELECT:

Оператор

Смысл

SELECT  *  FROM Книги

На экран выводится вся таблица. Звездочку пишут для сокращения, чтобы не перечислять все поля.

SELECT TOP 3 *

FROM Книги

ORDER BY Kol_str DESC

На экран выводятся 3 самые толстые книжки, выпущенные издательством. Выражение   ORDER BY Kol_str DESC   сортирует записи по убыванию числа страниц, а выражение   TOP 3   показывает на экране только верхние 3 записи из отсортированных.

SELECT * FROM Книги

WHERE Kol_str

BETWEEN 100 AND 200

На экран выводятся книжки с числом страниц МЕЖДУ 100 И 200.


Запускаем из проекта другие программы


VB позволяет, не выходя из проекта, запускать другие программы, например, Internet Explorer или Блокнот (Notepad). Я рассмотрю два инструмента, которые для этого применяются.



Затенение


Переменные и другие программные элементы разных модулей или разных процедур вполне могут иметь одинаковые имена. Спрашивается, как VB определяет, какой из видимых одноименных элементов имеется в виду в каждом конкретном случае? Здесь вступает в действие эффект

затенения: из нескольких одноименных элементов всегда имеется в виду более локальный, то есть тот, чья зона видимости меньше. Это значит, что локальные элементы имеют предпочтение перед модульными, а те – перед глобальными. В этом есть глубокий смысл. Программист, объявляющий элемент в своей процедуре, может не заботиться о том, что где-то в модуле есть элементы с тем же именем. А программист, объявляющий элементы в своем модуле, может не заботиться о том, что элементы с тем же именем есть где-то в проекте.

Посмотрим, к примеру, что напечатает следующий проект:

Форма 1:

    Public A = 10

    Public B = 20

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim A = 100

        Debug.WriteLine(A)

        Debug.WriteLine(B)

        Debug.WriteLine(C)

    End Sub

Модуль:

Module Module1

    Public A = 1

    Public B = 2

    Public C = 3

End Module

Вот что:

100

20

3



Жесткий диск


У оперативной памяти есть два существенных недостатка:  1)

Когда вы выключаете компьютер, все содержимое оперативной памяти стирается, потому что электронная оперативная память не может что-то помнить, если к ней постоянно не подведен электрический ток.    2) Оперативная память сравнительно дорога, много ее не купишь, поэтому на большинстве персональных компьютеров сегодня установлено от 32 до 1024 мегабайтов оперативной памяти (мегабайтом сокращенно и приблизительно называют миллион байтов, а гигабайтом – миллиард байтов). Однако некоторые программы, например, игровые, настолько велики, что в компьютеры с маленькой памятью они целиком просто не уместятся, а следовательно, не могут быть запущены.

Для преодоления этих двух недостатков большинство современных персональных компьютеров снабжаются «винчестером» – устройством памяти на жестких магнитных дисках. Запись информации в нем производится на быстро вращающиеся диски, покрытые магнитным веществом (см. рис. П3). Принцип записи и считывания тот же, что и при записи и воспроизведении песенки на аудиокассете. Цена одного мегабайта памяти винчестера гораздо меньше, чем цена одного мегабайта оперативной памяти, поэтому сегодня на большинстве персональных компьютеров она имеет размер от 20 до 300 гигабайтов.

Рис. П3

За дешевизну расплачиваемся быстродействием.  Чтобы считать информацию с диска, необходимо подвести магнитную головку к тому месту диска, где записана нужная информация, что занимает сотую долю секунды. По сравнению с оперативной памятью жесткий диск – черепаха.

Винчестер используется для хранения ваших данных и необходимых и часто используемых программ: операционных систем, сред программирования, игр и т.д. Теперь не беда, что большая программа не умещается в оперативной памяти, в нее загружается только та часть программы, которую необходимо выполнять именно в данный момент, а остальная часть программы находится на диске и ждет своей очереди, чтобы быть загруженной в память и выполняться.

Кроме этого, вы используете винчестер, чтобы сохранить на нем свою работу. Ведь она находится в оперативной памяти и при выключении компьютера сотрется.



Знакомство с CheckedListBox


Поместите на форму элемент управления список с флажками (CheckedListBox). Вы можете его видеть на Рис. 18.12.

Рис. 18.12

Щелчками мыши или клавишами перемещения курсора и пробела ставьте или снимайте флажки. Смысл списка с флажками в том, чтобы что-нибудь делать сразу со всеми элементами, на которых установлены флажки. Об этом – чуть позже.



Знакомство с другими элементами управления


Со следующими тремя элементами управления только познакомлю, не останавливаясь на их работе.