Абстрактные классы
Владельцы участков в нашем товариществе стали огораживать их всяк на свой манер: у одних участки прямоугольные, у других треугольные, у третьих круглые. Для нашего проекта это значит, что периметры теперь нужно считать по разным формулам. Соответственно создаем три класса: Участок_прямоугольный, Участок_треугольный и Участок_круглый. Их коды во многом одинаковы и различаются только исходными данными и функцией Периметр. Следовательно, целесообразно организовать наследование. Но кто кого будет наследовать? Вроде, все три класса равноправны. Из соображений стройности и логичности системы классов нам не хочется один из них делать родителем. Что же делать? Поступают так. Создают один класс, родительский для всех трех, и включают в него код, общий для всех трех классов. Поскольку функция Периметр разная для всех классов, то в этот класс включают только ее заголовок, без тела. Ясно, что от такого бестелесного класса нельзя создавать объекты, поэтому его называют абстрактным классом и помечают словом MustInherit. Вот этот класс:
Public MustInherit Class Участок_абстрактный
Public Владелец As String
Public Высота_забора As Integer
Public Shared Расход_краски_на_кв_м As Integer
Public MustOverride
Function Периметр() As Integer
Public Overridable Function Площадь_забора() As Integer
Return Периметр() * Высота_забора
End Function
Public Function Расход_краски_на_забор() As Integer
Return Расход_краски_на_кв_м * Площадь_забора()
End Function
End Class
Вы видите, что функция в этом классе помечена словом MustOverride, что означает «Необходимо пересилить, переопределить». От функции присутствует только заголовок. Ситуация того же смысла, что и со словом Overridable, только более строгая. Это значит, что обращаться к функции в этом классе бесполезно, все равно у нее нет тела, а вот в коде наследников эту функцию написать можно и нужно, причем тело функции у каждого наследника может быть своим. К этим функциям и будем обращаться. Вот три наших класса:
Public Class Участок_прямоугольный
Inherits Участок_абстрактный
Public Длина, Ширина As Integer
Public Overrides
Function Периметр() As Integer
Return 2 * (Длина + Ширина)
End Function
End Class
Public Class Участок_треугольный
Inherits Участок_абстрактный
Public Сторона1, Сторона2, Сторона3
As Integer
Public Overrides
Function Периметр() As Integer
Return Сторона1 + Сторона2 + Сторона3
End Function
End Class
Public Class Участок_круглый
Inherits Участок_абстрактный
Public Радиус As Integer
Public Overrides
Function Периметр() As Integer
Return 2 * Math.PI * Радиус
End Function
End Class
Вы видите, что абстрактные классы помогают поддерживать в проекте стройную иерархию классов.
В нашу систему классов я могу включить и класс Участок_штакетник. Я сделаю его сыном прямоугольного участка и, значит, внуком абстрактного:
Public Class Участок_штакетник
Inherits Участок_прямоугольный
Public Overrides Function Площадь_забора() As Integer
Return 0.5 * MyBase.Площадь_забора
End Function
End Class
Вы видите, что он может посчитать площадь штакетника только вокруг прямоугольного участка. Вопрос о штакетнике вокруг треугольного и круглого участков оставляю открытым для вашего размышления.
Автоматическое заполнение поля текстом
Пока мы вводили текст в поле RichTextBox вручную. Посмотрим, как можно заставить VB автоматически заполнять поле текстом и попутно форматировать его. Вот процедура обработки нажатия на кнопку Кнопка. Именно эта процедура заполняет нижнюю часть поля RichTextBox на Рис. 20.6.
Private Sub Кнопка_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Кнопка.Click
RTB.SelectedText = "А этот текст вводится автоматически. Сейчас я включу маркеры." + vbNewLine
RTB.SelectionBullet
= True
RTB.SelectedText = "Вот так." + vbNewLine
RTB.SelectedText = "А теперь выключу маркеры, но изменю шрифт и его цвет:" + vbNewLine
RTB.SelectionBullet
= False
RTB.SelectionFont
= New Font("Arial", 16)
RTB.SelectionColor
= Color.Green
RTB.SelectedText
= "Вот так теперь стало."
End Sub
Пояснения: Свойство SelectedText имеет значение выделенного фрагмента текста. Поэтому мы всегда можем написать
Debug.WriteLine(RTB.SelectedText)
и тем самым распечатать выделенный предварительно вручную фрагмент текста.
С другой стороны мы можем присваивать этому свойству значение произвольной строки. Тем самым строка будет введена в поле RichTextBox и примет на себя форматирование, предварительно установленное нами обычным образом.
Если вводимая в поле строка «упирается» в правую границу поля, то она автоматически продолжается со следующей строки. Чтобы принудительно продолжить ввод с новой строки (то есть сделать «абзац»), мы при ручном вводе нажимаем на клавишу Enter, а при автоматическом заполнении поля вставляем символ vbNewLine.
База данных с фото, видео и музыкой
Если вам любопытно, создайте и заполните в базе данных «Контакт» вдобавок к таблице «Книги» еще одну таблицу – «Сотрудники» – с такими полями: Фамилия, Дата рождения, Владение компьютером, Зарплата, Биография, Фото, Любимая мелодия и Любимое видео.
Работая в конструкторе таблицы, полю Владение компьютером придайте Логический тип (в смысле – да-нет, Владеет-не владеет). Полю Зарплата придайте Денежный тип. Полям Биография, Фото, Любимая мелодия и Любимое видео придайте тип Поле объекта OLE. Это значит, что значением этого поля может быть солидный объект, например, документ Word, картинка, звуковой или видеоклип.
Пусть у вас в таблице 3 сотрудника. Подготовьте заранее и сохраните на диске 12 файлов: 3 документа Word с биографиями, 3 фотографии, 3 звуковых и 3 видеоклипа.
При заполнении таблицы данными, дойдя до ячейки типа Поле объекта OLE, щелкните по ней правой клавишей мыши и выберите Добавить объект. Вы увидите диалоговое окно (Рис. 24.5).
Рис. 24.5
В нем вы можете просмотреть в списке типы объектов, которые можно вставить в базу данных.
Выберите в этом окне радиокнопку «Создать из файла». Окно изменилось (Рис. 24.6).
Рис. 24.6
Щелкните на кнопку Обзор и выберите на диске нужный файл.
После заполнения таблица будет выглядеть , как на Рис. 24.7.
Рис. 24.7
Чтобы увидеть биографию, фото, видео или услышать мелодию, просто щелкните дважды по соответствующей ячейке таблицы.
Теперь вы можете создать базу данных своих фотографий, а если вы работаете с документами, то базу данных документов. Правда, если фотографии или документы добавляются каждый день, то вручную вносить их в базу данных утомительно и рекомендуется программированием автоматизировать этот процесс.
Базы данных и язык XML
Вы видите, что порядок столбцов на Рис. 24.24 перепутан. Сейчас мы исправим это положение.
Восстанавливаем порядок столбцов. Обратите внимание на окно Solution Explorer. Вы видите, что в момент создания объекта DataSet в проект добавился файл DataSet1.xsd. Щелкните дважды по его значку, вы увидите схему вашего объекта DataSet (Рис. 24.25).
Рис. 24.25
Слева приведены имена полей таблицы в том порядке, что вы видите на Рис. 24.24, справа – их типы. Не пытайтесь что-нибудь менять в этой схеме. Щелкните по закладке XML. Вы увидите страницу текста, часть ее я привожу на Рис. 24.26.
Рис. 24.26
При внимательном взгляде на рисунок вы можете видеть, что это тоже не что иное, как схема вашего объекта DataSet, а конкретнее – список полей таблицы «Книги» в том порядке, какой вы видите на Рис. 24.24, с указанием типов полей и другой информации.
Чтобы поменять порядок полей, аккуратно поменяйте местами соответствующие строки на странице. Сохраните проект. Обратите внимание, что схема на Рис. 24.25 тоже изменилась соответствующим образом. Запустите проект. Порядок столбцов восстановился (Рис. 24.27).
Рис. 24.27
Язык XML. Что же за текст такой мы увидели на Рис. 24.26? Это не что иное, как XML-документ, то есть текстовый файл, написанный на так называемом языке XML. Этот язык идет на смену языку HTML, потому что его возможности намного шире. Его мощь выражается в том, что он способен описывать не только Web-страницы, но приспособлен для описания данных самых разных видов. Это настоящий универсальный язык представления структурированных данных. В частности, в нашем случае он был использован для описания структуры объекта DataSet.
Чтобы VB или другой язык программирования мог одинаковым образом работать с базами данных самых разных провайдеров, объекты DataSet, представляющие эти базы на вашем компьютере, должны получать данные изо всех баз в одинаковом формате (виде). Этот формат и есть формат языка XML. Данные, прежде чем их передадут из файла базы данных в DataSet, автоматически преобразуются в формат XML и в этом виде путешествуют по каналу связи, легко преодолевая всяческие файерволы и брандмауэры. На обратном пути все происходит, конечно, в обратном порядке.
Бесконечность форм
Недоступные формы. Создайте проект. В режиме проектирования добавьте в проект еще одну форму (как это делается, написано в 21.2). Перечитайте также 21.7. На первую форму (Form1) поместите две кнопки. Введите в ее окно кода такой код:
Dim Ф As Form2
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Ф = New Form2
Ф.Show()
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Ф.BackColor = Color.Green
End Sub
Запустите проект. Щелкните несколько раз по первой кнопке, а затем один раз – по второй. Вот что вы увидите (Рис. 27.3).
Рис. 27.3
С каждым щелчком по первой кнопке порождается и показывается на экране очередной объект – экземпляр класса Form2. Спрашивается: почему при щелчке по второй кнопке окрасился только последний экземпляр?
Дело вот в чем. Переменная Ф объявлена классом Form2, значит она имеет ссылочный тип. При первом щелчке по первой кнопке в памяти была создана большая область для объекта класса Form2, в которой предусмотрено место для хранения всех многочисленных свойств формы. Переменная же Ф получила значение ссылки на эту область. При втором щелчке в памяти была создана еще одна область для объекта класса Form2, а переменная Ф получила значение ссылки уже на эту новую область, потеряв, естественно, ссылку на старую. Очевидно, переменная Ф всегда ссылается только на последнюю созданную форму, поэтому при щелчке по второй кнопке обращается только к ней.
Поскольку ссылки на все формы, кроме последней, потеряны, то вы из кода никак не можете к ним обратиться и ничего не можете с ними сделать, даже закрыть. А вот вручную – пожалуйста! Таскайте мышкой, закрывайте и т.п.
Коллекция форм. Как исправить это положение? Пусть вам хочется управлять из кода всеми созданными формами. Создайте коллекцию и каждую созданную форму добавляйте в нее. Тогда к каждой форме можно обращаться, как к члену коллекции, по индексу. Вот код:
Dim Форма As New Collection
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Ф As New Form2
Ф.Show()
Форма.Add(Ф)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Форма(3).BackColor = Color.Green
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim i As Integer
For i = 1 To Форма.Count
Форма(i).Text = "Форма " & i
Next
End Sub
Щелкая по первой кнопке, мы создаем очередную форму, показываем ее на экране и добавляем в коллекцию. Щелкнув раз шесть, щелкните один раз по второй кнопке, которая покрасит третью форму, и один раз по третьей кнопке, которая поменяет заголовки у всех форм (см. Рис. 27.4).
Рис. 27.4
Член нашей коллекции не обладает всем богатством свойств формы Form2. Поэтому, если мы, скажем, добавим на Form2 текстовое поле и захотим покрасить вышеописанным способом текстовые поля на всех экземплярах формы, то нам не удастся этого сделать. Спасает положение следующий код:
Dim Объект As Form2
For Each Объект In Форма
Объект.TextBox1.BackColor = Color.Yellow
Next
Более сложный пример – собственная кнопка
Теперь попробуем вышеописанным способом создать собственную кнопку, такую же, как в 22.14.1. Сейчас перелистайте книжку до указанного пункта и внимательно перечитайте его. Напомню свойства и поведение создаваемой кнопки:
В левой части кнопки находится иконка с луной.
В правой части кнопки находится изменяющийся текст, указывающий, сколько раз по кнопке щелкнули с момента ее создания, например, «19 щелч.».
У кнопки есть дополнительное ReadOnly свойство Число_щелчков, смысл которого очевиден.
На 10-м щелчке по кнопке она порождает событие Наступление_зрелости. Таким образом, у кнопки есть дополнительное событие.
У кнопки есть дополнительное ReadOnly булевское свойство Ветеран, которое равно False до 10-го щелчка и True – после. Подразумевается, что пока на кнопку не нажали 10 раз, она еще неопытная, незрелая, а когда нажали – она уже опытная, зрелая, ветеран.
У кнопки есть дополнительный метод Прыжок, который заставляет кнопку прыгнуть направо. Дальность прыжка является параметром метода.
Чтение из незнакомого текстового файла
Задача 4: В задаче 2 мы еще до считывания знали, как располагаются по строкам данные в файле Filimon.txt. Однако, нередки случаи, когда мы не имеем об этом представления. Все, что мы знаем, это то, что файл текстовый и состоит из строк. Тем не менее, данные из файла прочитать хочется, чтобы хотя бы посмотреть на них. Для этого можно приказать Бейсику загружать каждую строку файла, независимо от того, из каких данных она состоит, в переменную типа String. Вот программа, распечатывающая три первые строки файла Fil.txt:
Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
Dim Чтение As New System.IO.StreamReader("E:\VB\Fil.txt")
Dim s1 As String = Чтение.ReadLine : Debug.WriteLine(s1)
Dim s2 As String = Чтение.ReadLine : Debug.WriteLine(s2)
Dim s3 As String = Чтение.ReadLine : Debug.WriteLine(s3)
Чтение.Close()
End Sub
Задача 5: В считываемом файле ровно 800 строк. Требуется их считать и распечатать.
Решение: Ясно, что без цикла здесь не обойтись:
Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button6.Click
Dim Чтение As New System.IO.StreamReader("E:\VB\Filimon.txt")
Dim s As String
Dim i As Integer
For i = 1 To 800
s = Чтение.ReadLine
Debug.WriteLine(s)
Next
Чтение.Close()
End Sub
Задача 6: Вам нужно прочесть все строки файла, а сколько строк в файле вы не знаете.
Решение: В этом случае оператор цикла For не подойдет, так как в нем нужно указывать точное число строк. Хорошо бы компьютеру можно было приказать: «Читай, пока в файле еще есть информация». И такой приказ есть. В его основе лежит метод Peek (который в данном контексте можно перевести, как «попробуй»). При его выполнении считывающая головка продвигается на 1 символ вперед, считывает его, но не остается там, как при выполнении методов ReadLine и WriteLine, а возвращается назад. Если же считывать было нечего, то есть файл кончился, метод Peek возвращает число -1.
Вот программа:
Private Sub Button7_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button7.Click
Dim Чтение As New System.IO.StreamReader("E:\VB\Filimon.txt")
Dim s As String
Do While Чтение.Peek() <> -1
s = Чтение.ReadLine
Debug.WriteLine(s)
Loop
Чтение.Close()
End Sub
Чтение из знакомого текстового файла
Задача 2: Пусть известно, что первыми тремя строками в текстовом файле с именем Filimon.txt являются слово, число и дата. Требуется прочесть их оттуда в оперативную память компьютера. Вот программа:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
'Создаем объект для чтения информации из текстового файла E:\VB\Filimon.txt:
Dim Чтение As New System.IO.StreamReader("E:\VB\Filimon.txt")
'Подготавливаем 3 переменные для хранения информации из текстового файла E:\VB\Filimon.txt:
Dim Строка As String
Dim Число As Double
Dim Дата As DateTime
'Считываем 3 строки из файла (4-ю считывать не стали):
Строка = Чтение.ReadLine
Число = Чтение.ReadLine
Дата = Чтение.ReadLine
Чтение.Close() 'Закрываем файл
'Демонстрируем, что со считанной информацией можно по-всякому работать:
Debug.WriteLine(Строка & "!")
Debug.WriteLine(Число - 1)
Debug.WriteLine(Дата.TimeOfDay)
End Sub
Пояснения: Для чтения из текстового файла будем применять объект класса StreamReader. Его мы создаем первым оператором нашей процедуры. Говорят, что при выполнении этого оператора файл открывается для чтения. При этом компьютер выполняет определенные подготовительные действия для работы с файлом. Так, магнитная головка перемещается в начало файла. Ничего, конечно, не стирается.
Затем мы должны подготовить в оперативной памяти компьютера место для принимаемой информации, что мы и делаем, объявляя 3 переменные. Имейте в виду, что в самом файле нигде не говорится, к какому типу данных относится та или иная информация в нем и даже сколько ее там. То есть совершенно невозможно узнать, что такое, например, 5,27 из предыдущей задачи. То ли это одно дробное число – пять целых двадцать семь сотых, то ли это два целых числа через запятую, то ли это пять целых две десятых и рядом стоящая семерка, то ли это просто строка из 4 символов. Поэтому нет смысла читать информацию из файла, если нам заранее не рассказали, какой тип имеют данные в файле.
Следующие 3 оператора процедуры считывают из файла 3 строки и присваивают их значения соответствующим переменным. Чтение осуществляется методом ReadLine объекта Запись. Этот метод просто считывает целиком очередную строку файла, что бы в ней ни было записано.
После завершения чтения из файла его нужно закрыть методом Close.
Вот что распечатает эта процедура, если файл был заполнен, как в предыдущей задаче:
Азия!
4,27
21:30:00
Преобразования типов при чтении из текстового файла. При чтении из текстового файла метод ReadLine рассматривает каждую строку файла как цепочку символов, то есть просто как обыкновенную строку (тип String), а при присваивании данной строки переменной величине VB пытается преобразовать ее к типу этой переменной. Так, при выполнении оператора
Дата = Чтение.ReadLine
VB пытается преобразовать строку
29.03.2005 21:30:00
к типу DateTime. Поскольку эта строка файла представляет правильную запись даты, присваивание проходит успешно.
Если бы мы вместо
Dim Число As Double
написали бы
Dim Число As Integer
VB попытался бы преобразовать строку 5,27 в целое число, у него бы это получилось и мы бы увидели такую распечатку:
Азия!
4
21:30:00
В случае неудачи преобразования VB выдает ошибку.
Считывается информация из текстового файла строго по порядку, сверху вниз. Мы не можем прочитать что-то сперва в начале файла, потом в конце, потом в середине. Поэтому к катастрофе приведет ситуация, когда в процедуре мы вот так перепутаем местами операторы:
Дата = Чтение.ReadLine
Число = Чтение.ReadLine
Верхний из них попытается преобразовать 5,27 к типу DateTime и потерпит неудачу. VB выдаст ошибку.
Лишняя работа. Из-за последовательного характера записи и чтения при работе с текстовым файлом, компьютеру приходится выполнять лишнюю работу. Так, если нас в текстовом файле интересует, скажем, только 3-я строка, нам, чтобы до нее добраться, все равно придется прочесть и 1-ю и 2-ю строки. Если мы в текстовом файле из большого количества строк хотим исправить только одну, нам придется вновь записать все строки файла, так как перед записью вся информация из файла стирается.
ComboBox
ComboBox – это тот же ListBox, только чуть более удобный и компактный, хотя по своим возможностям он уступает ему. В нем вдобавок к списку, сверху от него, имеется текстовое поле. ComboBox существует в трех вариантах. Рассмотрим их по мере усложнения.
ComboBox (вариант «Раскрывающийся список»). Поместите на форму элемент управления ComboBox. Вариант элемента ComboBox определяется значением его свойства DropDownStyle. Выберите вариант DropDownList – «Раскрывающийся список». Заполните ComboBox так же, как вы заполняли список ListBox. Для убыстрения заполнения вы можете просто скопировать в его Редактор строковых коллекций все содержимое Редактора строковых коллекций уже готового списка ListBox1. Запустите проект. В левой части Рис. 18.10 вы видите закрытый раскрывающийся список.
Рис. 18.10
Щелкните по черной треугольной стрелке в правой части списка. Список раскроется и будет выглядеть так, как в правой части Рис. 18.10. Теперь вы сможете выбрать из него любой элемент так же, как вы выбирали элементы из списка ListBox. Выбранный элемент появляется в текстовом поле наверху списка. В данном варианте ComboBox редактировать (вручную изменять) текстовое поле мы не можем.
Программа для ComboBox в нашем случае аналогична программе для ListBox.
Debug.WriteLine("Следующим соперником нашей команды будет команда " & ComboBox2.SelectedItem)
После того, как вы перейдете от ComboBox к другим элементам управления, ComboBox закроется. Как видите, преимуществом раскрывающегося списка перед обычным является экономия места на форме.
ComboBox (вариант «Простой ComboBox»). Поместите на форму ComboBox. В его свойстве DropDownStyle выберите вариант Simple – «Простой». Заполните ComboBox. Запустите проект. ComboBox выглядит так:
Рис. 18.11
Он не сворачивается в отличие от раскрывающегося списка, зато его текстовое поле можно редактировать. Зачем нужно редактировать, выяснится чуть позже.
ComboBox (вариант «Раскрывающийся ComboBox»). Раскрывающийся ComboBox объединяет в себе преимущества двух других вариантов ComboBox: он выглядит так же компактно, как раскрывающийся список, и позволяет редактировать текстовое поле, как простой ComboBox. Значение его свойства DropDownStyle равно DropDown.
Действия над файлами и папками
В этом подразделе я научу вас путешествовать в окнах Windows по папкам и дискам, находить в них нужную папку, нужный файл, а кроме этого – создавать, копировать, перемещать и удалять файлы и папки, без чего в работе с VB не обойтись.
Окно «Мои документы». Выберите в стартовом меню Windows пункт «Мои документы» (My Documents). На экране откроется окно папки «Мои документы» (Рис. П6). Большинство программ, установленных на вашем компьютере, привыкло сохранять результаты вашей работы именно в эту папку. Так, Visual Studio .NET сохраняет вашу работу в папку Visual Studio Projects, расположенную внутри папки My Documents.
Рис. П6
Окно папки предназначено для того, чтобы показывать внутри себя содержимое папки, а именно папки и файлы. На рисунке мы видим, что в папку My Documents входит 8 папок и ни одного файла. Очевидно, файлы находятся внутри этих 8 папок.
Путешествуем по папкам. Вы можете двойным щелчком мыши войти в любую из 8 папок. При этом откроется окно уже с содержимым папки, по которой щелкнули. В зависимости от настройки Windows это будет или новое окно, или же папка откроется на месте старого окна, а старое из соображений экономии и удобства исчезнет.
Таким образом вы можете продвигаться вглубь папок, как внутрь матрешки. А как продвигаться наружу? Для этого есть кнопка
. Щелчком по ней вы выйдете из папки. Еще щелчок – еще один шаг наружу. И так далее, пока не окажетесь на Рабочем столе Windows (Desktop). Если хотите опять идти внутрь и добраться до Visual Studio Projects, войдите в My Documents. А если хотите добраться вообще до информации на всех дисках компьютера, войдите на рабочем столе в значок Мой компьютер.Название папки, открытой в окне, видно в заголовке окна и в раскрывающемся списке Address. Щелкнув по кнопке-стрелке справа в этом списке, вы раскроете список папок в виде части дерева, похожего на дерево Проводника Windows. Это позволит вам быстрее путешествовать по папкам.
Если вам не нравится внешний вид папок и файлов в окнах, вы можете изменить его, зайдя в меню View окна папки.
Потренируйтесь. У вас должна возникнуть уверенность, что вы можете добраться до любой папки и любого файла на любом диске вашего компьютера.
Создаем файлы и папки. Щелкните правой клавишей мыши по белому пространству внутри окна и в контекстном меню выберите New. В возникшем подменю вы увидите типы файлов, которые вы можете создать внутри папки (см. Рис. П7).
Рис. П7
Щелкните по нужному типу. Среди этих типов вы найдете и Folder, что означает создание новой папки.
Копируем файлы и папки. Чтобы скопировать файл или папку в другое место, щелкните по нему правой клавишей мыши и выберите в его контекстном меню опцию Copy (копировать). Затем доберитесь до папки, в которую хотите данный файл или папку скопировать, откройте ее и щелкните правой клавишей мыши по свободному пространству внутри ее окна. В открывшемся контекстном меню выберите Paste (вставить). Если вы хотите, чтобы копия находилась в той же папке, что и оригинал, никуда добираться не нужно, а щелкайте внутри той же папки.
Перемещаем файлы и папки. Чтобы переместить файл или папку в другое место, делайте все то же, что и при копировании, только вместо Copy выберите в контекстном меню опцию Cut (вырезать).
Переименовываем файлы и папки. Чтобы переименовать файл или папку, выберите в контекстном меню опцию Rename (переименовать).
Удаляем файлы и папки. Чтобы удалить файл или папку, выберите в контекстном меню опцию Delete (удалить).
Деревья и искусственный интеллект
Ученые, занимающиеся созданием искусственного интеллекта, в качестве одного из направлений пытаются копировать работу человеческого мозга. В частности они моделируют способ человеческих рассуждений при решении тех или иных задач. Например, шахматист рассуждает так: Если противник сходит пешкой, я отвечу слоном или ладьей. Если я отвечу слоном, то противник в свою очередь ответит так-то, так-то или так-то. И так далее. Не правда ли: это рассуждение можно изобразить как дерево? Если заставить компьютер перед каждым ходом порождать такое дерево с ветками, изображающими все возможные ходы хотя бы на несколько ходов вперед, а затем еще и оценивать силу ходов и выбирать самый сильный, то мы будем иметь шахматную программу. По сути, все шахматные программы так и работают. Только ветки на дереве строятся не для всех возможных, а лишь для наиболее сильных ходов. А иначе дерево получилось бы слишком ветвистым, так что мощности компьютера не хватило бы на его обработку.
Этот способ рассуждений при помощи дерева применяется, конечно, не только в шахматах, но и для решения самых разных задач.
Диалоговое окно настройки шрифта
Типичное диалоговое окно настройки шрифта показано на Рис. 20.5.
Рис. 20.5
Такое или похожее окно мы видим во многих приложениях Windows, например, в Word. Такое же окно, но без настройки цвета, мы видим в VS при ручной настройке шрифта элементов управления в окне свойств (см. Рис. 3.5 в 3.2.2).
Настраиваем шрифт текстового поля. Создадим проект, в котором предоставим пользователю возможность, редактируя текст в текстовом поле, выбирать при помощи такого диалогового окна шрифт текста в текстовом поле.
Создайте проект. Разместите в нем текстовое поле, кнопку и элемент управления FontDialog. Вот программа:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
FontDialog1.ShowColor = True 'Предоставить возможность выбирать цвет шрифта
FontDialog1.ShowDialog() 'Показать диалоговое окно настройки шрифта
'Присвоить шрифту текстового поля значение шрифта, выбранного в диалоговом окне:
TextBox1.Font = FontDialog1.Font
'Присвоить цвету шрифта текстового поля значение цвета шрифта, выбранного в диалоговом окне:
TextBox1.ForeColor = FontDialog1.Color
End Sub
Пояснения: Оператор
FontDialog1.ShowColor
= True 'Предоставить возможность выбирать цвет шрифта
приказывает отображать в диалоговом окне настройки шрифта раскрывающийся список для настройки цвета.
На операторе
FontDialog1.ShowDialog() 'Показать диалоговое окно настройки шрифта
появляется диалоговое окно настройки шрифта, программа останавливается и ждет. Мы настраиваем шрифт и нажимаем на кнопку OK диалогового окна. После нажатия окно пропадает, настройки (кроме цвета) запоминаются в свойстве Font объекта FontDialog1, цвет запоминается в свойстве Color объекта FontDialog1 и программа продолжает работу.
Действие двух других операторов ясно из комментариев.
Настраиваем шрифт графического объекта. Дополним нашу задачу требованием написать что-нибудь выбранным шрифтом на форме. Добавим в проект пару кнопок. Вот программа:
'Выбираем шрифт в диалоговом окне:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
FontDialog1.ShowColor = True
FontDialog1.ShowDialog()
End Sub
'Применяем выбранный шрифт к текстовому полю:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
TextBox1.Font = FontDialog1.Font
TextBox1.ForeColor = FontDialog1.Color
End Sub
'Пишем выбранным шрифтом на форме:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
'Создать шрифт, равный шрифту, выбранному в диалоговом окне:
Dim Шрифт As Font = FontDialog1.Font
'Создать кисть цвета, выбранного в диалоговом окне:
Dim Кисть As New SolidBrush(FontDialog1.Color)
Dim Граф As Graphics = Me.CreateGraphics
Граф.DrawString("Всегда!", Шрифт, Кисть, 0, 0) 'Написать выбранным шрифтом текст
End Sub
Пояснения: Здесь мы применяем один объект FontDialog1 как для текстового поля, так и для формы. Кто забыл, как создаются шрифты, перечитайте 12.2.3.
Диалоговое окно выбора цвета
Диалоговое окно выбора цвета (ColorDialog) необходимо для удобного выбора цвета в режиме работы..
Диалоговые окна открытия и сохранения файла
До сих пор я знакомил вас с довольно простыми элементами управления, такими как кнопки, текстовые поля и т.п. Достаточно ли таких простых элементов, чтобы запрограммировать любые приложения Windows? В принципе, да. Возьмем, например, диалоговое окно сохранения файла. Оно примерно одинаково в большинстве приложений Windows (см. Рис. 20.1).
Рис. 20.1
Вы видите, что оно состоит из кнопок, меток, раскрывающихся списков и других простых элементов, значительную часть которых мы проходили. Все эти элементы могли бы быть собраны нами на форме с заголовком Save File As. Таким образом, если мы захотим организовать сохранение файла, мы должны будем создать в проекте еще одну форму, которая и будет диалоговым окном (как создавать в проекте дополнительные формы, я расскажу в 21.2). Затем мы поместим на эту форму нужные элементы управления и запрограммируем работу каждого из них. В общем, работа довольно большая, но выполнимая.
Разработчики библиотеки классов .NET Framework постарались облегчить жизнь программистам и создали элемент управления SaveFileDialog, который уже имеет вид нужного диалогового окна. Простые элементы управления внутри диалогового окна уже запрограммированы надлежащим образом. Все, что остается программисту – это написать немного кода для настройки окна в целом.
Все, сказанное здесь про SaveFileDialog, относится и к окну OpenFileDialog, применяемому для открытия файлов.
Создайте проект. Разместите под формой элементы управления SaveFileDialog и OpenFileDialog. В режиме работы диалоговые окна возникают при выполнении метода ShowDialog:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
SaveFileDialog1.ShowDialog()
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
OpenFileDialog1.ShowDialog()
End Sub
Проверьте работу приведенного фрагмента. Вы увидите, что путешествие по папкам диалоговых окон проходит совершенно «по-всамделишному», однако при нажатии кнопок Open и Save никакого открытия и сохранения файлов не происходит. Здесь ситуация аналогичная меню: выбор пункта меню приводит к каким-либо действиям только тогда, когда эти действия вами запрограммированы.
Реальное открытие и сохранение файлов изучим в следующем подразделе на примере создания простейшего текстового редактора.
Несмотря на то, что метод ShowDialog является функцией (Function), а не процедурой (Sub), в приведенном коде он встречается не справа от знака равенства и не в составе выражений, как пристало функциям, а как самостоятельный оператор. VB допускает такое употребление для многих функций. А почему бы и нет? Ведь иногда нас интересует не значение, которое функция возвращает, а ее побочный эффект, в данном случае диалоговое окно.
Добавляем в проект сборку из библиотеки классовNET Framework
Если вам не хватает пространств имен, заключенных в тех немногих сборках .NET Framework, на которые есть ссылки в окне Solution Explorer, вы можете описанным только что способом добавить в проект новые ссылки. Только в Browse заходить не нужно, так как сборки библиотеки классов .NET Framework перечислены в списке в центре окна. Естественно, вам заранее нужно знать, какая именно сборка из списка вам нужна.
DoEvents
Когда вы в предыдущем примере безрезультатно щелкали мышкой по второй кнопке, ваши щелчки все же не пропали бесследно. Они запомнились и встали в очередь на обработку. Вы можете удостовериться в этом, задав в первой процедуре не бесконечный цикл, а конечный, секунд эдак на 10. Пока он выполняется, щелкните по второй кнопке. Она, конечно, не среагирует. Спокойно ждите, пока цикл не кончится. Когда он завершится, тут же само собой начнется выполнение второй процедуры. Значит, щелчок не пропал.
Дополните ваш проект двумя текстовыми полями, а каждую из двух процедур – двумя строками. Строки же печати в окно Output уберите:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim i As Integer = 1
Do
TextBox1.Text = i
i += 1
Application.DoEvents()
Loop
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim i As Integer = 1
Do
TextBox2.Text = i
i += 1
Application.DoEvents()
Loop
End Sub
Теперь числа побегут не в окне Output, а в текстовых полях.
Вы видите, что на каждой итерации цикла будет выполняться метод DoEvents класса Application пространства имен System.Windows.Forms. Этот метод приказывает обработать все события, стоящие к этому моменту в очереди. Это решает дело. Поскольку цикл работает очень быстро, этот метод будет выполняться много раз в секунду, поэтому, стоит нам нажать на какую-нибудь кнопку, как тут же это нажатие будет обработано.
Проверим. Запустите проект. Щелкните по кнопке 1. В текстовом поле 1 побегут числа. Теперь нажмите один-два раза кнопку 2. Числа теперь побегут в текстовом поле 2, а в текстовом поле 1 остановятся. Снова щелкните по кнопке 1. Числа в текстовом поле 2 остановятся, а в текстовом поле 1 снова начнут бежать.
Обратите внимание, что бежать они начнут, но не с того места, где остановились, а начиная с 1. Значит, получается, что по приказу метода DoEvents управление «выпархивает» из процедуры-обработчика, и больше уж в точку «вылета», как мы привыкли, не возвращается. В следующий раз, если оно попадает в этот обработчик, то только законным путем (в нашем случае при новом нажатии кнопки 1, когда начинает с самого начала работать процедура 1). Одновременно обе процедуры работать не могут.
Таким образом, проблему мы решили, но решили частично. Если какая-то процедура работает слишком долго, мы можем ее прервать, но потом-то, если она не доделала что-то нужное, все равно придется ее запускать.
Доступ к локальному диску
Будем называть локальным диском жесткий диск чужого компьютера, на котором читается ваша Web-страница (если же она читается на вашем компьютере, то тогда локальный диск – это жесткий диск вашего компьютера). Доступом к локальному диску будем называть возможность при помощи сценария вашей Web-страницы читать, стирать или записывать информацию в файлы локального диска (примерно так, как мы это делали в 19.2 с файлами на диске нашего компьютера), а также осуществлять все другие операции с файлами и папками.
Спрашивается, хорошо это или плохо – при помощи вашей Web-страницы иметь доступ к диску чужого компьютера? Это примерно то же самое, что спросить, хорошо это или плохо – прийти к незнакомому человеку в гости и, пользуясь его доверчивостью, иметь доступ ко всем вещам и секретным документам в его квартире. Если вы честный человек, то ничего не украдете и подглядывать не будете. Зачем же вам тогда доступ? Получается, что доступ – это плохо? Не всегда. Он часто бывает нужен в общении между знакомыми людьми, которые доверяют друг другу и которым было бы удобно считывать информацию с локальных дисков друг друга. Или возьмите ситуацию сохранения в играх. Пользователь, играющий на вашей страничке в «Угадай число» и желающий после 20-й попытки сохраниться, должен иметь возможность это сделать. Сделать же это проще всего на локальном диске. Но для этого нужен доступ к нему со стороны сценария игры. Выходит, что без доступа в некоторых случаях все-таки не обойтись.
VBScript предоставляет программистам и доступ к локальным дискам, и другие потенциально опасные возможности, но браузер обнаруживает опасные операторы в сценарии загружаемой странички и предупреждает пользователя об опасности.
Проиллюстрирую работу VBScript с локальным диском на одном-единственном примере. Пусть ваша страничка посвящена разведению слонов в Антарктиде J и она настолько интересна J, что побывавший на ней снова и снова туда возвращается. Вставим на страничку сценарий, единственная цель которого – напомнить пользователю, сколько раз он был на этой странице. Внешний вид странички после открытия вы видите на Рис. 23.5.
Рис. 23.5
Идея сценария такова. При первом открытии страницы на данном компьютере сценарий выдает на страницу сообщение "Вы на этой страничке ни разу не были", создает на локальном диске в корне диска С файл INFORMAT.TXT и записывает в него значение счетчика посещений – число 0.
При каждом открытии страницы сценарий ищет в корне диска С файл INFORMAT.TXT и если находит, то считывает с него значение счетчика, увеличивает его на 1 и отображает на странице в виде «Вы здесь были 5 раз». Если же файл не найден, сценарий делает вывод, что на этом компьютере страница еще не открывалась, и делает то, что я описал в предыдущем абзаце.
Вот HTML-документ нашей страницы со включенным в него сценарием:
<html>
<h2>Кое-что о разведении слонов в Антарктиде</h2>
<script language="VBScript"><!--
Dim objFs 'Объект - Файловая система локального диска
Dim objFile 'Объект - Файл
Dim sAdres 'Переменная - Адрес файла на диске
Dim intSchetchik 'Переменная - счетчик посещений страницы
sAdres="c:\INFORMAT.TXT"
Set objFs = CreateObject ("Scripting.FileSystemObject")
If objFs.FileExists (sAdres) Then
Set objFile = objFs.OpenTextFile(sAdres, 1)
intSchetchik = objFile.ReadLine
intSchetchik = intSchetchik + 1
Document.Write "Вы здесь были " & intSchetchik & " раз"
Else
Document.Write "Вы на этой страничке ни разу не были"
intSchetchik = 0
Set objFile = objFs.CreateTextFile (sAdres)
End If
objFile.Close
Set objFile = objFs.OpenTextFile(sAdres, 2)
objFile.WriteLine (intSchetchik)
objFile.Close
Set ObjFs = Nothing
--></script>
</html>
Пояснения: Прочтите строки объявлений. Далее рассмотрим строку
sAdres = "c:\INFORMAT.TXT"
Она задает адрес и имя текстового файла на локальном диске, в котором сценарий будет хранить счетчик посещений. Строка
Set objFs = CreateObject ("Scripting.FileSystemObject")
создает экземпляр объекта Файловая система. С этого мгновения вступают в действие меры безопасности. На экране компьютера, читающего вашу Web-страницу, возникает сообщение (Рис. 23.6), которое предупреждает пользователя, что программы на этой страничке могут быть опасными и не рекомендует разрешать их выполнение.
Рис. 23.6
У пользователя еще есть возможность нажать на No. Мой совет прост: Если эта страничка не принадлежит вашему самому надежному другу – жмите No.
Теперь рассмотрим строку
If objFs.FileExists (sAdres) Then
Здесь используется метод FileExists объекта objFs, который определяет, существует ли файл по указанному адресу sAdres. Смысл строки такой: Если файл c:\INFORMAT.TXT существует, то …
Строка
Set objFile = objFs.OpenTextFile(sAdres, 1)
открывает объект – текстовый файл – для чтения (потому что 1). Строка
intSchetchik = objFile.ReadLine
считывает из него строку и присваивает счетчику. Следующая строка увеличивает счетчик на 1, а строка
Document.Write "Вы здесь были " & intSchetchik & " раз"
записывает на страничку указанный текст.
Из дальнейших строк поясню следующие. Строка
Set objFile = objFs.CreateTextFile (sAdres)
создает на диске файл по указанному адресу. Строка
objFile.Close
закрывает файл, независимо от того, какая ветвь оператора If выполнялась – Then или Else. Строка
Set objFile = objFs.OpenTextFile(sAdres, 2)
открывает файл для записи (потому что 2). Строка
objFile.WriteLine (intSchetchik)
записывает в файл значение счетчика. Строка
Set ObjFs = Nothing
освобождает память компьютера от объекта Файловая система.
Дозапись в текстовый файл
Задача 3: Если вы при записи не хотите стирать содержимое файла, а просто хотите дописать что-нибудь в его конец, то вам нужно для создания объекта StreamWriter использовать вариант конструктора с двумя параметрами:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
'Создаем объект для дозаписи информации в текстовый файл E:\VB\Filimon.txt:
Dim Дозапись As New System.IO.StreamWriter("E:\VB\Filimon.txt", True)
'Дописываем в файл 2 строки:
Дозапись.WriteLine("Европа")
Дозапись.WriteLine(999)
Дозапись.Close() 'Закрываем файл
End Sub
Пояснения: Второй параметр в этом варианте конструктора отвечает за режим дозаписи (Append). True означает, что дозапись включена, False – что выключена, а значит запись будет вестись сначала, стирая все, что было записано в файле раньше.
Запустите проект, щелкните пару раз по кнопке Button3. Вот что вы увидите в файле:
Азия
5,27
29.03.2005 21:30:00
12
Европа
999
Европа
999
Read и Write. Вы можете в одну строку файла записать не одно данное, а несколько, используя вместо метода WriteLine метод Write. Разница здесь та же самая, что и между методами Debug.WriteLine и Debug.Write. Вот как будет выглядеть файл Filimon.txt, если при решении задачи 1 мы будем использовать вместо метода WriteLine метод Write:
Азия5,2729.03.2005 21:30:0012
Не очень понятно, где кончается одно данное и начинается другое. Не думайте, также, что так вы много выиграете в объеме файла.
Считывать получившуюся невнятную строку можно по-разному. Можно использовать привычный ReadLine, чтобы присвоить эту цепочку символов одной строковой переменной, а затем разбивать ее на части методами класса String. Можно использовать метод Read, который читает строку по кусочкам из заданного числа символов. Если вы заранее знаете, сколько символов в строке занимает запись каждого данного, то это вполне приемлемо. Мы не будем на этом останавливаться.
Другие полезные свойства и методы ListView
Вид списка. Я уже говорил, что правая панель Проводника Windows – это ListView. То же самое можно сказать о любой папке, раскрытой на Рабочем столе Windows. Зайдите в этой папке или в Проводнике в меню Вид (View). Вы увидите, что можете выбирать из нескольких способов представления информации о содержимом папки: большие значки, малые значки, список, таблица и некоторые другие. В списке ListView, конечно, тоже есть свойство, позволяющее осуществлять этот выбор. Это свойство View. Удобно в окне свойств выбрать по очереди все 4 значения этого свойства и понаблюдать за изменением вида информации в списке. Только имейте в виду, что для того, чтобы список имел подобающий вид больших значков, вам необходимо предварительно создать еще один ImageList, в котором собрать крупные (скажем, 32 на 32 пикселя) значки, а затем привязать его к списку ListView, установив свойство LargeImageList списка ListView.
Сортировка списка. Установив свойство Sorting, вы отсортируете списка по возрастанию или убыванию текстовой информации первого столбца. Если вы захотите сортировать список по другим столбцам, вам придется воспользоваться методом Sort и потратить кое-какие усилия на дополнительное программирование.
Другие свойства и методы списка. Кроме упомянутых свойств и методов список ListView имеет множество других свойств и методов, некоторые из которых я сейчас перечислю:
Свойство или метод | Смысл | ||
CheckedItems | Коллекция элементов списка, на которых установлены флажки | ||
CheckedIndices | Коллекция номеров элементов списка, на которых установлены флажки | ||
HideSelection | Аналогично одноименному свойству поля RichTextBox (см. 20.4.1) | ||
Scrollable | При необходимости появляются полосы прокрутки. | ||
Clear | Полная очистка списка от строк и от столбцов (вместе с заголовками) |
Кроме списка ListView полезные свойства и методы имеют и элементы его многочисленных коллекций. На них я не останавливаюсь.
Движем ловца – вторая ступень проекта
Сейчас мы должны запрограммировать ловца полностью. Но сначала нужно очень точно продумать его поведение, определить до мелочей все, что он должен уметь делать. Выпишем все его умения:
А.
По приходе импульса от таймера он должен:
Проверить, не наткнулся ли он на бортик поля, и если да, то … Что? Мы не придумали еще. Надо придумать. Пуст он должен отскочить в исходное положение и остановиться.
В противном случае сдвинуться на некоторый шаг вверх, вниз, влево или вправо, подчиняясь соответствующим клавишам клавиатуры, или стоять на месте (клавиша Ctrl).
В.
При нажатии кнопки Начинай сначала он должен возвращаться в исходное положение.
Все. Вы спросите – а почему так мало, а как же умение ловить шары, ради которого ловец и создан? Я отвечу: В нашем случае проще запрограммировать «исчезалки», чем «ловилки». Не поняли? – Поясню. Наткнувшись на шар, ловец у нас не будет предпринимать никаких действий по его «поимке». Наоборот, шар, наткнувшись на ловца, потрудится добровольно исчезнуть. Со стороны – никакой разницы.
Запрограммируем все действия, перечисленные в пункте А, в процедуре класса clsЛовец, которую назовем Действие. Запрограммируем все действия, перечисленные в пункте В, в процедуре Начальная_установка класса clsЛовец, которую мы уже частично написали.
Таймер. Свои действия ловец должен производить по импульсам таймера. Но у класса нет ни одного элемента управления, значит и таймера тоже нет. Таймером нашего проекта будет таймер, принадлежащий форме. Поместите его на форму. Задайте ему интервал = 10. Щелкните по нему дважды – в окне кода формы появится заготовка процедуры. Напомню, что эта процедура выполняется на каждом импульсе таймера. Значит это и будет главная процедура нашего проекта, задающая ритм всем объектам и элементам управления.
Перечислю действия, которые должна выполнять эта процедура, пока шаров в проекте нет:
Разбудить ловца и заставить его выполнить свою процедуру Действие, в частности вычислить свое положение (координаты) на форме и переместить свое изображение в вычисленное место.
Увеличить на 1 счетчик времени (импульсов таймера) на форме.
Перечислю действия, которые должны выполняться при нажатии на кнопку «Начинай сначала»:
Установить в 0 счетчик времени.
Заставить ловца выполнить свою процедуру Начальная_установка, то есть прыгнуть в точку старта ловца.
Программа. Вот как выглядит наш проект на второй ступени. На третьей ступени добавится класс шара, к стандартному модулю и коду формы добавятся строки, касающиеся шаров. А модуль clsЛовец я привожу целиком, в окончательном варианте.
Стандартный модуль: Остался неизменным.
Модуль формы:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Форма = Me
KeyPreview = True 'Чтобы форма реагировала на клавиатуру
Счетчик_времени.ReadOnly = True 'Чтобы нельзя было вручную менять показания счетчика
Ловец = New clsЛовец 'Создаем объект Ловец класса clsЛовец
Начальная_установка()
End Sub
Private Sub Начальная_установка()
Счетчик_времени.Text = 0 'Обнуляем счетчик времени
Счетчик_времени.Focus() 'Чтобы фокус ушел с кнопки
Ловец.Начальная_установка() 'Ловец встает в исходную позицию
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Ловец.Действие()
Счетчик_времени.Text = Счетчик_времени.Text + 1 'Счетчик времени на форме увеличивается на 1
End Sub
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles MyBase.KeyDown
Ловец.Реакция_на_клавиатуру(e)
End Sub
Private Sub Начинай_сначала_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Начинай_сначала.Click
Начальная_установка()
End Sub
Модуль класса clsЛовец (останется неизменным):
Public Class clsЛовец
Private x As Double 'Горизонтальная координата ловца на форме
'Свойство - горизонтальная координата ловца на форме
Public ReadOnly Property Xл() As Double
Get
Return x
End Get
End Property
Private y As Double 'Вертикальная координата ловца на форме
'Свойство - вертикальная координата ловца на форме
Public ReadOnly Property Yл() As Double
Get
Return y
End Get
End Property
Private Enum типРуль ' Направление движения ловца или состояние неподвижности
вверх
влево
вниз
вправо
стоп
End Enum
Private Руль As типРуль
Public Sub New()
Форма.pictЛовец.Width = Размер_ловца
Форма.pictЛовец.Height = Размер_ловца
End Sub
Public Sub Начальная_установка() 'Ловец встает в исходную позицию и останавливается
Руль = типРуль.стоп
x = Форма.Поле.Left + Форма.Поле.Width * 1 / 4
y = Форма.Поле.Top + Форма.Поле.Height / 2
Ставим_изображение_ловца_на_место()
End Sub
Private Sub Ставим_изображение_ловца_на_место()
Форма.pictЛовец.Left = x
Форма.pictЛовец.Top = y
End Sub
Public Sub Действие() 'Главная процедура ловца, выполняется один раз на каждом импульсе таймера
'Если ловец врезается в бортик, то отскакивает в исходное положение и останавливается:
If Ловец_у_бортика() Then Начальная_установка()
Выбираем_куда_ехать_и_делаем_шаг() 'Ловец слушается клавиатуру
End Sub
Private Function Ловец_у_бортика() As Boolean
'ЕСЛИ ловец находится у потолка ИЛИ у пола, ИЛИ у левой стены ИЛИ у правой, ТО:
If y < Форма.Поле.Top Or y + Размер_ловца > Форма.Поле.Top + Форма.Поле.Height _
Or x < Форма.Поле.Left Or x + Размер_ловца > Форма.Поле.Left + Форма.Поле.Width Then
Return True
Else
Return False
End If
End Function
Private Sub Выбираем_куда_ехать_и_делаем_шаг()
Dim dx As Double = 1 'Шаг ловца по горизонтали и вертикали между двумя импульсами таймера
Dim dy As Double = 1
Select Case Руль
Case типРуль.вверх : y = y - dy
Case типРуль.вниз : y = y + dy
Case типРуль.влево : x = x - dx
Case типРуль.вправо : x = x + dx
Case типРуль.стоп 'Поскольку никуда идти не надо, постольку ничего не делаем
End Select
Ставим_изображение_ловца_на_место()
End Sub
Public Sub Реакция_на_клавиатуру(ByVal e As System.Windows.Forms.KeyEventArgs)
Select Case e.KeyCode
Case Keys.Left : Руль = типРуль.влево
Case Keys.Right : Руль = типРуль.вправо
Case Keys.Up : Руль = типРуль.вверх
Case Keys.Down : Руль = типРуль.вниз
Case Keys.ControlKey : Руль = типРуль.стоп
End Select
End Sub
End Class
Запустите проект. Проверьте, правильно ли движется ловец. Для его движения не нужно держать палец на клавише, достаточно щелкнуть.
А теперь пояснения.
Начну с клавиатуры. Она почти полностью копирует работу клавиатуры в игре «Гонки» (тех, кто подзабыл работу с клавиатурой, отсылаю туда – 14.4.4). Для того, чтобы проект мог реагировать на клавиатуру, в коде формы появились три строки:
KeyPreview = True 'Чтобы форма реагировала на клавиатуру
Счетчик_времени.ReadOnly = True 'Чтобы нельзя было вручную менять показания счетчика
Счетчик_времени.Focus() 'Чтобы фокус ушел с кнопки
Причины необходимости таких строк подробно объяснены в 14.4.4.
Отличие от Гонок состоит в том, что теперь у нас появился объект и в соответствии с принципом инкапсуляции обработку нажатия на клавишу клавиатуры я перенес в него. Раньше вся обработка происходила бы в процедуре Form1_KeyDown формы, а теперь я там оставил только обращение к процедуре ловца Реакция_на_клавиатуру. Чтобы не плодить ловцу лишних полей, я снабдил эту процедуру параметром e, который несет в себе всю информацию про нажатие клавиши.
В результате работы этой процедуры переменная перечислимого типа Руль запоминает, какая клавиша была нажата, и движение ловца поэтому осуществляется далее в соответствующем направлении без необходимости удерживать эту клавишу нажатой.
Процедуры таймера Timer1_Tick и кнопки Начинай_сначала_Click предельно кратки и полностью соответствуют описанию их работы, приведенной чуть выше в этом подразделе.
С пояснениями кода формы покончено. Перейдем к классу clsЛовец. Здесь добавлений много. В начальную установку ловца добавилась строка
Руль = типРуль.стоп
Она нужна потому, что начальная установка может застигнуть ловца в любой момент, чаще всего тогда, когда он находится в движении. Так вот, это нужно, чтобы он успокоился.
Главная процедура ловца Действие в нашем случае сводится к выполнению двух дел:
Проверке, не врезался ли ловец в бортик, а если врезался – к возврату его в исходное положение и остановке. Проверку осуществляет булевская функция Ловец_у_бортика, а возврат – уже рассмотренная нами процедура Начальная_установка.
Опрашиванию переменной Руль и перемещению изображения ловца в соответствующем направлении. Всем этим занимается процедура Выбираем_куда_ехать_и_делаем_шаг. Она работает совершенно аналогично своей тезке из Гонок (см. 14.4.4).
Получается, что нам осталось разобрать только булевскую функцию Ловец_у_бортика. Эта функция примет значение True, если ловец в своих путешествиях по полю врежется в бортик. Факт столкновения определяется сравнением координат ловца и координат каждого из 4 бортиков. В операторе If эти 4 сравнения отделены друг от друга знаком логической функции Or.
Координаты и шаг ловца и шаров я сделал дробного типа, а не целого. Причина в том, что скорости ловца и шаров вы можете захотеть сделать маленькими. Тогда может понадобиться шаг меньший 1.
Процесс работы проекта. Если вам пока непонятно, как работает проект, разберем последовательность вызова процедур и функций в процессе работы проекта. Эту последовательность я буду разбирать в хронологическом порядке, то есть в том, в котором вызываются процедуры и функции после запуска проекта.
После выполнения процедуры Form1_Load, которое мы разобрали ранее, на игровой площадке наступает покой до прихода первого импульса от таймера.
И вот импульс грянул. Заработала процедура Timer1_Tick и первым делом запустила процедуру Ловец.Действие. Давайте пока не будем в нее заглядывать, вообразим, что там ничего существенного не произошло, и пойдем дальше. Следующая строка увеличивает счетчик времени. Вместо 0 в счетчике на форме появляется 1. На этом процедура Timer1_Tick заканчивает свою работу. Все замирает до тех пор, пока через 10 тысячных долей секунды не придет следующий импульс от таймера.
Предположим, вы за это время еще не успели прикоснуться к клавиатуре. Вот пришел новый импульс. Заработала процедура Timer1_Tick и запустила процедуру Ловец.Действие. Рассмотрим, как она работает. Ее тело состоит из двух строк, вызывающих процедуры и функции с интуитивно ясными именами. Первой вызывается функция Ловец_у_бортика. Очевидно, что поскольку ловец пока далеко от бортиков, то эта функция принимает значение False.
Далее выполняется процедура Выбираем_куда_ехать_и_делаем_шаг. Руль у нас после начальной установки находится в положении стоп и ничто его оттуда не вывело, значит оператор Select Case не меняет ни x ни y. Следовательно процедура Ставим_изображение_ловца_на_место не сдвигает изображение ловца из начального положения.
На этом вторая строка процедуры Ловец.Действие завершается, а с ней и вся процедура. VB возвращается в процедуру Timer1_Tick, которая увеличивает счетчик времени еще на 1.
Ждем следующего импульса. Пусть до момента его прихода мы успели нажать на клавиатуре стрелку вправо, желая направить ловца направо. Немедленно сработала процедура Form1_KeyDown в модуле формы. Она вызвала процедуру ловца Реакция_на_клавиатуру, которая присвоила переменной Руль значение вправо. Импульса все нет.
Вот пришел импульс. Опять процедура Timer1_Tick направляет нас в процедуру Ловец.Действие, та – в процедуру Выбираем_куда_ехать_и_делаем_шаг. Поскольку руль повернут направо, вычисляется новое значение x, которое на dx больше предыдущего. Согласно новому значению x процедура Ставим_изображение_ловца_на_место смещает изображение ловца чуть вправо.
Ждем следующего импульса и так далее. Вот и вся механика ловца.
Теперь поговорим об инкапсуляции. В проекте я старался максимально придерживаться принципа инкапсуляции, все что можно я делал Private. Посмотрим по порядку.
Объекты Форма, Ловец и константу Размер_ловца я объявил в стандартном модуле. Потому что все они будут нужны не в одном, а в нескольких модулях. Естественно, я сделал их Public.
В форме все процедуры объявлены Private. Потому что в нашем проекте форма – это тот пульт, из которого осуществляется управление проектом. Ее процедуры предназначены, чтобы управлять, а не управляться. Чего не скажешь об элементах управления, которые видны снаружи формы и управляются из классов.
Теперь поглядите повнимательнее в код ловца. Координаты ловца на форме x и y я сделал Private, но поскольку знаю, что они понадобятся шару (который должен знать их величину, чтобы вовремя исчезнуть при столкновении), я создал два соответствующих свойства только для чтения: Xл и Yл.
Наводит на размышление тот факт, что методами класса стали именно те процедуры, которые должны вызываться из формы в ответ на какие-то события формы. Их просто нельзя было сделать Private. Это Начальная_установка (реагирует на загрузку формы и на кнопку Начинай сначала), Действие (реагирует на таймер) и Реакция_на_клавиатуру (реагирует на клавиатуру). О конструкторе я не говорю. Если его сделать Private, то нельзя будет создать объект.
Поведение ловца определяется процедурами и функцией, определенными в коде ловца. Именно они обеспечивают механику его работы. Никакая процедура и функция снаружи класса в этой механике не участвует. Ловец самодостаточен. Снаружи осуществляется только запуск трех методов объекта. Именно поэтому они сделаны Public, иначе их снаружи и запустить было бы нельзя. Но вся механика этих методов, опять же, находится внутри ловца, так что никакого нарушения суверенитета нет. Остальные процедуры и функция сделаны Private, они снаружи и не видны и недоступны.
Обратите внимание, что разные модули могут использовать одноименные компоненты. Например, процедура Начальная_установка. Проблемы в этом нет, так как если процедура задана, как Private, то из других модулей она просто не видна, а если даже Public, то перед своим именем она будет требовать имени хозяина.
Вот и все о второй ступени проекта.
Файлы и папки
Понятия файла и папки – основополагающие как для программиста, так и для пользователя. Здесь мы разберем структуру хранения информации на компьютерных дисках, будь то жесткий диск, дискета или компакт-диск. Я буду употреблять слово «диск», хотя информация на флэш-памяти (не имеющей отношения к дискам) имеет ту же структуру, поэтому все сказанное будет относится и к ней.
Фигурные (непрямоугольные) формы
Мы привыкли, что окна Windows имеют прямоугольные очертания, хотя в последнее время стали появляться приложения Windows, окна которых далеки от прямоугольных. Например, окно медиаплеера Windows ХР. В этом разделе мы тоже будем создавать такие окна.
Флажок (CheckBox)
Смысл флажков. Пример использования флажков (CheckBox) вы видите на Рис. 18.1. Если из рисунка вам непонятен их смысл, создайте проект, поместите на форму из Toolbox несколько флажков, поменяйте им в соответствии со своими вкусами свойство Text, запустите проект, пощелкайте внутри квадратиков. Флажок мы устанавливаем (ставим галочку) тогда, когда хотим сказать «да», а снимаем (делаем пустым), когда хотим сказать «нет». При помощи клавиатуры вы можете путешествовать между флажками клавишей Tab, а менять установку флажка – клавишей пробела. Можно безо всяких вредных последствий устанавливать и снимать любой флажок сколько угодно раз. В этом и состоит удобство флажков: перед тем, как совершить решающее нажатие на кнопку «Заказать», можно поддаться сомнениям и как угодно устанавливать флажки или, передумав, снимать.
Рис. 18.1
Поместите на форму флажок. Загляните в окно свойств флажка. Там вы увидите несколько знакомых нам свойств, присущих большинству элементов управления. Поэкспериментируйте с цветами, шрифтом флажка. Уберите с флажка текст и придайте картинку его свойству Image. Получается, что можно отлично обойтись и без текста, если картинка верно передает смысл флажка.
Там же вы увидите несколько свойств, специфичных для флажка. Нас интересуют некоторые из них:
При свойстве Appearance равном Button флажок выглядит, как нажатая или отжатая кнопка.
У каждого флажка мы видим два состояния (установлен – снят). Им соответствуют два значения свойства Checked: True и False.
Если вы склонны к неопределенностям, то можете настроить любой флажок на три состояния. Для этого установите свойство ThreeState в True. Все три состояния флажка являются значениями свойства (перечисления) CheckState. Вот они:
Состояние | Смысл | Внешний вид флажка | |||
Checked | Установлен | В белом квадратике – галочка | |||
Unchecked | Снят | Белый квадратик пуст | |||
Indeterminate | Не определен | В сером квадратике – галочка |
Пример программы. Для изучения работы флажков создайте проект. Поместите на форму пару флажков, пару текстовых полей и пару кнопок. Флажки получают имена CheckBox1, CheckBox2. Работу с флажками иллюстрирует следующая программа:
' При загрузке формы задаем количество состояний и начальное состояние каждому флажку:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
CheckBox1.ThreeState
= False '1 флажку разрешается устанавливать одно состояние из двух
CheckBox1.Checked = True
CheckBox2.ThreeState
= True '2 флажку разрешается устанавливать одно состояние из трех
CheckBox2.CheckState
= CheckState.Indeterminate
End Sub
'Определяем состояние первого флажка:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If CheckBox1.Checked Then TextBox1.Text = "Первый флажок установлен" _
Else TextBox1.Text = "Первый флажок снят"
End Sub
'Определяем состояние второго флажка:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Select Case CheckBox2.CheckState
Case CheckState.Checked
TextBox2.Text = "Второй флажок установлен"
Case CheckState.Unchecked
TextBox2.Text = "Второй флажок снят"
Case CheckState.Indeterminate
TextBox2.Text = "Второй флажок не определен"
End Select
End Sub
Здесь по нажатии на кнопку Button1 мы видим в поле TextBox1 состояние первого флажка. Аналогично по нажатии на кнопку Button2 мы видим в поле TextBox2 состояние второго флажка.
Мгновенная реакция. Загляните в события флажка (как это делается, написано в 3.11). Если вы хотите, чтобы компьютер в нашем проекте мгновенно реагировал на изменение состояния флажков, выберите такие события:
Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles CheckBox1.CheckedChanged
Debug.WriteLine("Изменили состояние первого флажка")
End Sub
Private Sub CheckBox2_CheckStateChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles CheckBox2.CheckStateChanged
Debug.WriteLine("Изменили состояние второго флажка")
End Sub
Задание 1.
«Виртуальный ресторан». Создайте проект (Рис. 18.1), который бы при нажатии на кнопку «Заказать» печатал в окне Output список выбранных блюд. Указание: поскольку блюд много, создайте коллекцию флажков и используйте цикл For Each.
Форма-лодочка или «и нашим – и вашим»
Совсем не обязательно, придавая окну экзотическую форму, лишаться заголовка и трех кнопок. Взгляните на Рис. 17.8. Вы можете таскать окно-лодочку за заголовок, пользоваться тремя привычными кнопками и менять размеры окна, привычно ухватившись мышкой за его углы, верхнюю границу и даже за остатки вертикальных границ в верхней части окна.
Рис. 17.8
Вот весь код:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim Гр As Graphics = Me.CreateGraphics
Dim Путь As New Drawing2D.GraphicsPath
Путь.AddEllipse(0, -300, Me.Width, 600)
Me.Region = New Region(Путь)
End Sub
Private Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Resize
Me.Invalidate()
End Sub
Пояснения: Сначала о тексте процедуры OnPaint. Все 4 ее строки привычны, надо только пояснить эллипс. Благодаря выбору чисел -300 и 600 его центр лежит на заголовке окна. Как следствие, число 0 заставляет левый край эллипса лежать на левом краю заголовка, а выражение Me.Width – правый край на правом.
Поскольку заголовок окна входит внутрь графической области Пути, то он виден на экране и им можно пользоваться.
Теперь о процедуре OnPaint. Я мог бы воспользоваться привычной процедурой Form1_Load и все бы работало. Но при этом ширина эллипса оставалась бы неизменной, несмотря на изменения ширины окна. Лодочка теряла бы свои очертания. Я поставил задачу менять ширину эллипса в унисон с шириной окна. Обработчик события Paint здесь не помогает. Процедура OnPaint подходит, но она выполняется при наступлении события Paint и поэтому делает дело, когда форма увеличивается или возникает из-за других окон, а при уменьшении размеров окна не срабатывает. Поэтому я дополнил код обработчиком события Resize, которое возникает при любом изменении размеров окна. Метод Invalidate просто заставляет форму перерисоваться. Подробное описание механизма упомянутых процедур и метода не входит в задачи книги для начинающих.
Функция Shell
С помощью функции Shell вы можете, не выходя из проекта, запускать другие программы. На экране компьютера у вас будут жить независимо друг от друга ваше приложение Windows и запущенная программа. Вы сможете закрыть проект – программа останется. Или наоборот. Разберитесь в примерах вызова этой функции:
'Запускаем в свернутом на панели задач виде программу Блокнот (Notepad) из папки WINDOWS:
Shell("E:\WINDOWS\Notepad.exe")
Пояснения: В кавычках вы должны указать адрес запускающего файла нужного вам приложения. Notepad.exe – запускающий файл стандартного текстового редактор Windows – Блокнот (Notepad).
'Запускаем программу Блокнот с открытым текстовым файлом c:\temp\1.txt:
Shell("E:\WINDOWS\Notepad.exe c:\temp\1.txt")
Пояснения: Вместо адреса можно писать так называемую командную строку с указанием открываемого файла и параметров.
'Запускаем программу в развернутом виде. Окно программы получает фокус:
Shell("E:\WINDOWS\Notepad.exe", AppWinStyle.NormalFocus)
Пояснения: Вы можете управлять видом окна, в котором откроется программа, при помощи перечисления AppWinStyle. Так, окно может быть минимизировано, максимизировано, может получить или не получить фокус.
'Пока Блокнот не закроем, в VB не сможем вернуться:
Shell("E:\WINDOWS\Notepad.exe", AppWinStyle.NormalFocus, True)
'В VB сможем вернуться через 3 секунды после запуска Блокнота:
Shell("E:\WINDOWS\Notepad.exe", AppWinStyle.NormalFocus, True, 3000)
Пояснения: Вы можете управлять тем, можете ли вы вернуться в ваш проект и вообще в VS, пока открыта программа.
это кирпичики, из которых строится
Элементы управления – это кирпичики, из которых строится внешний вид проекта – будущего приложения Windows. Посмотрите на любое известное вам приложение Windows: Microsoft Word, Paint, или хотя бы на нашу VS. Оно включает в себя кроме известных нам кнопок, меток, текстовых полей и меню также флажки, переключатели, полосы прокрутки, диалоговые окна и многое-многое другое, чего мы еще не проходили. Вот в том-то и дело, что не проходили! Надо пройти! Иначе по внешнему виду нашего приложения каждый скажет, что это явно не коммерческий продукт.
Элементы управления – вещь наглядная и веселая. Я бы уже давно о них вам рассказал, да вот многие из них были бы вам непонятны без знакомства с индексами и коллекциями.
Основную массу элементов управления я изложу в этой главе и Глава 20. . В этой главе мы будем изучать элементы управления в основном «маленькие», а в Глава 20. – в основном «большие».
Вот некоторые элементы управления, с которыми мы уже познакомились и на которых я останавливаться не буду:
Кнопка, метка, текстовое поле – в Глава 1. и Глава 3. .
Windows Media Player – в 3.10.
Меню – в 3.8.
PictureBox – в 12.3.1
Таймер – в 13.2
Рамка (GroupBox), панель (Panel) и вкладка (TabControl) – в 13.4
Следующие элементы управления мы пройдем позже, когда в них возникнет необходимость:
Web-браузер - в 23.2
DataGrid - в 24.5
HelpProvider – в 26.1.7
Я буду описывать только главное действие и главные свойства элементов управления, но этого в подавляющем большинстве случаев вполне достаточно. С другими любопытными и удобными, но редко используемыми их свойствами вы можете познакомиться в других книжках или с помощью системы Help.
Я не буду приводить примеры больших реальных программ с использованием этих элементов. Примеры будут простенькие, но достаточные для того, чтобы понять, как эти элементы управления работают. Вы уже вполне опытны, чтобы при некоторой фантазии самостоятельно придумать реальные проекты с их использованием или вспомнить использующие их реальные приложения Windows.
и простые» элементы управления, то
Если в Глава 18. мы изучали в основном «маленькие и простые» элементы управления, то в этой главе будем изучать в основном «большие и сложные».
Объектное программирование
В прошлой главе вы познакомились с грамматикой объектов. Сами объекты были простые и бессмысленные. В этой главе объекты наполнятся смыслом и я смогу показать вам, какая от них польза. То есть начинается настоящее объектное программирование.
Давным-давно введение процедур в программирование резко повысило надежность создаваемых больших программ и их обозримость, сделало сам процесс программирования более удобным. Следующим шагом на пути совершенствования программирования стало введение объектов. Объект – это синтез данных (в частности переменных величин, констант) и действий (процедур, функций), которые эти данные обрабатывают.
Объекты в программировании напоминают объекты реального мира, которые тоже в некотором смысле представляют синтез данных и действий. Например, чтобы описать стенные часы, мы должны описать две их стороны: совокупность их составных частей (радиусы шестеренок, длина маятника и прочее – в общем, «данные»); совокупность процессов взаимодействия этих частей (как качается маятник, как шестеренка цепляет шестеренку и так далее – в общем – «действия»). Если взять живой организм, то данные – это строение его тела (то что изучает анатомия), а действия – процессы функционирования организма (то что изучает физиология).
Почти все приложения Windows и сама операционная система Windows запрограммированы на основе объектного подхода. Типичные примеры объектов в Windows – окна, кнопки, флажки и пр. Все они характеризуются с одной стороны данными (размер, цвет и пр.), а с другой стороны – действиями, которые определяют их поведение.
Эта глава, пожалуй, самая сложная из всех. Первая ее половина до предела насыщена новым, непривычным материалом. До предела уменьшите темп изучения. Все объяснено. Не надо торопиться. Наградой вам будет проект игры «Ловец» во второй половине главы.
Разные важные вещи о VB
В этой последней главе я собрал разномастные, но важные разделы, которые раньше были бы непонятны. Из них Грамматика безусловно важна и нужна всем. Ссылочные типы, как и Потоки, много дают для общего понимания программирования. А Многодокументный интерфейс нужен тем, кому он нужен.
Создаем формы, модули, классы
Проекты, которые мы создавали до этого, имели максимально простое строение: единственная форма, вот и все. Однако большинство реальных задач требуют, чтобы проект имел более сложную структуру. В этой главе мы будем создавать проекты из нескольких форм, модулей и классов. Мы чрезвычайно расширим наши горизонты. Вам покажется, что мы из тесной комнаты вырвались в широкое поле! Впервые мы осуществим нашу давнюю мечту – напишем собственный класс и из него создадим несколько экземпляров-объектов. Однако у вас возникнет ощущение пустоты, вы не будете знать, что вам делать с внезапно обрушившейся на вас свободой и со всеми этими новыми возможностями. Все проекты, которые мы будем здесь создавать – учебные, направленность главы чисто грамматическая, это необходимые «маневры» перед «битвой». А «битва» – это следующая глава, где мы создадим наш первый проект (игра «Ловец») с использованием объектного программирования. И проект этот будет прекрасен! Вот когда вы узнаете настоящую цену свободы!
Создаем справочную систему (Help)
В этой главе мы научимся создавать справочную систему для приложений Windows.
В последние годы, если вы покупаете принтер или видеокарту, сканер или какое-нибудь другое устройство компьютера, вы часто обнаруживаете, что полная инструкция по работе с устройством представлена не в бумажном виде, а в электронном, обычно на прилагающемся диске. Она предназначена для просмотра на компьютере. Если вы покупаете программу, то инструкция по работе с программой представлена тоже не в бумажном виде, а в виде электронной справочной системы – системы помощи Help, которая доступна из меню этой программы.
Справочные системы приложений Windows обычно похожи друг на друга и аналогичны тем, что вы привыкли пользоваться в Microsoft Word или VB. Система помощи VB кратко описана мной в 4.4.9
Можем ли мы уже сейчас, без всяких дополнительных средств, создать простую справку? В общем, да. Ведь что такое справка? В простейшем случае это текстовое поле или метка с пояснительным текстом. Текст можно менять в зависимости от того, что именно мы хотим узнать. Однако, справочная система Microsoft Word или VB – это нечто гораздо более мощное. Она подразумевает и оглавление (Contents), и алфавитный указатель (Index), и возможности поиска (Search), и возможность давать контекстно-зависимую справку при нажатии на F1, и гиперссылки, и многое другое. Чтобы создать такую справочную систему, безусловно, нужно потрудиться. VB значительно облегчает нам жизнь, предоставляя для разработки справочных систем стандартный инструмент.
Строки и символы, файлы, обработка ошибок
В этой главе я собрал совершенно необходимые, но разнокалиберные вещи, которые по тематике не подходили к другим главам. Пропускать эту главу нельзя, так как ее материал понадобится при изучении последующих глав.
Связь между приложениями Windows
До сих пор приложения Windows, которые вы создавали, варились в собственном соку, как, впрочем, и в соку собственного языка программирования VB. Это значит, что при создании проекта вы никак не могли воспользоваться полюбившимися вам частями проектов других программистов или даже компонентами собственных проектов, созданных на других языках программирования. Все богатство приложений Windows, созданных в мире, было закрыто для вас, как и ваши богатства для всего мира. Правда, VB предоставляет в наше распоряжение массу полезных классов и других объектов из библиотеки классов .NET Framework, но нам этого мало.
Что конкретно я имею в виду? Пусть вам понравились какая-нибудь функция или класс в проекте на VB вашего друга. Что вы можете сделать? Вы можете скопировать их в окно кода своего проекта. Вот и все. А если это не друг, а незнакомый программист? Да еще и программирующий на другом языке? Он не захочет делиться с вами исходным текстом своих функций и других компонентов проекта, представляющих его интеллектуальную собственность. Хотя он был бы не прочь, чтобы вы ими воспользовались, как готовыми продуктами, не зная их кода. Как это сделать? С соответствующим механизмом мы и познакомимся в этой главе. Вы также узнаете, как из своего проекта запускать другие приложения и как воспользоваться богатством функций Windows API.
VB и базы данных
Если вы спросите профессионального программиста, зачем его коллегам нужен Visual Basic, он ответит – В основном для программирования баз данных. Да, действительно, ведь компьютер – это инструмент для обработки информации, а информация в деловом мире хранится в основном в базах данных, поэтому если язык программирования хочет быть нужным, он должен обеспечивать быструю, удобную и надежную с ними работу.
В этой главе я познакомлю вас с тем, как это делает VB. Вы сможете выполнять основные операции с базами данных и этого достаточно для общего представления о предмете и для создания и использования баз данных в ваших проектах.
Visual Basic и Интернет
В этой главе я сначала дам понятие об Интернете, Web-страницах, языке HTML и браузере Internet Explorer (23.1). Затем я покажу, как легко VB позволяет включить в ваш проект собственный браузер (23.2). В 23.3 я покажу вам, как безо всякого программирования создавать собственную простенькую Web-страницу с текстом и картинками. В следующих двух разделах я покажу, как знание Visual Basic поможет вам добавить на вашу Web-страничку такие элементы, которые подвластны только программистам. Вы обнаружите, что ваше знание не только полезно, но и опасно для окружающих. При программировании я буду использовать язык VBScript, который является «диалектом» языка Visual Basic. В последнем разделе я покажу, как создавать страницы при помощи VB.
Эта глава дает только понятие о работе с Интернетом, но ни в коем случае не систематические знания в этой области. Основываясь на примерах, приведенных в главе, вы сможете легко создать свою страничку и сделать на ней что-нибудь интересное. Однако, чтобы стать экспертом, читайте специальные книжки.
Графический путь
Существует класс GraphicsPath (графический путь) пространства имен Drawing2D. Если создать экземпляр этого класса, то в него можно «складывать», как в коллекцию, разные фигуры, чтобы затем весь «склад» можно было нарисовать одним махом. Этот класс полезен в частности тем, что подходящие соседние фигуры он соединяет линиями, образуя таким образом единый контур, и позволяет этот контур заливать. Так что название графический путь для этого класса неудачно, лучше подошло бы контур.
Поставим задачу нарисовать, а затем залить «Буратино», как на Рис. 17.6.
Рис. 17.6
Если бы мы рисовали обычным образом, нам бы пришлось рисовать 5 элементов: окружность (глаз), большую дугу (голова), маленькую дугу (кончик носа) и два отрезка (нос). А залить такую фигуру нам бы вообще не удалось.
Вот код с использованием графического пути:
Dim Гр As Graphics = Me.CreateGraphics
Dim Путь As New Drawing2D.GraphicsPath
Путь.AddArc(20, 20, 120, 120, 0, 330) 'Голова
Путь.AddArc(220, 20, 10, 10, 270, 180) 'Кончик носа
Путь.CloseFigure()
Путь.AddEllipse(90, 40, 20, 20) 'Глаз
Гр.DrawPath(Pens.Black, Путь) 'Рисуем левого Буратино
Гр.TranslateTransform(200, 0) 'Смещаем начало координат
Гр.FillPath(Brushes.Blue, Путь) 'Рисуем правого Буратино
Пояснения:
2 строка создает Путь. Пока он пустой.
3 строка добавляет в Путь большую дугу. Вы видите, что у Пути есть набор методов, начинающихся на Add и добавляющих в Путь фигуру того или иного типа. Например, метод AddLine добавляет в Путь отрезок прямой. Теперь Путь не пуст, но ничего еще пока не нарисовано.
4 строка добавляет в Путь маленькую дугу. И тут происходит примечательное событие: конечная точка предыдущего элемента Пути (большая дуга) самостоятельно, без всякого приказа с вашей стороны соединяется отрезком (верхняя часть носа) с начальной точкой следующего элемента Пути (маленькая дуга), образуя единый контур.
5 строка заставляет VB соединить отрезком (нижняя часть носа) конечную точку последнего элемента Пути (маленькая дуга) с начальной точкой первого элемента Пути (большая дуга), замыкая таким образом контур. Пока еще ничего не нарисовано.
6 строка добавляет окружность. Как видите, не ко всем фигурам проводится контур.
7 строка рисует Путь (левый Буратино).
Чтобы две фигуры не накладывались друг на друга, в 8 строке я сдвигаю вправо систему координат.
9 строка рисует залитый Путь (правый Буратино).
На подробностях проведения контуров и заливки Путей я не останавливаюсь.
Грамматика VB
В этом разделе мы займемся грамматикой. До этого я уже писал синтаксические схемы некоторых операторов, но этого мало. Нам нужны правила записи более крупных единиц программного кода: объявлений процедур, функций, классов, модулей и так далее. Конечно, все это время я на примерах показывал вам, как можно грамматически правильно писать эти элементы. Но нигде не показывал, как нельзя. В результате, если вам захочется написать что-нибудь по-своему, вам неоткуда будет узнать, правильно вы написали или нет.
Игра «Ловец»
Объекты, которые мы с вами создавали до этого, имели несколько «канцелярский» оттенок и совсем не походили на автомобили, самолеты и прочие веселые объекты реального мира. К тому же мы не очень-то прочувствовали пользу от создания многих объектов из одного класса. Сейчас мы создадим проект, в котором объекты будут иметь вполне «реальный» вид, и получим удовольствие от того, что из одного класса можем наштамповать очень много объектов.
ImageList – галерея (список) картинок
До этого момента, если наш проект использовал несколько картинок (скажем, для мультфильма), мы хранили эти картинки в памяти, в объектах Bitmap, причем организовывали это хранение программным способом, в коде. Однако, существует элемент управления, который позволяет сделать это гораздо быстрее и удобнее, причем вручную, в режиме проектирования. Это ImageList. Он и хранит в себе эти картинки, и позволяет удобно пользоваться ими во время работы проекта. ImageList нужен не только для удобства. Он необходим для создания некоторых элементов управления (например, панелей инструментов), если мы хотим, чтобы создаваемый элемент управления выглядел качественно.
Создайте проект. Поместите в него элемент управления ImageList. Он получит имя ImageList1. Подобно таймеру, он займет место под формой и во время работы проекта никогда виден не будет. В этом и нет нужды. Загляните в бедное окно свойств элемента ImageList. Сейчас нас интересует свойство Images. Оно представляет собой коллекцию картинок, содержащихся в ImageList. Пока коллекция пуста. Давайте заполним ее.
Щелкните по трем точкам в поле значения этого свойства. Возникнет Редактор коллекции картинок (на Рис. 20.7 вы видите его уже заполненным несколькими картинками). Пока он пустой.
Рис. 20.7
Заполним его картинками. Процесс аналогичен работе с Редактором вкладок (13.4.3). Нажмем кнопку Add. Возникнет диалоговое окно открытия графического файла. Теперь мы должны найти на дисках компьютера какой-нибудь графический файл. Если подходящего нет, нарисуйте сами что-нибудь в графическом редакторе Paint. Или можете взять картинки по адресу
Program Files\Microsoft Visual Studio .NET\Common7\Graphics
Нажимая Add, мы наращиваем коллекцию картинок. Слева от картинки вы видите ее номер в коллекции Images. Справа – информация о выделенной картинке. Две кнопки со стрелками вверх и вниз перемещают выделенную картинку внутри коллекции. Кнопка Remove удаляет ее.
Теперь в коде вы легко можете пользоваться картинками из ImageList. Например, вот так присвоим графическому полю картинку №2 из коллекции:
PictureBox1.Image = ImageList1.Images(2)
В окне свойств элемента ImageList мы видим свойство TransparentColor, которое устанавливает на картинках из коллекции этого элемента прозрачный цвет.
Размер всех картинок в пикселях устанавливаем свойством ImageSize. Причем, если даже исходные картинки в коллекции имеют разные размеры в пикселях, то взятые из коллекции, они все равно все будут иметь одинаковые размеры, указанные этим свойством. Причем, картинки не могут быть большими – их размер не должен превышать 256 на 256.
Богатство цвета устанавливаем свойством ColorDepth.
Задание 14.
Перепишите с использованием ImageList программу из 13.6.5, создающую мультфильм «Человечек», с целью сделать ее покороче. Указание: Вставив 3 кадра в ImageList, посмотрите на их приблизительный размер в пикселях и выставьте его в качестве значения свойства ImageSize.
Имена файлов и папок
Имена файлам и папкам можно придумывать произвольные, но с некоторыми ограничениями. Имена не должны содержать символов \ / : * ? " < > | Начинающим я, чтобы не запутаться, не рекомендую использовать в именах точки и вообще ничего, кроме букв, цифр и пробелов.
К имени файла вы можете справа приписать добавку, состоящую из точки и справа от нее нескольких символов, обычно не более трех латинских букв. Эта «фамилия» называется расширением. Например, файл, в котором вы описываете, как Ира печет булки, вы могли бы назвать Bulki.Ira. Но начинающим я не рекомендую самим приписывать расширения к файлу.
Часто расширение автоматически и незаметно для вас приписывается к имени файла программой (Word, VS, …), в которой вы работаете. Так, если вы в Word создали документ и решили записать его на диск под именем Train, то на самом деле файл с этим документом на диске будет иметь имя Train.doc. По расширению программа узнает «свои» файлы, а опытные пользователи узнают, в какой программе файл был создан. И наконец, расширения у файла может и не быть.
Индикатор процесса (ProgressBar)
Индикатор процесса ProgressBar из VS изображен сверху на Рис. 18.6. А в нижней части рисунка изображен один из двух видов аналогичного индикатора из Visual Basic 6.0.
Рис. 18.6
Смысл индикатора процесса. Вы наверняка видели такие индикаторы при инсталляции программ. Зачем они нужны? Пусть ваша программа запускает длительный процесс, во время которого на экране ничего не происходит (например, моделирует столкновение галактик или считывает информацию из 400 файлов). У пользователя при длительном наблюдении за неподвижным экраном может возникнуть тревожное ощущение, что программа зависла. Чтобы успокоить пользователя, вы можете, начиная процесс, выдать на экран текст «Я занята. Подождите минутку», который пропадет, когда дело сделано. Но опять же, пока дело делается, этот текст так долго и неподвижно красуется посреди экрана, что в душу опять закрадываются подозрения. Гораздо лучше создать ProgressBar и организовать дело так, чтобы темная полоса после считывания информации из каждого файла продвигалась на 1/400 часть длины ProgressBar. Поскольку компьютер работает быстро и за секунду может считать, скажем, десяток небольших файлов, полоса будет десять раз в секунду немножко продвигаться и у пользователя создастся впечатление, что полоса плавно ползет направо. А раз движение есть, значит компьютер не завис! К тому же, глядя на индикатор, можно примерно представлять, какая часть работы уже выполнена.
Задача. Пусть компьютер 400 раз подряд выполняет процедуру Процесс, которая каждый раз исполняется довольно долго (скажем, полсекунды) и делает что-то полезное. Получается, что в течение 200 секунд на экране ничего не будет происходить. Нужно для спокойствия пользователя организовать работу индикатора процесса.
Ваши действия: Поместите на форму ProgressBar. Задайте ему такие свойства:
Minimum - 0
Maximum - 400
Step - 1
Вот программа, иллюстрирующая работу индикатора процесса:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim k As Long
For k = 1 To 400 'всего 400 процессов
Процесс()
ProgressBar1.PerformStep() 'Делаем очередной шаг на полосе
Next
End Sub
Sub Процесс()
Dim i As Long
For i = 1 To 1000000 : Next 'Процесс (пустой цикл)
End Sub
Пояснения: Щелчком по кнопке мы запускаем 400 процессов. В качестве иллюстрации процесса я выбрал пустой цикл. После завершения каждого процесса выполняется строка
ProgressBar1.PerformStep() 'Делаем очередной шаг на полосе
по которой срабатывает метод PerformStep объекта ProgressBar1. Дело этого метода – просто продвинуть полосу индикатора на величину свойства Step.
Старый индикатор из Visual Basic 6.0. Мне он нравится больше индикатора из VS, так как его полоса может иметь гладкий, а не прерывистый вид. Как до него добраться? Поместите на Toolbox, как это рассказано в 4.2.3, элемент управления Microsoft ProgressBar Control, version 6.0. Это он и есть. Оттуда поместите его на форму. Он получит какое-то имя, скажем, AxProgressBar2. Гладкий или прерывистый вид его полосы определяется свойством Scrolling. Индикатор может располагаться и вертикально (свойство Orientation).
Вы можете управлять положением его полосы, задавая свойство Value.
Задайте ему такие свойства:
Min - 0
Max - 10000
Вот программа, иллюстрирующая его работу:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim a As Long
For a = 1 To 10000
AxProgressBar2.Value
= a
Next
End Sub
Во время выполнения этой процедуры полоса движется. Если движение слишком быстрое или слишком медленное, измените максимальное значение индикатора и переменной цикла.
Инкапсуляция – «Объект в футляре»
Нам будет легче проникнуть в суть объектов в программировании, если мы рассмотрим основные их понятия на аналогии с объектами реального мира, которые они очень напоминают. В качестве примера разберем игрушечный радиоуправляемый автомобиль. Посмотрим, где у него данные и действия:
Данные. У игрушечного автомобиля данных множество. Например:
Цвет кузова
Номер автомобиля
Скорость движения в данный момент
Громкость звукового сигнала
Высота кресел
Величина электрического тока в двигателе в данный момент
Толщина гайки в таком-то месте внутри корпуса
И так далее и тому подобное.
Действия. Действий тоже достаточно. Например:
Поворот по команде с пульта управления
Торможение по команде с пульта управления
Подпрыгивание автомобиля на маленьком камушке
Изменение скорости вращения электродвигателя при изменении в нем тока
Возникновение жужжания двигателя при трении шестеренок друг о друга.
И так далее и тому подобное.
«Внешние» и «внутренние» данные и действия. Как видите, данные и действия бывают крупные и мелкие, важные и менее важные. Но нас интересует другое подразделение, а именно подразделение:
на те данные и действия, что видны или доступны для запуска снаружи автомобиля со стороны внешних объектов, например, прохожих или мальчика, держащего в руках пульт управления (такие данные и действия близки понятию глобальных переменных и методов).
и на те данные и действия, что не видны или не доступны (а эти близки понятию модульных).
Проведем это подразделение построже и поподробнее. Сначала поговорим о данных.
Свойства. Данные будем делить на те, что видны снаружи (это первые 5 из списка), и те, что не видны (последние 2). Данные, видимые снаружи, назовем свойствами
объекта. (С точки зрения грамматики языка VB данные, видимые снаружи, делятся на свойства и на поля, но мы пока не будем концентрироваться на этом делении и все такие данные будем пока называть свойствами. Если переменная объявлена словом Public, то она видна снаружи и является полем.)
Итак, свойства – это данные, видимые снаружи. Теперь будем подразделять свойства по доступности к изменению. Мы их разделим на две категории:
Те, что можно произвольно менять снаружи. Вообразим, например, что любой прохожий, вооружившись кистью, ведром краски и крюком, может произвольно поменять цвет, номер и скорость проезжающего автомобиля. В реальной жизни так не бывает, но в программировании это сделать очень легко. Назовем их свойствами для чтения-записи.
Те, что снаружи менять нельзя (у нас это последние 4 данных из списка). Назовем их свойствами только для чтения.
Очевидно, что данные, которые не видны снаружи, и менять снаружи тоже нельзя. В программировании это локальные переменные или модульные переменные, объявленные модификаторами Dim или Private.
Методы. Действия будем делить на те, которые можно вызывать снаружи (первые 2 действия из списка), и те, что вызываются внутренней механикой автомобиля (остальные). Действия, вызываемые снаружи, назовем методами объекта. В программировании это процедуры и функции, объявленные, например, словом Public. Если процедура или функция объявлена словом Private, то снаружи она не видна и методом не является.
Надежность, защищенность, простота. Создатель нашего игрушечного автомобиля при его конструировании стремится к тому, чтобы автомобиль был надежен, защищен и просто управлялся. Для этого он должен придерживаться двух принципов:
Количество методов должно быть минимально необходимым. Разгон, торможение, поворот налево и направо. Этого достаточно. Если разрешить управлять с пульта жужжанием двигателя и тонким процессом изменения скорости вращения колес при изменении тока в двигателе, то недолго и сжечь двигатель, нажав на пульте не ту комбинацию кнопок.
Количество свойств для чтения-записи должно быть минимальным. Действительно, вообразим крайний случай: все данные мы сделали свойствами, да еще и для чтения-записи. Тогда любой, кому не лень, сможет в любой момент менять, например, величину тока в двигателе, что немедленно приведет к катастрофе. Или, например, вообразите, что вы мчитесь в автомобиле, а любой прохожий может протянуть руку и покрутить баранку вашего автомобиля.
А теперь о том, нужно ли все данные делать свойствами, то есть делать их видимыми отовсюду. Дело вкуса. Покуда вы не делаете их свойствами для чтения-записи, это не влияет на надежность объекта. Сами решайте, нужно ли всем желающим видеть высоту сидений и величину тока. Конечно, когда речь идет о реальном игрушечном автомобиле, все получается как-то само собой: что снаружи – видно, что изнутри – не видно и ничего тут не поделаешь. Но у программиста имеется полная свобода любое данное сделать или не сделать свойством, в том числе и для чтения-записи, и любое действие сделать или не сделать методом.
Инкапсуляция. При создании объектов программисты стараются придерживаться принципа инкапсуляции, который заключается в следующем:
Данные и действия объекта представляют собой единое целое, образующее всю механику объекта, и хранятся они в одной «упаковке». Упаковкой этой, является, как вы понимаете, класс. Они должны быть как можно меньше видимы снаружи. Хорошо инкапсулированный объект представляет собой некий «черный ящик», эдакого «человека в футляре». Вся работа идет внутри. Внутренние данные меняются при помощи внутренних действий. Никто снаружи не может вмешаться в эту внутреннюю работу. Наружу показывается лишь тот минимум (интерфейс), который необходим для связи с окружающим миром.
Влиять снаружи на работу объекта можно только тремя способами:
Методами
Изменяя значения свойств для чтения-записи
Изменяя свойства окружающего мира, например, положив на пути автомобиля камешек.
Инкапсуляция – то, что объединяет объекты в программировании с объектами реального мира. Возьмите летящий высоко самолет. Вы не можете снаружи ни видеть работу его двигателя, ни как-то повлиять на нее. Вы вообще никак не можете повлиять на самолет ни в чем. Общаетесь вы с ним не тогда, когда хотите вы, а когда хочет он и по правилам, которые устанавливаются не вами (например, в аэропорту).
Все вышесказанное является введением в идеологию объектного программирования. Как мне кажется, это введение поможет вам легче разобраться в механике работы реальных объектов, создаваемых вами на компьютере.
Используем кнопку в проектах
Кнопка готова. Чтобы проверить ее в работе, создайте проект приложения Windows и добавьте ее на Toolbox. Можете одновременно добавить туда и элемент Запретная_зона, находящийся в той же библиотеке и проверять обоих. Поместите на форму одну обычную кнопку и две наших. Последние, как и положено, приобретут имена Моя_кнопка1 и Моя_кнопка2. Обратите внимание, что у каждой из наших кнопок есть самое настоящее окно свойств, где мы можем эти свойства менять, и очень приятно, что в списке свойств имеются созданные нами свойства Число_щелчков и Ветеран (мы не можем их менять, потому что сами их сделали ReadOnly).
Для проверки кнопок введите в окно кода формы такой код:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Моя_кнопка1.Прыжок(40)
Моя_кнопка2.Прыжок(30)
Debug.WriteLine(Моя_кнопка1.Число_щелчков)
Debug.WriteLine(Моя_кнопка2.Ветеран)
End Sub
Private Sub Моя_кнопка1_Наступление_зрелости() Handles Моя_кнопка1.Наступление_зрелости
MsgBox("Зрелость кнопки 1 наступила")
End Sub
Private Sub Моя_кнопка2_Наступление_зрелости() Handles Моя_кнопка2.Наступление_зрелости
MsgBox("Зрелость кнопки 2 наступила")
End Sub
После ввода точки за именами Моя_кнопка1 и Моя_кнопка2 перед вами будет раскрываться список свойств и методов кнопки, среди которых вы с удовольствием найдете и запрограммированные нами свойства и методы.
Запустите проект, пощелкайте по кнопкам.
Используем сборку в других программах
Для проверки работы библиотеки классов (нашей или даже чужой) создадим привычный проект приложения Windows (Windows Application) и назовем его Проверка. Зайдем в окне Solution Explorer в «папку» References, где перечислены ссылки на пространства имен, которыми мы имеем право пользоваться. Нам нужно, чтобы там появилось пространство имен Библиотека. Щелкните правой клавишей мыши по «папке» References и в контекстном меню выберите пункт Add Reference. В возникшем окне (Рис. 25.7), находясь в закладке .NET, нажмите Browse и найдите на диске файл сборки Библиотека.dll. Нажмите Select и элемент Библиотека.dll появляется в поле в нижней части окна.
Рис. 25.7
Нажмите ОК и пространство имен Библиотека появляется в «папке» References. Теперь им можно пользоваться.
Поместите на форму кнопку и для проверки введите в окно кода формы такой код:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Библиотека.Мой_модуль.C = "Привет!"
Debug.WriteLine(Библиотека.C)
Debug.WriteLine(Библиотека.Мой_модуль.Функция(3, 2))
Debug.WriteLine(Библиотека.Функция(3, 2))
Библиотека.Мой_класс.Процедура1()
Dim MK As New Библиотека.Мой_класс
MK.Процедура2()
End Sub
Вот что будет напечатано:
Привет!
5
5
и кроме того будут выданы два сообщения: с единицей и двойкой.
Обратите внимание, что поле и функция, описанные в модуле, доступны как с указанием имени модуля, так и без него. С классом все построже: имя класса указывать нужно, а если компонент класса не объявлен Shared, то для доступа к нему нужно создавать экземпляр класса.
Заключение. Вы вполне можете создать библиотеку классов и открыть доступ к файлу ее сборки на своем компьютере друзьям-программистам. Ваша библиотека может стать популярной. Удобно то, что вы впоследствии можете как угодно без ведома друзей улучшать процедуры, функции и другие компоненты своей библиотеки и друзья будут пользоваться этими улучшенными компонентами, даже не подозревая об этом (пока, конечно, вы не запорете какой-нибудь из них).
Вашу сборку можно использовать и в проектах, создаваемых на других языках программирования, поддерживающих платформу .NET.
И вы сами точно так же можете использовать сборки других программистов.
Файлы библиотек классов, созданных в VB, и библиотек динамической компоновки имеют одинаковое расширение dll, но не путайте их, это не совсем одно и то же.
Используем созданный элемент управления в проектах
Для проверки работы созданного элемента управления (нашего или даже чужого) создайте привычный проект приложения Windows (Windows Application). Теперь нам нужно добавить наш элемент управления на Toolbox. Для этого обычным образом выберите в контекстном меню Toolbox пункт Add/Remove Items. Появляется окно Customize Toolbox. Выберите закладку NET Framework Components. Нажмите Browse и найдите на диске файл Моя_библиотека_элементов_управления.dll. Элемент Запретная_зона появляется в списке (см. Рис. 25.3).
Рис. 25.3
Поставьте напротив него флажок. Затем – ОК. Элемент управления Запретная_зона оказывается в Toolbox. Размещаем его на форме и немного растягиваем. Запускаем проект. Элемент работает, как положено, то есть при нашей попытке навести на него курсор мыши выводится сообщение «Запретная зона!» (Рис. 25.4).
Рис. 25.4
Впрочем, охрана «дырявая» и поэтому вы вполне можете ухитриться и нажать-таки мышкой на кнопку «Не нажмешь!».
Разместите на форме еще пару запретных зон. Проверьте работу.
Используем «старые» компоненты (COM)
До прихода .NET в мире тоже были озабочены использованием в одних приложениях компонентов других приложений. Самая распространенная технология, применявшаяся (и применяющаяся) для этого, называется COM, и разработанные на ее основе компоненты называются COM-компонентами. VB позволяет использовать в проектах COM-компоненты и это очень важно, так как в мире таких компонентов накопилось очень много.
Широко известными приложениями, которые поддерживают COM-технологию, являются программы из пакета Microsoft Office, в частности Microsoft Word и Microsoft Excel. Раз они поддерживают COM-технологию, значит вы можете пользоваться ими в своем проекте. Зачем это нужно? – Пусть, например, ваше приложение предназначено для получения по электронной почте неких исходных данных и автоматической распечатки на их основе красивых качественных документов. Обеспечить превращение сухих, бедно оформленных электронных писем в качественные документы с красиво отформатированными шрифтом и абзацами – задача, которая требует значительной работы программиста. С другой стороны, уже имеются готовые приложения Windows типа Microsoft Word, которые прекрасно справляются с этой задачей. Интегрируя Word в ваше приложение, вы достигаете поставленной цели.
Аналогично можно работать в вашем приложении и с электронной таблицей Microsoft Excel. Пример работы в вашем приложении с таблицей Excel мы сейчас и рассмотрим. Причем совершенно не важно, работали ли вы в Excel раньше.
Суть работы Excel. Прежде чем работать с Excel в вашем проекте, надо чуть-чуть привыкнуть к нему «живьем». На Рис. 25.8 вы можете видеть таблицу и диаграмму, созданные в Excel.
Рис. 25.8
Запустите Microsoft Excel. Он имеет примерно такой вид, как на Рис. 25.9. Основной частью Excel является таблица из многочисленных ячеек, которые вы и видите на рисунке.
Рис. 25.9
Excel предназначен для автоматического выполнения разнообразных арифметических действий с числами таблицы. Как это делается? Вы видите, что таблица разделена на ячейки. У каждой ячейки имеется адрес, как в игре «Морской бой». Например, на рисунке число 100 находится в ячейке D2. Введите в ячейки таблицы числа 100, 20 и 8, как на рисунке. Чтобы ввести число в ячейку, достаточно выделить ее мышкой и набрать число на клавиатуре. В арифметических действиях с числами таблицы обычно указываются не сами числа, а их адреса. Пусть, например, вы хотите, чтобы в ячейке A3 находилась сумма трех введенных чисел. Для этого вы выделяете ячейку A3 и вводите в нее такой текст:
=D2+E2+E3
после чего нажимаете Enter. Формула сразу же становится невидимой, а на ее месте в ячейке A3 возникает число 128, то есть сумма. Попробуйте теперь изменить одно из слагаемых, например, поменять в ячейке E3 число 8 на 1. Тут же автоматически изменится со 128 на 121 и число в ячейке A3.
В ячейки можно вводить и текст. Обычно он служит для пояснений к числам.
Сотрите ячейку A3. Давайте сохраним нашу таблицу. Для этого – Файл ® Сохранить ® Excel предложит нам сохраниться в файле под именем Книга1.xls ®
соглашаемся. Закрываем Excel.
Листы, книги. Обратите внимание, что на рисунке присутствует несколько закладок с так называемыми листами: Лист1, Лист2, Лист3. На виду находится таблица, принадлежащая Листу1. У каждого листа – своя таблица. Таблицы, принадлежащие другим листам, на рисунке не видны. Все листы, что мы видим на рисунке, собраны в так называемую книгу – Книга1.
Excel – многодокументное приложение. Каждый документ – книга. Каждую книгу можно открыть в своем окне и сохранить в своем файле. Каждая книга состоит из нескольких листов.
Задача нашего проекта. Имеется созданный в Excel файл Книга1.xls, в котором на 1 листе в вышеуказанных трех ячейках находятся три числа. Проект должен просуммировать эти числа и отобразить их сумму в текстовом поле.
Решение. Excel достаточно «добр» и из множества своих классов и других компонентов предоставляет некоторое количество в общее пользование. Все такие объекты собраны в специальной Объектной библиотеке Excel.
Создайте обычный проект приложения Windows с кнопкой и текстовым полем. Первое, что надо сделать, это создать ссылку на объектную библиотеку Excel. Поступайте обычным образом. Зайдите в окне Solution Explorer в «папку» References. Щелкните по ней правой клавишей мыши и в контекстном меню выберите пункт Add Reference. В возникшем окне (Рис. 25.10) зайдите в закладку COM, найдите и выделите строку, выделенную на рисунке. Нажмите Select и библиотека появляется в поле в нижней части окна. ОК. Теперь объекты Excel в нашем распоряжении.
Рис. 25.10
Вводим в окно кода такой код:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Экс As Excel.Application = CreateObject("Excel.Application")
Экс.Workbooks.Open("E:\Папка\Книга1.xls")
Dim Лист As Excel.Worksheet = Экс.Workbooks("Книга1.xls").Worksheets("Лист1")
TextBox1.Text = Лист.Cells(2, 4).Value + Лист.Cells(2, 5).Value + Лист.Cells(3, 5).Value
Экс.Quit()
End Sub
Запустите проект. После нажатия кнопки в текстовом поле появится число 128.
Пояснения: Первой строкой мы объявляем и создаем при помощи метода CreateObject объект Экс класса Application пространства имен Excel, к которому получил доступ наш проект после добавления ссылки. С этого момента Excel уже запущен и незримо присутствует на экране, выполняя наши приказания, как джинн из бутылки.
Второй строкой мы открываем нужную нам книгу из файла при помощи метода Open интерфейса Workbooks, принадлежащего объекту Экс.
Третьей строкой мы объявляем переменную Лист, как объект класса Worksheet (Рабочий лист) пространства имен Excel, и тут же присваиваем ей значение нужного нам 1 листа, найдя его при помощи интерфейсов Workbooks и Worksheets.
Поскольку лист готов к работе, мы можем пользоваться его ячейками. Четвертая строка складывает значения трех его ячеек и отображает сумму в текстовом поле. Выражение
Лист.Cells(2, 4).Value
обозначает вот что: Значение (Value) ячейки (Cells), находящейся во 2 строке и 4 столбце таблицы. Это как раз наша знакомая ячейка D2. Остальное очевидно.
Пятая строка закрывает Excel. Если этого не сделать, он останется в памяти и будет занимать там много места.
Вы можете сделать нашего незримого джинна видимым при помощи оператора
Экс.Visible = True
На экране наряду с проектом появится Excel с открытым нужным листом. В нем можно нормально работать. Только оператор Экс.Quit() уберите, а то Excel закроется, не успев открыться.
Из чего «собирается» приложение Windows
Посмотрим внимательнее, из каких составных частей «собрано» большинство солидных приложений Windows. Конечно, у каждого приложения есть черты, присущие только ему, они-то и определяют работу приложения в соответствии с его назначением. Так Microsoft Word работает с текстом, Paint – с рисунками а VB выполняет программы. Но многие составные части присущи большинству приложений. Основные из них мы и рассмотрим. При этом пометим для себя, какие элементы библиотеки классов .NET Framework создают упомянутые части, умеем ли мы их создавать и работать с ними:
Составная часть приложения Windows | Элемент VB | Умеем? | |||
Главное окно приложения | Форма | Да | |||
Несколько окон внутри главного окна приложения | MDI Form | Нет, но позже научимся | |||
Меню, контекстные меню | Menu Editor, ContextMenu | Да | |||
Списки | ListBox, ComboBox, CheckedListBox | Да | |||
Список ListView | ListView | Нет, но сейчас научимся | |||
Дерево TreeView | TreeView | Нет, но сейчас научимся | |||
Кнопки, метки, текстовые поля, графические поля, Media Player, рамки, панели, вкладки, флажки, переключатели, полосы прокрутки, ползунки, индикаторы процесса, календари, счетчики, всплывающие подсказки | Да | ||||
Текстовое поле с расширенными возможностями | RichTextBox | Нет, но сейчас научимся | |||
Панели инструментов | Toolbar, ImageList | Нет, но сейчас научимся | |||
Диалоговое окно открытия файла | OpenFileDialog | Нет, но сейчас научимся | |||
Диалоговое окно сохранения файла | SaveFileDialog | Нет, но сейчас научимся | |||
Диалоговое окно выбора цвета | ColorDialog | Нет, но сейчас научимся | |||
Диалоговое окно настройки шрифта | FontDialog | Нет, но сейчас научимся | |||
Диалоговое окно печати | PrintDialog | Нет | |||
Окно помощи Help | HelpProvider | Нет, но позже научимся |
Мы наглядно видим, что библиотека классов .NET Framework – это магазин кубиков, из которых можно собрать любое приложение Windows.
Из чего состоят классы, структуры и модули
Список компонентов. Вот из чего имеют право состоять классы, структуры и модули:
Процедуры (Sub)
Функции (Function)
События (Event)
Свойства (Property)
Конструкторы (New)
Объявления нелокальных переменных
Объявления нелокальных констант
Перечисления
Структуры
Классы
Интерфейсы
Делегаты
Классы внутри классов. Поговорим о содержимом классов. Мы привыкли к верхним 9 элементам списка, как к компонентам классов, потому что использовали их на практике. Мы не привыкли к 10 элементу списка – Классы. (Об интерфейсах и делегатах говорить не будем.) Получается, что классы могут включать в себя другие классы? Зачем? Рассмотрим пример проекта:
Public Class Form1
Inherits System.Windows.Forms.Form
Windows Form Designer generated code
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
A.B()
A.C.D()
End Sub
End Class
Public Class A
Public E As Integer
Public Shared Sub B()
Debug.WriteLine("Сработала процедура B класса A")
C.D()
End Sub
Public Class C
Public Shared Sub D()
Debug.WriteLine("Сработала процедура D класса C класса A")
End Sub
End Class
End Class
При щелчке по кнопке будет напечатано:
Сработала процедура B класса A
Сработала процедура D класса C класса A
Сработала процедура D класса C класса A
Что мы видим в коде? Мы видим два класса: Form1 и A. Класс Form1 состоит из единственной процедуры Button1_Click. Класс A состоит из переменной E, процедуры B и класса C, который в свою очередь состоит из процедуры D.
В данном случае не было смысла включать класс C внутрь класса A, так как ничего полезного из этого мы не извлекли. Попробуем теперь в строке
Public Class C
заменить Public на Private.
Мы сразу увидим, что строка
A.C.D()
дает ошибку, так как класс C становится недоступен снаружи своего «хозяина» – класса A. Строка же
C.D()
ошибки не дает, так как выполняется внутри класса A.
Вот в этом и смысл – в инкапсуляции. Если класс C нужен классу A для своих внутренних нужд и он не хочет делиться им с другими классами, класс C должен быть включен внутрь класса A и снабжен модификатором Private.
О структурах и модулях. Мы с вами не включали внутрь структур ничего, кроме переменных величин, теперь же мы видим, что туда позволено включать все остальное. Судя по тому, что классы, структуры и модули состоят из одинаковых компонентов, можно было бы сделать вывод, что структуры и модули очень близки к классам. Да, близки, но не очень. Например, от модулей нельзя порождать их экземпляры, как от классов мы порождаем объекты. Есть много и других отличий. Причины всего этого разнообразны, не всегда очевидны, относятся к разряду тонкостей и я на них не останавливаюсь.
О перечислениях. Об их грамматике достаточно сказано в 13.3.
Из чего состоят процедуры, функции, свойства и конструкторы
Основное содержание окон кода составляют операторы, которые мы привыкли писать внутри процедур и функций. Это всевозможные «исполняемые» операторы типа If, Do, операторов присваивания, вызовов процедур и т.п. Оказывается, мы не имеем права записывать их нигде, кроме как внутри процедур, функций, свойств и конструкторов. (Чтобы не повторять то и дело словосочетание «процедуры, функции, свойства и конструкторы», я в этом подразделе буду звать их просто «процедуры».)
Из чего же имеют право состоять процедуры? Из двух вещей: из объявлений локальных переменных и констант и из всех упомянутых операторов. Правило это я сформулировал не очень четко, в противном случае пришлось бы перечислять все типы операторов, а их много.
Как видите, внутри процедур не нашлось места ни одному из 12 элементов списка предыдущего подраздела и ни одному из 7 элементов списка подраздела перед ним. Это значит, что внутри процедуры нельзя определять другие процедуры подобно тому, как мы определяли классы внутри классов. И тем более нельзя определять классы, модули, структуры и перечисления, не говоря уж о пространствах имен.
Из чего состоят пространства имен
В нашем примере пространства имен состояли из классов, модулей и других пространств имен. А вот из чего пространства имен имеют право состоять:
Классы (Class)
Модули (Module)
Структуры (Structure)
Перечисления (Enum)
Другие пространства имен (Namespace)
Интерфейсы (Interface)
Делегаты (Delegate)
О двух последних компонентах я ранее кратко упоминал, но не углублялся в них и углубляться не собираюсь. Начинающим знать их не обязательно.
Все эти компоненты записываются в теле пространства имен в произвольном порядке друг под другом, как мы это и делали в примере, и в промежутках между ними ничего писать нельзя.
Для нас непривычным является то, что структуры и перечисления могут непосредственно входить в состав пространств имен. До этого мы их писали только внутри классов и модулей. Однако, это удобно. Например, структура Color настолько популярна и значима, что быть составной частью какого-нибудь класса ей явно не по рангу. Поэтому она входит непосредственно в состав пространства имен System.Drawing и это хорошо. Если бы она входила внутрь класса, это привело бы к тому, что обращение к ней в коде только удлинилось. То же самое можно сказать о популярных перечислениях.
Как видите, в списке компонентов пространств имен не значатся ни процедуры, ни функции, ни другие элементы. Значит, процедуры, функции, переменные и прочие мелкие детали не могут прямо входить в пространства имен. А как же они туда входят? Об этом в следующем подразделе.
На этом уровне классы, структуры и модули не могут быть объявлены модификатором Private. Это значило бы, что они никому не видны. Зачем тогда мы их создавали?
Мы определили из каких составных частей состоят пространства имен. А теперь определим, из чего состоят эти составные части.