Часть
1. Основы Visual Basiс
|
||||||
22.03.2005 | ||||||
Глава
17.
|
||||||
Пользовательские
типы данных. Файлы произвольного и двоичного
(бинарного) доступа. Перехват нажатий кнопок клавиатуры. |
||||||
Пользовательские типы данных. |
Иногда, при
использовании некоторых операторов, API-функций, а также просто для своих нужд
необходимо создавать собственные типы данных. Часто их называют структурами.
По своей сути структура - это как бы одномерный массив , который мы запихиваем
в одну переменную. Но в него могут входить данные разных типов.
Создание собственного типа данных осуществляется с помощью инструкции Type,
который используется в секции General кода формы.
Например, для какого-нибудь условного прайса нам нужно хранить следующие данные:
Name - наименование товара
Kolichestvo - количество товара
Cost - цена товара
С одной строны мы можем просто объявить эти три переменные с необходимым нам
типом и использовать их каждую в отдельность по необходимости:
Dim Name
As String
Dim Kolichestvo As Long
Dim Cost As Single
Но мы также
можем объединить эти три переменные в единую структуру, создав тип данных, например,
под названием Price. Однако, поскольку, инструкция Type предназначена
для использования во всем проекте, то и пользовательский тип данных должен создаваться
в стандартном модуле:
Type
Price
Name As String
Kolichestvo As Long
Cost As Single
End Type
Хотя, в
принципе, ее можно написать и в форме в разделе деклараций с ключевым словом
Private:
(Private Type Price)
Теперь нам, там же, в стандартном модуле, надо объявить какую-нибудь глобальную
переменную с нашим типом данных. Назовем ее Stroka:
Public
Stroka as Price
Теперь мы смело
можем пользоваться нашей переменной. Напишем в какой-нибудь процедуре (например
FormLoad) имя переменной Stroka и поставим точку, у нас тут же появиться всплывающее
меню, предлагающее на выбор три переменные, входящие в нашу переменную Stroka,
и мы можем присвоить каждой свое значение, в соответствии с ее типом. Помотри
на рисунок 29:
Рисунок
29.
Внешне это напоминает, как будто мы работаем со свойствами переменной, но это
не так.
Теперь, вооруженные этими знаниями, перейдем к файлам прямого доступа.
Файлы произвольного (прямого) доступа - база данных внутренними возможностями VB (тренируемся на кошках). |
Прежде всего надо уяснить себе следующее. Файлы произвольного доступа имеют свою постоянную структуру и по сути организованы как и базы данных. Они состоят из однотипных записей, а в свою очередь запись состоит из полей.
Name
|
Kolichestvo
|
Cost
|
Газонокосилка |
2
|
156.17
|
Сноповязалка |
5
|
312.76
|
Строка этой
таблицы представляет собой запись, а столбец (например Name) - это поле. Таким
образом в таблице показан файл с тремя полями, в котором храняться две записи
(верхняя строка - просто название полей). При такой организации возможен доступ
непосредственно к любой записи по ее номеру. При этом нет необходимости считывать
весь файл. Кроме того, возможно считывать и записывать данные, один раз открыв
файл и не закрывая его. Причем доступ может быть разрешен и другим пользователям
локальной сети.
Файл нельзя читать текстовым редактором, это не текстовой файл.
Ранее, в бейсиках, с помощью таких файлов можно было организовать что-то типа
базы данных. С появлением версии Visual Basic 3.0 этот подход утратил свою актуальность,
поскольку появилась возможность доступа из Visual Basic к различным "настоящим"
базам данных.
Однако с помощью таких файлов можно хранить служебную информацию собственной
программы, а кроме того, иногда просто нецелесообразно организовывать доступ
к серьезным базам типа Access, из-за небольших объемов данных или простоты структуры
БД. Кроме того, работа с такими файлами поможет разобратся с принципами организации
базы данных и окажет помощь в дальнейшем, когда мы будем рассматривать связь
Visual Basic и Access.
Итак. Как и при работе с
файлами последовательного доступа, файл прямого доступа надо открыть. Делается
это также с помощью все того же многострадального оператора Open. Вот
его синтаксис:
Open
Имя_ файла For Random Access
Тип_доступа Права_доступа As # Номер_файла
Len=Длина_записи
где:
Имя_
файла -
это, естественно, просто любое имя открываемого файла. Если файла с таким именем
не существует, VB его создаст (пустым).
For Random
- тип доступа - произвольный
Access -
ключевое слово, указывающее, что далее идет тип доступа
Тип_доступа
- позволяет установить доступ
к файлу: Read - для чтения, Write - для записи, Read Write
- для чтения и записи, и наконец если не указывать тип доступа, то по умолчанию
- для чтения и записи.
Права_доступа
- определяет права доступа к файлу различных пользователей одновременно (Shared
- открыто для чтения и записи всем, кому не лень, Lock Read - никто другой
не может читать открытый файл, Lock Write - никто другой не может записывать
в файл, Lock Read Write - никто другой не может ни писать ни считывать
данные файла)
As # Номер_файла
- это просто число - идентификатор,
как и в файлах последовательного доступа
Len=Длина_записи
- определяет длину записи.
Поскольку записи не могут быть произвольной или разной длины, необходимо раз
и навсегда определить длину записи для данного набора. Здесь есть проблема,
так как не всегда известна длина набора данных. Тем не менее если это значение
меньше реальной длины записи, то возникнет ошибка, если же задать длину записи
с "запасом", то файл будет занимать большое дисковое пространство.
Таким образом, чтобы открыть
в текущей директории файл price.bzd произвольного доступа для чтения и записи
с неограниченным для пользователей доступом под номером 1 и длиной записи 100
байт, нужен такой оператор:
Open
App.Path & "\price.bzd" For Random Access
Read Write Shared As #1 Len = 100
или в более коротком виде:
Open
App.Path & "\price.bzd" For Random As #1
Len = 100
Теперь,
после того, как файл открыт, мы можем производить в него запись и считывание.
Для записи в файл используют оператор Put, а для считывания - оператор
Get, синтаксис которых совершенно идентичен:
Put #Номер_файла,
Номер_записи, Переменная
Get #Номер_файла,
Номер_записи, Переменная
где:
#Номер_файла
- это понятно, что тот номер,
под которым мы его открыли (у нас #1)
Номер_записи
- номер записи, в которую
мы записываем данные (в соответствующие поля). Первой записи соответствует номер
1, второй - 2 и т.д. Если номер записи опущен, то записывается запись, на которой
стоит указатель после выполнения последнего оператора Put или Get,
или та, на которую установлен указатель с помощью функции Seek (см. ниже).
Переменная
- это данные, которые мы
записываем в файл. При наличии обычной переменной мы сможем сохранить в запись
одно-единственное поле. Чтобы сохранить или считать несколько полей в записи,
необходимо использовать пользовательский тип данных, о чем мы говорили в начале
главы.
Попробуем создать что-то, похожее на простенькую базу данных, внешне похожую на Виндусовый Access. Мы реализуем некоторые возможности БД, такие как создание записи, считывание, удаление записи, поиск записи по содержимому поля. И по ходу разработки познакомимся с сопутствующими этому делу функциями, инструкциями и методами. Прошу учесть, это всего лишь пример, хотя и полностью рабочий. Мы сделаем базу наличия товара на складе на одной-единственной таблице в запись которой будут входит четыре поля: ID для хранения номера записи (чисто иллюстративно), Name - для наименования продукции, Kolichestvo - для количества единиц товара и Cost - для цены товара. Вычисляемые значения, такие как общая сумма товара полями не являются и как в настоящей базе, в файле не хранятся. Они будут вычисляться всякий раз при выводе данных на экран. Именно так происходит и в Accesse.
Итак создадим
новый exe-проект. Положим на форму Form1 следующие объекты (рис. 30)
Рисунок
30.
В таблице слева
направо:
- первый столбец - массив кнопок Command1 (Control Array). Как их создавать
я не раз уже говорил (например смотри Главу 13). Мы будем выводить на экран
по 10 записей, поэтому и массивы объектов у нас содержат по 10 элементов (с
Command1(0) по Command1(9). Эти кнопки предназначены для удаления соответствующей
записи.
-второй столбец - массив лейблов Label12(0)-Label12(9). Они служат только
для показа номера записи.
-третий столбец - массив текстбоксов Text1(0)-Text1(9). Сюда будет выводиться
содержимое поля Name (наименование товара).
-четвертый столбец - массив текстбоксов Text2(0)-Text2(9). Сюда будет
выводиться содержимое поля Kolichestvo (количество товара).
-пятый столбец - массив текстбоксов Text3(0)-Text3(9). Сюда будет выводиться
содержимое поля Cost (цена единицы товара).
-шестой столбец - массив лейблов Label1(0)-Label1(9). Здесь будет вычисляемое
значение суммы по наименованию товара (Количество умножить на Цену).
Как и положено в настоящей базе, у нас будет и итог по столбцам: Label2 вычисляет количество единиц товара, а Label3 - общую сумму за весь товар. Кроме того у нас будет вертикальный скроллбар Vscroll1 для просмотра нашей таблицы, а также для поиска по наименованию кнопка Command2 и текстбокс Text4. Ну есть еще лейблы общего заголовка и заголовков столбцов и надписи Итого, но они только для красоты и могут быть любыми.
Сразу создадим
стандартный модуль Module1 (или Модуль1) - это уж у кого какой VB, и зададим
там пользовательский тип Price для переменной Stroka, которую мы там же и объявим:
Option Explicit
'создаем пользовательский тип
Type Price
ID As Long
Name As String * 28
Kolichestvo As Integer
Cost As Single
End Type
'объявляем переменную Stroka с типом Price
Public Stroka As Price
На фига я,
спрашивается Name умножил на 28. А это я ничего не умножил, а задал длину строковой
переменной, в которую будет записываться наименование товара. Все, что длинее,
будет усекаться справа до 28 символов, а строки короче будут дополняться пробелами
опять-таки справа до 28 символов. Почему 28? А потому, что я от фонаря на форму
положил текстбокс Text1, и в него влезло только 28 буковок. Длины полей числовых
переменных определяются их типом и свободное место справа тоже заполняется пробелами.
Обратите внимание на тип данных в пользовательской структуре. Поскольку я предполагаю,
что количество товара - целое число и количество записей не будет чрезмерным,
то Kolichestvo имеет тип Integer. А вот Cost - цена, может быть дробной, поэтому
- Single. Я не использовал денежное представление числа, хотя можно было бы.
Ну вот теперь о модуле можно забыть и полностью отдаться форме.
Закрываем модуль и пишем в форме. Сперва, конечно, объявим несколько переменных:
Option Explicit
Dim BeginStroka As Long
'номер записи, с которой начинается вывод десяти строк
на экран
Dim LastStroka As Long
'номер следующей за последней записью в
'файле (куда будет записываться новая запись)
Dim OldIndex As Integer
'значение старого индекса массива объектов (Control array)
до обновления
Dim IndexForSub As Integer
'промежуточная переменная для передачи значения индекса
'элемента массива из одной процедуры в другую
Dim ItogProm As Single
' промежуточный итог от умножения поля Kolichestvo на Cost
Далее продолжим процедурой загрузки формы:
Private Sub
Form_Load()
OldIndex = 0 'обнуляем переменную и текстбокс
Text4.Text = ""
Command2.Caption = "Поиск" 'назначаем кнопке
название
Open App.Path & "\price.bzd" For
Random As #1 Len = Len(Stroka) 'открываем
наш долгожданный файл
' произвольного доступа. В качестве длины нашей записи используем оператор Len(Stroka).
Однако при
'использовании различных типов данных возможна ситуация, когда длина записи
больше фактической
'длины записи как минимум на 2 байта. Это вызвано тем, что VB прописывает собственные
' дополнительные служебные байты. Но в нашем случае все работает и так.
Do While Not EOF(1) 'Запускаем
цикл чтения записей до конца файла.
Get #1, , Stroka ' Поскольку
номер записи мы не указали, указатель записи с каждым проходом цикла
'сдвигатся на один и оператор читает следующую запись.
Loop
LastStroka = Seek(1) - 1'узнем количество записей в файле
c помощью инструкции SEEK (см. ниже)
'Задаем свойства для Vscroll, чтобы у нас корректно
выводилось на экран и менее 10 записей
VScroll1.Min = 1
If LastStroka > 10 Then
VScroll1.Max = LastStroka 'максимальное значение соответствует
последней+1 записи
VScroll1.LargeChange = 10
Else
VScroll1.Max = LastStroka 'максимальное значение соответствует
последней+1 записи
VScroll1.LargeChange = 1
End If
VScroll1.SmallChange = 1
VScroll1.Value = 1
BeginStroka = VScroll1.Value 'присваиваем переменной начала
вывода значение VScroll
SummaCount 'переходим к процедуре вычисления итоговых
значений
End Sub
А теперь разберем функцию Seek
Функция Seek. |
Ее синтаксис следующий:
Seek(Номер_файла)
Эту функцию можно использовать
для файлов, открытых в режиме Random (как в нашем случае) и Binary.
Для
файлов, открытых в режиме Random. С
помощью этой функции мы можем узнать текущее положение указателя чтения записи
внутри файла. То есть в нашем примере , после считывания всего файла указатель
устанавливается на последней записи. А использовав эту функцию, мы узнаем, какая
запись будет считана (обработана) следующей. Но поскольку следующей записи пока
не существует, это будет номер создания новой записи.
Для
файлов, открытых в режиме Binary. Поскольку
режимы Random и Binary аналогичны, то и функция Seek здесь
работает также, за исключением того, что возвращает она не значение номера записи,
а номер байта, с которого начнется выполнение следующей операции (считывания
или записи). Файлы, открытые в режиме Binary мы разберем в конце этой
главы.
Заметь, что кроме функции Seek существует
еще инструкция Seek#:
Инструкция Seek#. |
Синтаксис:
Seek
#Номер_файла, позиция
Номер_файла
- номер открытого файла
позиция
- номер записи или байта, на который установится указатель
В отличие от своей одноименной
функции, инструкция Seek задает положение указателя чтения или записи,
т.е. номер записи в режиме Random или номер байта в режиме Binary, с
которых начнется выполнение операции. Причем, если в операторах Put или
Get задан номер записи, то инструкция Seek игнорируется.
Установка указателя за конец файла приводит к увеличению размера файла.
Теперь вернемся к коду нашей программы. В конце процедуры Form_Load у нас переход к процедуре SummaCount, где вычисляется итоговые значения по столбцам и обновляется переменная LastStroka . Но чтоб туды перейти, ее надо создать. В ней мы высчитываем итоговые значения по полю Kolichestvo и по сумме. Поля по сумме у нас нет - это вычисляемое значение, поэтому мы суммируем произведения Kolichestvo * Cost по каждой записи. Кроме того Здесь же мы вычисляем новое количество записей в файле. Обратите внимание, что, мы, раз открыв файл, больше его не закрываем, а постоянно обращаемся к нему и для записи и для считывания.
Private Sub
SummaCount()
Dim ItogAll As Single
Dim ItogKol As Single
Seek #1, 1 'устанавливаем
указатель на первую запись инструкцией Seek#
Do While Not EOF(1) ' Cчитываем
весь файл до конца.
Get #1, , Stroka ' Читаем
в цикле каждую запись.
ItogAll = ItogAll + (Stroka.Kolichestvo * Stroka.Cost)
Label3.Caption = ItogAll 'выводим итог по сумме
ItogKol = ItogKol + Stroka.Kolichestvo
Label2.Caption = ItogKol 'выводим итог по количеству товара
Loop
LastStroka = Seek(1) - 1 'обновляем количество записей
' переходим к процедуре вывода данных на экран
ShowRecords
End Sub
Из этой процедуры мы переходим к процедуре ShowRecords, которая, наконец, выводит наши поля на экран. Здесь такие нюансы. Поскольку, при записи в файл незаполненные байты полей заполняются пробелами, то при выводе содержимого поля в текстбокс данные справа будут завершаться ненужными пробелами. Чтобы избавиться от них мы будем использовать функцию RTrim.
Функции Trim, LTrim и RTrim. |
Эти функции
удаляют пробелы из начала строки - слева (LTrim), с конца строки - справа (RTrim)
или с обоих концов строки сразу (Trim). Синтаксис этих функций (как и использование)
предельно простой:
LTrim(строка)
RTrim(строка)
Trim(строка)
где строка
- любое строковое выражение
Теперь вернемся
к нашим баранам, т.е., виноват, к процедуре ShowRecords. Давайте, наконец,
ее создадим:
Private Sub
ShowRecords()
Dim X As Long
'включаем обработку ошибок, цель ее в том, чтобы предотвратить
ситуацию,
'когда при наличии менее, чем 10 записей в файле
' или если файла вообще не существует, оператор Get# в цикле For... Next будет
пытаться
' считать несуществующие записи.
On Error Resume Next
For X = 0 To 9
'считываем запись, номер которой складывается из индекса
текстбокса и номера строки,
' которая выводится на экран первой в текстбокс с индексом 0.
Get #1, X + BeginStroka, Stroka
'выводим содержимое полей в текстбоксы и лейбл, удаляя ненужные пробелы справа
Text1(X) = RTrim(Stroka.Name)
Text2(X) = RTrim(Stroka.Kolichestvo)
Text3(X) = RTrim(Stroka.Cost)
Label12(X).Caption = RTrim(Stroka.ID)
'а для последней, еще не существующей записи вместо нуля
выведем для красоты стрелку
'в Caption кнопки Command1 (которая вообще-то предназначена для удаления записи,
' а в Label12 - стрелочку. У нас будет, ну прямо как в Access'е
If Label12(X).Caption = 0 Then
Label12(X).Caption = "new"
Command1(X).Caption = "->"
End If
'кроме того в Label1 выведем результат умножения Количества
на Цену
ItogProm = Stroka.Kolichestvo * Stroka.Cost
Label1(X).Caption = ItogProm
'нижеследующие условия нужны для того, чтобы сделать невидимыми
текстбоксы, лейблы
'и кнопки, которые будут после окончания файла. Таким образом, пользователь
не сможет
' при последней записи, например, 20 ввести данные в запись 26.
If LastStroka - BeginStroka < 10 And
X + BeginStroka > LastStroka Then
Text1(X).Visible = False
Text2(X).Visible = False
Text3(X).Visible = False
Label1(X).Visible = False
Label12(X).Visible = False
Command1(X).Visible = False
Else
Text1(X).Visible = True
Text2(X).Visible = True
Text3(X).Visible = True
Label1(X).Visible = True
Label12(X).Visible = True
Command1(X).Visible = True
End If
Next X
'отключаем обработку ошибок
On Error GoTo 0
End Sub
Чегой-то мы устанавливали свойства VScroll1, даже использовали ее значение, а самой процедуры нет. Быстренько ее пишем:
Private Sub
VScroll1_Change()
BeginStroka = VScroll1.Value
ShowRecords
End Sub
Из нее мы вызываем процедуру
ShowRecords, чтобы обновить записи на форме.
Ну
вот, теперь нам нужна процедура, которая бы записывала данные в файл. Написать
ее довольно просто, но нам нужно событие, которое мы бы могли использовать для
инициализации сохранения записи на файле. Тут возможны самые разнообразные решения,
зависит от степени извращенности. Я использовал событие _GotFocus, которое
возникает при получении объектом фокуса. Надо заметить, что объекты, для которых
пишется процедура с событием _GotFocus должны быть видимы (Visible=True)
и не заблокированы (Enabled=True). Все эти условия удовлетворяют нашим
текстбоксам. Поэтому для каждого из них пишем собственную процедуру:
Private Sub
Text1_GotFocus(Index
As Integer)
IndexForSub = Index
MakeRecord
End Sub
Private Sub Text2_GotFocus(Index
As Integer)
IndexForSub = Index
MakeRecord
End Sub
Private Sub Text3_GotFocus(Index As
Integer)
IndexForSub = Index
MakeRecord
End Sub
Переменная IndexForSub
служит для хранения индекса текстбокса, получившего фокус, так как Index
доступен в пределах только своей процедуры. И из каждой процедуры мы вызываем
процедуру MakeRecord. Она-то и будет записывать нашу запись в файл (тавтология:
масло маслянное, я думал об этом с начала главы, но не смог ничего придумать).
Поскольку фокус получает новая запись, а записывать-то
надо старую, то номер старой записи храниться в переменной OldIndex.
Изначально мы ей присвоили значение 1 в процедуре Form_Load. Эта запись и запишется
первой. А в конце процедуры записи MakeRecord мы присвоим OldRecord значение
IndexForSub, которая теперь станет номером старой, т.е. текущей записи. Только
не спрашивайте, зачем такой гемморой, и почему я не использовал событие _LostFocus,
которое возникает при утрате объектом фокуса. Ты можешь попробовать это сам,
типа потренируйся. Прелесть программирования и заключается в том, что гланды
можно удалить и через жопу и другими разнообразнейшими способами. Тем более,
что мне захотелось показать, как применять переменную, которая постоянно обновляет
свое значение (OldIndex). Это я отмазался. На самом деле просто так захотелось.
Cамое время все-таки написать (ударение на последнем слоге - замучился искать синонимы) нашу процедуру MakeRecord.
Private Sub
MakeRecord()
Dim ItogProm As Single
'При условии, что хотя бы один текстбокс что-нибудь, да
содержит
If Len(Text1(OldIndex))
> 0 Or Val(Text2(OldIndex)) <> 0
Or Val(Text3(OldIndex)) <> 0 Then
'присваиваем пользовательской переменной их значения
Stroka.Name = Text1(OldIndex)
Stroka.Kolichestvo = Val(Text2(OldIndex))
Stroka.Cost = Val(Text3(OldIndex))
Stroka.ID = OldIndex + BeginStroka
'сразу выводим номер записи, на случай, если это новая
запись
Label12(OldIndex).Caption = RTrim(Stroka.ID)
'а также удаляем стрелочку из Caption кнопки
Command1(OldIndex).Caption = ""
'собственно записываем запись
Put #1, OldIndex + BeginStroka, Stroka
End If
'получаем номер новой старой записи
OldIndex = IndexForSub
'и отправляемся считать итоги, а оттуда - вывод полей
по-новой
SummaCount
End Sub
В основном
ядро программы сделано.
Далее, поскольку мы налепили целый массив кнопок Command1 для уничтожения
записи, надо стало-быть ваять под это дело процедуру. Для уничтожения записи
я использовал такой метод: мы просто переписываем наш файл в новый под именем
Copyprice.bzd без той записи, которую мы хотим удалить, затем закрываем
текущий файл и удаляем его. А новую копию переименовываем в старый и открываем
его по-новой. Все это реализовано в процедуре Command1_Click:
Private Sub
Command1_Click(Index
As Integer)
Dim X As Integer
Dim Z As Integer
'открываем файл-копию
Open App.Path & "\Copyprice.bzd"
For Random As #2 Len
= Len(Stroka)
For X = 1 To LastStroka
- 1
'считываем запись с основного файла
Get #1, X, Stroka
'проверяем, чтобы не перезаписать уничтожаемую запись
If Index + BeginStroka <> X Then
Z = Z + 1
Stroka.ID = Z
'записываем запись во вспомогательный файл
Put #2, Z, Stroka
End If
Next X
'закрываем все файлы
Close
'убиваем основной файл
Kill App.Path & "\price.bzd"
'переименовываем вспомогательный в основной
Name App.Path & "\Copyprice.bzd"
As App.Path & "\price.bzd"
'открываем его, как основной
Open App.Path & "\price.bzd" For
Random As #1 Len = Len(Stroka)
' вперед на обсчет суммы и далее по коду
SummaCount
End Sub
Аналогично можно сделать
и сортировку записей в файле. Но так в настоящих базах не делают. Там содержимое
файла остается неизменным, меняется только вывод его на экран. Мы этого здесь
делать вообще не будем. Но процедуру поиска записи по полю Наименование под
кнопкой Command2 все-таки создадим:
Private Sub
Command2_Click()
Dim X As Long
Dim FoundStroka As Boolean
FoundStroka = False 'флаг - если не найдено, значение
не меняется,если найдено - становится True
'проверяем не пусто ли поле с критерием поиска, а то всяко
бывает
If Text4.Text <> "" Then
For X = 1 To LastStroka
- 1
'Считываем записи
Get #1, X, Stroka
'находим первое вхождение
If InStr(RTrim(Stroka.Name), Text4.Text) Then
'в соответствии с номером строки меняем VScroll, чтобы
искомая запись стала первой
BeginStroka = X
VScroll1.Value = X
'Устанавливаем флаг
FoundStroka = True
End If
Next X
'выводим сообщение, если ничерта не найдено
If FoundStroka = False Then
MsgBox "Результат поиска", , "Ничего не найдено"
'устанавливаем фокус на найденную строку (первую)
Text1(0).SetFocus
'переписываем текстбоксы
ShowRecords
End If
End Sub
Здесь
мы для задания фокуса Text1(0) использовали метод SetFoсus. Он передает
фокус заданному элементу управления, в нашем случае текстбоксу. Фокус можно
передавать только видимому элементу - это как-бы понятно. Нельзя использовать
этот метод внутри процедуры Form_Load, так как до ее окончания все элементы
формы не видны. Чтобы сделать их видимыми до окончания процедуры Form_Load,
можно использовать метод Show.
Теперь последний штришок, в котором мы рассмотрим перехват нажатий клавиатуры.
Для начала рассмотрим некоторые события связанные с нажатием кнопок на клаве.
Они приведены ниже
События и перехват кнопок клавиатуры. |
Теперь вернемся
к нашей программе. После ввода данных в текстбокс и нажатии клавиши ENTER хорошо
бы, чтобы курсор , то бишь фокус передавался следующему текстбоксу. Тогда это
вызовет событие LostFocus И наша запись обновится. И выглядеть это будет красиво.
Для этих целей мы можем использовать, в принципе, любое событие из перечисленных
в таблице. Возьмем, например _KeyDown и для каждого техтбокса напишем по короткой
процедурке:
Private Sub
Text1_KeyDown(Index
As Integer, KeyCode As Integer, Shift As
Integer)
If KeyCode = 13 Then
IndexForSub = Index
Text2(Index).SetFocus
MakeRecord
End If
End Sub
Private Sub Text2_KeyDown(Index As
Integer, KeyCode As Integer, Shift As
Integer)
If KeyCode = 13 Then
IndexForSub = Index
Text3(Index).SetFocus
MakeRecord
End If
End Sub
Private Sub Text3_KeyDown(Index As
Integer, KeyCode As Integer, Shift As
Integer)
If KeyCode = 13 Then
Text1(Index + 1).SetFocus
IndexForSub = Index
MakeRecord
End If
End Sub
Код клавиши ENTER - 13. Мы
отслеживаем ее и передаем фокус следующему текстбоксу. При этом не забываем,
где нужно, запоминать индекс массива элемента объектов.
На этом код весь. Песня кончилася. Всю эту хрень ты можешь скачать вверху страницы.
Еще раз повторяю, что это лишь пример, а не полнофункциональная программа.
А сейчас, напоследок нам надо разобрать пример работы с файлом двоичного доступа.
Работа с файлами посредством двоичного (бинарного) доступа. |
Работа с файлами, открытыми для двоичного доступа практически такая же, как и с файлами произвольного доступа. Разница заключается в том, что мы считываем не запись, а отдельный байт. При этом файл открывается в режиме Binary. Синтаксис оператора Open cледующий:
Open
Имя_ файла For Binary Access
Тип_доступа Права_доступа As # Номер_файла
где все тоже самое,
см. файл произвольного доступа в начале главы. Зато здесь нет необходимости
парится с пользовательскими типами данных и главное с длиной записи. Поскольку
ничего этого нет. Байт мы читаем прямо в переменную.
С помощью такого доступа мы можем залезть в любой файл, считать байты, которые
нам нужны, записать в файл свои, какие хотим, после этого файл скорее всего
работать не будет. Потому что, прежде чем лезть писать (снова ударение на последнем
слоге), надо знать структуру файла данного формата, будь то .exe или там jpg,
но тем не менее разберем маленький примерчик. Кстати, структуры графических
файлов BMP, GIF и JPG(JFIF) и кое-какие издевательства над ними с помощью именно
бинарного чтения файлов опубликованы в Части 3 "Реализация некоторых задач".
Для информации, если кому интересно: в конец exe-файла можно дописать чего угодно
и он останется работоспособным.
Но продолжим.
Считывание и запись осуществляется с помощью все тех же операторов Get
и Put.
Практически это делается так.
Давайте создадим exe-проектик, положим на него Text1 и Command1. В процедуре
кнопки и будем писать код. Переменная, в которую мы будем считывать байт назовем
Bait. Обратите внимание, что объявляться она должна с типом Byte. Итак, чтобы
нам считать пятый байт из файла save.Bmp и его содержимое отобразить в Text1,
нам надо написать следующее:
Private Sub
Command1_Click()
Dim Bait As Byte
Text1.Text = ""
Open App.Path & "\save.Bmp" For
Binary As #1
Get #1, 5, Bait
Text1.Text = Bait
Close #1
End Sub
Однако в Text1
большинство символов не отобразится, поэтому лучше использовать не содержимое
Bait, а его ASCII-код. Для этого можно использовать функцию AscB(строка_байтов).
Она похожа на функцию Asc, но предназначена специально для байт в строке:
Text1.Text
=AscB(Bait)
Для того, чтобы считать несколько
байтов, можно использовать цикл. Считаем 10 байт с 12 по 21-й:
Private Sub
Command1_Click()
Dim x As Integer
Dim Bait As Byte
Text1.Text = ""
Open App.Path & "\save.Bmp" For
Binary As #1
For x = 12 To 21
Get #1, x, Bait
Text1.Text = Text1.Text & "*" & AscB(Bait)
Next x
Close #1
End Sub
Иным способом
это можно сделать, исходя из того, что число считываемых байт равняется числу
символов, уже содержащихся в строке. Поэтому, если установить указатель с помощью
функции Seek# на 12 байт и передать в переменную 10 пробелов, мы должны получить
тот же результат. К сожалению, в этом случае функция AscB считает лишь первый
байт:
В этом случае переменную Bait надо объявлять как String (строковая).
Private Sub
Command1_Click()
Dim x As Integer
Dim Bait As String
Text1.Text = ""
Open App.Path & "\save.Bmp" For
Binary As #1
Seek #1, 12
Bait = String(10, " ")
Get #1, , Bait
Text1.Text = AscB(Bait)
Close #1
End Sub
И в конце,
если мы положим на форму кнопку Command2, то в ее процедуре можем попробовать
написать код, который будет записывать в первые 10 байтов нашего несчастного
файла букву "Ш" или ее код - 216:
Private Sub
Command2_Click()
Dim x As Integer
Dim Bait As String
Text1.Text = ""
Open App.Path & "\save.Bmp" For
Binary As #1
Bait = Chr(216) 'Ш
For x = 1 To 10
Put #1, , Bait
Text1.Text = Text1.Text & "*" & (Bait)
Next x
Close #1
End Sub
При очень большом желании
исходник последнего варианта можно скачать здесь.
|
Copyright
|