Пользовательский тип данных

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

В Модуль3 наберите следующий код и выполните программу:

Type Персона

Имя As String

ДатаРождения As Date

Возраст As Integer

End Type

Sub ПользовательскийТипПеременной()

Dim User1 AsПерсона

User1.Имя = "Иванов"

User1.ДатаРождения = #2/23/75#

User1.Возраст = Year(Date) - Year(User1.ДатаРождения)

MsgBox User1. Имя & ", возраст " & User1.Возраст & _

" дата рождения " & User1.ДатаРождения

End Sub

Обратите внимание на встроенные функции Date, которая возвращает текущую системную дату и Year, которая возвращает год из указанной даты. Первый раз год возвращается из текущей даты, которую определяет функцияDate, второй раз – из даты рождения (третьего элемента пользовательского типа), которая объявлена как Date.

Задание:

1. В Модуль3 напишите программу Данные_Сотрудника, которая, по запросу пользователя, выводит на экран данные о сотруднике (Ф.И.О., Кафедра, Должность, Возраст). Информация хранится в базе данных Excel C:\St\Институт.xls на листе Кадры и имеет следующую структуру:

1.1. Диалог программы с пользователем осуществляется с помощью встроенной функции InputBox:

§ сначала запрашивается название кафедры:

§ затем Ф.И.О.:

1.2. Выходные данные выводятся с помощью MsgBox в следующем виде:

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

1.4. Предусмотреть обработку событий, связанных с различными состояниями исходной БД: закрыта, открыта, не существует.

Массивы

Массивы VBA и других языков программирования весьма схожи; они незаменимы работе с большими объемами данных. Попросту говоря, массив — это переменная, содержащая несколько значений, или еще проще — пронумерованная группа значений одного и того же типа. Если обычные переменные полезны при работе с одиночными значениями определенного типа, массив пригодится при действиях с набором значений того же типа.

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

Нумерация массивов VBA может начинаться с 0 или 1. Конкретный способ указан в инструкции Option Base в начале модуля. Так, Option Base 0 задает нумерацию всех массивов по умолчанию с 0, a Option Base 1 — с 1. В отсутствие инструкции Option Base нумерация массивов по умолчанию начинается с 0. Но в следующих примерах мы для простоты считаем, что она начинается с 1.

В Модуль4 наберите и протестируйте работу следующих программ:

1. Пример одномерного трехэлементного целочисленного массива (при отсутствии инструкции Option Base 1 этот массив стал бы четырехэлементным):

Option Base 1

Sub ЦелочисленныйМассив()

Dim Vals(3) As Integer

Vals(1) = Int(100 * Rnd())

Vals(2) = Int(100 * Rnd())

Vals(3) = Int(100 * Rnd())

MsgBox "Выиграли лотерейные номера: " & Vals(1) & ", " & _

Vals(2) & ", " & Vals(3)

End Sub

2. Пример одномерного, трехэлементного массива Variant, в котором можно хранить «разношерстные» данные:

Sub МассивVariant()

Dim Data(3) As Variant

Data(1) = "Иванов"

Data(2) = #3/21/1947#

Data(3) = Year(Date) - Year(Data(2))

MsgBox Data(1) & " , возраст " & Data(3) & ", родился " _

& Data(2)

End Sub

3. Пример одномерного двухэлементного массива, в котором определены нижняя и верхняя границы:

Sub НомерПервогоЭлемента()

Dim Data4(4 To 5) As Integer

Data4(4) = lnt(100 * Rnd())

Data4(5) = Int(100 * Rnd())

MsgBox "Выиграли лотерейные номера: " & Data4(4) & ", " _

& Data4(5)

End Sub

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

Sub ДинамическийМассив()

Dim Data5() As Variant

DimКолЗаписейAs Integer

'Первое объявление массива Data5 как одномерного _

двухэлементного массива:

ReDim Data5(2)

Data5(1) = Int(100 * Rnd())

Data5(2) = Int(100 * Rnd())

MsgBox " Выиграли лотерейные номера: " & Data5(1) & ", " _ & Data5(2)

КолЗаписей = Int(InputBox("Введите количество записей:", _

"Кадры"))

'Второе объявление массива Data5 как двухмерного массива:

ReDim Data5(КолЗаписей,3)

Data5(1,1) = "Иванов"

Data5(1,2) = #3/21/1947#

Data5(1,3) = Year(Date)-Year(Data5(1,2))

 

MsgBox "Определено " & КолЗаписей & " записей!"

MsgBox "1-я запись: " & Data5(1,1) & ", возраст: " & _

Data5(1,3) & ", родился: " & Data5(1,2)

End Sub

5. Пример динамического массива с сохранением данных.

Пусть имеется база данных (БД) Excel с полным именем C:\St\Институт.xls, в которой на листе Кадры хранятся сведения о сотрудниках института (Рис. 4‑1). Книга имеет один лист. Количество записей в БД может меняться.

Рис. 4‑1. Фрагмент БД

Требуется: вывести в новую книгу данные о доцентах (Кафедра, Ф.И.О., Разряд), работающих в институте.

1) Sub МассивДоценты_СохранениеДанных()
2) Dim Сотрудники() As String
3) DimКолДоцентовAs Integer
4) DimНомерСтрокиAs Integer
5) КолДоцентов = 0
6) НомерСтроки = 3
7) ' Данные о доцентах считываем в массив:
8) While Cells(НомерСтроки,2).Value <> ""
9) If Cells(НомерСтроки,3).Value = "Доцент" Then
10) КолДоцентов = КолДоцентов + 1
11) ReDim Preserve Сотрудники(3,КолДоцентов)
12) Сотрудники(1,КолДоцентов) = Cells(НомерСтроки,1).Value
13) Сотрудники(2,КолДоцентов) = Cells(НомерСтроки,2).Value
14) Сотрудники(3,КолДоцентов) = Cells(НомерСтроки,3).Value
15) End If
16) НомерСтроки = НомерСтроки + 1
17) Wend
18) ' Добавляем новую книгу и переписываем в неё данные _ из массива:
19) WorkBooks.Add
20) For I = 1 To КолДоцентов
21) Cells(I + 2,1).Value = Сотрудники(1, I)
22) Cells(I + 2,2).Value = Сотрудники(2, I)
23) Cells(I + 2,3).Value = Сотрудники(3, I)
24) Next I
25) Range("A1").Select
26) MsgBox "Операция завершена!", vbInformation
27) End Sub

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

ОБРАТИТЕ ВНИМАНИЕ: при использовании ключевого слова Preserve, изменяемая размерность должна быть последней. Это значит, что команда:

ReDim Preserve Сотрудники(КолДоцентов, 3)

недопустима!

Замечание: данный код корректен, если находится в книге Институт.xls. Для того чтобы отделить программу от данных, модернизируйте код, как показано ниже, и протестируйте его (код поместите в Модуль4).

1) Sub МассивДоценты_СохранениеДанных_Мод()
2) Dim Сотрудники() As String
3) DimКолДоцентовAs Integer
4) DimНомерСтрокиAs Integer
5) Dimflag As Integer
6) ' Проверяем существование книги C:\St\Институт.xls
7) If Dir("C:\St\Институт.xls") = "" Then
8) MsgBox "Файл C:\St\Институт.xls не найден!", _
9) vbInformation
10) Exit Sub
11) End If
12) ' Проверяем, открыта ли книга Институт.xls:
13) For I = 1 To Workbooks.Count
14) If Workbooks(I).Name = "Институт.xls" Then
15) Workbooks(I).Activate
16) flag = 1
17) Exit For
18) End If
19) NextI
20) If flag = 0 ThenWorkbooks.Open Filename:= _
21) "C:\St\Институт.xls"
22) ' Проверяем, существует ли лист Кадры:
23) flag = 0
24) For I = 1 To Worksheets.Count
25) If Worksheets(I).Name = "Кадры" Then
26) flag = 1
27) Exit For
28) End If
29) Next I
30) If flag = 1 Then
31) Sheets("Кадры").Select
32) Else
33) MsgBox "Лист Кадры не найден!", vbInformation
34) Exit Sub
35) End If
36) Sheets("Кадры").Select
37) КолДоцентов = 0
38) НомерСтроки = 3
39) ' Данные о доцентах считываем в массив:
40) While Cells(НомерСтроки,2).Value <> ""
41) If Cells(НомерСтроки,3).Value = "Доцент" Then
42) КолДоцентов = КолДоцентов + 1
43) ReDim Preserve Сотрудники(3,КолДоцентов)
44) Сотрудники(1,КолДоцентов) = _
45) Cells(НомерСтроки,1).Value
46) Сотрудники(2,КолДоцентов) = _
47) Cells(НомерСтроки,2).Value
48) Сотрудники(3,КолДоцентов) = _
49) Cells(НомерСтроки,3).Value
50) End If
51) НомерСтроки = НомерСтроки + 1
52) Wend
53) ' Добавляем новую книгу и переписываем в неё данные _ из массива:
54) WorkBooks.Add
55) For I = 1 To КолДоцентов
56) Cells(I + 2,1).Value = Сотрудники(1, I)
57) Cells(I + 2,2).Value = Сотрудники(2, I)
58) Cells(I + 2,3).Value = Сотрудники(3, I)
59) Next I
60) ' Вызываем программу оформления таблицы, передавая в неё _
61) количество форматируемых строк и столбцов:
62) ' Call ОформлениеТаблицы(КолДоцентов, 3)
63) Range("A1").Select
64) MsgBox "Операция завершена!",vbInformation
65) End Sub

Замечания:

1. Обратите внимание на строки с 24 по 29 – здесь происходит проверка существования листа Кадры. В строка 30 – 35 происходит обработка событий «лист существует/не существует».

2. Строка 62 – вызов подпрограммы оформления таблицы – пока занесена в комментарий (см. задание на стр. 48).

Функция Erase

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

SubФункцияErase()

DimData7(2) As Integer

Data7(1) = Int(100 * Rnd())

Data7(2) = Int(100 * Rnd())

MsgBox "Лотерейные номера: " & Data7(1) & ", " & Data7(2)

EraseData7

MsgBox "Лотерейные номера: " & Data7(1) & ", " & Data7(2)

End Sub

Здесь элементам массива присваиваются значения и выводятся в информационном окне. Затем для удаления содержимого массива вызывается функция Erase. Второй вызов функции MsgBox свидетельствует, что во всех элементах Data7 содержатся нули. В следующем примере показан вызов функции Erase для динамического массива.

Sub EraseДинамичМассив()

Dim Data8() As Integer

ReDim Data8(2)

Data8(1) = Int(100 * Rnd())

Data8(2) = Int(100 * Rnd())

MsgBox "Лотерейные номера: " & Data8(1) & ", " & Data8(2)

EraseData8

MsgBox "Лотерейные номера: " & Data8(1) & ", " & Data8(2)

End Sub

Выполнение программы EraseДинамичМассив приводит к ошибке в строке 8, потому что функция Erase в предыдущей строке освободила всю память, занятую динамическим массивом. После этого VBA уже не может работать с индексами Data8, и обращение к этому массиву вызывает ошибку.

Функции LBound и UBound

Эти функции помогут вам определить верхнюю и нижнюю границы индексов массива:

Sub Lbound_UBound()

Dim Data10(4 To 15) As Integer

MsgBox "Нижняя граница массива" & LBound(Datal0) & "."

MsgBox "Верхняя граница массива " & UBound(Datal0) & "."

End Sub

Замечание:

Применение этих функций к обычной переменной или динамическому массиву, не описанному инструкцией ReDim, вызывает ошибку при выполнении программы. Чтобы избежать этого, перед вызовом функций LBound и UBound проверьте переменную функцией IsArray(см. курс лекций).

5. Подпрограммы. Функции. Область видимости
переменных, подпрограмм и функций.

Вызов подпрограммы

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

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

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

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

В Модуль5 наберите и протестируйте работу следующих программ:

1. Пример вызова подпрограммы DisplayMessage без передачи данных:

Sub ВызовПодпрограммы()

Dim Range1 As Range

Set Range1 = Worksheets(1).Range("A1")

Range1.Value = 500

DisplayMessage

End Sub

Sub DisplayMessage()

MsgBox "Данные введены!"

End Sub

2. Пример вызова подпрограммы, которая хранится в другом модуле (в Модуль3):

Sub ВызовПодпрограммы()

Dim Range1 As Range

Set Range1 = Worksheets(1).Range("A1")

Range1.Value = 500

Модуль3.DisplayMessage

End Sub

Sub DisplayMessage()

MsgBox "Данные введены!"

End Sub

Следующий пример мы выполним вместе. Модернизируем программу
СлучайныеЧисла следующим образом: оформление заполненных ячеек «поручим» подпрограмме Оформление. Получим следующий код:

19) Sub СлучайныеЧисла()
20) Dim СлучЧисло As Integer
21) Dim Листов As String
22) Листов = Application.SheetsInNewWorkbook
23) Application.SheetsInNewWorkbook = 1
24) Workbooks.Add
25) ActiveSheet.Name = "Случ. числа"
26) For i = 1 To 10
27) СлучЧисло = Int(100 * Rnd())
28) Cells(i, 1).Value = СлучЧисло
29) Next i
30) 'Вызов подпрограммы Оформление:
31) Оформление
32) Sheets.Add After:=Worksheets(Worksheets.Count)
33) Worksheets(1).Select
34) Range("A1").Select
35) Application.SheetsInNewWorkbook = Листов
36) ActiveWorkbook.SaveAs Filename:="C:\St\Случайные числа.xls"
37) MsgBox "Случайные числа разыграны!"
38) End Sub

Сравните полученный код с программой СлучайныеЧислаОформление – он гораздо легче для восприятия.

Замечание:

При вызове подпрограммы можно использовать другой синтаксис. Так, 31-ю строку можно записать в виде:

Call Оформление