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

         

Мультфильм «Летающая тарелка»


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

Покажите своим друзьям фильм, снятый «секретной видеокамерой»: В небе над вашим домом летит летающая тарелка.

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

Затем зайдите в любой графический редактор, например, в Paint, настройте очень маленький размер листа, на котором будете рисовать, и на белом фоне нарисуйте во весь этот маленький лист летающую тарелку. Сохраните ее изображение в файл в формате BMP или другом каком-нибудь, не искажающем цвета пикселей (например, для рисунков очень хорош формат PNG, он и цвета пикселей не искажает и размеры файла сильно уменьшает). Формат JPEG не подойдет, потому что он цвета пикселей чуть-чуть, почти незаметно для глаза, но искажает. Почему нельзя искажать даже чуть-чуть, скажу чуть-чуть позже. JPEG, однако, хорош для фотографий, потому что он сильно уменьшает размеры файла.

Создайте проект с формой, таймером и PictureBox. Будем писать программу, постепенно ее улучшая.

1 версия (плохая):

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

        Me.BackgroundImage = Image.FromFile("Ландшафт.jpg")

        PictureBox1.Image = Image.FromFile("Тарелка.bmp")

End Sub

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

        PictureBox1.Left = PictureBox1.Left + 1

End Sub

Результаты работы программы вы видите на Рис. 13.9. Тарелка действительно летит по небу слева направо. Плохо то, что летит она в обрамлении большого серого и маленького белого прямоугольников. Большой серый прямоугольник – это наш PictureBox. Займемся им во 2 версии программы, а маленьким белым – в 3 версии.



Рис. 13.9
2 версия (получше). Для избавления от серого прямоугольника сделаем 3 вещи:
Избавим PictureBox от бордюра:
        PictureBox1.BorderStyle = BorderStyle.None


Чтобы избавиться от серого цвета, сделаем PictureBox «прозрачным». Для этого или в режиме проектирования установим его свойству BackColor значение Transparent (в закладке Web), или напишем в коде:
        PictureBox1.BackColor = Color.Transparent
Помогло. А теперь замечаем, что движение чуть притормаживает. Происходит это от того, что PictureBox слишком велик и двигать его компьютеру в нашем случае трудно. Заставим PictureBox сжаться до размеров тарелки:
        PictureBox1.SizeMode = PictureBoxSizeMode.AutoSize
Вот программа:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.BackgroundImage = Image.FromFile("Ландшафт.jpg")
        PictureBox1.Image = Image.FromFile("Тарелка.bmp")
        PictureBox1.BorderStyle = BorderStyle.None
        PictureBox1.BackColor = Color.Transparent
        PictureBox1.SizeMode = PictureBoxSizeMode.AutoSize
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        PictureBox1.Left = PictureBox1.Left + 1
End Sub
Результаты работы программы вы видите на Рис. 13.10.

Рис. 13.10
3 версия (нормальная). Белый прямоугольник вокруг тарелки достался нам в наследство от графического редактора. Ведь сохранилась в файле не только сама тарелка, но весь прямоугольный лист, на котором ее рисовали. А лист-то был белый.
Мы имеем возможность, работая с объектом Bitmap, объявить любой цвет прозрачным. Поэтому для избавления от белого прямоугольника сделаем 3 вещи: Образуем из нашей тарелки объект Bitmap, объявим белый цвет прозрачным и присвоим Bitmap свойству Image нашего PictureBox. Вот программа:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.BackgroundImage = Image.FromFile("Ландшафт.jpg")


        Dim Тарелка As New Bitmap("Тарелка.bmp")
        Тарелка.MakeTransparent(Color.White)
        PictureBox1.Image = Тарелка
        PictureBox1.BorderStyle = BorderStyle.None
        PictureBox1.BackColor = Color.Transparent
        PictureBox1.SizeMode = PictureBoxSizeMode.AutoSize
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        PictureBox1.Left = PictureBox1.Left + 1
End Sub
Здесь я полужирным выделил три новых оператора. Использован метод MakeTransparent объекта Bitmap, который делает прозрачным цвет, указанный в качестве параметра.  Удовлетворительные результаты работы программы вы видите на Рис. 13.11.

Рис. 13.11
Вопрос: Что делать, если тарелка нарисована в графическом редакторе не на белом фоне, а на другом, причем, что за цвет у фона, неизвестно? Не беда. Выберите на картинке пиксель, который наверняка имеет этот самый цвет фона и узнайте его цвет при помощи функции GetPixel, как мы это делали в 12.7.4. А затем используйте его в качестве параметра метода MakeTransparent:
        Dim Цвет As Color = Тарелка.GetPixel(1, 1)
        Тарелка.MakeTransparent(Цвет)
Чем плох JPEG. Сейчас становится ясно, почему нельзя было сохранять тарелку в формате JPEG. Дело в том, что этот формат платит за свою компактность некоторым искажением мельчайших деталей фотографии. При этом однородный фон вполне может превратиться в пестрый набор пикселей, имеющих очень близкие, но все-таки не абсолютно одинаковые цвета. Для глаза незаметно, но компьютер не проведешь! Он видит,  что белый фон на рисунке тарелки не абсолютно белый, вернее – не везде абсолютно белый. Ведь что такое белый цвет для компьютера? Это когда все три составляющие цвета (красная, зеленая и синяя) равны 255. JPEG же может какие-нибудь из них для некоторых пикселей сделать, например, 254. И все! Компьютер отказывается считать этот цвет белым, а значит и не делает его прозрачным. Белая окантовка тарелки местами остается.
Задание 92.          
Пусть тарелка, полетав туда-сюда, совершит посадку. Указание: Для этого вы можете организовать счетчик импульсов таймера и в зависимости от его величины при помощи операторов выбора выбирать в процедуре Timer1_Tick то или иное направление полета тарелки.
Пусть тарелка время от времени выстреливает вниз лазерным лучом. Указание: Для этого вы можете использовать летающий вместе с тарелкой высокий узкий PictureBox и менять его фон с прозрачного на белый и обратно или делать PictureBox иногда видимым.

Мультфильм «Человечек»


Сейчас мы с вами создадим простейший мультфильм, в котором человечек шагает среди египетских пирамид (Рис. 13.12).

Рис. 13.12

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

Рис. 13.13

Зайдите в графический редактор Paint или какой-нибудь другой и на белом фоне нарисуйте первый кадр примерно так же, как вы рисовали летающую тарелку. Вы можете сделать человечка гораздо красивее и подробнее, чем это сделал я. Если вы отлично работаете в солидном графическом редакторе, то можете даже взять фотографию вашего знакомого, только не забудьте выкрасить белой краской все, что выходит за контуры его фигуры. Сохраните человечка под именем Кадр1. Теперь, не стирая человечка, измените ему положение ног и сохранитесь как Кадр2 (не Save, а Save as…). Аналогично создайте и сохраните Кадр3. У вас получилось три файла.

Теперь создайте проект в VB. Придайте форме картинку пирамид или чего-нибудь другого подходящего, например, улицы. Поместите на форму объект PictureBox. Создайте в коде из упомянутых 3 кадров 3 объекта Bitmap. Наша идея – придавать объекту PictureBox по очереди с достаточной скоростью картинки из этих 3 объектов. Тогда мы увидим, что человечек в PictureBox передвигает на месте ногами. Менять кадры нужно в такой последовательности: 1-2-3-2-1-2-3-2-1-2-3-2-1-2-…. Если при этом объект PictureBox будет еще и двигаться налево по форме, то и получится ходьба.

Давайте-ка для лучшего понимания мысленно разобьем эту последовательность на одинаковые участки:  1-2-3-2----1-2-3-2-----1-2-3-2- …. Длина участка получилась равной 4. Поместите на форму таймер и придайте ему интервал 100 (потом, если движение будет слишком быстрым или медленным, вы его измените). Наша задача – сделать так, чтобы при каждом выполнении процедуры таймера мы видели следующий очередной кадр из приведенной мной последовательности. Для этого я организовал переменную N и заставил ее пробегать значения   0-1-2-3-----0-1-2-3-----0-1-2-….Не путайте эту последовательность с предыдущей. Как видите, длину участка на ней я подобрал тоже равной 4. Вот программа:


Dim Кадр1 As New Bitmap("Кадр1.png")
Dim Кадр2 As New Bitmap("Кадр2.png")
Dim Кадр3 As New Bitmap("Кадр3.png")
Dim N As Integer = 0
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.BackgroundImage = Image.FromFile("Пирамиды.jpg")
        Кадр1.MakeTransparent(Color.White)
        Кадр2.MakeTransparent(Color.White)
        Кадр3.MakeTransparent(Color.White)
        PictureBox1.BorderStyle = BorderStyle.None
        PictureBox1.BackColor = Color.Transparent
        PictureBox1.SizeMode = PictureBoxSizeMode.AutoSize
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Select Case N
            Case 0 : PictureBox1.Image = Кадр1
            Case 1 : PictureBox1.Image = Кадр2
            Case 2 : PictureBox1.Image = Кадр3
            Case 3 : PictureBox1.Image = Кадр2
        End Select
        N = N + 1                                                            'Увеличиваем N на 1
        If N = 4 Then N = 0                                            'После 3 должен идти 0, а не 4       
        PictureBox1.Left = PictureBox1.Left - 10
 End Sub
Пару операторов
        N = N + 1                                                            'Увеличиваем N на 1
        If N = 4 Then N = 0                                            'После 3 должен идти 0, а не 4       
можно заменить одним изящным оператором
        N = (N + 1) Mod 4
Задание 93.          
Добавьте в проект три кнопки. По одной человечек идет направо, по другой налево, по третьей останавливается. Подсказка: Рисовать новые кадры в графическом редакторе не надо.
Задание 94.          
«Улыбка». Для тех, кто умеет рисовать. Попросите у своей знакомой ее фотографию, где она снята с серьезным выражением лица. Введите фото в компьютер. Сделайте в Paint, а лучше в PhotoShop еще два-три кадра этого фото, аккуратно понемножку приподнимая уголки губ на изображении. Подумайте, в какой последовательности нужно показывать кадры, чтобы улыбка постепенно возникала и исчезала.

Анимация


Анимация означает придание неподвижному предмету движения. Еще одно значение слова анимация –  мультфильм.



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


Что такое фокус? Создайте проект. Поместите на форму сначала два текстовых поля, а затем две кнопки. Не наоборот. Запустите проект. Щелкните по одной кнопке, по другой, по одному полю, по другому. Ничего, конечно, не происходит, но обратите внимание вот на что. Если последний раз вы щелкнули по кнопке, то она имеет несколько другой, чем у других кнопок, «бывший нажатый» вид. Это от того, что рамочка вокруг нее стала чуть потолще. А если последний раз вы щелкнули по текстовому полю, то именно в нем мигает курсор, а в других текстовых полях не мигает. VB как бы показывает вам, каким из элементов управления вы пользовались последним. Можно сказать, что из множества разнородных объектов на форме один какой-то выделяется, то есть находится в фокусе. Говорят, что в этом случае объект обладает фокусом.

На вашей форме 4 объекта. В фокусе всегда только один из них.

События приобретения и потери фокуса. У объектов, способных иметь фокус, имеются два события: Enter, которое возникает в момент приобретения объектом фокуса, и Leave, которое возникает в момент потери фокуса.

Запишите в окно кода такие процедуры (если вы забыли, как получать заготовки процедур, перечитайте 3.11):

Private Sub Button1_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Enter

        Debug.WriteLine("Кнопка 1 получила фокус")

End Sub

Private Sub Button2_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Enter

        Debug.WriteLine("Кнопка 2 получила фокус")

End Sub

Private Sub TextBox1_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.Enter

        Debug.WriteLine("Текстовое поле 1 получило фокус")

End Sub

Private Sub Button1_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Leave

        Debug.WriteLine("Кнопка 1 утратила фокус")

End Sub

Private Sub Button2_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Leave


        Debug.WriteLine("Кнопка 2 утратила фокус")
End Sub
Private Sub TextBox1_Leave( ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.Leave
        Debug.WriteLine("Текстовое поле 1 утратило фокус")
End Sub
Я не стал писать процедуры для второго текстового поля.
Запустите проект. Обратите внимание, что первое текстовое поле уже в фокусе, о чем в окне Output вы уже имеете сообщение от вашей программы:
Текстовое поле 1 получило фокус
Щелкните по второму текстовому полю. В окне Output появляется:
Текстовое поле 1 утратило фокус
Щелкните по второй кнопке. В окне Output появляется:
Кнопка 2 получила фокус
Щелкните по первому текстовому полю. В окне Output появляется:
Кнопка 2 утратила фокус
Текстовое поле 1 получило фокус
Именно в таком порядке, а не наоборот. Пощелкайте по объектам. Понаблюдайте, что появляется при этом в окне Output и в какой последовательности.
Придаем фокус в коде. Фокус можно переводить на объект и программным путем. Дополним наш проект процедурой:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        TextBox1.Focus()
        Button2.Focus()
End Sub
Запустите проект. Щелкните по первой кнопке. Фокус, вопреки ожиданиям, переместился не на нее, а на вторую кнопку. А в окне Output появляется:
Текстовое поле 1 утратило фокус
Кнопка 1 получила фокус
Кнопка 1 утратила фокус
Текстовое поле 1 получило фокус
Текстовое поле 1 утратило фокус
Кнопка 2 получила фокус
Произошло это благодаря методу Focus. Дело его простое – переводить фокус на своего хозяина. Как видите, один щелчок по кнопке может приводить сразу к нескольким событиям, следующим друг за другом в строго определенном порядке.
Не все объекты, получившие фокус, проявляют это зримо. Например, метка. Если фокус на ней, мы этого никак не заметим.
Если сломалась мышь. Фокус нужен хотя бы для того, чтобы компьютер в каждый момент времени знал, какой из элементов управления должен реагировать на клавиатуру. Если вы набираете на клавиатуре текст, то вводиться он будет только в то текстовое поле, которое обладает фокусом. Если фокус находится на кнопке, то вы можете нажимать на нее не только мышкой, но и клавишей Enter.


Переводить фокус с одного объекта на другой вы можете вручную клавишей Tab. При этом фокус останавливается только на тех объектах, для которых это имеет смысл. Так, метка упомянутым способом фокуса не получит. Порядок пробегания определяется численным значением свойства TabIndex каждого элемента управления. Его вы можете видеть в окне свойств. Значение 0 получает элемент управления, первым появившийся на форме при проектировании, 1 – второй и т.д. Фокус переходит от объекта к объекту в порядке возрастания этого значения. Так, в предыдущем примере, создавая проект, я на пустой форме первым разместил текстовое поле 1, затем – текстовое поле 2. Поэтому при запуске проекта в фокусе сразу же оказалось текстовое поле 1, а после нажатия Tab – текстовое поле 2.
Вы можете изменить этот порядок, изменив некоторым объектам значения свойства TabIndex. С несколько большим комфортом вы сможете изменить их, зайдя во View ® Tab Order и пощелкав мышкой по элементам управления. Только не забудьте потом выйти тем же путем: View ® Tab Order.
Вы можете запретить фокусу останавливаться на объекте при нажатии Tab, установив в False его свойство TabStop.
Между кнопками фокус можно перемещать и клавишами перемещения курсора.

Основные события, связанные с мышью


Создадим проект с кнопкой. Зайдем в окно кода и заглянем в события, связанные с формой. Среди них отыщем события, относящиеся к мыши. Нас интересуют такие: Click (щелчок), DoubleClick (двойной щелчок), MouseDown (нажали клавишу мыши), MouseUp (отпустили клавишу мыши), MouseEnter (мышь появилась над формой), MouseLeave (мышь покинула форму),  MouseMove (сдвинули мышь).

Проверим работу этих событий, для чего введем такой код:

Private Sub Form1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Click

        Debug.WriteLine("Сработало событие Click")

End Sub

Private Sub Form1_DoubleClick(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.DoubleClick

        Debug.WriteLine("Сработало событие DoubleClick")

End Sub

Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)  _

Handles MyBase.MouseDown

        Debug.WriteLine("Сработало событие MouseDown")

End Sub

Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseUp

        Debug.WriteLine("Сработало событие MouseUp")

End Sub

 Private Sub Form1_MouseEnter(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.MouseEnter

        Debug.WriteLine("Сработало событие MouseEnter")

End Sub

Private Sub Form1_MouseLeave(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.MouseLeave

        Debug.WriteLine("Сработало событие MouseLeave")

End Sub

Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)  _

Handles MyBase.MouseMove

        Debug.WriteLine("Сработало событие MouseMove")

End Sub

Запустите проект. Сделайте окно Output повыше размером. Перемещая мышь и щелкая разными ее кнопками, а также перемещая мышь при нажатых кнопках, наблюдайте в окне Output порядок наступления событий. Обратите внимание, что вне формы и над кнопкой события не срабатывают. Вот необходимые пояснения:



Подробности событий мыши. Класс MouseEventArgs


Мы с вами бесчисленное количество раз писали процедуру

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

и ни разу не заглядывали внутрь скобок. А видим мы там два параметра процедуры: один с именем sender, другой – e. Первый имеет тип Object и обозначает объект, с которым случилось событие, в нашем случае – Button1. Второй имеет тип EventArgs. Это некий общий тип для второго параметра и на нем мы останавливаться не будем. Рассмотрим лучше заголовки процедур для событий MouseDown, MouseUp и MouseMove. Например:

Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)  _

Handles MyBase.MouseDown

События эти похожи и параметры у всех трех одинаковые. Мы уже сейчас можем извлечь пользу из параметра e, являющегося объектом класса MouseEventArgs. Польза от него та, что он содержит информацию о событии, например, какая кнопка мыши была нажата и какие у мыши в этот момент были координаты. Информация эта находится в нескольких свойствах объекта MouseEventArgs. Приведу те, что нас интересуют:

Свойства

Смысл

Тип значения

X, Y

Координаты острия курсора мыши в момент события

Integer

Button

Какая именно из кнопок мыши была нажата

Перечисление MouseButtons со значениями: Left (левая), Right (правая), Middle (средняя), None (никакая) и пара значений для пятикнопочной мыши.

Clicks

Равняется 0 (если не было щелчка), 1 (если был одинарный щелчок) или 2 (если был двойной щелчок).

Integer

Кроме этого класс MouseEventArgs поддерживает работу с колесом мыши.

Для того, чтобы понять и проверить смысл этих свойств, прочтите (чтобы понять) и запустите (чтобы проверить) такую программу:

Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)  _

Handles MyBase.MouseDown

        Debug.WriteLine(e.X & "   " & e.Y)              'Печатаем координаты события

        Debug.WriteLine(e.Button.ToString)            'Печатаем английское название кнопки


        If e.Button = MouseButtons.Left Then Debug.WriteLine("Нажата левая кнопка мыши")
        If e.Button = MouseButtons.Right Then Debug.WriteLine("Нажата правая кнопка мыши")
        If e.Button = MouseButtons.Middle Then Debug.WriteLine("Нажата средняя кнопка мыши")
End Sub
Private Sub Form1_MouseUp( ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseUp
        Debug.WriteLine(e.X & "   " & e.Y)                'Печатаем координаты события
        Debug.WriteLine(e.Button.ToString)            'Печатаем английское название кнопки
        If e.Button = MouseButtons.Left Then Debug.WriteLine("Отпущена левая кнопка мыши")
        If e.Button = MouseButtons.Right Then Debug.WriteLine("Отпущена правая кнопка мыши")
        If e.Button = MouseButtons.Middle Then Debug.WriteLine("Отпущена средняя кнопка мыши")
End Sub
Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)  _
Handles MyBase.MouseMove
        Debug.WriteLine(e.X & "   " & e.Y)              'Печатаем координаты события
        Debug.WriteLine(e.Button.ToString)            'Печатаем английское название кнопки
        If e.Button = MouseButtons.Left Then Debug.WriteLine("Удерживается нажатой левая кнопка мыши")
        If e.Button = MouseButtons.Right Then Debug.WriteLine("Удерживается нажатой правая кнопка мыши")
        If e.Button = MouseButtons.Middle Then Debug.WriteLine("Удерживается нажатой средняя кнопка мыши")
        If e.Button = MouseButtons.None Then Debug.WriteLine("Не нажата ни одна кнопка мыши")
End Sub
Пояснения: Здесь я использовал свойственный объектам метод ToString, который возвращает в виде вразумительной строки имя или значение объекта.
Пощелкайте в разных местах формы всеми тремя кнопками мыши. Повозите мышь над формой, удерживая нажатой ту или иную кнопку. В окне Output вы будете наблюдать последовательность сообщений примерно такого вида:
264   24
None
Не нажата ни одна кнопка мыши
264   24
Left
Нажата левая кнопка мыши
337   83
Left
Удерживается нажатой левая кнопка мыши
337   83
Left
Отпущена левая кнопка мыши
341   83
None
Не нажата ни одна кнопка мыши
Пояснения здесь излишни.
Итоги. В тонкости я, конечно, не вникал, но полученной информации о мыши вполне достаточно для решения реальных задач.

Две задачи: Глаз-ватерпас и Мышка-карандаш


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

Здесь подойдет событие MouseDown, так как оно сообщает координаты мыши во время щелчка. Вот программа для поставленной задачи:

Dim X_кружка As Integer

Dim Y_кружка As Integer

Dim Граф As Graphics = Me.CreateGraphics

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

        Dim i As Integer

        Randomize()

        X_кружка = 200 * Rnd()                   'Определяем координаты кружка (центра кружка)

        Y_кружка = 200 * Rnd()

        Граф.DrawEllipse(Pens.Black, X_кружка - 10, Y_кружка - 10, 20, 20)      'Чертим кружок

        For i = 1 To 3000000 : Next              'Пауза, чтобы мы успели заметить кружок

        Граф.Clear(Me.BackColor)               'Очищаем форму от кружка

End Sub

Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)  _

Handles MyBase.MouseDown

        Граф.DrawEllipse(Pens.Black, X_кружка - 10, Y_кружка - 10, 20, 20)   'Кружок снова возникает

        Граф.DrawEllipse(Pens.Black, e.X - 5, e.Y - 5, 10, 10)                            'Чертим кружочек на месте щелчка

        Dim Расстояние_до_кружка As Double = Math.Sqrt((e.X - X_кружка) ^ 2 + (e.Y - Y_кружка) ^ 2)

        MsgBox("Промах на " & Math.Round(Расстояние_до_кружка) & " пикс")


        Граф.Clear(Me.BackColor)                                                                         'Очищаем форму от кружков
End Sub
Пояснение того, как вычислялось расстояние ( для тех, кто знает теорему Пифагора): Мысленно соедините отрезком прямой центр первой окружности и точку щелчка. Проведите из концов отрезка вертикальные и горизонтальные линии. Вы легко увидите прямоугольный треугольник. Искомым расстоянием будет гипотенуза этого прямоугольного треугольника. Нетрудно заметить, что горизонтальный катет равен  e.X  - X_кружка, а вертикальный равен  e.Y - Y_кружка  (знак я не учитываю). Теорема Пифагора гласит, что квадрат гипотенузы равен сумме квадратов катетов. Отсюда, гипотенуза равна корню квадратному из суммы квадратов катетов (каковая формула и записана в программе).
Вы можете превратить эту программу в игру. Создайте сумматор расстояний, чтобы игрок видел свой накапливающийся суммарный результат. Предоставьте игроку возможность сделать, скажем, ровно 10 щелчков, после чего игра заканчивается. Цель – набрать наименьшую сумму в конце игры.
Мышка-карандаш. Вот программа, превращающая мышку в карандаш:
Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)  _
Handles MyBase.MouseMove
        Dim Граф As Graphics = Me.CreateGraphics
        Граф.FillEllipse(Brushes.Black, e.X, e.Y, 5, 5)
End Sub
Запустите ее и медленно ведите мышкой по форме. За мышкой остается нарисованный след из множества черных кружочков диаметра 5. Кружочки рисуются так близко друг к другу, что образуют непрерывную линию. Если мышку передвигать побыстрее, то след будет прерывистым, потому что, хоть событие MouseMove наступает и часто, но все же не бесконечно часто. В 20.9.2 мы решим проблему непрерывности, хотя, собственно говоря, вы и сейчас можете это сделать.
Задание 96.          
Сделайте так, чтобы мышь-карандаш рисовала только при нажатой левой клавише, что более привычно для всех пользователей компьютера. Сделайте так, чтобы при щелчке по правой клавише толщина линии возрастала на 1. Если сможете, сделайте так, чтобы при двойном щелчке по правой клавише толщина линии убывала на 1.

Работа с мышью


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



Событие KeyPress. Класс KeyPressEventArgs. Структура Char


Создайте проект из одной формы, без единого элемента управления. Зайдите в окно кода и выберите для формы событие KeyPress. В появившуюся заготовку процедуры запишите следующий код:

Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs) Handles MyBase.KeyPress

        Debug.Write(e.KeyChar)

End Sub

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

qwertyQWERTY12345,.!фывапроФЫВАПРО()"№;%:?*

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

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

Из программы видно, что символы, выводимые в окно Output, являются значениями свойства KeyChar объекта e, принадлежащего классу KeyPressEventArgs. Строго говоря, значение свойства KeyChar есть структура Char, обладающая рядом полезных методов, действие некоторых из которых видно из следующей программы:

Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) _

Handles MyBase.KeyPress

        Dim Символ As Char = e.KeyChar

        Debug.WriteLine("Набран символ                                         " & Символ)

        Debug.WriteLine("Это цифра?                                              " & Char.IsDigit(Символ))

        Debug.WriteLine("Это буква?                                                " & Char.IsLetter(Символ))

        Debug.WriteLine("Правда ли, что это буква или цифра?   " & Char.IsLetterOrDigit(Символ))


        Debug.WriteLine("Это строчная буква?                                " & Char.IsLower(Символ))
        Debug.WriteLine("Это заглавная буква?                              " & Char.IsUpper(Символ))
        Debug.WriteLine("Это знак препинания?                             " & Char.IsPunctuation(Символ))
        Debug.WriteLine("Это пробел?                                             " & Char.IsSeparator(Символ))
        Debug.WriteLine("Превратить в строчную                           " & Char.ToLower(Символ))
        Debug.WriteLine("Превратить в заглавную                         " & Char.ToUpper(Символ))
End Sub
Вот как отреагирует эта программа на ввод заглавной русской буквы «Ж»:
Набран символ                         Ж
Это цифра?                            False
Это буква?                            True
Правда ли, что это буква или цифра?   True
Это строчная буква?                   False
Это заглавная буква?                  True
Это знак препинания?                  False
Это пробел?                           False
Превратить в строчную                 ж
Превратить в заглавную                Ж
Анализировать вводимые с клавиатуры символы для управления компьютером можно, например, такими операторами:
        If Символ = ":" Then Debug.WriteLine("Набрано двоеточие")
        If Char.IsDigit(Символ) Then Debug.WriteLine("Набрана цифра")

События KeyDown и KeyUp. Класс KeyEventArgs


KeyDown и KeyUp.  Создайте проект из одной формы, без элементов управления. Введите такой код:

Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs) Handles MyBase.KeyPress

        Debug.WriteLine("Сработал KeyPress")

End Sub

Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles  MyBase.KeyDown

        Debug.WriteLine("Сработал KeyDown")

End Sub

Private Sub Form1_KeyUp(ByVal sender As Object, ByVal e As KeyEventArgs) Handles MyBase.KeyUp

        Debug.WriteLine("Сработал KeyUp")

End Sub

Запустите проект, пощелкайте по клавишам клавиатуры и понаблюдайте за результатами в окне Output. Удерживайте нажатыми разные клавиши и тоже наблюдайте.

Выводы: При нажатии почти любой клавиши клавиатуры срабатывает событие KeyDown. Если при этом нажатие приводит к вводу символа, то сразу за ним срабатывает событие KeyPress. Если вы удерживаете клавишу нажатой, эта парочка срабатывает многократно и часто. При отпускании клавиши однократно срабатывает событие KeyUp. Я не рассматриваю ситуацию, когда одновременно удерживаются нажатыми две и более клавиш.

KeyCode. Какие именно клавиши клавиатуры были нажаты или отпущены, вам подскажет свойство KeyCode объекта e, принадлежащего классу KeyEventArgs. Введите такой код:

Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e KeyEventArgs) Handles MyBase.KeyDown

        Debug.WriteLine(e.KeyCode.ToString)

End Sub

Пояснения: Здесь я использовал свойственный объектам метод ToString, который возвращает в виде вразумительной строки имя или значение объекта.

Запустите проект, пройдитесь по всем клавишам клавиатуры (кроме тех, конечно, что выключают компьютер). Вот что вы увидите:

Печать в окне Output

Какая клавиша была нажата

D                  

L                  

F1          

Tab         

Enter       

Left        

Space       

D1          

D2          

Escape     

ControlKey         

ShiftKey           

Menu        

D

L

F1

Tab

Enter

Стрелка влево

Пробел

Цифра 1

Цифра 2

Esc

Ctrl

Shift

Alt

<
И так далее. Пройдитесь по клавишам дополнительной цифровой клавиатуры в правой части клавиатуры при включенном и выключенном индикаторе NumLock.
Свойства Control, Alt, Shift. VB позволяет определить, какие из трех клавиш Ctrl, Alt, Shift удерживались нажатыми в момент срабатывания события KeyDown или KeyUp. Дополните процедуру:
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs)  _
Handles MyBase.KeyDown
        Debug.WriteLine(e.KeyCode.ToString & "      " & e.Control
& "      " & e.Alt & "      " & e.Shift)
End Sub
Здесь я использовал свойства Control, Alt, Shift объекта e. Их смысл будет ясен из распечатки в окне Output. Запустите проект, нажмите и удерживайте нажатыми клавиши Alt и Shift (но не Ctrl), после чего щелкните по клавише W. Вот что вы увидите последней напечатанной строкой в окне Output:
W      False      True      True
Все сказанное относится и к событию KeyUp.
Анализировать нажатия клавиш для управления компьютером можно, например, такими операторами:
If e.KeyCode = Keys.F2 Then Debug.WriteLine("Нажата клавиша F2")
If e.Shift Then Debug.WriteLine("Нажата клавиша Shift")
If e.Control And e.KeyCode
= Keys.Right Then Debug.WriteLine("Нажата стрелка направо при нажатой кл Ctrl")
Перечисление Keys включает в себя коды клавиш клавиатуры.
События клавиатуры у элементов управления. События, связанные с клавиатурой, имеются у многих элементов управления. Поместим на форму, к примеру, пару кнопок и текстовое поле. Вдобавок к процедуре Form1_KeyDown напишем три процедуры:
Private Sub Button1_KeyDown …
Private Sub Button2_KeyDown …
Private Sub TextBox1_KeyDown …
Запустим программу и щелкнем по какой-нибудь клавише. Какая из четырех процедур сработает? Та, чей элемент управления находится в фокусе. А поскольку один какой-нибудь элемент на форме всегда находится в фокусе, до процедуры Form1_KeyDown дело никак не доходит. Это не всегда бывает удобно. И против этого в VB есть специальный прием: свойство формы KeyPreview устанавливается в True. Это означает приказ компьютеру при нажатии на клавишу сначала вызывать событие формы, а уж потом другого объекта. Так, если в фокусе находится кнопка 2, то при нажатии на клавишу клавиатуры сначала выполняется процедура Form1_KeyDown, а сразу за ней – Button2_KeyDown. Сказанное касается большинства клавиш и элементов управления, но не всех.


«Телефонный номер». Разберем пример использования событий клавиатуры. Пусть на форме имеется текстовое поле, которое мы предназначили для ввода телефонных номеров. Сделаем текстовое поле умным – запретим ему воспринимать символы за исключением цифр, пробелов, черточек и точек. Если пользователь попытается ввести неподходящий символ, должно появляться сообщение об ошибке, а сам символ в поле появляться не должен. После окончания ввода можно нажать клавишу Enter, пусть при этом выводится сообщение «Ввод закончен».
Вот код:
Private Sub TextBox1_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs)  _
Handles TextBox1.KeyPress
        Dim С As Char = e.KeyChar       'Вводимый символ
        If Char.IsDigit(С) Or Char.IsSeparator(С)  Or С = "-" Or С = "." Then Exit Sub
        Beep()
        MsgBox("В телефонном номере можно употреблять только цифры, пробелы, черточки и точки!")
        e.Handled
= True
End Sub
Private Sub TextBox1_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles TextBox1.KeyDown
        If e.KeyCode = Keys.Enter Then MsgBox("Ввод закончен")
End Sub
Пояснения: Используются события KeyPress и KeyDown текстового поля. Событие KeyDown используется для обработки нажатия на клавишу Enter. Оператор If в первой процедуре предназначен для того, чтобы не создавать проблем цифрам, пробелам, черточкам и точкам. Ведь выход из процедуры (Exit Sub) и есть выход без проблем. Если символ не принадлежит к упомянутым, то выхода из процедуры не происходит и выполняются последние три строки процедуры: раздается сигнал, затем появляется сообщение, а напоследок оператор   e.Handled = True   заставляет компьютер считать, что неосуществленная стандартная обработка события (в частности – появление символа в текстовом поле) якобы уже произошла, а значит и символу незачем появляться в текстовом поле.
Задание 97.          
«Управляемый объект». На клавиатуре имеются четыре клавиши со стрелками для управления курсором. Поместите на форму какой-нибудь элемент управления. При щелчке по любой клавише управления курсором элемент управления должен двигаться с постоянной скоростью в указанном на клавише направлении.
Задание 98.          
 «Светофор». Нарисуйте светофор: прямоугольник и три круга. При наборе на клавиатуре символа R светофор должен загораться красным светом, G – зеленым, Y - желтым.
Задание 99.          
«Зенитка». Вверху справа налево медленно движется вражеский самолет. В подходящий момент вы нажатием любой клавиши запускаете снизу вверх зенитный снаряд. При попадании компьютер выдает соответствующее сообщение.
Не шали!  Поставьте при помощи событий клавиатуры преграду тем шалунам, кто в нашем калькуляторе хочет ввести с клавиатуры в текстовое поле для результата любые символы. А потом вспомните, что это можно было сделать, просто превратив текстовое поле в метку или придав текстовому полю свойство ReadOnly.

Работа с клавиатурой


Познакомимся с событиями, связанными с клавиатурой. Их три: KeyDown (клавиша нажата), KeyUp (клавиша отпущена) и KeyPress.(ввели с клавиатуры символ).



Постановка задачи


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

На Рис. 14.1 вы видите внешний вид игры (о красоте я не заботился, красоту вы всегда сможете добавить по вкусу):

Рис. 14.1

Процесс игры таков. После загрузки проекта вы нажимаете на кнопку Начинаем сначала. На форме появляется белое квадратное поле для гонки со случайно расположенными газонами. Машина стоит на старте. Нажатием на клавишу пробела вы даете газ и машина со старта набирает скорость. Ваша цель – любым путем побыстрее добраться до финиша. На белом асфальте вы можете газовать до предельной скорости (я выбрал ее значение равным 15). Если же ненароком попадете на газон, то на газоне ваша скорость будет очень мала (я выбрал 1). Поэтому имеет смысл по газонам ездить пореже. Направление движения может быть только горизонтальное и вертикальное, наискосок машина не движется. Выбор направления (руль) – это клавиши со стрелками. Тормоз – клавиша Ctrl. В ограждение въезжать нельзя – катастрофа. Когда приедете на финиш, посмотрите на счетчик времени и увидите ваш результат. Снова нажимайте на кнопку Начинаем сначала. Теперь расположение газонов будет другим. Сажайте за клавиатуру приятеля и смотрите, не покажет ли он время лучше, чем у вас. Можете посмотреть, кто из вас покажет лучшее время из 10 заездов.

Не удивляйтесь, что скорость на спидометре не равна пути, деленному на время. Ведь это мгновенная скорость (то есть настоящая скорость в данное мгновение), а не средняя скорость автомобиля (см. Физика, 9 класс).

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

Для проекта я выбрал вариант игры с одним автомобилем. Я вам намекну, как модифицировать проект, чтобы получилась игра с двумя автомобилями. Однако помните, что для грамотного создания игры с несколькими автомобилями вам нужно будет подняться на новый уровень программирования – научиться создавать собственные классы объектов.



Делим проект на части


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

Можно заметить, что в  нашем проекте нет таких независимых частей, как в будильнике. Тем не менее, удобно разделить создание проекта на 3 последовательные части:

1. Рисование всех элементов поля в результате нажатия на кнопку Начинаем сначала. Этим будет заниматься одна группа процедур.

2.       Затем управление машиной во время гонки. Этим будет заниматься другая группа процедур. Мы полностью задействуем идею использования таймера так, как я ее изложил в 13.5.11. На каждом импульсе таймера автомобиль должен будет проделывать весь цикл своего функционирования.

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



Первая часть – рисуем поле для гонки


Что рисовать.

1.       Сначала рисуем сам квадрат поля

2.       Затем старт и финиш

3.       Затем газоны

Код первой части. Разместите на форме кнопку. Дайте ей имя Кнопка_начинай_сначала. Приведу работающую версию первой части программы, а вслед за ней – пояснения.

Const Отступ As Short = 20                        'Имеется в виду отступ поля от края формы слева и сверху

Const Размер_поля As Short = 500           'Поле - квадрат, это сторона квадрата

Const Высота_формы As Short = Размер_поля + 4 * Отступ       'Оставляем отступ снизу    

Const Ширина_формы As Short = Размер_поля + 10 * Отступ    'Оставляем справа место для кнопок и меток

Const Размер_старта As Short = 40                                        'Старт - квадрат, это сторона квадрата

Const Размер_финиша As Short = 40                                     'Финиш - квадрат, это сторона квадрата

Dim X_старта, Y_старта, X_финиша, Y_финиша As Short   'Координаты старта и финиша

'Создаем пустую поверхность величиной с форму:

Dim Картинка As New Bitmap(Ширина_формы, Высота_формы)                      

Dim Гр As Graphics = Graphics.FromImage(Картинка)           'Создаем над ней объект класса Graphics

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

    Me.Width = Ширина_формы

    Me.Height = Высота_формы

    Randomize()                                                                            'Нам придется рисовать случайные газоны

End Sub

Private Sub Кнопка_начинай_сначала_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)  _

Handles Кнопка_начинай_сначала.Click

    Гр.Clear(Color.LightGray)                      'Стираем светло-серым цветом предыдущую картинку, если была

    Рисуем_поле()

    Рисуем_старт()

    Рисуем_финиш()

    Рисуем_газоны()

    Me.BackgroundImage = Картинка                'Придаем полю картинку, нарисованную в памяти


Будем действовать, как сказано в 12.4. Организуем в памяти «пустой лист для рисования». Для этого предназначены строки 8-10.
Загружаем форму. Содержимое процедуры Form1_Load ясно без пояснений. В эту процедуру программисты всегда вставляют то, что должно быть сделано один раз за все время выполнения проекта, причем в самом начале. После загрузки формы мы ничего на ней не видим, кроме кнопки. Эта процедура у нас еще будет расти.
Кнопка_начинай_сначала. Процедура Кнопка_начинай_сначала_Click начинается с того, что мы заполняем картинку в памяти светло-серым цветом. Это цвет ограждения в наших гонках. Дальше идет обращение к процедуре Рисуем_поле, которая просто рисует на этом сером фоне белый квадрат поля для гонки. Дальше идет обращение к трем процедурам рисования старта, финиша и газонов. Картинка в памяти готова. Предпоследняя строка процедуры делает эту картинку значением свойства формы BackgroundImage, то есть она становится «фоном» этой формы. Я мог поступить по-другому и перенести эту картинку на поверхность формы методом DrawImage класса Graphics, но в этом случае мне не удалось бы привычными средствами сделать PictureBox с изображением машины прозрачным, ведь так называемая «прозрачность» работает только по отношению к BackgroundImage. К тому же я предпочел не заботиться о перерисовке. Запустив проект, растяните форму и вы увидите, что картинка покрывает форму мозаикой. Последняя строка процедуры «проявляет» новый «фон» на форме, перерисовав ее оператором Me.Refresh().
Рисуем поле, старт и финиш. Содержимое соответствующих процедур очевидно. В формулах, определяющих координаты старта и финиша, вы легко разберетесь самостоятельно.
Рисуем газоны. Здесь в цикле рисуется 30 газонов, в каждой итерации по одному. Вы видите, что некоторые константы и переменные я объявил внутри процедуры. Так поступают, когда знают, что их значения нигде, кроме как в этой процедуре, не нужны. Значения чисел и вид формул для X_газона и Y_газона я выбрал так, чтобы газоны получались не слишком большие и не накладывались ни на ограждения, ни на старт с финишем. К тому же слишком большие газоны сольются в один большой газон и между ними нельзя будет проехать. Вид формул не принципиален, и если вам не хочется разбираться в них, можете их просто списать или придумать свои.
Результат. После ввода всего вышеприведенного кода у вас при каждом нажатии на кнопку Начинаем сначала должны рисоваться поле, старт, финиш и новая конфигурация газонов.

Вторая часть – управляем машиной


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

Для начала поместим на форму маленький PictureBox. Это ваша машина. Так и назовем его – Машина. Поставим задачу управлять им с клавиатуры как написано в задании на игру:

Направление движения может быть только горизонтальным и вертикальным, наискосок машина не движется. Выбор направления – это клавиши со стрелками. Тормоз – клавиша Ctrl. Газ – клавиша пробела. Щелчок по клавише газа или тормоза увеличивает или уменьшает скорость на какое-то значение.

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

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

Dim Цвет_фона As Color = Color.White                                       'Цвет фона при рисовании машины

Dim Исходная_машина As New Bitmap("Машина.BMP")           'Создаем исходное изображение машины

'Определяем 4 рабочих изображения машины:

Dim Машина_налево, Машина_вверх, Машина_направо, Машина_вниз As Bitmap                    

Dim x, y As Short            'Горизонтальная и вертикальная координаты автомобиля


Dim Шаг As Short           'Шаг  численно равен перемещению автомобиля по форме  на каждом такте таймера
Dim Газ As Boolean = False                                'Нажимаем ли мы на газ
Dim Тормоз As Boolean = False                         'Нажимаем ли мы на тормоз
Enum типРуль                                        'Куда едем
    вверх
    влево
    вниз
    вправо
End Enum
Dim Руль As типРуль
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
     '……………здесь расположены уже знакомые нам предыдущие строки процедуры……………
    Создаем_изображения_машины()
    Машина.BackColor = Color.Transparent
    Me.KeyPreview = True                                       'Чтобы машина отзывалась на клавиатуру
    txtВремя.ReadOnly = True
End Sub
Private Sub Кнопка_начинай_сначала_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)  _
Handles Кнопка_начинай_сначала.Click
     '……………здесь расположены уже знакомые нам предыдущие строки процедуры……………
    Ставим_машину_на_старт()
    txtВремя.Focus()
End Sub
Sub Создаем_изображения_машины()
    Создаем_изображение(Машина_налево, RotateFlipType.RotateNoneFlipNone)
    Создаем_изображение(Машина_вверх, RotateFlipType.Rotate90FlipNone)
    Создаем_изображение(Машина_направо, RotateFlipType.Rotate180FlipNone)
    Создаем_изображение(Машина_вниз, RotateFlipType.Rotate270FlipNone)
End Sub
Sub Создаем_изображение(ByRef Автомобиль As Bitmap, ByVal Поворот As RotateFlipType)
    Автомобиль = New Bitmap(Исходная_машина)     'Порождаем новый Bitmap из исходного
    Автомобиль.RotateFlip(Поворот)                            'Поворачиваем его
    Автомобиль.MakeTransparent(Цвет_фона)           'Делаем фон автомобиля прозрачным
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    Изменяем_скорость()
    Выбираем_куда_ехать_и_делаем_шаг()
    Машина.Left = x     : Машина.Top = y                'Передвигаем PictureBox


End Sub
Sub Ставим_машину_на_старт()
    x = X_старта      : y = Y_старта     ' Координаты машины приравниваются координатам точки старта
    Шаг = 0                                           'На старте стоим, а не едем
    Руль = типРуль.влево                   'Это чтобы машина знала, что когда стартуем, нужно ехать влево
    Машина.Image = Машина_налево                        'Ориентируем машину налево
End Sub
Sub Изменяем_скорость()
    If Газ Then Шаг = Шаг + 1
    If Тормоз Then
        Шаг = Шаг – 2                       'Потому, что тормоз действует быстрее газа
        'В результате быстрого торможения скорость может стать отрицательной, что и предотвращается:
        If Шаг < 0 Then Шаг = 0
    End If
    'Чтобы во время набора скорости и торможения приходилось без перерыва жать на педаль:
    Газ = False    : Тормоз = False
End Sub
Sub Выбираем_куда_ехать_и_делаем_шаг()
    Select Case Руль
        Case типРуль.вверх    : Машина.Image = Машина_вверх         : y = y - Шаг
        Case типРуль.вниз            : Машина.Image = Машина_вниз           : y = y + Шаг
        Case типРуль.влево    : Машина.Image = Машина_налево             : x = x - Шаг
        Case типРуль.вправо : Машина.Image = Машина_направо            : x = x + Шаг
    End Select
End Sub
'Обработка события - нажатия клавиши на клавиатуре для управления автомобилем:
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs)  _
Handles MyBase.KeyDown
    Select Case e.KeyCode
        Case Keys.Left                   : Руль = типРуль.влево
        Case Keys.Right          : Руль = типРуль.вправо
        Case Keys.Up              : Руль = типРуль.вверх
        Case Keys.Down          : Руль = типРуль.вниз
        Case Keys.Space        : Газ = True
        Case Keys.ControlKey        : Тормоз = True
    End Select
End Sub
Создаем изображения машины. Пустой PictureBox передвигать по форме неинтересно, поэтому рисуем в каком-нибудь графическом редакторе маленькую машинку (вид сверху) точно так же, как мы рисовали летающую тарелку для мультфильма. Сохраняем ее.


Во время гонок машина может ехать в 4 направлениях: влево, вправо, вверх, вниз. Значит, для придания естественности игре (чтобы машина не ехала «боком»), мы должны иметь 4 ориентации машины, между которыми будем в нужные моменты переключаться. Поэтому нам нужны 4 одинаковых изображения машины, повернутые в разные стороны. Для этого вовсе не нужно рисовать 4 рисунка и сохранять 4 файла (хотя и это можно, конечно). Вспомним, что метод RotateFlip умеет поворачивать картинки под прямыми углами. Его и используем.
Обращение к процедуре Создаем_изображения_машины мы находим, естественно, в процедуре Form1_Load, так как создать эти изображения достаточно один раз. Здесь же мы видим, как следующая строка делает прозрачным фон элемента управления Машина. Тело процедуры Создаем_изображения_машины, как видите, состоит из 4 операторов, каждый из которых создает одно из 4 изображений. Каждый из этих операторов есть в свою очередь обращение к процедуре Создаем_изображение. Рассмотрим ее получше.
В верхней части окна кода мы видим строки:
Dim Исходная_машина As New Bitmap("Машина.BMP")           'Создаем исходное изображение машины
'Определяем 4 рабочих изображения машины:
Dim Машина_налево, Машина_вверх, Машина_направо, Машина_вниз As Bitmap                    
Первая из них на основе нарисованной нами машины создает объект Исходная_машина типа Bitmap, который послужит исходным материалом для упомянутых 4 изображений. Каждое из них в дальнейшем получится поворотом картинки Исходная_машина на нужный угол. Тут же объявляются (но не создаются) объекты типа Bitmap для этих 4 изображений.
У процедуры Создаем_изображение два параметра: Первый из них (Автомобиль) как раз и является тем изображением, которое создает процедура. Он имеет тип Bitmap и обозначен словом ByRef, а не ByVal, так как в процессе работы изображение и создается и меняется. Второй параметр (Поворот) имеет тип перечисления RotateFlipType, которое как раз и используется при повороте картинок. Когда я рисовал машину, я нарисовал ее глядящей влево, отсюда становятся понятны значения второго параметра в обращении к процедурам.


Для формирования требуемого изображения машины нужно проделать 3 вещи:
Создать его из исходной картинки машины
Повернуть на нужный угол
Сделать фон прозрачным
Именно эти 3 вещи делают 3 оператора процедуры. Взгляните на них. Если вы не привыкли еще к параметрам объектного типа или вообще не понимаете текста этих операторов, я «перепишу» их по-другому. Например, для случая выполнения 2-го оператора процедуры Создаем_изображения_машины я просто для наглядности подставлю в них вместо параметров их значения:
    Машина_вверх = New Bitmap(Исходная_машина)     'Порождаем новый Bitmap из исходного
    Машина_вверх.RotateFlip(RotateFlipType.Rotate90FlipNone)                       'Поворачиваем его
    Машина_вверх.MakeTransparent(Цвет_фона)            'Делаем фон автомобиля прозрачным
Управляем машиной. Поместим на форму таймер. Настроим его интервал в пределах от 10 до 100 (по вкусу) На каждом импульсе таймера автомобиль должен будет проделать весь цикл своего функционирования. Из этого цикла нам для нормального передвижения машины достаточно пока озаботиться тремя вещами. Автомобиль должен:
Изменить или не изменить скорость в соответствии с приказами клавиатуры.
Изменить или не изменить направление движения в соответствии с приказами клавиатуры
Сделать очередной шаг в нужном направлении.
Эти три вещи как раз и выполняются операторами, которые мы видим в процедуре Timer1_Tick. Разберемся в них, но сначала заглянем в верхнюю части окна кода, где мы видим объявления переменных  x, y, Шаг, Газ, Тормоз и Руль. Зачем нам нужны последние три переменные? Нельзя ли попроще: управлять движением объектов с клавиатуры безо всяких переменных. Попытаться можно, и программа поначалу получится короче. Но с ростом сложности проекта будут расти неудобства. Например, машина не будет знать направления своего движения, а без этого трудно будет запрограммировать отскок от ограждения.
С учетом вышесказанного проглядите процедуру Form1_KeyDown. Она проста и в комментариях не нуждается.


События, связанные с клавиатурой, имеются у многих элементов управления. Почему мы выбрали события формы? Выбор какого-нибудь элемента управления был бы для нашей игры неудобен. Если мы для программирования реакции автомобиля на нажатия клавиш выберем, например, процедуру Private Sub Button1_KeyDown, то во время гонки мы не сможем щелкать по другим кнопкам, кроме Button1, так как иначе Button1 выйдет из фокуса и автомобиль перестанет реагировать на клавиши.
Изменяем скорость. Теперь посмотрим, как регулируется скорость. Для этого заглянем в процедуру Изменяем_скорость. Действие ее полностью определяется значением переменных Газ и Тормоз. Если это газ, то на данном такте таймера шаг, а значит и скорость, возрастет на 1. Если тормоз – упадет на 2 (потому что тормоз обычно действует сильнее газа). Отрицательный шаг в результате торможения означал бы задний ход, что неестественно, поэтому в программе это предотвращается.
Как видите, значения переменных Газ и Тормоз в конце процедуры принудительно приравниваются False. Это значит, что щелчок по клавише газа или тормоза приводит только к однократному увеличению или уменьшению скорости. Ведь на следующем тике таймера переменные Газ и Тормоз будут иметь значение False, а значит процедура не приведут к изменению шага. Значит на следующем такте таймера скорость не изменится. Чтобы она изменилась, нам нужно еще раз нажать на клавишу газа или тормоза. Обычно поступают по-другому – просто удерживают клавишу нажатой, при этом событие KeyDown возникает несколько раз в секунду и скорость меняется достаточно быстро. Это соответствует механике реального автомобиля – чтобы набирать скорость, нужно непрерывно и усиленно нажимать на педаль газа, а чтобы тормозить – тормоза.
Выбираем куда ехать и делаем шаг. Именно эта процедура задает ориентацию машины и направление движения. Загляните в нее. Ее дело – чувствовать одно из 4 значений переменной Руль и в соответствии с этим значением делать две вещи: поворачивать машину в нужном направлении и менять в этом направлении ее координату. Само же изображение автомобиля на экране прыгнет на указанную координату мгновением позже, на последней строке процедуры Timer1_Tick.


В этой процедуре каждая строка оператора Select Case присваивает свойству Image нашего элемента управления Машина значение одного из 4 изображений машины, а именно как раз того, которого требует нажатие клавиши со стрелкой на клавиатуре. При этом в выбранном направлении изменяется и координата x или y. В результате мы видим, что автомобиль глядит в ту же сторону, куда он едет.
Последняя строка процедуры Timer1_Tick перемещает элемент управления Машина на экране в соответствии с вычисленными координатами x и y.
Ставим машину на старт. Когда мы нажимаем кнопку Начинаем сначала, машина, где бы она ни была и что бы ни делала, должна прыгнуть на старт и замереть, глядя влево. Именно это поведение обеспечивают все пять операторов процедуры Ставим_машину_на_старт. И именно поэтому обращение к этой процедуре включено в процедуру Кнопка_начинай_сначала_Click.
Чтобы машина реагировала на клавиатуру. Я уже говорил ранее, что поскольку один какой-нибудь элемент управления на форме всегда находится в фокусе, до процедуры Form1_KeyDown дело не дойдет. Это значит, что наша машина не будет реагировать на клавиши. Для борьбы с этим в процедуре Form1_Load свойство формы KeyPreview установлено в True. Это означает приказ компьютеру при нажатии на клавишу вызывать событие формы, а уж потом другого объекта. Но этого недостаточно. И вот почему. При запуске проекта фокус автоматически устанавливается на кнопку «Начинаем сначала». Первое нажатие во время гонки на клавишу со стрелкой приводит не к выбору направления машиной, а к перескакиванию фокуса с кнопки на другой объект формы. Если же вы вместо этого нажимаете на пробел, то машина дает газ, но радоваться тоже рано. Кнопки воспринимают пробел и Enter, как приказ на нажатие, и поэтому кнопка «Начинаем сначала» тут же сама собой нажимается и получается, что немедленно после старта газоны перерисовываются, а машина снова прыгает на старт.
Чтобы прекратить это безобразие, поместим на форму текстовое поле. Позже оно нам пригодиться для отображения счетчика времени, а сейчас у нас другая забота: увести от вредной кнопки фокус куда-нибудь подальше. Текстовое поле ведет себя смирно, назовем его txtВремя и включим в процедуру оператор, уводящий фокус с кнопки на это поле:


    txtВремя.Focus()
Помогло. При нажатии клавиш со стрелками фокус не покидает текстовое поле. Но осталась одна шероховатость. Если в текстовом поле есть текст, то при нажатии клавиш со стрелками текстовый курсор будет слоняться по текстовому полю влево-вправо, а при нажатии пробела – вставлять в текст пробел. Чтобы от этого избавиться и чтобы предотвратить в дальнейшем возможность нечаянно с клавиатуры испортить показания счетчика времени, помещаем в процедуру Form1_Load оператор
    txtВремя.ReadOnly = True
Это означает, что содержимое текстового поля можно только читать, но вручную не менять. Изменения возможны только программным путем.
Теперь все нормально.
Результат. После ввода всего вышеприведенного кода у вас при каждом нажатии на кнопку Начинаем сначала должны рисоваться поле, старт, финиш и новая конфигурация газонов, машина должна нормально вставать на старт, а при нажатии нужных клавиш должна нормально ускоряться, тормозить и ездить по всей форме во всех направлениях, не разбирая пока, где асфальт, где преграды и все остальное.

Третья часть – Поведение машины, организация счетчиков и пр.


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

Определить, где он находится (асфальт, газон, ограждение, финиш) и действовать соответственно

Изменить нужным образом показания приборов на пульте управления

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

Dim Шаг, Время, Путь As Integer  

'Чтобы секундомер судьи запускался автоматически, когда мы стартуем:                          

Dim Секундомер_запущен As Boolean

Private Sub Кнопка_начинай_сначала_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)  _

Handles Кнопка_начинай_сначала.Click

     '……………здесь расположены уже знакомые нам предыдущие строки процедуры……………

    Me.KeyPreview = True                                                  'чтобы машина слушалась руля и педалей

    Секундомер_запущен = False                                     'Мы еще не стартовали, секундомер не пущен,

    Шаг = 0    : Время = 0    : Путь = 0                               'на приборах - нули

    lbl_Сообщение.Text = ""                                              'Пока никакого сообщения нет

End Sub

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

    Определяем_где_мы()

    Изменяем_скорость()

    Выбираем_куда_ехать_и_делаем_шаг()

    Машина.Left = x     : Машина.Top = y            

    Отображаем_информацию()

End Sub

Sub Определяем_где_мы()

    Dim Цвет_под_автомобилем As Color = Картинка.GetPixel(x, y)

    If Цвет_под_автомобилем.ToArgb = Color.Green.ToArgb And Not Шаг < 1 _

                                Then Шаг = 1                       'На газоне скорость мала

    If Цвет_под_автомобилем.ToArgb = Color.Red.ToArgb    _

                                Then Приехали()    : lbl_Сообщение.Text = "Финиш!"


    If Цвет_под_автомобилем.ToArgb = Color.LightGray.ToArgb  _
                                Then Приехали()    : lbl_Сообщение.Text = "Врезались в ограждение!"
End Sub
Sub Приехали()
    Шаг = 0                                                            'Раз приехали, надо остановиться, …
    Секундомер_запущен = False                              'выключить секундомер и сделать так, …
    Me.KeyPreview = False                                          ' чтобы машина НЕ слушалась руля и педалей
End Sub
Sub Изменяем_скорость()
    Dim Максимальная_скорость As Short = 15                         'Быстрее мотор не тянет
    If Газ And Шаг < Максимальная_скорость Then Шаг = Шаг + 1
             '……………здесь расположены уже знакомые нам последующие строки процедуры………
End Sub
Private Sub Отображаем_информацию()
    'Счетчик времени запускается только тогда, когда мы стартуем:
    If Секундомер_запущен Then Время = Время + 1             'Время – число импульсов таймера.
    txtВремя.Text =   "Время = "   &    Время                           'Показания секундомера
    lbl_Скорость.Text =   "Скорость = "   &    Шаг              'Показания спидометра – скорость (Шаг)
    Путь = Путь + Шаг                                                       'Путь – это сумма шагов
    lbl_Путь.Text =   "Путь = "   &    Путь                           'Показания спидометра - путь
End Sub
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs)  _
Handles MyBase.KeyDown
    Секундомер_запущен = True    'Секундомер запускается от нажатия на любую клавишу
    Select Case e.KeyCode
             '……………здесь расположены уже знакомые нам строки процедуры……………
    End Select
End Sub
Поведение машины на газоне, на ограждении, на финише. Разместите на форме метку, в которую будут помещаться сообщения о том, что мы находимся на финише или врезались в ограждение. Назовите ее lbl_Сообщение.


Рассмотрим главную процедуру проекта – Timer1_Tick. В соответствии с двумя дополнительными делами, которые должен выполнить автомобиль, в процедуру добавились два оператора:
    Определяем_где_мы()
     '……………
    Отображаем_информацию()
Поговорим о процедуре Определяем_где_мы. Ее задача – задать реакцию автомобиля на три ситуации: попадание на газон, на финиш и на ограждение. Метод, при помощи которого автомобиль определяет, где он, самый простой – GetPixel. Его мы разобрали в 12.7.4. Поскольку газон я рисовал зеленым, финиш – красным , а ограждение – светло-серым (LightGray), то метод GetPixel выдает на них разные результаты. Операторы If эти результаты анализируют и задают реакцию автомобиля. Как видите, на газон машина реагирует только установкой скорости = 1 (если она уже не была меньше 1), на два других цвета она выполняет процедуру Приехали и выдает подходящее сообщение в метку lbl_Сообщение.
Немного о грамматике. Мы не могли записать
    If Цвет_под_автомобилем = Color.Green
так как VB не может устанавливать с помощью знака равенства равенство таких объектов, как цвет. Пришлось предварительно методом ToArgb преобразовывать цвета к специальному представлению ARGB, которое допускает подобное сравнение:
    If Цвет_под_автомобилем.ToArgb = Color.Green.ToArgb ……
Цель процедуры Приехали ясна: остановить машину, заблокировать руль и педали и остановить секундомер, чтобы мы могли поглядеть на результат. Раз так, то при следующем нажатии на кнопку Начинаем сначала руль и педали должны быть разблокированы, для чего мы переносим оператор
    Me.KeyPreview = True                                                  'чтобы машина слушалась руля и педалей
из процедуры Form1_Load  в процедуру Кнопка_начинай_сначала_Click.
Организация счетчиков. В дополнение к текстовому полю txtВремя, в котором отображается текущее время пробега, разместите на форме метку lbl_Скорость, в которой будет отображаться текущая скорость (Шаг) машины, и  метку lbl_Путь, в которой будет отображаться путь, пройденный машиной.


Вычислением этих трех величин и их отображением занимается процедура Отображаем_информацию. Она выполняется на каждом тике таймера. Заглянем в нее. Содержание ее очевидно. В пояснении нуждаются только моменты запуска и остановки секундомера. Когда мы нажимаем на кнопку Начинаем сначала, все три упомянутые величины устанавливаются в 0, а переменная Секундомер_запущен – в False (см. текст процедуры Кнопка_начинай_сначала_Click). Поэтому после нажатия на кнопку секундомер стоит на нуле. Как только мы нажимаем клавишу газа или какую-нибудь другую, секундомер запускается (см. текст процедуры Form1_KeyDown). Останавливается он после этого только в том случае, если машина «приехала» или мы нажали на кнопку Начинаем сначала.
Упомяну еще, что переменная Время является не временем в секундах, а количеством тактов таймера, прошедших со старта.
Максимальная скорость. При большой скорости машина делает огромные шаги от одного такта таймера к другому. А ведь цвет под собой она ощущает не непрерывно, а только на тактах таймера. Значит она может перепрыгнуть через тоненький газон, не заметив его. То же относится к финишу и к ограждению. Если шаг превысит размер финиша, то машина и его может перепрыгнуть, не заметив. Машина может  перепрыгнуть через тонкое ограждение и упереться в край формы, при этом VB выдаст ошибку (кстати – почему?). Значит, нужно ограничить скорость машины. Поскольку размер финиша равен 40, а минимальная толщина ограждения у нас равна 20, то я выбрал максимальный шаг = 15. Простая механика ограничения приведена в процедуре Изменяем_скорость. А через газоны пусть перепрыгивает!
Резюме. Ну вот, кажется, и все пояснения. Организовывая переменные для хранения информации о состоянии машины и создавая много маленьких процедур с удачными длинными названиями, мы сделали программу более читабельной и открытой для усовершенствования. Попробуйте обойтись малым числом длинных процедур – и вы увидите, что программа, может быть и став покороче (а вполне может быть и нет), потеряла четкость, понятность и легкость модификации.

Недоработки проекта


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

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

2. Координатами (х, у) машины считается ее левый верхний угол, а не центр. Это приводит к некоторой неестественной асимметрии поведения машины при поворотах и переезде с цвета на цвет. Причина этого – слишком простая строка в процедуре Timer1_Tick:

    Машина.Left = x     : Машина.Top = y                'Передвигаем PictureBox

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

3. Сам Бог велел добавить в проект управление машиной с помощью мыши. Например, щелчок мышью в стороне от машины или сзади вызывает поворот машины в соответствующую сторону. Щелчок спереди – газ, щелчок на правую клавишу мыши в любой точке формы – тормоз. Для этого вам нужно будет написать процедуру Form1_MouseDown, придавая в ней нужные значения переменным Руль, Газ и Тормоз аналогично тому, как это делает процедура Form1_KeyDown. Вам придется поразмыслить, как правильно сравнивать координаты машины и мыши, чтобы добиться нужного результата.

Задание 100.    

Исправьте недоработки проекта.



Гонки двух автомобилей


Если вы хотите играть вместе с другом, вам нужны гонки двух машин. Несмотря на то, что ничего нового вам придумывать не придется, размер программы вырастет. Вам придется иметь по два экземпляра переменных, описывающих поведение каждой машины. Например, вместо переменной Руль вам придется иметь две переменные – Руль1 и Руль2. Чтобы не писать по два экземпляра процедур, задающих поведение каждой машины, вам нужно будет снабдить эти процедуры параметрами. Процедуру Form1_KeyDown вам придется дополнить новыми клавишами для управления второй машиной. При этом нужно учесть, что удерживать клавиши нажатыми будет теперь нельзя без усложнения программы. А если у вас гонки 10 автомобилей? Проблемы нарастают.

VB конечно же предлагает универсальное решение этой проблемы. Он, как и все серьезные и мощные языки программирования, является объектно-ориентированным языком. В частности, имеется в виду, что он позволяет программисту создавать собственные классы объектов. Научившись создавать собственные классы, вы сможете создать класс «автомобиль» и пользоваться каким угодно числом экземпляров этого класса. Программу поведения автомобиля вы пишете один раз и только для одного автомобиля. Остальные автомобили будут автоматически пользоваться ей же. Как все это делается, описывается в Глава 22. , но вам пока рано читать тот материал.



Задания на проекты


Нельзя научиться кататься на велосипеде, только следя за тем, как катаются другие. Нужно самому сесть на велосипед. Вы не научитесь создавать проекты, только разбирая их создание по книжке. Нужно создать свой собственный проект. В Задание 97 вы уже создавали небольшой проект «Шахматные часы». Пришла пора создать проект посложнее и побольше размером. Я предложу вам на выбор один из двух.

Торпедная атака. Начинается все красивой заставкой с названием игры. По щелчку мыши заставка пропадает. Компьютер спрашивает ваше имя, чтобы во время игры оно горело красивыми яркими буквами в углу экрана. В меню вы можете посмотреть правила игры, сведения об авторе, выбрать игру со звуком или без. Но вот игра началась. На экране морской пейзаж (нарисованный вами или взятый готовым). Вы – капитан подводной лодки и смотрите в перископ. Вдали экрана по горизонтали плывет вражеский корабль. У нижнего края экрана находится ваш торпедный аппарат. Вы целитесь, с клавиатуры или мышкой поворачивая торпедный аппарат. В подходящий момент времени вы нажимаете клавишу – и торпеда плывет вдаль, уменьшаясь по мере удаления. Если вы попали, то экран озаряется вспышкой от взрыва, на мгновение виден и сам взрыв, раздается коротенькая торжествующая мелодия, на экране – коротенький поздравительный текст, счетчик подбитых кораблей на экране увеличивается на 1. Если не попали, то зрительные и звуковые эффекты, сами понимаете –  совсем другие. В любом случае уменьшается на 1 счетчик оставшихся торпед. Затем плывет следующий корабль. И так далее. Когда торпеды у вас кончатся (скажем, их было 10), игра заканчивается. Программа анализирует ваши успехи и в зависимости от них выдает на экран текст, скажем «Мазила!», если вы не попали ни разу из 10, или «Профи!», если вы попали 8 раз. Затем спрашивает, будете ли вы играть еще.

Помощь: Как компьютер определит, попала торпеда в корабль или не попала? Нужно в тот момент, когда торпеда доплывет до линии движения корабля, сравнить горизонтальные координаты корабля и торпеды, и если они достаточно близки, считать, что попала.


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

Рис. 14.2
Слева – большая ракетка компьютера, справа – маленькая ваша. Сверху и снизу – края корта. Мяч летает по корту, отскакивая от его краев по закону отражения (что такое «по закону отражения», объяснено в Задание 102). Ракетка компьютера совершает равномерные, медленные, бессмысленные возвратно-поступательные движения вверх-вниз, отскакивая от краев корта. Движением вашей ракетки управляете вы. Или с клавиатуры, или мышкой. Вы можете двигать ракетку вверх или вниз с постоянной скоростью, или останавливать ее. Влево-вправо ракетки не движутся. Наткнувшись на ракетки, мяч отскакивает. Если ракетка неподвижна, то мяч отскакивает по закону отражения. Если ракетка движется, то на направление отскока мяча немного влияет направление движения ракетки. Цель – не пропустить мяч за пределы корта. Если мяч улетит направо – очко проиграли вы, налево – проиграл компьютер.
Красивая заставка, меню, счет партий, подведение итогов, зрительные и звуковые эффекты должны быть не хуже, чем у «Торпедной атаки».
Помощь: Как компьютер определит, попал мяч в ракетку или не попал? Нужно в тот момент, когда мяч долетит до линии движения ракетки, сравнить вертикальную координату мяча с вертикальными координатами верхнего и нижнего краев ракетки, и если мяч находится между краями ракетки, считать, что попал.


Улучшение.  Пусть игрок сможет в меню выбирать из нескольких уровней трудности. Трудность удобнее всего увеличивать, увеличивая скорость мяча.
Может возникнуть не совсем естественная ситуация, когда скорость мяча в результате ударов о ракетки приобретет слишком большую вертикальную составляющую. Тогда мяч будет в основном биться между верхним и нижним краями корта, лишь изредка попадая на ракетки. Возможно, имеет смысл вертикальную составляющую скорости ограничить.
Чтобы избежать бесконечной нудной перекидки мяча, можно при отскоке от движущейся ракетки увеличивать горизонтальную составляющую скорости на очень маленькое случайное число. Тогда скорость мяча от удара к удару будет постепенно возрастать, и скучно не будет.
Другие проекты. Вам совсем не обязательно выполнять именно одно из двух предложенных мною заданий. Если вам хочется создать проект на другую тему – на здоровье! Но помните, задуманный вами проект, легкий на вид, на деле может оказаться слишком сложным для выполнения и вам придется его бросить. Или наоборот – слишком простым, и вы не получите от его выполнения большой пользы.
_______________    _    _________________
Мы с вами закончили первый из двух циклов знакомства с Visual Basic.NET. Если вам удался последний проект и он работает не хуже, чем указано в задании (пусть даже и без звука), если вы при этом умело применяли переменные величины и давали им длинные имена, если у вас в проекте много маленьких (а не мало больших) процедур, то все в порядке: у вас должна появиться уверенность, что теперь вы можете создавать проекты любого размера и любой сложности, а значит цель этого цикла достигнута. Я вас поздравляю – вам присваивается звание «Программист-любитель III ранга»!

Проект – Гонки (игра)


Мы с вами создали уже два правильно написанных больших проекта: «Парк под луной» и «Будильник». И этого, конечно, мало. У нас нет опыта создания проектов с движением объектов по форме, да к тому же под управлением мыши и клавиатуры. Поэтому я решил создать с вами такой проект (строк на 150). На этот раз это будет игра.



Переменные с индексами


В основе массивов лежит понятие индекса. В математике широко применяются так называемые индексированные переменные. На бумаге они записываются так:

x1        x2        b8        yi        yi-6           z i j        z i+1 j

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

X(1)     X(2)      B(8)     Y(i)      Y(i-6)      Z(i,j)      Z(i+1, j)

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

 Возьмем, например, бесконечный ряд чисел Фибоначчи:  

1   1   2   3   5   8   13   21   34 .....

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

Первое число Фибоначчи обозначим так:                f(1),

Второе число Фибоначчи обозначим так: f(2)        

и т.д.

Тогда можно записать, что     

f(1)=1    f(2)=1    f(3)=2    f(4)=3    f(5)=5    f(6)=8 ......

Очевидно, что   

f(3)=f(1)+f(2), 

f(4)=f(2)+f(3),  

f(5)=f(3)+f(4)     

и т.д.

Как математически одной формулой записать тот факт, что каждое

из чисел является суммой двух предыдущих? Математики в индексном виде записывают это так:

f(i)=f(i-2)+f(i-1).

Для пояснения этой формулы подставим вместо i любое число, например, 6.  Тогда получится:

f(6)=f(6-2)+f(6-1)               

или упрощая:

f(6)=f(4)+f(5),

что соответствует определению чисел Фибоначчи.


Какое бы i, большее 2, мы не подставляли, получается правильное равенство. Значит, формула верна сама по себе.
Задание 101.    
Запишите на бумаге в индексном виде, как получается из предыдущего числа ряда последующее:
1)       14     18     22     26   .....
2)        6      12     24     48   ....
3)        3     5     9    17    33    65   ....
Еще примеры. Вот еще примеры, когда математики предпочитают использовать индексы. Пусть мы на протяжении года каждый день раз в сутки измеряли температуру за окном. Тогда вполне естественно обозначить через t(1) температуру первого дня года,  t(2) – второго, ..... , t(365) – последнего. Пусть 35 спортсменов прыгали в высоту. Тогда через h(1) можно обозначить высоту, взятую первым прыгуном, h(2) – вторым и т.д.
Пока мы только немного привыкли к индексам, но никакой выгоды от них не получили. Выгода – в следующем разделе.

Основы работы с одномерными массивами


Пример. Рассмотрим на примере простой задачи, как VB управляется с массивами. Предположим, в зоопарке живут три удава. Известна длина каждого удава в сантиметрах (500, 400 и 600). Какая длина получится у трех удавов, вытянутых в линию?

Обозначим длину первого удава – dlina(1),  второго – dlina(2), третьего – dlina(3). Прикажем VB отвести под эту индексированную переменную массив ячеек в памяти. Делается это так:

Dim   dlina  (3)   As Integer

Здесь 3 - верхняя граница индекса. В целом эту строку можно перевести так: Отвести в памяти под переменную dlina ряд ячеек типа Integer, пронумерованных от 0 до

3.

Почему от 0? Нам не нужно от 0! Нам нужно от 1! – Ну, это уже ваши проблемы – отвечает VB  – я могу только от 0! – Ну, что ж – отвечаем мы – в конце концов, пусть будет от 0. В конце концов, это означает всего лишь то, что в памяти будет отведена лишняя ячейка для значения dlina(0). А мы ее просто не будем использовать, и все! И никаких значений туда не будем записывать. И никаких проблем.

Вот программа полностью:

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

        Dim dlina(3) As Integer

        Dim summa As Integer

        dlina(1) = 500

        dlina(2) = 400

        dlina(3) = 600

        'В этот момент в трех ячейках памяти уже находятся числа

        'и с ними можно выполнять арифметические действия

        summa = dlina(1) + dlina(2) + dlina(3)

        Debug.WriteLine(summa)

End Sub

А теперь запустите отладочный пошаговый режим выполнения программы и загляните в окне Locals в значения dlina(1), dlina(2), dlina(3), summa. Заметьте на будущее, что желтая полоса не перескакивает через оператор

        Dim dlina(3) As Integer

и плюсик в окне Locals у переменной dlina появляется только после его выполнения. Жмите на плюсик – и перед вами значения всех 4 элементов массива.

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



Мощь одномерных массивов


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

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

Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button6.Click

        Dim dlina() As Integer = {0, 500, 400, 600}

        Debug.WriteLine(dlina(1))

        Debug.WriteLine(dlina(2))

        Debug.WriteLine(dlina(3))

End Sub

Private Sub Button7_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button7.Click

        Dim dlina() As Integer = {0, 500, 400, 600}

        Dim i As Integer

        i = 1

        Debug.WriteLine(dlina(i))

        i = 2

        Debug.WriteLine(dlina(i))

        i = i + 1

        Debug.WriteLine(dlina(i))

End Sub

Private Sub Button8_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button8.Click

        Dim dlina() As Integer = {0, 500, 400, 600}

        Dim i As Integer = 1

        Dim k As Integer = 10

        Debug.WriteLine(dlina(i))

        Debug.WriteLine(dlina(k - 8))

        i = 2

        k = 1

        Debug.WriteLine(dlina(k + i))

End Sub

Мощи здесь пока не видно, увидите чуть позже.

Вопрос: Продолжаем привыкать. Не подходя к компьютеру, ответьте, что напечатает следующая процедура:

Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click

        Dim a(100), i As Integer

        i = 2   : a(3) = 10   : a(i) = 100   : a(i + 6) = a(i) + a(5 - i)   : i = 0   : Debug.WriteLine(a(i + 3) + a(2) + a(i + 8))

End Sub

Ответ: 220

Хорошо бы вы получили правильный ответ самостоятельно. Если не смогли получить, значит вы не понимаете чего-то важного или просто невнимательны. В этом случае читайте пояснение:


Первая ступенька:   i = 2 ® a(3) = 10 ® a(2) = 100 ® a(2 + 6) = a(2) + a(5 - 2) ® i = 0 ® Debug.WriteLine( a(0 + 3) + a(2) + a(0 + 8))
Вторая ступенька:   i = 2 ® a(3) = 10 ® a(2) = 100 ® a(8) = a(2) + a(3) ® i = 0 ® Debug.WriteLine( a(3) + a(2) + a(8))
Третья ступенька:   i = 2 ® a(3) = 10 ® a(2) = 100 ® a(8) = 110 ® i = 0 ® Debug.WriteLine( 10 + 100 + 110)
Цикл – мощное оружие при работе с массивами. Теперь решим задачу про суммарную длину удавов в предположении, что удавов не 3, а 1000:
Private Sub Button9_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button9.Click
        Dim Число_удавов As Integer = 1000
        'Верхняя граница индекса может быть выражена переменной величиной:
        Dim dlina(Число_удавов) As Integer
        Dim summa, i As Integer
        'Вводим с клавиатуры длины тысячи удавов, хоть это и утомительно и никто так не делает.
        'Здесь на первом выполнении цикла i=1 и поэтому компьютер вводит число в ячейку dlina(1),
        'на втором -  i=2 и поэтому компьютер  вводит число в ячейку dlina(2) и т.д.
        For i = 1 To Число_удавов
            dlina(i) = InputBox("Введите длину " &  i & "-го удава")
        Next
        'Определяем суммарную длину тысячи удавов:
        summa = 0
        For i = 1 To Число_удавов
            summa = summa + dlina(i)
        Next
        Debug.WriteLine(summa)
End Sub
Отлаживая эту программу, возьмите, конечно, вместо числа 1000 какое-нибудь маленькое число.
Пример. Продолжаем привыкать к индексам. Решим еще одну задачу. Дан ряд из 10 произвольных чисел: a(1), a(2), ... , a(10). Подсчитать и напечатать суммы каждой из восьми троек стоящих рядом чисел.
первая тройка:          a(1)+a(2)+a(3)  
вторая тройка:          a(2)+a(3)+a(4) 
третья тройка:          a(3)+a(4)+a(5)
  ......
восьмая тройка:        a(8)+a(9)+a(10)
Вот программа:
Private Sub Button10_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button10.Click
        Dim a() As Integer = {0, 5, 3, 4, 0, 20, 10, 23, 2, 9, 1000}
        Dim i As Integer
        For i = 1 To 8
            Debug.WriteLine(a(i) + a(i + 1) + a(i + 2))
        Next
End Sub
Вот что она напечатает:
12
7
24
30
53
35
34
1011
Следующие два задания очень важны, так как это ваши первые задания по массивам.
Задание 102.      
Напишите с использованием массива программу вычисления среднегодовой температуры. Подсчитайте также количество теплых дней в году (когда температура выше  20 градусов). Узнайте, каким по порядку идет самый жаркий день. (Для отладки в компьютере годом можно считать неделю.)
Задание 103.      
Вычислить и распечатать первые 139 чисел Фибоначчи.

Одномерные массивы


Одна из типичных задач программирования формулируется примерно так. Имеется большое количество данных, например, тех же температур или высот. С этими данными компьютер должен что-нибудь сделать, например, вычислить среднегодовую температуру, количество морозных дней, максимальную взятую высоту и т.п. Раньше мы уже вычисляли подобные вещи, и при этом данные вводили в компьютер с клавиатуры одно за другим. При этом всегда получалось, что они вводятся в одну и ту же ячейку памяти (см. Глава 10. ). Однако, программистская практика показывает, что удобно, а часто и необходимо иметь данные в оперативной памяти сразу все, а не по очереди. Тогда для задачи, скажем, про температуру нам понадобится 365 ячеек. Эти 365 ячеек мы и назовем массивом. Итак, массивом можно назвать ряд ячеек памяти, отведенных для хранения значений индексированной переменной. На вопрос о том, как большое количество значений оказывается в памяти, отвечу, что обычно они вводятся из файла (19.2).



Двумерные массивы


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

1-й день

2-й день

3-й день

4-й день

Метеостанция 1

-8

-14

-19

-18

Метеостанция 2

25

28

26

20

Метеостанция 3

11

18

20

25

Требуется (в порядке возрастания трудности):

1) Распечатать температуру на 2-й метеостанции за 4?й день и на 3-й метеостанции за 1?й день.

2) Распечатать показания термометров всех метеостанций за 2?й день

3) Определить среднюю температуру на 3-й метеостанции

4) Распечатать всю таблицу

5) Распечатать, в какие дни и на каких метеостанциях температура была в диапазоне 24-26 градусов тепла

Для этого обозначим показания термометров при помощи индексированной переменной с двумя индексами по следующей схеме:

t(1,1)

t(1,2)

t(1,3)

t(1,4)

t(2,1)

t(2,2)

t(2,3)

t(2,4)

t(3,1)

t(3,2)

t(3,3)

t(3,4)

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

В памяти отводим массив из ячеек типа Integer под значения индексированной переменной t. Будем называть его двумерным массивом:

Dim   t  (3, 4)   As Integer

Аналогично одномерному массиву, первый индекс здесь будет меняться не от 1 до 3, а от 0 до 3, а второй – от 0 до 4. Таким образом в памяти будет отведен следующий массив ячеек:

t(0,0)

t(0,1)

t(0,2)

t(0,3)

t(0,4)

t(1,0)

t(1,1)

t(1,2)

t(1,3)

t(1,4)

t(2,0)

t(2,1)

t(2,2)

t(2,3)

t(2,4)

t(3,0)

t(3,1)

t(3,2)

t(3,3)

t(3,4)

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

Программа:

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


        Dim t(3, 4) As Integer
        Dim i, j, s As Integer
        ' Зададим значения элементов массива примитивным присваиванием:
        t(1, 1) = -8              : t(1, 2) = -14     : t(1, 3) = -19     : t(1, 4) = -18
        t(2, 1) = 25             : t(2, 2) = 28             : t(2, 3) = 26            : t(2, 4) = 20
        t(3, 1) = 11             : t(3, 2) = 18             : t(3, 3) = 20            : t(3, 4) = 25
        'Выполняем 1 пункт задания:
        Debug.WriteLine(t(2, 4) & "         " & t(3, 1))
        'А теперь распечатаем второй столбец массива (2 пункт задания):
        Debug.Write("Второй столбец:       ")
        For i = 1 To 3 : Debug.Write(t(i, 2) & "      ") : Next
        Debug.WriteLine("")                                                               'Переводим строку
        'Определим среднее значение элементов третьей строки (3 пункт задания):
        i = 3
        s = 0
        For j = 1 To 4 : s = s + t(i, j) : Next
        Debug.WriteLine("Средняя температура равна  "  &   s / 4)
        'Распечатаем всю таблицу (4 пункт задания):
        For i = 1 To 3
            For j = 1 To 4
                Debug.Write(t(i, j) & "         ")
            Next j
            Debug.WriteLine("")                                                           'Переводим строку
        Next i
        'Распечатаем станции и дни с температурой 24-26 градусов (5 пункт задания):
        For i = 1 To 3
            For j = 1 To 4
                If t(i, j) >= 24 And t(i, j) <= 26 Then Debug.WriteLine("Станция " & i & "      день " & j)
            Next j
        Next i
End Sub
Вот что напечатает эта программа:
20         11
Второй столбец:       -14      28      18     
Средняя температура равна 18,5
-8         -14         -19         -18        
25         28         26         20        


11         18         20         25        
Станция 2      день 1
Станция 2      день 3
Станция 3      день 4
Инициализация двумерного массива. Вместо фрагмента, объявляющего массив и задающего его значения присваиванием:
Dim t(3, 4) As Integer
t(1, 1) = -8         : t(1, 2) = -14     : t(1, 3) = -19     : t(1, 4) = -18
t(2, 1) = 25        : t(2, 2) = 28            : t(2, 3) = 26             : t(2, 4) = 20
t(3, 1) = 11        : t(3, 2) = 18            : t(3, 3) = 20             : t(3, 4) = 25
можно использовать более короткую запись:
Dim t ( , ) As Integer = { {99, 99, 99, 99, 99} , {99, -8, -14, -19, -18} , {99, 25, 28, 26, 20} , {99, 11, 18, 20, 25} }
Разберемся в ней. Верхние границы индексов не указываются, но запятая в круглых скобках остается, чтобы было понятно, что массив двумерный, а не одномерный. Чисел в фигурных скобках нужно столько, чтобы заполнить столбцы 0,1,2,3,4 и строки 0,1,2,3. Каждую строку берем в свои фигурные скобки и отделяем от других строк запятой. Весь массив снаружи «обнимается» дополнительной парой фигурных скобок. Ненужные нам нулевую строку и нулевой столбец я заполнил числами 99, а мог бы и любыми другими, так как в программе мы их не используем.
Задание 104.      
Вычислить разницу между максимальной и минимальной температурой во всей таблице.

Какие бывают массивы


Массивы бывают не только числовые, но и строковые и типа Date и многие прочие. Например:

Dim s(50) As String

Это означает, что в каждой из 51 ячеек должно находиться не число, а произвольная строка. А объявление

Dim DT(10) As Date

означает, что в каждой из 11 ячеек должна находиться дата.

Пример. Вот элементарный пример использования строкового массива:

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

        Dim s(50) As String

        s(21) = "Привет"

        s(22) = s(21) + " всем!!!"

        Debug.WriteLine(s(22))

        Debug.WriteLine(Len(s(21)))

End Sub

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

Привет всем!!!

6

Пример. Вот пример работы с массивами других типов:

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

        Dim b(30, 6) As Boolean

        Dim DT(10) As Date

        b(2, 3)  =  False

        b(5, 0)  =  Not  b(2, 3)

        Debug.WriteLine (b(5, 0))

        DT(2)  =  #1/15/2003 11:59:42 PM#

        DT(0)  =  DT(2).AddDays(10)

        If  b(5, 0)  Then Debug.WriteLine (DT(0))

End Sub

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

True

25.01.2003 23:59:42

Еще пример:

Enum типРуль

        вверх

        влево

        вниз

        вправо

End Enum

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

        Dim Руль(300) As типРуль

        Руль(200) = типРуль.влево

        Руль(220) = Руль(200) + 1

        Debug.WriteLine(Руль(220))

End Sub

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

вниз

Бывают массивы, состоящие из структур, объектов, но о них мы поговорим позже.

Многомерные массивы. Массивы могут быть одномерные, двумерные, трехмерные, четырехмерные и т.д.:

Dim  a (10) As Integer

-одномерный массив

11 ячеек

Dim  a (10, 5) As Integer

-двумерный массив

66 ячеек (11*6)

Dim  a (9, 4, 1) As Integer

-трехмерный массив

100 ячеек (10*5*2)

Dim  a (9, 4, 1, 2) As Integer

-четырехмерный массив

300 ячеек (10*5*2*3)



Использование массивов при программировании игр


Идеология. Есть ли польза от массивов при программировании игр? Вопрос праздный. Массивы необходимы и для шахмат, и для шашек, и для морского боя, и для крестиков-ноликов, и для многих других игр, в особенности для тех, где игра проходит на прямоугольном поле, расчерченном на квадраты. Возьмем для примера игру против компьютера в крестики-нолики на поле размером 3 на 3. Компьютеру приходится здесь рисовать на экране большие клетки, а в них – нолики (кружочки) после ваших ходов и крестики (пересекающиеся косые линии) после своих. Но этого умения недостаточно. Компьютеру ведь еще надо соображать, куда ставить крестики. А для этого нужно как минимум знать, где уже стоят крестики и нолики. А откуда он это знает? Если знание об этом хранится только на экране, то это очень неудобно, так как анализировать информацию о пикселях экрана трудно. Гораздо разумнее заранее организовать массив  Dim  a (3, 3) As Integer  и записывать туда в нужные места нолики после ходов человека и, скажем, единички после ходов компьютера. Сразу же после записи в элемент массива нуля или единицы программа должна рисовать в соответствующем месте экрана кружок или крестик. Мыслить компьютер мог бы при помощи примерно таких операторов –

If   a(1,1)=0   And   a(1,2)=0   Then   a(1,3)=1

Это очевидный защитный ход компьютера – на два кружочка в ряду он ставит в тот же ряд крестик.

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

Проиллюстрируем идею использования массивов в играх подробнее, на специально придуманном примитивном примере (типа морского боя, но гораздо проще).

Задание на создание игры: Играют друг против друга два человека на квадратном поле размером 2 на 2:

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

Правила: Сначала первый игрок тайком от второго сообщает компьютеру, в каких двух клетках находятся его одноклеточные корабли (например, в правой верхней и правой нижней). Затем второй сообщает компьютеру, в какие клетки он производит два выстрела (тоже, конечно, наугад – например, в левую верхнюю и правую нижнюю). Если подбиты оба корабля – он выиграл, если подбит один – ничья, если ни одного – выиграл первый игрок.


Сначала запрограммируем эту игру без графики, а потом с графикой.
Поле боя должно быть показано на экране только один раз – после двух выстрелов, причем, если без графики, то в виде распечатки из 4 букв:
м к
о х
Здесь я использовал такие обозначения:
о                       - корабля здесь нет и сюда не стреляли
к                       - неподбитый корабль
х                       - подбитый корабль
м                      - мимо (стреляли и промахнулись)
Других вариантов быть не может. Вы видите, что приведенная распечатка отражает результат расстановки кораблей и выстрелов, описанных мной в качестве примера.
Вот программа без графики:
Dim a(2, 2) As String                                          'Поле боя
Dim i, j, Подбито As Integer
'Главная процедура:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        a(1, 1) = "о" : a(1, 2) = "о"                          'Поначалу на поле боя кораблей нет
        a(2, 1) = "о" : a(2, 2) = "о"
        Устанавливаем_корабль(1)
        Устанавливаем_корабль(2)
        Подбито = 0                                               'Пока не стреляли
        Выстрел(1)
        Выстрел(2)
        Показываем_поле_боя()
        Debug.WriteLine(Подбито)                        'Показываем исход битвы -  количество подбитых кораблей
End Sub
Sub Устанавливаем_корабль(ByVal Номер_корабля As Integer)
        i = InputBox("Первый игрок, назовите номер строки для корабля " & Номер_корабля)
        j = InputBox("Первый игрок, назовите номер столбца для  корабля " & Номер_корабля)
        a(i, j) = "к"
End Sub
Sub Выстрел(ByVal Номер_выстрела As Integer)
        i = InputBox("Второй игрок, назовите номер строки для выстрела " & Номер_выстрела)
        j = InputBox("Второй игрок, назовите номер столбца для выстрела " & Номер_выстрела)


        If a(i, j) = "к" Then                    'Если попал, то
            a(i, j) = "х"                            'ставим крестик
            Подбито = Подбито + 1     'и увеличиваем счетчик подбитых кораблей
        ElseIf a(i, j) = "о" Then             'иначе если промахнулся, то
            a(i, j) = "м"                           'ставим м
        End If
End Sub
Sub Показываем_поле_боя()
        For i = 1 To 2
            For j = 1 To 2
                Debug.Write(a(i, j))
            Next j
            Debug.WriteLine("")
        Next i
End Sub
Пояснения: Вы видите, что в качестве массива, представляющего поле для игры, я выбрал строковый массив
Dim a(2, 2) As String                                          'Поле боя
Теперь прочтите главную процедуру. Убедитесь, что она правильно отражает основной порядок действий в процессе игры. Затем разберитесь в процедурах Устанавливаем_корабль и Выстрел. Они с параметрами. Наконец, разберитесь в процедуре Показываем_поле_боя.
Программа не объявляет итогов боя, а всего лишь печатает количество подбитых кораблей. Определение и объявление победителя оставляю вам.
Обратите внимание, что игра мгновенно переделывается из игры на поле 2 на 2 в игру на поле, скажем, 30 на 30, простой заменой числа 2 в тексте программы на число 30. Этой возможностью мы наслаждаемся только благодаря использованию массива! В этом случае, конечно, придется в цикле записать во все клеточки поля букву «о». А если мы хотим при этом иметь больше двух кораблей и двух выстрелов, нам придется в главной процедуре обратиться в цикле к процедуре Устанавливаем_корабль и в цикле к процедуре Выстрел, что очень просто.
Если бы мы пренебрегли секретностью, то могли бы показывать поле боя после каждого хода игроков. Для этого достаточно в конец процедур Устанавливаем_корабль и Выстрел включить строку
        Показываем_поле_боя()
 
Программа с графикой. Придумаем для простоты такую графику. Поле состоит из 4 цветных квадратов. Вот возможные цвета:


Голубой квадрат                       - корабля здесь нет и сюда не стреляли
Серый квадрат                          - неподбитый корабль
Красный квадрат                      - подбитый корабль
Зеленый квадрат                      - мимо (стреляли и промахнулись)
Замечательно, что от добавлении графики программа абсолютно не изменится за исключением единственной процедуры Показываем_поле_боя. Причем и в ней-то вся структура цикла останется неизменной. По большому счету выкинем только строку
            Debug.WriteLine("")
как нужную только для текстового вывода, а строку, печатающую очередную букву из четырех:
                Debug.Write(a(i, j))
заменим фрагментом, рисующим очередной квадрат из четырех. Вот новая процедура Показываем_поле_боя:
Sub Показываем_поле_боя()
        Dim Размер As Integer = 100                            'Размер квадрата
        Dim Гр As Graphics = Me.CreateGraphics
        Dim Кисть_для_воды As New SolidBrush(Color.LightBlue)
        Dim Кисть_для_корабля As New SolidBrush(Color.Gray)
        Dim Кисть_для_попадания As New SolidBrush(Color.Red)
        Dim Кисть_для_промаха As New SolidBrush(Color.Green)
        Dim Кисть As SolidBrush                             'Текущая кисть для квадрата
        Гр.Clear(Color.White)                                          'Стираем поле, нарисованное после предыдущего хода
        For i = 1 To 2
            For j = 1 To 2
                Select Case a(i, j)                                       'Выбираем кисть для очередного квадрата
                    Case "о" : Кисть = Кисть_для_воды
                    Case "к" : Кисть = Кисть_для_корабля
                    Case "х" : Кисть = Кисть_для_попадания
                    Case "м" : Кисть = Кисть_для_промаха
                End Select                                                 'Рисуем очередной квадрат:
                Гр.FillRectangle(Кисть, Размер * j, Размер * i, Размер, Размер)


            Next j
        Next i
End Sub
Пояснения: Предположим, наша процедура работает после каждого хода игроков. Мы могли бы написать ее так, чтобы после каждого очередного хода (поставили корабль или выстрелили) компьютер перерисовывал только тот квадрат, о котором шла речь. Остальные ведь остались неизменными – чего их перерисовывать? В этом случае компьютеру пришлось бы «меньше трудиться». Но я пошел по более простому и универсальному пути – после каждого хода все поле со всеми квадратами стирается и рисуется заново согласно содержимому массива a.
Кстати, строка
        Гр.Clear(Color.White)                                          'Стираем поле, нарисованное после предыдущего хода
в нашем случае излишня. Ведь мы все равно заново перерисовываем все квадраты поля, поэтому предварительно стирать их не имеет смысла.
Крестики-нолики 3х3 – советы. В принципе вы уже готовы к программированию игры против компьютера в обычные крестики-нолики 3х3. Всю техническую сторону дела мы прошли. Остается логика, то есть объяснение компьютеру, куда ставить крестики. И вот с логикой-то у нас будет проблема. Вы скажете: Какая проблема? – ведь клеточек всего 9 штук! Один из возможных операторов уже написан:
If   a(1,1)=0   And   a(1,2)=0   Then   a(1,3)=1
Напишу еще пару десятков подобных операторов – и дело с концом! – А пару сотен не хотите?! Вы только попробуйте перебрать все возможные варианты расстановки крестиков, ноликов и пустых клеток! Их вообще несколько тысяч. В этом случае, чтобы сократить программу, нужно применять в качестве индексов переменные величины и ломать голову над тем, какие писать процедуры, ветвления и циклы.
Поэтому любителям игр рекомендую для тренировки запрограммировать крестики-нолики не против компьютера, а как игру человека с человеком, где компьютер – лишь судья. Если получится, вот тогда можно замахнуться и на большее. Но и здесь идите постепенно. Рекомендую написать большую процедуру для правильной простановки крестика в произвольном одномерном
массиве из трех клеток. У этой процедуры будет три параметра – по числу клеток. Затем заметьте, что в реальной игре 3х3 вас интересует только 8 рядов: 3 по горизонтали, 3 по вертикали и 2 по диагонали. Значит у вас будет 8 обращений к этой процедуре. Дальше думайте сами. Все это совсем не просто.

Массивы как объекты


Оказывается, массив – это объект. Объект класса Array пространства имен System. Как?! – скажете вы, – мы до сих пор прекрасно работали с массивами и, как говорится, «ни сном, ни духом»! Мы нигде не писали New, не пользовались свойствами и методами массивов.  – Что ж, верно, многим программистам вполне можно работать с массивами и не подозревать, что это объекты. Авторы VB замаскировали этот факт (как мне кажется), чтобы не пугать программистов, переходящих с Visual Basic 6.0 на VB. Массивы-объекты рождаются в вашей программе «нечувствительно» для вас безо всякого New.

И все же, вот как можно создать массив при помощи New:

        Dim a() As Integer = New

Integer() {8, 1, 4, 3}

Нам будут полезны некоторые свойства и методы массивов (см. процедуру):

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

    Dim a() As Integer = {80, 60, 50, 90, 40, 20, 50, 70}

    Dim t(,) As Integer = {{99, 99, 99, 99, 99}, {99, -8, -14, -19, -18}, {99, 25, 28, 26, 20}, {99, 11, 18, 20, 25}}

    Debug.WriteLine(a.Length)                          'Длина массива a (число элементов) = 8

    Debug.WriteLine(t.Length)                           'Длина массива t (число элементов) = 20

    Debug.WriteLine(t.GetUpperBound(0))      'Число строк (макс. индекс первого измерения) - 1   = 3

    Debug.WriteLine(t.GetUpperBound(1))      'Число столбцов (макс. индекс второго измерения) - 1 = 4

    'Ищется первое вхождение числа 50 в одномерный массив a и находится его индекс (2):

    Debug.WriteLine(Array.IndexOf(a, 50))  

    'Ищется последнее вхождение числа 50 в одномерный массив a и находится его индекс (6):

    Debug.WriteLine(Array.LastIndexOf(a, 50))

    Debug.WriteLine(Array.IndexOf(a, 55))        'Ищется число 55 в массиве a и не находится (-1)

    Array.Reverse(a)      'Все элементы массива a меняют порядок на обратный = {70, 50, 20, 40, 90, 50, 60, 80}

    Array.Sort(a)            'Все элементы массива a сортируются по возрастанию = {20, 40, 50, 50, 60, 70, 80, 90}

    Array.Clear(a, 4, 3)  'Обнуляется 3 элемента массива a, начиная с индекса 4= {20, 40, 50, 50, 0, 0, 0, 90}

End Sub

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

Замечание. Учитывая, что массив – это объект, я призываю вас до поры не присваивать массив массиву целиком, без индексов, например, вот так:

        Dim a() As Integer = {8, 1, 5, 2}

        Dim b() As Integer

        b = a

Присвоение, конечно, состоится, но совсем не такое, как вы ждали. Оно может привести к неожиданным для начинающих последствиям. Вот к каким, например. Продолжу фрагмент:

        a(2) = 99

        Debug.WriteLine(b(2))

Напечатается 99, а не 5,  потому что массив – это объект. Почему? Расскажу позднее, в 27.2.



Массивы как параметры


До этого момента параметр процедуры или функции был для нас каким-то одним данным: это или одно число, или одна строка, или один объект. Но параметр может быть и массивом.

Задача: Имеется два массива, по три числа в каждом. Напечатать сумму элементов каждого массива. Использовать функцию sum, единственным параметром которой является суммируемый массив.

Программа:

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

        Dim a() As Integer = {4, 10, 20}

        Dim b() As Integer = {100, 40, 50}

        Debug.WriteLine(sum(a))

        Debug.WriteLine(sum(b))

End Sub

Function sum(ByVal c() As Integer) As Integer

        sum = c(0) + c(1) + c(2)

End Function

Выполните программу в пошаговом режиме, глядя в окно Locals. Понаблюдайте, как массив c принимает в себя значения элементов массивов a и b.

Задание 105.      

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

Указание: Создать функции Минимум(c), Максимум(c) и Разница(c).

Задание 106.      

На двух метеостанциях (A и B)  в течение года измерялась температура. Соответственно созданы два массива чисел длиной 365. Затем оказалось, что на обеих станциях термометры были испорчены: на станции A термометр все время показывал температуру на 2 градуса выше настоящей, а на станции B – на 3 градуса ниже. Написать процедуру с двумя параметрами, которая исправляет один произвольный массив и с ее помощью исправить оба массива. Один параметр процедуры – величина поправки, другой – массив температур.



Массивы элементов управления


Задача: Выстроить вдоль нижней кромки формы на равных расстояниях друг от друга 40 элементов управления PictureBox – улыбающихся рожиц, так, как это сделано на Рис. 15.1.

Рис. 15.1

При нажатии кнопки эти рожицы должны прыгнуть вверх, так, как на Рис. 15.2.

Рис. 15.2

Трудности: По-старинке нам нужно вручную поместить на форму 40 элементов управления PictureBox и загрузить в них картинку рожицы, что само по себе утомительно. Расположить их вручную на равных расстояниях без помощи VS тоже нелегко. VB дает возможность изящно преодолеть все эти трудности программным путем.

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

Второй способ – использование массива. В 6.1.2 мы не тащили вручную на форму элемент управления, а создавали его программным путем. Перечитайте тот раздел. Здесь мы поступим совершенно аналогично, только создадим не один элемент управления, а массив из 40 элементов управления. Вот программа:

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))     'Добавляем новую рожицу в коллекцию

        Next

End Sub

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


        Dim i As Integer
        For i = 1 To 40
            Рожица(i).Top = Рожица(i).Top - i                 'Прыжок всех объектов
        Next
End Sub
Пояснения: Как обычно, первой выполняется процедура Form1_Load, которая все создает и расставляет. Оператор
            Рожица(i).SizeMode = PictureBoxSizeMode.AutoSize
понадобился для того, чтобы каждый PictureBox уменьшился до размеров рожицы.
Затем процедура Button1_Click обеспечивает прыжок всех элементов управления, каждого на свою высоту.
Как только элемент управления создан, вы можете как угодно задавать и менять его свойства, обращаясь к нему по индексу. Например, оператор
        Рожица(38).Visible = False
делает невидимой 38-ю рожицу.
Обратите внимание, что пока я для простоты создаю элементы управления без возможности обработки свойственных им событий. Так, на созданную кнопку нажимать бесполезно. Как обрабатывать события элементов управления, созданных в коде, я написал в 22.13.

Индукция. Рекурсия


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

Здесь мне никуда не уйти от классического примера о факториале. Факториалом целого положительного числа N называется произведение всех целых чисел от 1 до N. Например, факториал пяти равен 1*2*3*4*5, то есть 120. Обратите внимание, что факториал единицы по определению считается равным 1.

Все понятно. Однако, существует еще один, совершенно ужасный способ определения, что такое факториал. Этот способ определения называется индуктивным. Вот он:

«Факториал единицы равен 1. Факториал любого целого положительного числа N, большего единицы,  равен числу N, умноженному на факториал числа N-1.»

Если вам уже все ясно, значит вы – профессор математики. Для обычных смертных поясню. Возьмем какое-нибудь конкретное N, например, 100. Тогда ужасное определение будет звучать проще: Факториал числа 100 равен числу 100, умноженному на факториал числа 99.

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

 

Смотрю в определение: Факториал трех равен 3 умножить на факториал двух. Не знаю, чему равен факториал двух. Поэтому спускаюсь на ступеньку ниже.

Смотрю в определение: Факториал двух равен 2 умножить на факториал единицы. Не знаю, сколько это. Спускаюсь еще на ступеньку.

Смотрю в определение: Факториал единицы равен 1. Вот, наконец-то – впервые узнал конкретное число. Значит можно подниматься обратно.

Поднимаюсь на одну ступеньку. Я на этой ступеньке уже бывал. Вспоминаю: Факториал двух равен 2 умножить на факториал единицы, то есть на 1, полученную нами ступенькой ниже. Получается 2. Хорошо.

Поднимаюсь еще на ступеньку. Я на этой ступеньке тоже бывал. Вспоминаю: Факториал трех равен 3 умножить на факториал двух, то есть на 2, полученную нами ступенькой ниже. Получается 6. Задача решена!


Рассуждая таким образом, можно вычислить факториал любого числа. Этот способ рассуждения называется рекурсивным. Ну как он вам?
:
Какое отношение все это имеет к компьютерам? Дело в том, что рекурсивный способ рассуждений реализован во многих языках программирования, в том числе – и в VB. Значит, этим языкам должен быть понятен и индуктивный способ написания программ.
Обозначим кратко факториал числа N, как Factorial(N), и снова повторим наш индуктивный способ определения:
Если N=1,  то Factorial(N) = 1.
Если N>1, то Factorial(N) вычисляется умножением N на Factorial(N-1).
В соответствии с этим определением, ни минуты не сомневаясь, напишем на VB функцию Factorial для вычисления факториала:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Debug.WriteLine(Factorial(3))
End Sub
Function Factorial(ByVal N As Integer) As Long
        If N = 1 Then Factorial = 1
        If N > 1 Then Factorial = N * Factorial(N - 1)
End Function
Обратите внимание, как удивительно соответствует код процедуры последнему варианту нашего индуктивного способа определения. Это практически дословный перевод определения с русского на английский язык. Единственное маленькое несоответствие – это употребление слева от знака равенства слова Factorial вместо Factorial(N). Но это требование грамматики, не меняющее смысла.
Что самое удивительное – функция работает! Несмотря на то, что в программе нигде не употребляется оператор цикла. А как же получается повторение? Вся соль программы в том, что функция Factorial вместо цикла включает в себя вызов самой себя – Factorial(N-1).
Что же происходит в компьютере во время выполнения программы? Механизм происходящего необычен, но в точности соответствует нашему путешествию по ступенькам. «Мыслей много, дела мало J». Запустите проект в отладочном режиме и обязательно понаблюдайте за окнами Locals и Call Stack:
Все начинается с того, что мы щелкаем по кнопке и VB пробует выполнить строку Debug.WriteLine(Factorial(3)). Для этого он вызывает функцию Factorial. Выполнение функции начинается с того, что в памяти отводится место для всех параметров функции и ее локальных переменных (в нашем случае это единственный параметр N). Затем число 3 подставляется на место параметра N, то есть в память в ячейку N посылается 3. Затем выполняется тело функции. Так как 3>1, то VB пытается выполнить умножение 3 * Factorial(3-1) и сталкивается с необходимостью знать значение функции Factorial(2), для чего вызывает ее, то есть отправляется ее выполнять, недовыполнив Factorial(3), но предварительно запомнив, куда возвращаться.


Спускаюсь на ступеньку ниже, выполнять Factorial(2). В другом месте памяти отводится место для N. Это уже другое N, чем в Factorial(3), путать их нельзя! В эту ячейку N посылается 2 (а в той продолжает сидеть 3). Затем выполняется тело функции. Пусть вас не смущает, что VB второй раз выполняет тело функции, не закончив его выполнять в первый раз. Так как 2>1, VB пытается выполнить умножение 2 * Factorial(2-1) и сталкивается с необходимостью знать значение функции Factorial(1), для чего, недовыполнив Factorial(2),вызывает ее. Итак, уже два недовыполненных обращения к одной и той же функции ждут своего часа на довыполнение.
Спускаюсь еще на ступеньку, выполнять Factorial(1). В другом месте памяти отводится место еще для одного N. В эту ячейку N посылается 1. Затем выполняется тело функции. Так как 1=1, то VB вычисляет Factorial=1. Вот – впервые конкретное число. Затем VB пытается выполнить следующую строку if N>1 then Factorial = N * Factorial(N-1). Поскольку нельзя сказать, что 1>1, то строка не выполняется и выполнение тела функции закончено. Значит можно подниматься.
Поднимаюсь на одну ступеньку. VB возвращается внутрь тела функции (той, где N=2), имея в кармане в качестве результата единицу, и успешно выполняет умножение –    Factorial  = 2*1 = 2.
Поднимаюсь еще на ступеньку. VB возвращается внутрь тела функции (той, где N=3), имея в кармане в качестве результата двойку, и успешно выполняет умножение –  Factorial  = 3*2 = 6. Задача решена.
Все произошло так, как если бы в окне кода было не одно объявление функции Factorial , а три. Из главной процедуры мы обратились к первому, из него – ко второму, а из того – к третьему. Выполнив третье, компьютер вернулся во второе, выполнив второе – в первое, выполнив первое – в главную процедуру.
Здесь мы рассмотрели рекурсию на примере функции. Однако обращаться к самой себе может с тем же успехом и процедура. Итак, рекурсией
в программировании называется вызов метода из тела самого метода.
Пути рекурсии не всегда бывают такими короткими. Часто функция А вызывает, например, процедуру В, которая вызывает процедуру С, которая в свою очередь вызывает функцию А. Круг замкнулся – рекурсия есть!


Чем хорош рекурсивный стиль программирования? В нашей программе о факториале мы как бы и не программировали вовсе, а просто объяснили компьютеру, что такое факториал. Как бы перешли на новый уровень общения с компьютером: вместо программирования – постановка задачи.
Чем плох рекурсивный стиль программирования? Если мы для решения той же задачи напишем программу не с рекурсией, а с обычным циклом, то такая программа будет выполняться быстрее и потребует меньше памяти.
Как преимущества, так и недостатки рекурсии вы поймете на примере выполнения следующего задания:
Задание 107.      
Напишите рекурсивную функцию fib для вычисления чисел Фибоначчи. В любом случае загляните в ответ.
Оглядимся вокруг. Итак, мы изучили 5 способов заставить компьютер многократно выполнять код:
Операторы Do …. Loop
Операторы For
Устаревший оператор Goto
Использование таймера
Рекурсия

Простая сортировка


Задача: Задан массив из 100 произвольных положительных чисел. Отсортировать его по возрастанию.

Идея решения: Отсутствует. Тогда поговорим об идее поиска идеи. Если мы не можем придумать, как запрограммировать задачу, нужно подробно представить себе, в каком порядке мы решали бы ее вручную, без компьютера. Как бы мы сами сортировали 100 чисел, записанных на бумаге? Мы сделали бы вот что. Запаслись карандашом, ластиком и другим, пустым листом бумаги из 100 клеток. Затем нашли бы в исходном массиве максимальное число  и записали его в самую правую клетку, а в исходном массиве на его месте записали бы число, меньшее самого маленького в массиве (в нашем случае подойдет 0). Затем нашли бы в изменившемся исходном массиве новое максимальное число и записали его на второе справа место, а на его место в исходном массиве – 0. И так далее.

Вот программа, воплощающая эту идею для 10 чисел:

'Вспомогательная функция для поиска максимума в массиве m размера N+1. Она выдает значение

'максимального элемента (maximum) и заодно мы узнаем номер этого элемента (Nomer_max):

Function maximum(ByVal m() As Integer, ByVal N As Integer, ByRef Nomer_max As Integer) As Integer

        Dim i, max As Integer

        max = m(0) : Nomer_max = 0              'max -  "временный" максимум

        For i = 1 To N

            If max < m(i) Then

                max = m(i)

                Nomer_max = i

            End If

            maximum = max

        Next

End Function

'Основная процедура сортировки исходного массива  mass_ish размера N+1 в результирующий - mass_rez:

Sub sortirovka(ByVal mass_ish() As Integer, ByVal N As Integer, ByVal mass_rez() As Integer)

        Dim i, Nom_max As Integer

        For i = 0 To N

            mass_rez(N - i) = maximum(mass_ish, N, Nom_max)                       'Пишем "в правую клетку"

            mass_ish(Nom_max) = 0                                                                   'Ноль - на старое место

        Next

End Sub


Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim massiv_ishodn() As Integer = {41, 8, 17, 82, 20, 2, 30, 12, 6, 9}             'Это исходный массив
        Dim N As Integer = massiv_ishodn.Length - 1                                                 'Это его размер без 1
        Dim massiv_rezult(N) As Integer                                                                     'Это наш пустой лист бумаги
        sortirovka(massiv_ishodn, N, massiv_rezult)                                                   'Сортируем массив
        Dim i As Integer
        For i = 0 To N
            Debug.WriteLine(massiv_rezult(i))                          'Распечатываем отсортированный массив
        Next
End Sub
Примечания: В программе вам полезно разобраться самому. Обратите внимание, что в заголовке функции в одном месте вместо ByVal употребляется ByRef. Разберитесь, почему. Обратите внимание, что функция maximum, кроме того, что сама имеет значение максимального элемента массива, выдает еще и порядковый номер максимального элемента – Nomer_max. Это, как я уже говорил, называется побочным эффектом функции.

Метод пузырька


Методов сортировки много. Приведенный метод – самый примитивный. Мало того, что нам пришлось расходовать память на второй массив, нам для выполнения сортировки массива из 100 элементов понадобилось бы около 100*100=10000 операций сравнения элементов массива между собой.

Существуют методы гораздо более эффективные. Приведу один из них – метод пузырька. Представьте себе тонкую вертикальную трубку с водой. Запустим снизу пузырек воздуха. Он поднимется до самого верха и остановится. Затем пустим еще один. Он поднимется наверх и остановится сразу же под первым. Затем запустим третий и так далее все сто пузырьков.

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

Вот алгоритм:  Сравним первый элемент массива со вторым, и если второй больше,  то это хорошо, так как они расположены в порядке возрастания. Мы ничего не делаем. А вот если первый больше, то меняем местами первый и второй элементы. В этом вся соль метода. Затем повторяем это со вторым и третьим элементами – если третий больше, то ничего не делаем, а если второй больше, то меняем местами второй и третий элементы. Затем повторяем все это с третьим и четвертым элементами и так далее. Где-то по пути мы встретим максимальный элемент, и он, согласно нашей механике постоянно меняясь местами с вышестоящими соседями, как пузырек, поднимется у нас до самого верха.

Теперь, когда мы знаем, что элемент номер 100 у нас самый большой, нам предстоит решить задачу сортировки для массива из остальных 99 элементов. Метод тот же. Запускаем второй пузырек и так далее.

Метод пузырька не требует второго массива, да и сравнений здесь в два раза меньше.

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

'Сортировка массива mass размером N+1:

Sub puziryok(ByVal mass() As Integer, ByVal N As Integer)

        Dim i, c, m As Integer

        For m = N To 1 Step -1                              'Всего пузырьков - 9

            For i = 0 To m - 1                                   'i увеличивается - пузырек ползет вверх


                If mass(i) > mass(i + 1) Then             'Стоит ли обмениваться значениями
                    c = mass(i)                                     'Три оператора для обмена значений
                    mass(i) = mass(i + 1)                      'двух элементов с помощью
                    mass(i + 1) = c                                'транзитного элемента c
                End If
            Next i
        Next m
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim massiv() As Integer = {41, 8, 17, 82, 20, 2, 30, 12, 6, 9}              'Это  массив
        Dim N As Integer = massiv.Length - 1                                                 'Это его размер без 1
        puziryok(massiv, N)                                                                            'Сортируем массив
        Dim i As Integer
        For i = 0 To N
            Debug.WriteLine(massiv(i))                    'Распечатываем отсортированный массив
        Next
End Sub
В заключение скажу, что существуют методы гораздо более эффективные, чем даже метод пузырька.
Задание 108.      
Задача для ученого-диетолога: Определить связь между ростом и весом боксеров-профессионалов. Для этого диетолог измерил рост и вес нескольких сотен боксеров и записал результаты измерений в длинную таблицу:

Фамилия
Рост
Вес
Иванов
173
71
Петров
182
93
Сидоров
169
62
Николаев
175
70
…………….
……………….
……………..

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

Фамилия
Рост
Вес
Сидоров
169
62
Николаев
175
70
Иванов
173
71
Петров
182
93

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

Сортировка


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

Пусть имеется ряд чисел:  8  2  5  4. Под сортировкой понимают их упорядочивание по возрастанию (2  4  5  8) или убыванию (8  5  4  2). Сортировать можно и строки (как слова в словаре).

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



Тип Object


Переменные типа Object. Типы данных, которые мы проходили до этого, строгие. Если уж мы написали

        Dim a As Integer

то переменная a не может быть ни строкой, ни дробным числом и ничем другим неположенным, она может быть только целым числом, и точка! Но есть и «добрые» типы данных. Самый добрый тип – это Object. Мы уже говорили о нем в 11.5.1. Там переменная типа Object принимала значения различных элементов управления. Рассмотрим теперь такую процедуру:

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

        Dim a As Object

        a = Button1

        a = 77

        a = "кот"

End Sub

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

Выполните проект в пошаговом режиме, наблюдая за значением переменной в окне Locals.

Вместо

        Dim a As Object

разрешается писать

        Dim a

В этом случае VB все равно будет считать, что переменная a  объявлена, как Object.

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

А зачем нужны добрые типы? Какой толк от типа Object? – Вот какой. Иногда бывает так, что заботясь о безопасности и объявляя тип каждой переменной, мы наталкиваемся во время выполнения программы на сообщения об ошибке вроде Type mismatch (несовпадение типов) или на другие подобные сообщения, о которых я говорил, например, в 11.5.3. Это значит, что выполняя операции над данными разных типов и преобразовывая один в другой, VB не захотел преступать строгие рамки безопасности и потерпел крах. Не всегда у начинающего программиста хватает знаний, чтобы разобраться в причинах краха. В этом случае, если вы не прочь немножко рискнуть, объявите данные типом Object. Он, хоть и допускает значения любого типа, все-таки присматривает, чтобы выполнение программы при этом проходило по возможности гладко и без неприятностей для программиста. Теперь вероятность сообщений об ошибке снизится, хотя несколько увеличится вероятность получения недостоверного результата.


Массивы типа Object. Массив – это набор однотипных элементов. Если вы объявили массив некоторого типа, то он обязан состоять из элементов только этого типа. Например, каждый из десяти элементов массива, объявленного как
        Dim  a(9)  As Integer
обязан быть целым числом. Ни один из них не имеет права быть строкой или дробным числом.
Имеется целый ряд задач, где хотелось бы, чтобы элементы в наборе имели разный тип. Этого можно достичь хотя бы при помощи массива, если объявить массив, как вы уже догадались, типом Object:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim a(3) As Object
        a(1) = 32
        a(2) = "кошка"
        a(3) = #8:56:00 AM#
        a(3) = "поварешка"
End Sub
Выполните проект в пошаговом режиме, наблюдая за значениями элементов массива в окне Locals.
Вместо
        Dim a(3) As Object
разрешается писать
        Dim a(3)
И все же, когда дело касается желания иметь набор из элементов разных типов, программисты не хотят использовать массивы типа Object. Они предпочитают так называемые коллекции и структуры. О них сейчас и пойдет речь.

Создание коллекции, методы коллекции


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

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

Решение: Создадим проект. Для простоты разместим на форме только шесть элементов управления: Label1, TextBox1, Button1, Label2, TextBox2, Button2. Пусть в нашу «дюжину» входят из них только Label2, TextBox2, Button2.

Мы могли бы поместить нашу «дюжину» в рамку (GroupBox). Но рамка не обладает такой функциональностью, как коллекция. К тому же она требует пространственной отграниченности входящих в нее элементов управления от остальных.

Программным путем создадим из наших объектов коллекцию. Для этого сначала придумаем ей имя  Моя_коллекция. Коллекция – это объект, экземпляр класса Collection, и создается она соответствующим привычным образом:.

        Dim Моя_коллекция As New Collection                  'Объявляем и создаем коллекцию

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

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

        Dim Моя_коллекция As New Collection                 'Объявляем и создаем коллекцию


        Моя_коллекция.Add(Label2)                                   'Добавляем в коллекцию 1-й элемент
        Моя_коллекция.Add(TextBox2, "Текстик")             'Добавляем в коллекцию 2-й элемент
        Моя_коллекция.Add(Button2, "Кнопочка")             'Добавляем в коллекцию 3-й элемент
        Debug.WriteLine(Моя_коллекция.Count)                'Печатается  3 - число элементов в коллекции
        Debug.WriteLine(Моя_коллекция(2))                       'Печатаются сведения об объекте TextBox2
        Debug.WriteLine(Моя_коллекция(1).Text)               'Печатается "Label2"
        Моя_коллекция(3).Height = 80                                 'Задается высота кнопки Button2
        Моя_коллекция("Текстик").Width = 100                   'Задается ширина поля TextBox2
        Dim i As Integer
        For i = 2 To 3                                                              'ДЛЯ 2 и 3 объекта
            Debug.WriteLine(Моя_коллекция(i).Top)              'распечатывается вертикальная координата
        Next
        Dim Мой_объект As Control
        For Each
Мой_объект In Моя_коллекция               'ДЛЯ КАЖДОГО объекта В
коллекции:
            Мой_объект.BackColor = Color.Yellow                 'задается желтый цвет
        Next
        For Each Мой_объект In Моя_коллекция                 'ДЛЯ КАЖДОГО объекта В коллекции:
            If TypeName(Мой_объект) = "Button" Then           'если имя типа объекта - Button, то
                Мой_объект.Enabled = False                             'деактивировать ее
            End If
        Next
        Моя_коллекция.Remove(2)                                      'Удаляем из коллекции 2-й элемент
        Моя_коллекция.Remove("Кнопочка")                      'Удаляем из коллекции кнопку
End Sub
Пояснения: У всех коллекций, как экземпляров класса Collection, есть несколько методов, три из них (Add, Remove, Count) я сейчас поясню.


При помощи метода Add мы добавляем в коллекцию элементы. При этом обязательно указывается имя элемента. Также удобно через запятую указать произвольную строку – ключ элемента, по которому можно к нему при желании обращаться, что мы и сделали в двух местах ("Текстик" и "Кнопочка"). Элементы, добавляемые в коллекцию, получают номера в том порядке, в котором их добавляли, начиная с 1 (а не с 0). Обращаться к отдельным элементам коллекции можно по полученному номеру (индексу) прямо как к элементу массива, а можно и по ключу, там, где он задан.
Как видите, здесь для добавления каждого элемента мы использовали отдельный оператор, хотя есть ситуации, когда можно это делать и в цикле.
Метод Count просто сообщает число элементов в коллекции.
Строки процедуры с 6 по 13 иллюстрируют обращение к элементам коллекции по индексу или по ключу, в том числе в цикле.
Чтобы удалить отдельный элемент из коллекции (не с формы), используется метод Remove с указанием индекса или ключа элемента.
Очевидно, что работа с коллекциями напоминает работу с одномерными массивами, в чем-то побогаче ее,  а в чем-то и менее удобна.
В следующем подразделе мы продолжим пояснения нашего примера.

Оператор цикла For Each


Работа For Each с коллекциями. Для коллекций удобно применять специальную разновидность оператора цикла – For Each. Для этого необходимо придумать имя переменной цикла, значение которой будет пробегать все элементы коллекции. Мы придумали имя Мой_объект. Надо объявить переменную цикла так, чтобы ее тип подходил для всех элементов коллекции. Поскольку в нашей коллекции одни только элементы управления, мы объявили так:

        Dim Мой_объект As Control

хотя могли и так:

        Dim Мой_объект As Object

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

Когда цикл выполняется в первый раз, Мой_объект «равняется» одному элементу коллекции, во второй раз – другому и т.д., пока элементы не будут исчерпаны. В остальном синтаксис и порядок выполнения оператора For Each такой же, как и у привычного нам For. К нему также можно применять оператор Exit For.

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

Работа For Each с массивами. Оператор For Each никто не запрещал использовать и при работе с массивами. Вот пример:

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

        Dim a() As Integer = {10, 11, 12, 13, 14}

        Dim Элемент As Integer

        For Each Элемент In a

            Debug.WriteLine(Элемент)

        Next

End Sub

Напечатает эта процедура вот что:

10

11

12

13

14



Коллекции, принадлежащие контейнерам


Мы знакомы с такими объектами, как форма, рамка, панель, вкладка. Каждый из них может включать в себя другие элементы управления. Поэтому такие объекты называются контейнерами. У контейнера есть собственная коллекция, в которую автоматически заносятся элементы управления, входящие в контейнер. Поэтому специально заботиться о добавлении их в контейнер не нужно. Коллекция формы называется Controls. Коллекция рамки GroupBox1 является ее свойством Controls, поэтому к ней можно обращаться так: GroupBox1.Controls. К коллекции панели Panel1 можно обращаться так: Panel1.Controls и т.д. Пример:

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

        Dim Объект As Control

        For Each Объект In Controls

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

        Next

        For Each Объект In Panel1.Controls

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

        Next

End Sub

Во вкладках элементы управления принадлежат страницам вкладки. У каждой страницы – своя коллекция элементов управления. Пусть во вкладке TabControl1 имеется три страницы: TabPage1, TabPage2 и TabPage3. Тогда фрагмент

        For Each Объект In TabPage2.Controls

            Debug.WriteLine(Объект)

        Next

распечатает элементы управления, находящиеся на 2-й странице. Сами страницы составляют коллекцию, принадлежащую вкладке. Поэтому фрагмент

        For Each Объект In TabControl1.Controls

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

        Next

распечатает имена всех страниц вкладки:

TabPage1

TabPage2

TabPage3

Получается коллекция в коллекции. Матрешка. Поэтому сработает такой оператор:

        Debug.WriteLine(TabControl1.Controls(1).Controls(2))

Здесь распечатывается элемент управления, стоящий под номером 2 в коллекции элементов управления, принадлежащей некой странице. А страница эта стоит под номером 1 в коллекции страниц, принадлежащей вкладке TabControl1.



Коллекции


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



Структуры


Со структурами мы с вами знакомы слабо. Несколько слов я сказал о них в 12.1. А именно, я сказал, что структура – это один из видов объектов (в широком смысле), который наряду с классами, модулями и перечислениями входит в состав пространств имен. У структуры, также как и у класса, могут быть свойства и методы. Нам известны структуры Точка, Прямоугольник и др.

В Паскале структурам соответствуют записи, в Visual Basic 6.0 структурам соответствует пользовательский тип данных, в Си структуры тоже называются структурами.

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

Рассмотрим пример.

Задание. Вы хотите занести в компьютер информацию о ваших любимых компьютерных играх (хотя бы для того, чтобы затем как-то ее анализировать, например, определить, какая игра занимает больше места на диске). Для простоты ограничим информацию об игре тремя элементами:

Название игры

Сколько места игра занимает на диске (в мегабайтах)

Хорошая или плохая графика у игры (ваша оценка)

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

Распечатать название первой игры

Определить, сколько места займут на диске обе игры вместе

Ответить, хороша или плоха графика у второй игры

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

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


Structure типИгра
        Dim Название As String
        Dim Объем As Integer
        Dim Графика_хорошая As Boolean
End Structure
Слово Structure как раз и обозначает структуру.
Тип определен. Теперь VB знает, что входит в информацию об игре и сколько места в памяти она займет. Можно объявлять переменные, то есть отводить место в памяти:
        Dim Игра1, Игра2 As типИгра        'Отводим в памяти место под информацию о двух играх
Вот полный текст программы:
'Создаем тип структуры. Его нельзя объявлять в процедуре:
Structure типИгра
        Dim Название As String
        Dim Объем As Integer
        Dim Графика_хорошая As Boolean
End Structure
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim Игра1, Игра2 As типИгра         'Отводим в памяти место под информацию о двух играх
        'Заносим в память компьютера информацию об играх:
        Игра1.Название = "StarC"                    : Игра2.Название = "Heroes III"
        Игра1.Объем = 90                                : Игра2.Объем = 200
        Игра1.Графика_хорошая = False : Игра2.Графика_хорошая = True
        Debug.WriteLine(Игра1.Название)                                              'Выполняем 1 задание
        Debug.WriteLine(Игра1.Объем + Игра2.Объем)                        'Выполняем 2 задание
        'Выполняем 3 задание:
        If Игра2.Графика_хорошая     Then Debug.WriteLine("Хорошая графика")  _
                                                   Else Debug.WriteLine("Плохая графика")
End Sub
Обратите внимание, что имя элемента структуры отделяется от имени переменной точкой. Нам это привычно по работе со структурами Размер, Прямоугольник и др., когда имя такого элемента структуры, как свойство, тоже отделялось от имени переменной точкой.
Массивы и структуры отличаются от простых типов данных, таких как Integer или String. Если переменная простого типа содержит в каждый момент времени только одно значение (число, строку, дату и т.п.), то массив или структура в каждый момент времени содержат по нескольку значений. Поэтому такие типы называют составными (composite).


Можно писать операторы такого вида:
        Игра2  = Игра1                        'Игре 2 присваиваются все элементы 1-й игры
В этом случае всем элементам игры 2 присваиваются значения соответствующих элементов игры 1.
Базы данных. Выстроенную подобным образом в памяти информацию о чем-либо часто называют базой данных. Всю информацию об одной переменной (в нашем случае об одной игре) называют записью в этой базе данных. Программу, которая извлекает информацию из базы данных, сортирует записи и производит другую обработку информации в базе данных, называют системой управления базой данных. В нашем примере роль системы управления базой данных играет процедура Button1_Click.
Базы данных являются настолько распространенным средством хранения информации, что в VB есть специальные мощные инструменты для работы с ними. Мы рассмотрим их в Глава 24. .
Массивы структур: Вы хотите создать базу данных о 30 играх. Для этого достаточно вместо очень длинного объявления
Dim Игра1, Игра2, …………… Игра30  As типИгра
объявить массив:
        Dim Игра(30) As типИгра  
Теперь можно использовать операторы такого вида:
        Игра(16).Название = "KU4"                        'название 16-й игры
        Игра(25).Объем = 400                                 'объем 25-й игры
        Debug.WriteLine(Игра(8).Графика_хорошая)
        Игра(29) = Игра(12)                        '29-й игре присваиваются все элементы 12-й игры
Структура в структуре: Элементы структуры могут иметь не только простой тип, но и составной, в том числе быть массивом, структурой или объектом.
Пусть мы хотим иметь более подробную, чем в предыдущем примере, информацию о графике. Для этого организуем отдельную структуру:
Structure типГрафика
        Dim Хорошая As Boolean
        Dim Число_цветов As Integer
        Dim Максимальное_разрешение As String
End Structure
А поскольку информация о графике является составной частью информации об игре, вставим переменную созданного типа в качестве элемента в структуру игры:


Structure типИгра
        Dim Название As String
        Dim Объем As Integer
        Dim Графика As типГрафика
End Structure
Теперь мы можем писать такие операторы:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim Игра(30) As типИгра
        Игра(8).Графика.Хорошая = True
        Игра(14).Графика.Число_цветов = 65000000
        Игра(29).Графика.Максимальное_разрешение = "1280х960"
        Debug.WriteLine(Игра(14).Графика.Число_цветов)
End Sub
Заключение. Вы увидели, что составные элементы структуры сами могут включать в себя структуры. Они могут вкладываться друг в друга, как матрешки, до бесконечности. Так можно создать иерархии данных практически в любой области знаний. Примеры же о массивах и объектах в качестве элементов структур я не буду рассматривать, так как они кажутся мне несколько сложноватыми для начинающих.
Задание 109.      
Создайте базу данных о себе, ближайших родственниках или друзьях. О каждом должно быть известно:
Имя
Дата рождения
Цвет глаз
Массивы не используйте. Программа должна:
Распечатать ваш возраст и цвет глаз
Ответить на вопрос – правда ли, что ваш дядя старше тети.
Задание 110.      
Создайте базу данных о своих книжках. О каждой книжке должно быть известно:
Название
Автор
Дата издания
Число страниц
Обязательно используйте массив не меньше, чем из 5 структур. Программа должна:
Вычислить средний возраст ваших книжек
Определить, каких книжек больше – толстых или тонких
Ответить на вопрос – есть ли у вас разные книги одного автора (это нелегко)

Оператор With


Когда мы используем длинные имена структур или объектов или глубоко вложенные друг в друга конструкции, обращение к элементам таких структур или объектов занимает в окне кода довольно много места. Например:

        Игра(29).Графика.Хорошая = True

        Игра(29).Графика.Число_цветов = 65000000

        Игра(29).Графика.Максимальное_разрешение = "1280х960"

Оператор With позволяет сократить запись. Вот фрагмент, равносильный предыдущему:

        With Игра(29).Графика

            . Хорошая = True

            . Число_цветов = 65000000

            . Максимальное_разрешение = "1280х960"

        End With

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

 



Алфавитный указатель


"................................................................... 154

#.................................................................. 362

&......................................................... 172, 492

*................................................................... 154

/.................................................................... 154

^................................................................... 154

+.................................................................. 492

<.................................................................. 201

<=................................................................ 201

<>................................................................ 201

=.................................................................. 201

>.................................................................. 201

>=................................................................ 201

A

A.................................................................. 357

Abort........................................................... 751

AboutBox..................................................... 87

Abs................................................................ 59

Access........................................................ 668

ActiveMdiChild......................................... 746

Add........................................... 178, 458, 484

Add project.................................................. 39

Add Reference......................................... 712

AddArc....................................................... 468

AddDays.................................................... 363

AddEllipse................................................. 468

AddHandler...................................... 639, 642

AddressOf................................................. 639

AddSeconds............................................. 365

AddTicks.................................................... 365


AddYears................................................... 364

ADO.NET................................................... 668

Alias............................................................ 718

Align........................................................... 118

Alt................................................................ 414

Anchor.......................................................... 71

And............................................................. 211

AND............................................................ 698

Appearance............................ 474, 477, 542

Append............................................. 502, 512

Application................................................ 717

AppWinStyle............................................. 719

Array........................................................... 445

Asc.............................................................. 495

ASCII символы......................................... 495

AscW.......................................................... 494

ASP. NET Web Application..................... 662

Assembler................................................. 759

Auto Hide................................................... 109

AutoPopDelay.......................................... 489

AutoScroll......................................... 375, 377

AutoSize.................................................... 336

B

B.................................................................. 356

BackColor.................................... 49, 69, 552

BackgroundImage............................ 76, 336

BackSpace....................................... 784, 785

Beep.................................................... 54, 718

BETWEEN................................................. 698

Binary......................................................... 512

BinaryReader............................................ 515

BinaryWriter.............................................. 515



Bitmap........................................................ 339

Bold............................................................... 66

Bold Italic..................................................... 66

Boolean..................................................... 214

BorderStyle.................................................. 70

break.......................................................... 149

Breakpoint Properties............................. 252

Breakpoints...................................... 147, 251

Bring to Front............................................ 118

Brushes...................................................... 187

Build..................................................... 94, 705

Busy............................................................ 653

Button.................................................. 32, 409

ButtonClick................................................ 543

Buttons....................................................... 540

ByRef.......................................................... 303

Byte.................................................... 157, 158

ByVal.......................................................... 303

C

Call............................................................. 275

Call Stack.................................................. 255

Cancel........................................................ 142

CapsLock.................................................. 784

Caption...................................................... 686

Catch.......................................................... 522

CBool......................................................... 163

CByte.......................................................... 162

CChar......................................................... 163

CDate......................................................... 163

CDbl........................................................... 163

CDec.......................................................... 163



CD-R........................................................... 770

CD-ROM.................................................... 770

CD-RW....................................................... 770

Ceiling........................................................ 155

Cells........................................................... 717

CenterImage............................................. 336

Changed.................................................... 650

Char................................................... 412, 494

CharacterCasing........................................ 73

Chars.......................................................... 496

CheckBox.................................................. 473

CheckBoxes.............................................. 544

Checked........................................... 474, 476

CheckedChanged................................... 475

CheckedIndices.............................. 485, 550

CheckedItems................................. 485, 550

CheckedListBox.............................. 483, 485

CheckState................................................ 474

Child........................................................... 552

Chr.............................................................. 495

Chr(10)....................................................... 508

Chr(13)....................................................... 508

ChrW.......................................................... 494

CInt............................................................. 162

Class Library............................................. 711

Class View................................................ 731

Clear....................... 190, 445, 484, 550, 697

Click..................................... 36, 89, 407, 408

Clicks.......................................................... 409

CLng........................................................... 163

Close........................................ 500, 501, 515

Close Solution................................... 38, 116



Closed.......................................................... 92

CloseFigure.............................................. 468

CLR............................................................... 18

CObj........................................................... 163

Code............................................................. 35

Collection.................................................. 457

Color....................... 329, 331, 356, 532, 558

ColorDepth................................................ 539

ColorDialog............................................... 558

Columns........................................... 544, 546

ColumnWidth............................................ 482

COM........................................................... 714

Combo простой...................................... 483

Combo раскрывающийся..................... 483

ComboBox................................................ 482

Command Window-Immediate............ 254

Comment Selection................................ 117

Common Language Runtime.................. 18

Compile..................................................... 726

Component Designer................................ 79

Connection................................................ 677

Console..................................................... 570

Console Application................................ 569

Const................................................. 310, 586

CONSTRAINT........................................... 700

Contains........................................... 484, 549

Contents........................................... 123, 724

ContextMenu............................................ 490

Continue.................................................... 149

Control..................................... 312, 414, 703

Controls............................................ 178, 459

Copy.................................. 43, 116, 519, 781

Cos............................................................. 156



Count................................................. 458, 484

COUNTER................................................. 700

CREATE TABLE....................................... 700

Created...................................................... 649

CreateDirectory........................................ 519

CreateObject............................................. 717

CShort........................................................ 162

CSng.......................................................... 163

CStr............................................................. 163

CurrentDirectory....................................... 520

CurrentPosition........................................... 87

Cursor........................................................... 71

Cut............................................... 43, 116, 781

D

DashStyle.................................................. 330

DataColumn............................................. 690

DataGrid.................................. 686, 691, 692

DataGridTableStyle................................. 693

DataSet.................................... 684, 685, 690

DataSource............................................... 686

DataTable.................................................. 690

Date................................................... 362, 364

DateAdd..................................................... 367

DateAndTime........................................... 366

DateDiff..................................................... 366

DatePart..................................................... 366

DateSerial................................................. 367

DateString................................................. 367

DateTime.......................................... 362, 363

DateTimePicker....................................... 487

DateValue................................................. 367

Day............................................................. 364

DayOfWeek............................................... 364



DayOfYear................................................. 364

DaysInMonth............................................. 365

Debug........................................................ 255

Debug.WriteLine............................... 55, 147

Decimal............................................ 157, 160

Declare...................................................... 718

Delete....................... 43, 116, 519, 782, 785

DELETE..................................................... 699

Deleted...................................................... 649

Delphi......................................................... 759

DESC......................................................... 683

design........................................................... 33

Designer...................................................... 31

Details........................................................ 544

DialogResult............................................. 531

Dim........................................... 143, 144, 586

Directory.................................................... 518

Dispose...................................................... 190

DLL............................................................. 705

Do............................................. 232, 233, 236

Do …. Loop............................................... 232

Do …. Loop Until...................................... 234

Do …. Loop While.................................... 233

Do Until …. Loop...................................... 235

Do While …. Loop.................................... 235

Dock..................................................... 71, 542

Dockable................................................... 108

DocumentComplete................................ 653

DoEvents................................................... 747

Double................... 144, 157, 159, 160, 161

DoubleClick..................................... 407, 408

DrawArc..................................................... 187



DrawBezier............................................... 466

DrawClosedCurve................................... 467

DrawCurve................................................ 466

DrawEllipse............................................... 185

DrawImage...................................... 339, 344

Drawing..................................................... 182

DrawLine.......................................... 183, 185

DrawLines................................................. 464

DrawPath................................................... 468

DrawPie..................................................... 187

DrawPolygon............................................ 465

DrawRectangle......................................... 185

DrawRectangles....................................... 464

DrawString................................................ 191

DROP TABLE............................................ 701

DropDown Combo.................................. 483

DropDownButton..................................... 541

DropDownList........................................... 482

DropDownStyle........................................ 482

Duration....................................................... 87

DVD-ROM.................................................. 770

Dynamic Help........................................... 123

E

e......................................................... 155, 637

E.................................................................. 155

Edit.............................................................. 116

Else............................................................. 198

ElseIf.......................................................... 208

Enabled........................................................ 71

EnableRaisingEvents.............................. 650

End..................................... 46, 221, 279, 280

End Function............................................. 308

End If.......................................................... 208



End Sub.............................................. 36, 271

EndCap...................................................... 331

EndsWith................................................... 496

Enter........................................................... 405

Enum.......................................................... 372

Environment.................................... 119, 520

EOF............................................................. 513

Event.......................................................... 641

EventArgs.................................................. 409

Excel........................................................... 714

Exception................................................... 522

Exists.......................................................... 519

Exit....................................................... 38, 116

Exit Do........................................................ 238

Exit For....................................................... 243

Exit Sub...................................................... 280

Exp.............................................................. 155

Expand....................................................... 552

ExpandAll.................................................. 552

F

False........................................................... 214

File..................................................... 116, 518

File System Object................................... 498

FileAttributes............................................. 519

FileClose.......................................... 510, 512

FileGet........................................................ 511

FileMode.................................................... 515

FileName............................................ 85, 528

FileOpen........................................... 510, 512

FilePut............................................... 510, 512

FileStream................................................. 514

FileSystem................................................ 498



FileSystemWatcher................................. 649

Fill............................................. 684, 687, 697

FillEllipse................................................... 187

FillPath....................................................... 468

FillPie......................................................... 187

FillPolygon....................................... 465, 467

FillRectangle............................................. 187

FillRectangles........................................... 465

Filter................................................... 529, 649

Find............................................................. 117

Fix............................................................... 155

FlatStyle....................................................... 70

Flip.............................................................. 345

Floating...................................................... 108

Floor........................................................... 155

Focus.......................................................... 406

Font............................................. 66, 333, 532

Font Style..................................................... 66

Fonts and Colors...................................... 119

FontStyle.................................................... 333

For............................................. 239, 240, 242

For  Each................................................... 458

ForeColor............................................ 70, 552

Format............................................... 117, 164

FormBorderStyle........................................ 70

Friend......................................................... 586

FROM......................................................... 683

FromArgb......................................... 353, 354

FromFile.............................................. 77, 336

FromImage............................................... 348

FullPath...................................................... 650



Function..................................................... 308

G

G.................................................................. 356

Get.............................................................. 600

GetAttributes............................................. 519

GetChar..................................................... 492

GetCreationTime..................................... 519

GetCurrentDirectory................................ 520

GetDirectories........................................... 520

GetFiles...................................................... 521

GetItemChecked...................................... 486

GetLastAccessTime................................ 519

GetLastWriteTime.................................... 519

GetLogicalDrives..................................... 521

GetNodeCount......................................... 552

GetParent.................................................. 520

GetPixel..................................................... 356

GetUpperBound....................................... 445

GoTo........................................................... 226

Graphics.................................................... 182

GraphicsPath............................................ 467

GraphicsUnit............................................. 345

Grid Pane.................................................. 680

Grid Size.................................................... 121

GridColumnStyles................................... 693

GridLines................................................... 544

GroupBox.................................................. 374

H

Handled..................................................... 415

Handles............................................... 89, 637

HatchBrush...................................... 331, 332

HatchStyle................................................. 333

Height........................................... 70, 75, 342

Help.......................................... 122, 721, 727



HelpKeyword on HelpProvider1........... 727

HelpNamespace...................................... 727

HelpNavigator on HelpProvider1......... 727

HelpProvider............................................. 727

Hide..................................................... 48, 109

HideSelection.................................. 535, 550

HorizontalResolution............................... 342

Hour............................................................ 364

HScrollBar................................................. 477

HTML.......................................................... 652

HTML Help Workshop............................. 722

HTML-документ....................................... 659

Hue............................................................. 559

I

Icon............................................. 73, 339, 341

If......................................................... 198, 210

If блочный................................................. 206

If вложенные............................................ 210

If многострочный.................................... 206

If однострочный...................................... 200

Image....................... 77, 336, 339, 349, 477

Image Editor............................................. 360

ImageAlign.................................................. 77

ImageIndex............................................... 546

ImageList.......................................... 538, 540

Images....................................................... 538

ImageSize................................................. 539

Imports......................................................... 60

In................................................................. 457

IncludeSubdirectories............................. 649

Indeterminate........................................... 474

Index.................................................. 123, 726

IndexOf........................... 445, 484, 496, 543

Inflate......................................................... 326



Inherits....................................................... 606

InitialDelay................................................ 489

Input............................................................ 512

InputBox..................................................... 141

InputString................................................. 513

Insert......................................... 484, 496, 785

INSERT...................................................... 698

InStr............................................................ 493

Int................................................................ 658

Integer...................................... 143, 157, 158

IntelliSense............................................... 320

Internet....................................................... 771

Internet Explorer....................................... 652

Invalidate................................................... 472

Is......................................................... 217, 543

IsDate......................................................... 367

IsDigit......................................................... 412

IsLeapYear................................................ 365

IsLetter....................................................... 412

IsLetterOrDigit........................................... 412

IsLower...................................................... 412

IsMdiContainer......................................... 745

IsNumeric.................................................. 219

IsPunctuation............................................ 412

IsSeparator................................................ 412

IsUpper...................................................... 412

Italic............................................................... 66

Item............................................................. 692

Items......................................... 480, 483, 545

J

Java............................................................ 759



JPEG.......................................................... 401

K

KeyChar..................................................... 412

KeyCode.................................................... 413

KeyDown.......................................... 411, 413

KeyEventArgs........................................... 413

KeyPress................................................... 411

KeyPressEventArgs................................. 412

KeyPreview...................................... 414, 424

Keys............................................................ 414

KeyUp............................................... 411, 413

L

Label............................................................. 65

LabelEdit................................................... 547

LargeChange........................................... 477

LargeImageList........................................ 550

LastIndexOf...................................... 445, 496

Lcase.......................................................... 493

Leave......................................................... 405

Left....................................... 74, 75, 409, 492

Len....................................................... 60, 492

Length............................................... 445, 496

Let............................................................... 137

Lib............................................................... 718

LinearGradientBrush...................... 331, 332

LineCap..................................................... 331

LineInput.................................................... 513

LinkClicked............................................... 491

LinkLabel................................................... 490

LinkVisited................................................. 491

LISP............................................................ 759

ListBox....................................................... 480

ListView..................................................... 544



ListViewItem............................................. 545

Load.............................................................. 92

LoadFile..................................................... 536

Locals......................................................... 250

Lock Controls............................................ 118

Locked.......................................................... 70

LOF............................................................. 513

Log.............................................................. 155

Log10......................................................... 156

Logo........................................................... 759

Long.................................................. 157, 158

Loop........................................................... 233

Lowercase................................................. 117

LTrim.......................................................... 493

Luminosity................................................. 559

M

Main............................................................ 568

MainMenu.................................................... 79

MakeTransparent..................................... 401

MappingName......................................... 693

Math..................................................... 59, 155

Max............................................................. 155

MaximizeBox.............................................. 73

Maximum.................................................. 477

MaximumSize............................................. 73

MDI............................................................. 744

MdiChildren.............................................. 747

MdiParent.................................................. 746

Media Player............................................... 83

Members............................................. 55, 123

Message.................................................... 523

Metafile............................................. 339, 341



Microsoft...................................................... 54

Microsoft Chart Control........................... 565

Microsoft Multimedia Control 6.0..... 84, 88

Microsoft WebBrowser............................ 652

Mid.............................................................. 492

Middle........................................................ 409

Min.............................................................. 155

MinimizeBox............................................... 73

Minimum................................................... 477

MinimumSize.............................................. 73

Minute........................................................ 364

Mod............................................................. 154

Module....................................................... 566

Month......................................................... 364

MonthCalendar........................................ 487

MonthName.............................................. 367

MouseDown..................................... 407, 409

MouseEnter........................................ 90, 407

MouseEventArgs...................................... 409

MouseLeave............................................. 407

MouseMove............................ 407, 408, 411

MouseUp................................................... 407

Move........................................................... 519

MsgBox...................................................... 222

MsgBox........................................................ 78

MsgBoxResult........................................... 224

MsgBoxStyle............................................. 223

MultiColumn............................................. 481

Multiline........................................................ 72

MultiSelect................................................. 549

MustInherit................................................. 610

MustOverride............................................ 611



MyBase............................................... 92, 610

MyClass..................................................... 610

N

Name................................................... 64, 541

Namespace.............................................. 730

Navigate.................................................... 653

NET Framework......................................... 18

New.................. 39, 116, 178, 328, 578, 602

New Item................................................... 577

Next............................................................ 240

Nodes................................................ 551, 552

None........................................................... 410

Normal....................................................... 336

Not.............................................................. 213

Nothing............................................. 336, 740

Now............................................................ 365

NumericUpDown..................................... 488

O

Object....................................... 311, 455, 742

Object Browser......................................... 111

OldFullPath............................................... 649

OleDbCommand..................................... 697

OleDbConnection.................................... 684

OleDbDataAdapter.................................. 684

OnClick...................................................... 658

OnPaint...................................................... 472

Opacity......................................................... 73

Open........................................... 38, 116, 717

Open File..................................................... 41

Open Project............................................... 39

OpenFileDialog........................................ 527

OpenMode....................................... 510, 512

Option Explicit........................................... 145

Options....................................................... 118



Or....................................................... 212, 334

Order.......................................................... 118

ORDER BY................................................. 683

Orientation........................................ 478, 480

Output......................................... 56, 139, 512

Overloading.............................................. 612

Overridable............................................... 609

Overrides................................................... 609

P

Page Setup............................................... 116

Paint........................................................... 348

Panel.......................................................... 375

Parent......................................................... 552

Pascal........................................................ 759

PasswordChar.......................................... 221

Paste........................................... 43, 116, 781

Path............................................................ 649

PathGradientBrush.................................. 331

PC Speaker............................................... 768

Peek........................................................... 503

PeekChar.................................................. 518

Pen............................................................. 327

Pens........................................................... 183

PerformStep............................................. 480

PI................................................................. 155

PictureBox................................................. 336

Play............................................................... 85

PlayCount.................................................... 87

Point.................................................. 324, 464

PointF......................................................... 325

PRIMARY KEY........................................... 700

Print................................................... 116, 513



PrintLine.................................................... 513

Priority........................................................ 750

Private........................................................ 586

Private Const............................................ 586

Process...................................................... 720

ProgressBar.............................................. 479

Project............................................... 117, 576

Project Explorer........................................ 574

Prolog......................................................... 759

Properties Window..................................... 63

Property..................................................... 600

Protected................................................... 607

Providers................................................... 677

Public......................................................... 586

PushButton................................................ 541

Q

Query Builder............................................ 695

Quick Watch.............................................. 254

R

R.................................................................. 356

RadioButton.............................................. 476

RaiseEvent................................................ 641

Random............................................ 510, 512

Randomize................................................ 203

Rate............................................................... 87

Read........................................................... 502

ReadBoolean............................................ 518

ReadByte................................................... 515

ReadDouble.............................................. 518

ReadLine.......................................... 501, 570

ReadOnly............................ 72, 87, 415, 600

ReadString................................................ 518

ReadToEnd............................................... 507



Recent Projects........................................ 116

Rectangle.................................................. 325

RectangleF................................................ 325

Redo........................................................... 116

Reference Types...................................... 741

Refresh...................................................... 349

Region класс............................................ 469

Region свойство...................................... 469

Regular......................................................... 66

Remove................. 458, 484, 496, 548, 552

RemoveAt.................................................. 484

Rename.............................................. 43, 781

Renamed................................................... 649

RenamedEventArgs................................ 649

Replace............................................. 117, 496

Resize........................................................ 472

Resolution................................................. 341

Resume..................................................... 751

Return......................................................... 308

Reverse...................................................... 445

RichTextBox.............................................. 533

RichTextBoxStreamType........................ 537

Right.................................................. 409, 492

Rnd.................................................... 156, 203

Rotate......................................................... 345

RotateFlip.................................................. 345

RotateTransform...................................... 360

Round......................................................... 155

Rows........................................................... 690

RTrim.......................................................... 493

run................................................................. 34

Run To Cursor.......................................... 251



S

Saturation.................................................. 559

Save........................................................... 346

Save All............................................... 38, 116

SaveFile..................................................... 536

SaveFileDialog......................................... 526

ScaleTransform....................................... 358

Scroll.......................................................... 478

Scrollable.................................................. 550

ScrollBars.................................................... 72

Scrolling..................................................... 480

Search........................................................ 123

Second....................................................... 364

Seek.................................................. 512, 515

SeekOrigin................................................ 515

SELECT................................... 683, 694, 698

Select Case............................................... 216

SelectCommand...................................... 697

SelectedIndex........................................... 484

SelectedIndexChanged.......................... 484

SelectedIndices........................................ 549

SelectedItem.................................... 481, 484

SelectedItems........................................... 549

SelectedNode........................................... 552

SelectedText............................................. 536

SelectionAlignment................................. 535

SelectionBullet......................................... 535

SelectionColor.......................................... 535

SelectionFont............................................ 535

SelectionMode......................................... 486

Send to Back............................................. 118

sender........................................................ 637

Separator................................................... 541



Server Explorer........................................ 676

Set............................................................... 600

SET............................................................. 699

SetAttributes.............................................. 519

SetLastAccessTime................................. 520

SetLastWriteTime.................................... 519

SetPixel...................................................... 357

SetResolution........................................... 343

SetToolTip................................................. 489

Shadows.................................................... 610

Shared....................................................... 598

Shell........................................................... 719

Shift................................................... 414, 784

Short.................................................. 157, 158

Shortcut........................................................ 82

Show............................................................. 50

Show Grid.................................................. 121

Show Start Page...................................... 123

ShowColor................................................ 532

ShowDialog..................................... 527, 558

ShowHelp.................................................. 727

ShowHelpIndex........................................ 727

ShowInTaskbar........................................... 73

Sibling........................................................ 552

Sign............................................................ 155

Simple Combo......................................... 483

Sin............................................................... 156

Single....................................... 157, 159, 160

Size............................................... 66, 70, 325

SizeF.......................................................... 325

SizeMode.................................................. 336



Sleep.......................................................... 751

SmallChange........................................... 477

SmallImageList........................................ 544

SnapToGrid............................................... 121

SolidBrush................................................. 331

Solution...................................................... 575

Solution Explorer...................... 95, 109, 574

Sort............................................................. 445

Sorted......................................................... 484

Sorting........................................................ 550

Split.................................................... 121, 497

Splitter........................................................ 565

SQL.................................................... 684, 698

Sqrt............................................................. 155

Start...................................................... 33, 720

Start Page.................................................. 115

StartCap..................................................... 331

StartPosition................................................ 73

StartsWith.................................................. 496

Startup object............................................ 568

Static.................................................. 301, 586

StatusBar................................................... 565

Step................................................... 241, 480

Stop............................................. 87, 280, 653

Stop Debugging......................................... 34

Str................................................................ 493

StreamReader.......................................... 500

StreamWriter............................................. 499

StretchImage............................................ 336

String........................................ 172, 492, 496

Strings................................................. 60, 492



Structure.................................................... 460

Style............................................................ 541

Sub....................................................... 36, 271

SubItems................................................... 546

Substring................................................... 496

Suspend.................................................... 751

System......................................................... 54

System.IO.................................................. 499

SystemBrushes........................................ 353

SystemColors........................................... 353

SystemPens.............................................. 353

T

Tab Order.................................................. 407

TabControl................................................ 376

TabIndex.................................................... 406

TableName............................................... 693

Tables........................................................ 690

TableStyles............................................... 693

TabPage.................................................... 376

TabStop..................................................... 407

Tan.............................................................. 156

Text............................................. 64, 484, 546

TextAlign............................................. 71, 542

TextBox........................................................ 32

TextChanged.............................................. 92

TextureBrush................................... 331, 350

Then........................................................... 198

Thread........................................................ 750

ThreadPriority........................................... 751

ThreeState................................................. 474

TickFrequency.......................................... 478

Ticks........................................................... 365



TickStyle.................................................... 479

Timer.......................................................... 367

TimeSerial................................................. 367

TimeSpan.................................................. 363

TimeString................................................. 367

TimeValue................................................. 367

To....................................................... 217, 240

ToCharArray............................................. 496

Today......................................................... 365

ToggleButton............................................ 541

ToLocalTime............................................. 365

ToLongDateString................................... 365

ToLongTimeString................................... 365

ToLower..................................................... 412

Toolbar....................................................... 539

Toolbars..................................................... 124

Toolbox............................................... 32, 110

Tools........................................................... 118

ToolTip....................................................... 488

Top......................................................... 74, 75

TOP............................................................. 698

ToShortTimeString.................................. 365

ToString............................................ 365, 410

ToUniversalTime..................................... 365

ToUpper..................................................... 412

TrackBar.................................................... 478

TranslateTransform................................. 359

TransparentColor..................................... 539

TreeNode.................................................. 552

TreeView.......................................... 544, 550

Trim............................................................ 493

True............................................................ 214



Try............................................................... 522

TypeName................................................ 458

U

Ucase......................................................... 493

Unchecked................................................ 474

Uncomment Selection............................ 117

Undo........................................................... 116

Unicode-символы................................... 494

Until............................................................ 234

Update.............................................. 684, 687

UPDATE.................................................... 699

Uppercase................................................. 117

User Control Designer............................ 704

UserControl............................................... 704

V

Val........................................................ 67, 493

Value.... 477, 480, 487, 488, 601, 658, 717

Value Types.............................................. 741

ValueChanged................................ 487, 488

VALUES..................................................... 699

VB.................................................................. 15

vbNewLine................................................ 508

VBScript..................................................... 657

View................................................... 117, 544

View Code................................................. 575

View Designer.......................................... 575

Visible........................................................... 71

Visual Basic.............................................. 759

Visual Basic .NET....................................... 15

Visual C# .NET........................................... 18

Visual C++................................................. 759

Visual C++ .NET......................................... 18

Visual J# .NET............................................ 18

Visual Studio .NET........................ 18, 26, 95



Volume......................................................... 87

VS.................................................................. 26

VScrollBar................................................. 477

W

Watch......................................................... 252

WebApplication........................................ 662

Web-приложение................................... 662

Web-сервер.............................................. 651

Web-страница......................................... 654

Web-форма.............................................. 663

WeekdayName........................................ 367

WHERE...................................................... 683

While.......................................................... 234

While …End While................................... 239

Width.................................... 70, 75, 329, 342

Window...................................................... 121

Windows API............................................. 717

Windows Form Designer generated code 179

WindowState............................................... 73

With............................................................. 463

WithEvents....................................... 636, 640

Word Wrap.......................................... 94, 117

WordWrap.................................................... 72

Workbooks................................................ 717

Worksheet................................................. 717

WrapMode................................................. 351

Write................................ 502, 512, 515, 570

WriteLine................................. 500, 512, 570

WriteOnly................................................... 602

X

X........................................................... 74, 409

XML............................................................ 688

Y

Y............................................................ 74, 409

Year............................................................ 364



Z

ZoomFactor............................................... 536

А

абсолютная величина.......................... 155

абстрактные классы.............................. 610

адаптер..................................................... 685

адрес................................................ 304, 782

активный объект....................................... 62

алгоритм................................................... 756

алфавитно-цифровая клавиша......... 412

анимация.................................................. 395

арифметическое выражение.............. 138

Ассемблер................................................ 759

Б

база данных................................... 461, 666

байт.......................................... 763, 772, 773

библиотека динамической компоновки 705

библиотека классов............................... 711

библиотека классов .NET Framework 18, 714

бит..................................................... 513, 773

блок............................................................ 299

браузер..................................................... 652

булевский тип.......................................... 214

буфер обмена......................................... 788

Бэйсик.......................................................... 19

В

ввод............................................................ 498

векторная графика................................ 338

верхняя граница индекса.................... 434

вершина.................................................... 552

ветвление........................................ 197, 198

ветка.......................................................... 552

видео............................................................ 83

видеоадаптер......................................... 767

видеокарта............................................... 767

визуальное программирование......... 180

винчестер................................................. 761

вкладка...................................................... 376



вложенные циклы......................... 262, 265

возврат каретки...................................... 507

возврат управления.............................. 764

выбор................................................ 197, 198

выборка..................................................... 684

вывод......................................................... 498

выделение фрагмента......................... 787

выделенный канал................................ 771

выделенный объект................................ 62

вызов программы................................... 764

вызов процедуры................................... 272

выражение...................................... 138, 305

выход из VS................................................ 38

выход из цикла........................................ 230

Г

гарнитура................................................. 333

главное меню............................................. 79

глобальная область видимости......... 586

градиентная заливка............................ 332

грамматика............................................... 728

графика................................... 176, 324, 464

графический путь................................... 467

графический редактор VB................... 360

графическое поле.................................. 182

Д

данные............................................. 157, 763

движение.................................................. 229

двумерные массивы.............................. 437

действия арифметики.......................... 154

делегаты................................................... 643

деление.................................................... 154

Дельфи...................................................... 759

дерево....................................................... 779

десятичные дроби................................. 155

Джава......................................................... 759

диалоговое окно выбора цвета.......... 558

диалоговое окно настройки шрифта 531



диалоговые окна открытия и сохранения файла  526

дизайнер компонентов........................... 79

динамические компоненты объектов 599

дискета...................................................... 769

дисковод................................................... 769

дисплей..................................................... 766

дозапись................................................... 502

документ................................................... 777

дополнительная цифровая клавиатура 414

дорожка..................................................... 782

доступ к локальным дискам................. 659

дочерняя вершина................................ 552

дуга............................................................. 187

Е

если............................................................ 198

Ж

жесткий диск............................................ 761

З

заголовок процедуры............................ 271

загрузка............................................ 498, 764

закладка.................................................... 376

закладка alphabetic.................................. 63

закладка categorized................................ 63

закрытие проекта..................................... 38

заливка...................................................... 331

запись...................................... 461, 498, 666

запрос.............................................. 667, 673

запятая.............................................. 68, 155

затенение................................................ 587

зацикливание.......................................... 226

звук............................................................... 83

звуковая карта........................................ 768

звуковой адаптер................................... 768

зерно......................................................... 767

знак сравнения....................................... 201

значение................................................... 137



И

И.................................................................. 211

идеология .NET......................................... 18

ИЛИ............................................................ 212

имя.............................................. 64, 151, 777

иначе......................................................... 198

индекс...................................... 432, 458, 496

индексированные переменные......... 432

индикатор процесса.............................. 479

индукция................................................... 448

инициализация....................................... 294

инициализация двумерного массива 439

инициализация массива...................... 434

инкапсуляция.......................................... 591

инсталляционный пакет...................... 125

инсталляция..................................... 95, 125

инсталляция Visual Studio .NET........... 95

инструкция............................................... 760

Интернет.................................................. 651

интерфейсы............................................ 608

исключение.............................................. 521

Истина....................................................... 214

исходные данные.................................. 763

итерация.................................................. 228

К

календари................................................ 487

каталог...................................................... 777

кисть........................................................... 187

клавиатура............................................... 411

клавиши перемещения курсора........ 785

класс........................................ 181, 577, 733

ключ............................................................ 458

ключевое поле........................................ 670

ключевые слова...................................... 152

книга........................................................... 716

кнопка.......................................................... 32



кнопка возврата...................................... 788

кнопка максимизации.............................. 73

кнопка минимизации............................... 73

кнопка отмены........................................ 788

код................................................................. 35

кодовая страницы 1251....................... 505

коллекция................................................. 456

команда..................................................... 760

комментарии.............................................. 93

компакт-диски.......................................... 770

компиляция..................................... 705, 760

компонент................................................... 55

компоненты объекта............................. 123

компьютер................................................ 756

консольное приложение...................... 569

константы................................................. 310

конструктор..................................... 328, 602

конструктор запросов........................... 680

конструктор таблицы............................ 670

конструктор форм.................................... 33

конструктор элементов управления пользователя              704

контейнер................................................. 459

контекстное меню......................... 489, 775

копирование................................... 781, 788

копирование файла.............................. 518

копирование файлов и папок............... 43

корень........................................................ 552

корень квадратный................................ 155

кривая Безье........................................... 466

круг............................................................. 184

Л

лазерные диски...................................... 770

Лисп........................................................... 759

лист............................................................ 716

логарифм.................................................. 155



логические выражения......................... 214

логические операции............................ 211

логические переменные...................... 214

логический диск...................................... 782

Лого............................................................ 759

Ложь........................................................... 214

локальные переменные.............. 296, 586

локальный диск....................................... 659

ломаная.................................................... 464

М

максимум.................................................. 268

маркер......................................................... 32

маска.......................................................... 529

массив.............................................. 432, 433

массивы как объекты............................ 445

массивы как параметры....................... 446

массивы многомерные......................... 441

массивы одномерные........................... 433

массивы структур................................... 461

массивы элементов управления....... 446

мастер....................................................... 133

мастер установки................................... 125

математика.............................................. 153

математические функции.................... 155

машинный язык...................................... 757

медиаплеер............................................... 83

меню............................................................. 79

меню «Пуск»............................................ 774

метка................................................... 65, 226

метка с гиперссылкой........................... 490

метод пузырька...................................... 453

методы.................... 48, 194, 315, 316, 319

микрофон................................................. 766

минимум.................................................... 268

многозадачность.................................... 765

многоугольник......................................... 465



множественный выбор......................... 486

моделирование...................................... 394

модель....................................................... 394

модем........................................................ 771

модуль............................ 155, 567, 569, 733

модульные переменные............. 296, 586

монитор.................................................... 766

мусор......................................................... 740

мышь.......................................................... 407

Н

наследник................................................. 605

наследование......................................... 605

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

настройка среды.................................... 118

насыщенность........................................ 559

научный формат.................................... 164

НЕ............................................................... 213

О

области видимости....................... 293, 585

обработчик события............................. 635

обращение к процедуре...................... 272

обращение к функции.................. 306, 307

объект................................................. 25, 591

объектное программирование........... 591

объектный тип........................................ 178

объекты.................................................... 176

объявление.................................... 140, 167

объявление процедуры....................... 272

объявление функции............................ 307

окно............................................................ 775

окно Output................................................. 56

окно дизайнера компонентов............. 708

окно кода..................................................... 35

окно свойств............................................... 62

округление............................................... 155

оперативная память.. 761, 762, 763, 768

оператор............................................ 24, 760



оператор безусловного перехода.... 226

оператор варианта............................... 215

оператор присваивания.... 137, 139, 146

оператор цикла...................................... 232

операционная система........................ 764

ОС............................................................... 764

остаток от деления............................... 154

открытие проекта..................................... 38

открытие файла..................................... 499

отладка........................................ 25, 95, 249

отрезок...................................................... 184

оттенок...................................................... 559

ошибки....................................... 43, 257, 521

ошибки выполнения................................ 44

ошибки компиляции................................. 46

П

память.............................................. 761, 763

панель....................................................... 375

панель задач........................................... 774

панель инструментов............ 33, 123, 539

папка.......................................................... 777

параметры............................. 185, 276, 319

пароль....................................................... 221

Паскаль....................................................... 19

перевод строки....................................... 507

перегрузка................................................ 612

передача параметров по значению. 304

передача параметров по ссылке...... 304

передача управления........................... 764

переименовывание........................ 43, 781

переключатель....................................... 476

переменная цикла................................. 240

переменные величины 137, 149, 245, 283

переменные уровня модуля............... 296

перемещение файлов и папок.... 43, 781

перемещение фрагментов.................. 788

перенос оператора................................. 93

перенос файла....................................... 518



переопределение.................................. 609

перечисления......................................... 371

перо........................................................... 183

персональный компьютер................... 760

пиксель.................................... 757, 766, 772

плавающие окна..................................... 108

платформа .NET....................................... 18

побочный эффект.................................. 309

подсказка.................................................. 488

поле.................................................. 594, 666

ползунок................................................... 478

полиморфизм.......................................... 608

полосы прокрутки......................... 477, 779

пользователь.......................................... 756

потоки............................................... 747, 749

пошаговый режим.................................. 149

преобразование типов................ 161, 501

привязанные окна................................. 108

привязка................................................... 686

приложение............................................... 25

приложение Windows.............................. 25

принтер..................................................... 768

принтер лазерный................................. 768

принтер матричный.............................. 768

принтер струйный................................. 768

принцип инкапсуляции......................... 593

пробел....................................................... 784

провайдер................................................ 651

проводник................................................. 778

программа................................ 24, 157, 756

программист............................................ 756

продукт........................................................ 25

проект.......................................................... 25

проект - создание..................................... 39



прозрачность.......................................... 354

прозрачный цвет...................................... 77

Пролог....................................................... 759

пространство имен......................... 53, 728

простые типы данных.......................... 461

процедуры......................................... 24, 270

процедуры обработки событий......... 272

процедуры пользователя.................... 271

процедуры с параметрами................. 275

процессор................................................ 761

прямоугольник...................... 184, 325, 464

путь............................................................ 782

Р

рабочий лист........................................... 717

рабочий стол........................................... 774

радиокнопка............................................ 476

разветвляющиеся программы........... 197

размер....................................................... 325

размер картинок..................................... 341

размер шрифта........................................ 66

разрешение.................................... 341, 767

разрешение картинок........................... 341

рамка......................................................... 374

растровая графика................................ 338

расширение............................................. 778

регистр верхний..................................... 784

регистр нижний....................................... 784

регистр русский...................................... 784

Редактор вершин дерева.................... 551

Редактор коллекции картинок............ 538

Редактор коллекции кнопок................ 540

Редактор коллекции подэлементов списка             546

Редактор коллекции столбцов........... 544

Редактор коллекции элементов списка 545

Редактор строковых коллекций......... 480

режим вставки......................................... 785

режим замещения................................. 785

режим прерывания................................ 148



режим проектирования.......................... 33

режим работы........................................... 34

результат................................................. 763

рекурсия................................................... 448

решение................................................... 575

родитель................................................... 605

родительская вершина........................ 552

С

сборка............................................... 112, 711

свойства............................................. 69, 600

свойства для чтения-записи............... 600

свойства текстового поля...................... 72

свойства только для записи................ 601

свойства только для чтения................ 600

свойства формы....................................... 73

свойство...................................................... 34

свойство объекта................................... 592

связанные таблицы.............................. 667

сектор........................................................ 187

сетевая карта.......................................... 770

сетка............................................................. 34

сеть глобальная..................................... 770

сеть компьютерная................................ 770

сеть локальная....................................... 770

Си.................................................................. 19

символы........................................... 494, 771

символы типа.......................................... 161

символы формата.................................. 165

синтаксис......................................... 200, 735

синтаксическая схема........................... 200

система координат................... 74, 75, 358

система управления базами данных 461, 666

системные цвета.................................... 351

системный блок...................................... 760

сканер........................................................ 766

скобки........................................................ 154



случайное число..................................... 156

случайные величины............................ 203

смысловые ошибки.................................. 43

собственный класс................................ 577

события.............................. 24, 88, 635, 641

соединение.............................................. 677

создание папок......................................... 42

создание файлов............................ 42, 518

соответствие типов............................... 315

сортировка............................................... 452

составные типы данных...................... 461

сохранение.............................................. 505

сохранение проекта................................ 37

спецификаторы формата.................... 165

список........................................................ 480

список раскрывающийся...................... 482

список с флажками................................ 483

сплайн....................................................... 466

справочная система.............................. 721

среда визуальной разработки программ. 25

ссылка.............................................. 304, 739

ссылочные типы............................ 737, 741

стандартный модуль............................. 567

стартовая страница....................... 26, 115

стартовое меню...................................... 774

статические компоненты объектов.. 598

статические переменные.................... 301

степень - возведение............................ 154

страница................................................... 376

строка состояния................................... 565

строки............................................... 172, 492

строковые выражения.......................... 173

строковые литералы............................. 173

строковые переменные....................... 171

структуры........................................ 460, 733

ступенчатая запись программы........ 209

СУБД.......................................................... 666



сумматор.................................................. 261

сценарий.................................................. 657

счетчик............................................. 258, 488

счетчик циклов........................................ 228

съемные носители информации....... 769

Т

таймер....................................................... 369

тактовая частота.................................... 761

тег............................................................... 655

текстовое поле.......................................... 32

текстовый курсор................................... 783

текстовый редактор.............................. 782

текстурная кисть.................................... 350

текущая папка......................................... 520

тело процедуры...................... 36, 272, 307

тело цикла....................................... 226, 233

тик............................................................... 365

тип.............................................................. 143

тип выражения....................................... 163

типы данных............................................ 157

точка........................................... 68, 155, 324

точки прерывания......................... 147, 251

трансляция.............................................. 760

У

удаление файлов и папок.... 43, 518, 782

удаление фрагмента............................ 788

узел............................................................ 651

указатель.................................................. 739

умножение............................................... 154

умолчание.................................................. 64

управляющие клавиши......................... 412

условие..................................................... 201

условный оператор............................... 198

установка.................................................. 125

устройства ввода................................... 765

устройства вывода....................... 765, 766



устройства компьютера....................... 765

Ф

файл................................................. 498, 777

файлы нетипизированные................. 513

файлы с последовательным доступом 499

файлы с произвольным доступом 512, 514

файлы текстовые................................... 499

файлы типизированные...................... 510

фильтр...................................................... 529

флажок...................................................... 473

флэш-память........................................... 770

фокус......................................................... 405

форма............................... 31, 566, 743, 744

форма фигурная.................................... 469

форматирование.......................... 163, 368

фото............................................................. 76

функции........................................... 301, 306

функции пользователя......................... 306

функции преобразования типов....... 162

функциональные клавиши.................. 412

Ц

цвет объекта.............................................. 69

цвет текста................................................. 70

целая часть числа.................................. 155

целое число.................................... 143, 144

целочисленное деление...................... 154

цикл............................................................ 225

циклические программы....................... 225

Ч

числа Фибоначчи.................................... 432

числовые литералы.............................. 156

чтение....................................................... 498

Ш

шаблон...................................................... 529

шаг цикла.................................................. 241

шина.......................................................... 761

шрифты.............................................. 66, 333

штрихованая кисть................................ 332

Э

экземпляры класса................................ 176

экспоненциальный формат................ 164

элемент управления пользователя. 704

элементы управления.......... 32, 473, 525

эллипс....................................................... 184

Я

Ява............................................................. 759

язык программирования...................... 758

яркость...................................................... 559

ячейка............................................... 146, 147

Специально для http://all-ebooks.com

[†] В этой главе я кое- где буду применять термины, которые еще не объяснял и буду объяснять лишь позже. Вы можете спокойно не обращать на них внимания. Они нужны только тем, кто их уже понимает.

[‡]  Модулем он стал только в VB 2003. В предыдущей версии VB он был классом, но для начинающего программиста это несущественно.


Часть III. Программирование на VB – второй уровень


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

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



Разветвляющиеся программы


Выбор (ветвление) в VB осуществляют три оператора:

Однострочный If

Многострочный If

Оператор выбора Select Case

Их применению в программировании на VB и посвящена эта глава.



Циклические программы


Идею цикла я поясню на примере движения объектов по экрану. Возьмем игру в воздушный бой. Самолетик по экрану должен двигаться. Но в списках операторов большинства языков программирования, используемых профессиональными программистами для создания игр, нет команды движения. Здесь нужно задаться вопросом – а что такое движение? Рассмотрим иллюзию движения, возникающую на экране кинотеатра. Если вы держали в руках кинопленку фильма, изображающего, скажем, движение автомобиля, то должны были обратить внимание, что она состоит из множества неподвижных слайдов-кадров, на каждом следующем из которых автомобиль находится чуть-чуть в другом месте, чем на предыдущем. Показывая эти кадры один за другим с большой скоростью, мы создаем иллюзию движения автомобиля. Точно так же поступают с созданием иллюзии движения на экране компьютера. Запишем алгоритм движения самолетика по экрану слева направо:

1.Зададим  в уме компьютера  позицию самолетика в левой части экрана.

2.Нарисуем в задуманном месте самолетик.

3.Сотрем его.

4.Изменим  в уме компьютера  позицию самолетика на миллиметр правее.

5.Перейдем к команде 2.

Каждая из приведенных команд алгоритма легко программируется на большинстве языков. Любой компьютер, выполнив очередную команду, автоматически переходит к выполнению следующей. Так, выполнив команду 2, компьютер всегда перейдет к выполнению команды 3. Однако, если мы захотим, то можем заставить компьютер изменить этот порядок, что мы и сделали в команде 5. Из-за нее компьютер выполняет команды в таком порядке: 1-2-3-4-5-2-3-4-5-2-3-…. Напомню, что, выполнив команду 2, компьютер всегда перейдет к выполнению команды 3, независимо от того, какую команду он выполнял перед командой 2 – 1-ю или 5-ю. Таким образом, многократно выполняется последовательность команд 2-3-4-5. Такая многократно выполняемая  последовательность называется циклом. Можно сказать, что цикл – это одно из средств заставить компьютер долго работать при помощи короткой программы.

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

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



Отладка программы


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

Сначала перечитайте, пожалуйста, материал про сообщения об ошибках в 1.3.8 и про пошаговый способ в 5.3.2.

Приступим. На Рис. 9.1 вы видите в окне кода в процессе отладки некую бессмысленную процедуру Button1_Click и несколько отладочных окон. Вот эта процедура:

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

        Debug.WriteLine("Начало работы")

        Dim a As Integer = 20

        Dim b As Integer = 3

        Do Until b > a

            Debug.WriteLine(a & "         " & b)

            a = a - 2

            b = b + 1

        Loop

        Debug.WriteLine("Конец работы")

End Sub

Легко видеть, что выполнившись с начала до конца, эта процедура печатает в окне Output такую информацию:

Начало работы

20         3

18         4

16         5

14         6

12         7

10         8

Конец работы

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

Начало работы

20         3

18         4

Конец работы

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

Что же мы делаем? Прежде всего мы обычным способом помещаем на экран окно Output. Затем мы ставим точку прерывания на заголовке процедуры и запускаем проект. Затем нажимаем на кнопку Button1 и проект прерывается на заголовке процедуры. Ну вот, теперь мы находимся в режиме прерывания. Теперь можно отлаживать.

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



Рис. 9.1
Окно Locals. Идем дальше. При помощи Debug ® Windows ® Locals мы вызываем на экран окно Locals (см. рисунок). Назначение окна Locals – показывать в режиме прерывания значения локальных переменных и других объектов выполняемой процедуры. (Локальные переменные – это переменные, объявленные внутри процедуры[†]). Столбец Name в окне Locals означает имя переменной или объекта. Столбец Value показывает нам их значение. К тому же в столбце Type вы можете видеть информацию о типе переменных и объектов.
После того, как проект прервался на заголовке процедуры, мы запускаем клавишей F11 пошаговый режим выполнения проекта (если VS настроена на профиль Visual Basic 6, то это будет клавиша F8). После нескольких нажатий на F11 окна Locals и Output принимают тот вид, что вы видите на рисунке. Правда, мы и без окна Locals в любой момент можем узнать значения a и b, просто поместив на них мышиный курсор. Однако применение окна Locals избавляет нас и от этих мизерных трудов.
Щелкните по плюсику слева от Me. Форма расскажет вам о текущих значениях своих свойств. Такие же плюсики вы видите у других объектов. Если в вашей процедуре имеются сложные переменные, например, массивы или структуры, щелкнув по плюсику рядом с ними, вы увидите значения всех их составных частей.
Если вы перейдете к выполнению другой процедуры, то в окне Locals будут видны уже именно ее локальные переменные.
Точки прерывания (Breakpoints). Когда программа большая или циклов много, жать на F11 приходится слишком часто. Но ведь и нужды-то в этом нет. Обычно вам интересно останавливаться не на каждой строке программы, а только на некоторых подозрительных, остальные можно выполнять и без остановки. VS позволяет вам задать такие строки – точки прерывания
(Breakpoints). Щелкните по вертикальной серой полосе в левой части окна кода против строки, на которой хотите прерывать выполнение программы. На полосе появится черная точка и вся строка будет выделена черным (все это описано в 5.3.2).
Щелкните также против всех нужных вам строк. Все они станут черными. Запустите проект обычным образом (кнопка Start или клавиша F5).  Дойдя до первой же точки прерывания, проект перейдет в режим прерывания. Продолжайте его работу клавишей F5. Проект будет останавливаться только на точках прерывания. Убираются точки прерывания так же, как и ставятся – щелчком мыши.


«Беги до курсора» – Run To Cursor. Это еще один удобный способ остановки в нужной вам строке. Щелкните правой клавишей мыши по нужной строке и в возникшем контекстном меню выберите команду Run To Cursor. При этом происходит вот что. Щелчок правой клавишей мыши ставит текстовый курсор в нужную строку, а команда Run To Cursor запускает выполнение. Программа будет выполняться до тех пор, пока не наткнется на строку с курсором. А теперь щелкните правой клавишей мыши в другой строке и в возникшем контекстном меню снова выберите команду Run To Cursor. Программа продолжит работу с того места, где остановилась, и будет выполняться до тех пор, пока не наткнется на строку с курсором. И так далее.
Переключаемся между способами прерывания. В процессе выполнения программы вы можете достаточно свободно переключаться между разными способами прерывания. Ставьте и убирайте точки прерывания, нажимайте то F11, то F5, бегите временами до курсора. VB будет вас слушаться. Это удобно.
Если вы  работаете в пошаговом режиме в какой-то процедуре, которая вызывает много других процедур, вам не интересных, то работайте не клавишей F11, а клавишей F10. При этом все другие процедуры будут проскакиваться мгновенно.
Окна Watch. Вообразим, что окно Locals не заставило нас поумнеть. Нам все кажется, что цикл должен был бы прерваться уже при значении b=5, а почему не прервался – непонятно. Чтобы разобраться, что к чему, нам хотелось бы в любой момент времени с удобством присматривать, правда или неправда, что b=5. Также нам хотелось бы в любой момент знать, правда или неправда, что b>a. То есть нам хотелось бы, чтобы вычислялись булевские выражения  b=5  и  b>a  и результаты в виде True или False были видны в окне. Окно Locals такой возможности не дает. Более широкие возможности – у окна Watch. Вызовем его так: Debug ® Windows ® Watch ® Watch1 (можно было вызвать и другие три окна Watch). Типичный вид этого окна при отладке другой программы вы можете видеть на Рис. 9.2.

Рис. 9.2
Его вид не отличается от окна Locals. Отличие – в возможностях. В столбец Name мы можем вводить имена каких угодно (а не только локальных) переменных и объектов. А также мы можем вводить туда какие угодно выражения. В столбце Value мы видим значения этих переменных и выражений. Если вы щелкнете по плюсику слева от Button1, то кнопка расскажет вам о текущих значениях своих свойств. Если в вашем проекте имеются сложные переменные, например, массивы или структуры, введите их имена в столбец Name и, щелкнув по плюсику рядом с ними, вы увидите значения всех их составных частей.


В нашем случае я ввел в окно Watch1 оба наших выражения: b=5 и b>a. На Рис. 9.1 вы видите значения этих выражений в тот момент выполнения проекта, когда b равно 5 и a равно 16.
Настройка точек прерывания. Окно Watch помогает неплохо. Но мы устали жать на F11. Чтобы не уставать, мы ставим точку прерывания на одну из строк в цикле, скажем, на Loop, и жмем уже на F5, а не на F11. Теперь выполнение останавливается реже – один раз на итерацию (выполнение цикла). Стало легче. Но это когда итераций мало. А если их штук 500?  500 раз нажимать на  F5 – тоже не подарок. В этом случае вам захочется найти более умные и быстрые средства отладки. И они есть. Рассмотрим их применительно к нашему случаю.
Хорошо бы наша программа исполнялась в своем обычном сверхбыстром режиме и проскакивала нашу точку прерывания без остановки, но каждый раз, однако, проверяя, выполняется ли некое условие, скажем, b=5. Оно по нашим подозрениям поначалу равно False, но затем в какой-то момент выполнения вроде бы станет равным True. Вот этот самый момент мы и хотим, чтобы VB поймал и тут же остановил программу.
Итак, нам нужен совсем другой способ остановки – хоть и на нужной строке, но не всегда, а только при выполнении заданного условия. Для этого точку прерывания, установленную на данной строке, нужно настроить.
Для настройки точки прерывания завершим выполнение проекта и перейдем в режим проектирования. Щелкнем правой клавишей мыши по точке прерывания и в возникшем контекстном меню выберем Breakpoint Properties. Возникнет окно настройки свойств точки прерывания (см. Рис. 9.3).

Рис. 9.3
В этом окне нас сейчас интересуют только кнопки Condition и Hit Count.
Condition. Нажав кнопку Condition, мы видим окно настройки условия для прерывания (Рис. 9.4).

Рис. 9.4
Вводим в поле наше условие (b=5). Затем – ОК. Теперь компьютер остановится на точке прерывания только тогда, когда будет выполнено условие. Этот момент остановки вы и видите на Рис. 9.1.
 Под полем для условия вы видите переключатель на два положения. Нижнее положение приказывает прерваться не при выполнении условия, а при любом изменении значения выражения, записанного в поле.


  Hit Count. Нажав кнопку Hit Count, мы видим окно настройки счетчика прерываний (Рис. 9.5).

Рис. 9.5
Его дело – останавливать компьютер на точке прерывания не всегда, когда позволяет условие, а еще реже. Как именно реже, определяет список из 4 способов. Важную роль играет число, которое вы вводите в поле справа. На рисунке введено число 3 и мы для определенности будем пояснять относительно него. Вот 4 способа:

break always
Останавливай всегда, когда позволяет условие, а если условия нет – то вообще всегда.
break when the hit count is equal to
Останавливай на 3-й раз и больше никогда
break when the hit count is a multiple of
Останавливай через каждые два раза на 3-й
break when the hit count is greater than or equal to
Останавливай на 3-й раз и после этого всегда

Окно Quick Watch. Кроме этих окон вы видите на Рис. 9.1 еще одно окно – Quick  Watch. Оно понадобилось вот для чего. Иногда в процессе работы в режиме прерывания вам захочется узнать, чему равно в настоящий момент значение того или иного выражения из процедуры, например a - 2. Вы, конечно, можете включить это выражение в окно Watch, но быстрее поступить так: Выделите нужное выражение в окне кода, как это сделано на рисунке, затем щелкните по выделенному выражению правой клавишей мыши и в возникшем контекстном меню выберите команду Quick Watch. Окно появляется уже с нужной информацией. Если вы захотите ввести это выражение в окно Watch, щелкните кнопку Add Watch.
Окно  Command Window-Immediate позволяет в режиме прерывания вычислять выражения, узнавать значения переменных и свойств объектов проекта, и, что самое пикантное, принудительно менять их.
Вызывается оно так: Debug ® Windows ® Immediate. Возникает окно с заголовком Command Window-Immediate. Не спутайте его с окном, имеющим заголовок Command Window, которое вызывается по-другому (View ® Other Windows ® Command Window) и не совсем вам подходит.
Пользуются этим окном так. Нужно в режиме прерывания вводить в него команды (вручную или скопировав из окна кода) и нажимать Enter. Тут же, в следующей строке вы будете видеть результат. Вот пример содержимого окна Command Window-Immediate (вводимые вами команды я выделил полужирным шрифтом):


?a
14
?a+10
24
?a<33
True
Debug.WriteLine(a & "    " & b)
14    6
a=1000
?a
1000
? textbox1.text
"TextBox1"
textbox1.text="Привет!"
? textbox1.text
"Привет!"
textbox1.BackColor=color.LightSeaGreen

Пояснения: Знак вопроса означает запрос значения переменной, свойства или выражения. Так, команда
?a
означает запрос на значение переменной a и получает результат 14.
Команда
Debug.WriteLine(a & "    " & b)
скопирована из окна кода и дала ожидаемый результат:
14    6
Команда
a=1000
принудительно меняет значение переменной a. То есть происходит прямое и неприкрытое вмешательство в святая-святых – работу программы. Но польза от этого для отладки есть. (Это еще ничего, предыдущие версии Visual Basic разрешали менять даже операторы программы в процессе выполнения. Представляете?! И тоже, кстати, не без пользы.)
Команда
? textbox1.text
узнает значение свойства объекта.
Команды
textbox1.text="Привет!"
и
textbox1.BackColor=color.LightSeaGreen
меняют свойства объекта.
Окно Command Window-Immediate не дает вам привычной свободы окна кода. Скорее оно похоже на командную строку с историей. Для навигации по окну вы можете пользоваться мышкой и на клавиатуре стрелками вверх-вниз. Нажатие клавиши Enter на любой промежуточной строке в этом окне отправляет эту строку вниз. Стрелки вверх-вниз не движут курсор, а «вспоминают» команды. Ничего, привыкнуть можно.
Окно Call Stack. Когда в программе содержится много взаимодействующих процедур, вам при отладке может быть совершенно необходимо разобраться в последовательности их выполнения. В этом случае вам поможет окно Call Stack, которое и покажет вам эту последовательность. Вызывается оно так: Debug ® Windows ® Call Stack.
Все описанные в этом разделе возможности отладки, а также некоторые другие, не описанные мной, приведены в меню Debug (отладка).

Типичные приемы программирования


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



Процедуры и функции


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



Работа с таймером, временем, датами


Мы с вами пока не умеем управлять временем. А это нам необходимо для работы с движением, анимацией, а также для решения других полезных и интересных задач, приведенных в этой главе.  Собственно, все игры с одновременным движением персонажей делаются с помощью таймера. Венец главы – создание вашего первого не учебного, а реального проекта «Будильник-секундомер». Вы ведь видели часы в правой части панели задач Windows? Если на них поставить мышь, они покажут дату. Давайте сделаем что-нибудь получше, а именно – большие, красивые часы-будильник (можно даже во весь экран), а заодно и с секундомером. Для этого нам придется сначала познакомиться с новым типом данных – типом даты и времени суток. Также в этой главе вы изучите перечисления и освоите новые элементы управления.



Работа с мышью и клавиатурой


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

Прежде чем говорить о мыши и клавиатуре, поговорим о фокусе.



Массивы, рекурсия, сортировка


Массивы – одно из главных средств хранения в памяти компьютера больших объемов информации.

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

Рекурсия – это вызов процедуры из тела самой процедуры. Она лежит в основе рекурсивного стиля программирования, очень интересного и плодотворного стиля для определенного круга задач.

Сортировка – это процесс упорядочивания элементов массива. Очень популярная и распространенная задача программирования.

Начнем с массивов.



Разные звери в одном ковчеге


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