1. Перенос строки кода VBasic на следующую делается с помощью символа '_' в конце строки (можно применять не более 10 разделителей, и суммарная длина строки не должна превышать 1023 символа). В одной строке можно использовать два и более различных оператора, разделённых символом ':'. Комментарий указывается с помощью символа ' (можно использовать в любом месте строки) или оператора REM (только в начале строки).
2. Можно задавать переменные неявно, просто присваивая им значение. Переменные задаются явно с помощью оператора DIM:
DIM имя_переменной [AS тип_переменной]
Внимание! Через запятую можно указать несколько переменных, но для каждой ОБЯЗАТЕЛЬНО нужно отдельно указать тип (не так, как в C, с непривычки получается попадалово!). Как обычно, имя переменной должно начинаться с буквы и не может быть зарезервированным словом. Длина имени не может превышать 255 символов. Указывать тип данных при объявлении не обязательно, имеется возможность указать тип добавлением к имени переменной в конце специального символа (используется для совместимости со старыми версиями Basic, и Microsoft не рекомендует использовать эти символы):
Тип переменной |
Символ |
Пример |
Integer |
% |
Dim IntegerSample% |
Long |
& |
Dim LongSamle& |
Single |
i |
Dim SingleSamlei |
Double |
# |
Dim DoubleSample# |
Currency |
@ |
Dim CurrencySample@ |
String |
$ |
Dim StringSample$ |
Если ни символ в конце, ни тип переменной явно не указан, то тип переменной становится Variant. VBasic всегда по умолчанию использует тип Variant. Тип хранимой величины по ходу выполнения может меняться. Использование такого типа чревато логическими ошибками в программе, которые трудно выявить, поскольку в ходе чтения кода тип данных не виден. Кроме того, для поддержки Variant расходуется больше памяти.
3. Видимость переменных - существует 3 вида:
- локальная (переменная доступна только в процедуре. Для этого она должна быть определена внутри неё). - видимости контейнера (переменная доступна только в текущей форме, модуле или классе). Для этого переменная должна быть определена в секции General Declarations, Или, говоря по-русски, в начале листинга модуля. - глобальная (видна во всех процедурах и модулях проекта. Для этого переменная опять-таки определяется в начале модуля, но уже с ключевым словом Public [раньше, до 5-й версии, использовалось ключевое слово Global]).
4. Время жизни переменных - локальные переменные при каждом выходе из процедуры удаляются из памяти, а при новом вызове инициализируются заново. Если это не нужно, то надо либо определить переменную в начале модуля (т. е. сделать её переменной контейнера или глобальной). Но при этом существует опасность (при совпадении имён) случайного изменения содержимого переменной в другом месте программы. Поэтому есть возможность сделать переменную в процедуре статической. Такая переменная сохраняет значение при выходе из процедуры, пока существует в памяти форма или модуль. Для этого вместо оператора Dim применяют оператор Static. Чтобы сделать все переменные процедуры статическими, процедура объявляется с оператором Static:
Static Sub AllVarsTheseSubAutoDeclaredAsStatic (...)
...
End Sub
5. Массивы в VBasic.
- Элементы массива всегда индексируются с нуля (стартовый элемент массива имеет индекс 0), но с помощью оператора Option Base 1, указанного в начале модуля, можно задать начало индексации с 1. Для установки других границ массива используют другой синтаксис:
[Static|Piblic|Dim] Имя_массива([Нижний_предел To] Верхний_предел)
Например:
Dim aBirthDate (1980 To 2050)
- Многомерный массив задаётся так:
Dim Название_массива (Измерение1, Измерение2, ..., ИзмерениеN)
Например:
Dim aStudent (5, 10, 30) ' 5-номер школы, 10-номер класса, 30-номер ученика
aStudent(3, 5, 17) = "Иванов"
Для отдельных размеров (измерений) тоже могут задаваться пределы индекса.
- Массивы бывают статические и динамические. Ключевое слово Static тут ни при чём! Статические массивы - это совсем не то, что имелось в виду, когда описывалось время жизни переменной (ключевое слово Static). Здесь подразумевается то, что размер (количество элементов) массива фиксированный (статический). Статический массив определяется так:
Dim SampleArray(150) As String 'статический массив из 150 строк
Теперь немного про динамические массивы. Динамические массивы создаются в 2 этапа. Сначала в начале контейнера - модуля, формы, класса - (по-другому эта область называется General Declarations) определяют массив без указания размера:
Затем внутри процедур с помощью оператора ReDim устанавливают фактический размер массива:
...
ReDim aArray (50,10)
...
Синтаксис оператора ReDim:
ReDim [Preserve] Имя_массива(Границы) [As Тип_данных]
В отличие от оператора Dim, оператор ReDim используется только в процедурах. При этом тип данных указывать не обязательно, особенно если он уже определён оператором Dim. Вы не можете переопределять тип данных массива, если он был заранее определён оператором Dim, за исключением того случая, когда был указан тип Variant (который, как мы знаем, является типом по умолчанию, т. е. если мы не указали тип, или указали тип Variant, эффект одинаковый).
Мы можем при необходимости менять размер массива, но! Нужно помнить, что записанные в массив данные мы при этом потеряем (они инициализируются значением по умолчанию. Каким? Молчит наука...). И тут опять но... Сохранить размерность массива позволяет ключевое слово Preserve:
Dim aArray() As Variant
...
Private Sub Subroutine1()
...
ReDim Preserve aArray (50,15)
...
End Sub
Теперь при изменении размера массива его содержимое сохраняется. Но (которое по счёту?!) следует учитывать, что для многомерных массивов можно менять только последнее измерение.
Область видимости динамического массива бывает либо глобальной (если он был определён с помощью оператора Public), либо контейнерной (если с помощью Dim). Практический пример использования одномерного массива с пользовательским типом данных:
'создаем пользовательский тип данных
Private Type tSklad
Name As String
Summa As Double
Kol As Long
End Type
Dim Store() As tSklad
...
'инициализируем массив
Erase Store
ReDim Store(0) 'получаем массив с одним элементом
...
'пример сканирования всего массива для доступа к его данным
Dim U As Long
Dim L As Long
L = LBound(Store)
U = UBound(Store)
For i = L To U
If (Store(i).Name = sname) Then
...
Exit For
End If
Next i
...
'пример добавления в массив одного элемента с сохранением
' данных существующих элементов массива
U = UBound(Store)
ReDim Preserve Store(U + 1)
Store(U).Name = sname
Store(U).Summa = 0
Store(U).Kol = 0
...
Массивы можно присваивать друг другу (с копированием содержимого), как обычные переменные. Это, безусловно, удобно, однако (опять...) если массивы имеют разные типы данных, размерность, а также если один статический, а другой динамический, то возникает куча тонкостей, цитировать и помнить которые нет сил. Любителей отсылаю к спецлитературе.
6. Типы, задаваемые пользователем (собственные).
Определение общего (Public) собственного типа данных возможно только в секции General Declarations модуля (по-русски - в начале исходного файла, где задаём глобальные переменные). В этом случае этот тип будет доступен во всех процедурах всех форм, модулей и модулей классов. Для определения пользовательского типа в форме или модуле класса следует использовать ключевое слово Private, поскольку объявление общего типа в данной ситуации не допускается. При этом область видимости такого типа будет ограничена тем контейнером, где он был объявлен. Определив собственный тип данных, вы можете использовать его для объявления переменных этого типа (они могут быть, как обычно, локальными, глобальными или переменными контейнера).
[Private|Public] Type Имя_типа
Элем1 [([Размерность])] As Тип
Элем2 [([Размерность])] As Тип
...
End Type
Пример:
(General)(Declarations)(Module)
Type usrType
Num As Long
Name As String
Price As Currency
End Type
...
(General)(Declarations)(Form)
Dim usrTools As usrType
Private Sub Command1_Click()
usrTools.Name = "Отвёртка"
usrTools.Price = 2.95
End Sub
...
7. Константы.
Область видимости может быть та же, что и у переменных (они могут быть локальными, глобальными или контейнера). Глобальная константа определяется с Public и её можно определить только в модуле.
[Public|Private] Const Имя_константы [As Тип] = Значение
8. Замечания по поводу процедур обработки событий:
- имя процедуры обработки событий составляется как "имяэлементауправления_имясобытия". - при удалении элемента управления связанная с ним процедура становится общей. - если создать элемент управления с тем же именем, что и удалённый, то все процедуры событий старого элемента привязываются к вновь созданному. - добавление процедуры обработки события производится так - из выпадающего списка (Object) выбираем имя объекта, и из выпадающего списка (Procedure) выбираем требуемое событие.
9. Процедуры бывают общего назначения (глобальные Public), их можно вызвать из процедур другого контейнера и закрытые (Private, то есть принадлежащие определённому контейнеру - форме, модулю, классу). Закрытые процедуры доступны только изнутри контейнера. Начиная с VBasic 4.0, все процедуры обработки событий формы являются по умолчанию Private, то есть их можно вызвать только изнутри этой формы. Общие процедуры формы или модуля также являются закрытыми, причём они останутся закрытыми даже после того, как вы их объявите как Public. Тем не менее, из процедуры одной формы есть возможность вызвать процедуру другой формы - надо делать вызов в виде имя_формы.имя_процедуры_этой_формы. Внутри модуля полностью работают директивы Public и Private, причём Public указывать не обязательно, то есть все глобальные процедуры и переменные модуля являются по умолчанию общими (доступными из других модулей). Для того, чтобы сделать модуль закрытым для других приложений и проектов, есть выражение
Это выражение делает составные элементы модуля (переменные, процедуры, функции, пользовательские типы данных и проч.), не объявленные как Private, доступными только для модулей проекта, но не для других проектов и приложений. В модуле выражение Option Private Module должно находиться перед самой первой процедурой (в начале модуля).
10. Аргументы.
Могут передаваться по ссылке (ключевое слово ByRef, которое можно опустить, поскольку такой тип передачи используется по умолчанию - не так, как в C и Pascal. Внимание! Не испортите случайно передаваемые переменные, при необходимости применяйте ByVal) или по значению (ключевое слово ByVal). При вызове можно использовать именованные аргументы, например:
Sub S(arg1, arg2, arg3)
...
End Sub
...
S 1, 2, 3 'обычный вызов с неименованными параметрами
Call S (1, 2, 3) ' то же самое
S arg2:=2, arg1:=1, arg3:3 'использование именованных параметров
При использовании именованных аргументов их можно передавать в любом порядке. Если при вызове использовать не все аргументы, то возникает сообщение об ошибке. Однако есть возможность объявить некоторые аргументы как необязательные, для этого перед именем аргумента ставится ключевое слово Optional. После первого необязательного аргумента все остальные тоже должны быть необязательными. Внутри функции можно проверить, был ли передан аргумент с помощью специальной функции IsMissing (см. help).
11. Циклы.
Вроде ничего особенного, за исключением того, что цикл while условие wend считается в VBasic "нестандартным", и для него нет оператора досрочного выхода из цикла Exit.
12. Существует такое понятие, как массив элементов управления (ЭУ), например, кнопок. Чтобы создать массив ЭУ, достаточно скопировать в буфер обмена элемент и потом снова вставить его в форму, оставив его имя тем же. ЭУ в массиве имеют одно и то же имя и обработчики событий, но разное значение свойства Index. Значение этого свойства передаётся в обработчик события, что бывает очень удобно. С помощью операторов Load и Unload во время выполнения можно создавать на форме новые ЭУ. Более широкие возможности по созданию ЭУ runtime предоставляет оператор Add.
13. Для работы с реестром есть функции (установки программ на VBasic сохраняются в HKEY_CURRENT_USER\Software\VB and VBA Program Settings и дублируется в HKEY_USERS\S-1-5-21-1864085942-2410699049-2459454283-1003\Software\VB and VBA Program Settings):
SaveSetting Имя_приложения, Секция, Ключ, Установка
GetSetting Имя_приложения, Секция, Ключ[, Значение_по_умолчанию]
GetAllSettings Имя_приложения, Секция
DeleteSetting Имя_приложения, Секция[, Ключ]
Значение Имя_приложения не имеет значения, но для пользователя было бы удобнее, чтобы оно носило осмысленный характер (для облегчения поиска по реестру).
14. Кроме обычных, очевидных свойств, элементы управления имеют свойства Parent (которое позволяет считывать свойства родительского элемента-контейнера, например, формы) и Container (позволяет менять контейнер для ЭУ). Следующие ЭУ могут служить контейнером для других ЭУ - Form, Frame, Picture, Toolbar.
15. События клавиатуры - Keypress, KeyUp, KeyDown. Обычно событие вызывается для активного элемента управления. Если свойство KeyPreview установлено в true, то событие, связанное с клавиатурой, передаётся сначала форме, а затем текущему элементу управления. Оператор SendKeys позволяет записывать коды нажатий в буфер клавиатуры, тем самым имитируя клавиатурные нажатия.
16. Интересные свойства формы:
ControlBox определяет, отражается или нет системное меню, с помощью которого пользователь может выйти из программы (Alt+F4). Если системное меню удаляется, следует обеспечить пользователю другой способ выхода из программы. MaxButton если false, то кнопка максимизирования в правой части окна удаляется, и из системного меню удаляется пункт Maximize. MinButton... по аналогии ...
17. Для передачи фокуса приложению существует оператор AppActivate. Его первый параметр должен в точности совпадать с заголовком активируемой программы. Следующий пример запускает калькулятор Windows, складывает числа 1 и 2, передаёт результат в буфер обмена и закрывает калькулятор:
Private Sub Command1_Click()
ret = Shell ("calc.exe", vbNormalFocus)
AppActivate "Калькулятор", False
SendKeys "1+2=^C%(F4)", True
Text1.Text = Clipboard.CetText()
End Sub
18. Принцип работы с файлами в VBasic несколько необычен. Он осуществляется в такой последовательности:
- получение свободного дескриптора файла - handle (с помощью FreeFile) - открытие файла путём связывания полученного handle с дисковым именем файла (Open для произвольного доступа, Input, Output и Append для последовательного доступа) - чтение или запись данных (LineInput#, input$, print#, write#, put, Get) - закрытие файла и связанного с ним handle (оператор Close)
19. Для печати используют 2 способа:
- оператор PrintForm (выводит всё содержимое формы без заголовка и рамки) - объект Printer
20. Перехват ошибок времени выполнения (runtime).
Все ошибки периода выполнения разделяются на 2 вида - ожидаемые и неожиданные. Подготовка обработки ожидаемых ошибок осуществляется в 3 этапа:
- подготовка перехвата; - проверка и устранение ошибки; - продолжение выполнения программы.
Подготовка перехвата осуществляется оператором
On Error {Goto label|Resume Next|Goto 0}
При возникновении ошибки внутри блока On Error выполнение программы не прерывается и стандартное сообщение об ошибке не выдаётся, управление при ошибке передаётся на метку label, которая должна находиться в той же процедуре, что и оператор On Error. Обычно блок, начинающийся с метки label, находится в конце процедуры, и перед меткой label стоит оператор Exit (при безошибочном выполнении код после label не выполняется). Если в качестве метки указан 0, то снова включается стандартный режим обработки ошибок.
Второй этап заключается в выявлении типа ошибки. Для этого в VBasic существует объект Err, у которого свойство Number содержит код последней ошибки, а Description - текст её системного описания.
Завершив обработку ошибки, на третьем этапе следует продолжить нормальное выполнение программы. Оператор Resume позволяет продолжить выполнение со строки, вызвавшей ошибку, а Resume Next - со строки, следующей за строкой с ошибкой. Оператор Resume Next можно использовать вместе с оператором On Error, и тогда каждая строка, вызвавшая ошибку, игнорируется:
Однако такая простейшая обработка ошибок не позволяет определить источники ошибки и исправить её.
Типичным примером использования обработчика ошибок может быть обработка нажатия кнопки Cancel элемента диалога CommonDialog. Если установить свойство CommonDialog.CancelError=true, то при щелчке на кнопке Cancel возникает ошибка. Если значение CancelError==false, то различить, какая из кнопок была нажата - Ok или Cancel - будет затруднительно. Пример:
Function OpenFile As String
On Error Goto Cancel
CommonDialog.CancelError = true
CommonDialog.Filter = "Все файлы (*.*)|*.*"
CommonDialog.ShowOpen
OpenFile = CommonDialog.Filename
Exit Function
Cancel:
If Err.Number=cdlCancel Then
OpenFile = ""
MsgBox Err.Description
Exit Function
Else
MsgBox Err.Description
Stop
End If
End Function
Можно генерировать ошибки искусственно, вызвав Err.Raise код_ошибки. Например,
генерирует ошибку деления на ноль.
21. Строки можно объединять (склеивать) оператором + или &.
22. Внимание! Если изменить имя объекта (например, кнопки), то все обработчики событий этого объекта перестают работать, поскольку они привязаны к старому имени. Поправить можно, поменяв соответственно новому имени все названия обработчиков событий объекта.
23. Интересные (необычные) отличия функций от процедур в VBasic:
- у функции аргументы всегда передаются в скобках. - у процедур аргументы могут передаваться без скобок (кто бы мог подумать). Если же нам обязательно захотелось в процедуре засунуть аргументы в скобки, то процедуру нужно вызывать через слово CALL.
24. Кое что из "горячих клавиш".
F2 |
показать браузер объектов (что-то типа мини-справочника по объектам) |
F4 |
показать диспетчер свойств объекта |
Tab, Shift-Tab |
в текстовом редакторе перемещает текст (блок) вправо, влево на позиции табуляции |
Ctrl-I |
Quick Info - отображает синтаксис переменной, процедуры, метода и т. п. |
Shift-F9 |
Quick Watch |
Ctrl-L |
Call Stack - отображает стек вызова процедур. |
Ctrl-F8 |
Run To Cursor |
F8 |
Step Into - шаг с заходом во все вложенные процедуры |
Shift-F8 |
Step Over - трассировка только текущей процедуры (без захода во вложенные). |
Ctrl-Shift-F8 |
Step Out - выполнить текущую процедуру до конца и попасть в точку её вызова. |
F9 |
Toogle BreakPoint |
25. Если вы по недосмотру не установили для Visual Basic 6.0 сервис-пак 3, то не удивляйтесь, что всё глючит не по-детски (например, при попытке скомпилировать простейший проект и получить exe-файл среда разработки неприлично рушилась).
26. OCX означает "OLE Custom Control".
27. В других проектах можно использовать некоторую составную часть другого проекта, например - модуль, форму и т. д. Для этого их просто копируют в рабочую папку проекта и добавляют в проект командой Project\Add... .
28. Очень полезна опция, запрещающая неявное определение переменных. Махом избавляемся от кучи ошибок. Для активизации надо:
- либо указать в начале модуля (это так называемая область General Declarations) строку Option Explicit - либо через Tools\Options... закладка Editor установить галочку Require Variable Declaration.
29. Exit применяют в следующих контекстах:
Exit Do Exit For Exit Function Exit Property Exit Sub
Для выхода из процедуры нельзя использовать GoSub, GoTo, или Return.
30. Для преобразования числа в строку применяют Str, Hex (хорошо подходят для целых чисел) и Format (для чисел с плавающей запятой и многих других типов). Format довольно гибко позволяет форматировать выходные данные. Вот пример обработки функцией Format даты и времени:
'
' Автоматическое генерирование имени файла из текущих даты и времени.
'
Function FileName() As String
Dim currentTimeDate As Variant
Dim iYear, iMonth, iDay As Integer
Dim iHour, iMinute, iSec As Integer
currentTimeDate = Now()
iYear = Year(currentTimeDate)
iMonth = Month(currentTimeDate)
iDay = Day(currentTimeDate)
iHour = Hour(currentTimeDate)
iMinute = Minute(currentTimeDate)
iSec = Second(currentTimeDate)
FileName = Format(currentTimeDate, "yymmdd") _
+ Format(currentTimeDate, "hhmmss") + ".bmp"
End Function
31. Для преобразования строки в число применяют Val, (хорошо подходят для целых чисел) или, в зависимости от типа данных, одну из следующих функций:
CBool(expression)
CByte(expression)
CCur(expression)
CDate(expression)
CDbl(expression)
CDec(expression)
CInt(expression)
CLng(expression)
CSng(expression)
CStr(expression)
CVar(expression)
32. Для чтения текстового файла в VBA (Visual Basic for Application) есть, по крайней мере, 3 способа - использование CreateObject("Scripting.FileSystemObject"), object.OpenAsTextStream([iomode, [format]]) и импорт в таблицу Excel. Пример использования CreateObject("Scripting.FileSystemObject") для считывания файла exclude.txt в динамический массив:
Dim ExceptionsFN As String
Dim Exclusions() As String
'считываем файл исключаемых магазинов
Path = ActiveWorkbook.FullName
Path = Mid(Path, 1, Len(Path) - Len(Dir(Path)))
ExceptionsFN = Path + "exclude.txt"
Dim FS As Object
Dim tf As Object
Dim LineCnt As Integer
Set FS = CreateObject("Scripting.FileSystemObject")
Set tf = FS.OpenTextFile(ExceptionsFN)
LineCnt = 0
Do
If tf.AtEndOfStream Then
Exit Do
End If
ReDim Preserve Exclusions(LineCnt)
Exclusions(LineCnt) = tf.ReadLine
LineCnt = LineCnt + 1
Loop
tf.Close
33. VBA Excel: как обработать в цикле по столбцам и строкам все ячейки выделенной области?
Для доступа к выделению служит объект Selection. Для доступа к столбцам выделения служит дочерний объект Selection.Columns, а к строкам Selection.Rows. И Columns, и Rows имеют свойство Count, которое дает количество столбцов и строк соответственно. Пример заполнения выделенной области символом "A":
Dim xCount, yCount
xCount = Selection.Columns.Count
yCount = Selection.Rows.Count
For x = 1 To xCount
For y = 1 To yCount
Selection.Cells(y, x).Value = "A"
Next
Next
34. VBA Excel: почему при попытке присвоить цвет через Interior.ColorIndex происходит ошибка выполнения 1004 (невозможно установить свойство ColorIndex класса Interior)?
Run-time error '1004': Unable to set the ColorIndex property of the Interior Class
Пример кода, который дает такую ошибку:
Dim clYellow As Long
clYellow = RGB(&HFF, &HFF, &H0)
'На этой строке ошибка Run-time error '1004':
MyRange.Cells(1, 2).Interior.ColorIndex = clYellow
Этот код может нормально работать в Excel 2010, и давать ошибку в Excel 2003, причем нерегулярно. Причина в том, что Microsift поменяло реализацию основного поведения методов VBA, так что это может повлиять на то, как работает код в разных версиях Excel. Если у Вас нет возможности в данной ситуации обновить версию Office, то используйте вместо присвоения свойства ColorIndex свойство Color.
'Теперь проблемы с установкой цвета нет:
MyRange.Cells(1, 2).Interior.Color = clYellow
35. VBA: как открыть диалог выбора файла?
Dim lngCount As Long
With Application.FileDialog(msoFileDialogOpen)
' В свойство InitialFileName можно записать путь, от которого
' стартует диалог, например "c:\temp". В этом примере берется
' путь текущего каталога, где находится файл документа Office.
.InitialFileName = Application.ActiveWorkbook.Path
' Разрешить выбрать несколько файлов сразу:
.AllowMultiSelect = True
' Если это надо, добавим фильтр по расширению файла:
.Filters.Add "Bitmap", "*.bmp; *.gif; *.jpg; *.jpeg", 1
' Отобразить диалог:
.Show
' Отобразить полный путь до каждого выбранного файла
For lngCount = 1 To .SelectedItems.Count
MsgBox .SelectedItems(lngCount)
Next lngCount
End With
[Ссылки]
1. Разработка GUI с помощью Excel.
|