Что такое модуль класса в excel

Хитрости »

1 Август 2013              63765 просмотров


Работа с модулями классов

Многие наверняка слышали про модули классов, но не все их используют. На самом деле довольно многие программирующие на VBA за все время программирования прекрасно обходятся без применения модулей классов. Т.к. VBA не является языком объектно-ориентированного программирования(ООП) в строгом смысле слова, то пользовательские классы здесь не обязательны и как следствие не так уж и часто используются при разработке. Это не значит, что VBA не содержит модулей классов: модули книги, листов, пользовательские формы — все это модули классов. Многие, кстати, используют их даже не зная того, что используют именно модули классов. Т.к. модуль листа, книги и формы — это модуль класса, то почти каждый, кто работал с формой работал с модулем класса. В чем их большая польза — с их помощью можно отслеживать различные события объектов. Для форм это события самой формы или любого её элемента — например CommandButton_Click или TextBox_Change. Но мы сейчас рассмотрим лишь тот тип модулей, который в VBA обычно называют модулем класса — Class Module.

Модуль класса(Class Module) – это модуль, содержащий программные коды, которые реализуют работу пользовательских классов. В подавляющем большинстве случаев создается специально для отслеживания событий различных объектов. Создается так же, как и любой другой объект проекта: в окне проводника объектов щелкаем правой кнопкой мыши на нужном проекте-InsertClass Module
Вставить модуль класса
Но прежде чем создать модуль, необходимо понять, что мы будем в нем хранить и для чего он нам. Возьмем для примера самую распространенную проблему: на форме создано несколько ТекстБоксов и необходимо отследить событие ввода данных в эти ТекстБоксы. Обычно делается все просто — для каждого ТекстБокса прописывается отслеживание события:

Private Sub TextBox1_Change()
    MsgBox "Изменено значение TextBox1"
End Sub
Private Sub TextBox2_Change()
    MsgBox "Изменено значение TextBox2"
End Sub
Private Sub TextBox3_Change()
    MsgBox "Изменено значение TextBox3"
End Sub
'и т.д.

С одной стороны — все верно. А с другой: что если таких текстбоксов у нас не 3, а 43? Не очень удобно для каждого событие прописывать. Да и читабельность такой «портянки» кода тоже значительно падает.
Или другая ситуация — необходимо «на ходу» создать ТекстБоксы на форме и в дальнейшем отслеживать их события. Как тут быть? Ведь раз ТексБоксов еще нет — то и события в форме для них не создать. Создание для них кодов обработки событий заранее ничего не даст — они не будут связаны с самими объектами, поэтому и пытаться даже не стоит. Почему так — при создании элемента вручную VBE делает за нас всю грязную работу — он сам ассоциирует созданный объект с событиями, предназначенные для него заранее. Если же создать объект программно — то часть грязной работы придется делать самим. И создание модуля класса, с описанием в нем объекта ТекстБокс и его событий, как раз очень даже подойдет.
Рассмотрим сразу оба случая. Что нам для этого потребуется:

  1. для начала создать модуль класса с именем clsmTxtBxes(InsertClass Module)
  2. создать стандартный модуль с именем mMain(InsertModule)
  3. ну и сама форма тоже не лишняя(InsertUserForm). У меня форма называется frmTest.
  4. очень желательно наличие у вас опыта написания хотя бы простейших процедур. Иначе может показаться все очень сложным и непонятным.

Чтобы было проще вникать советую скачать файл с готовыми кодами:

  Tips_Macro_UseClassModules.xls (63,5 KiB, 5 960 скачиваний)

Для начала создадим на нашей форме frmTest 4 ТекстБокса, не меняя их имена(по умолчанию они будут TextBox1, TextBox2, TextBox3, TextBox4). Это для того, чтобы понять как применить модули класса к уже созданным ранее на форме элементам.
Далее в стандартный модуль mMain поместим следующий код:

Option Explicit
Public aoTxtBxes(1 To 8) As New clsmTxtBxes 
Sub Show_Form()
    frmTest.Show
End Sub

aoTxtBxes — массив, который будет содержать до 8 ТекстБоксов. Объявляется как Public (чтобы был доступен из любого модуля проекта. Подробнее в статье: Что такое переменная и как правильно её объявить?). Обращаю внимание, что данный массив объявлен как созданный нами модуль класса — As clsmTxtBxes. Это обязательное условие. Если у вас модуль класса называется ClassModule1, то и объявлять aoTxtBxes следует соответственно:

Public aoTxtBxes(1 To 8) As New ClassModule1

но я не приветствую подобный подход, т.к. имя ClassModule1 ни о чем нам не говорит, в то время как clsmTxtBxes сразу дает понять, что там мы обрабатываем ТекстБоксы. Хотя это дело вкуса. Если в одном модуле класса собраны различные событийные процедуры для разных типов(TextBox, ComboBox, ListBox и т.д.) — то конечно, имя лучше дать более общее.
Теперь в созданный модуль класса clsmTxtBxes запишем создание объекта и код, который хотим применить для всех наших ТекстБоксов:

Option Explicit
Public WithEvents oTxtBx As MSForms.TextBox    
'событие изменения текста в TextBox-ах
Private Sub oTxtBx_Change()
    MsgBox "Вы изменили значение " & oTxtBx.Name, vbInformation, "Информационное окно"
End Sub

Public WithEvents oTxtBx As MSForms.TextBox — создаем объект типа ТекстБокс с отслеживанием его событий. Идентификатором объекта с отслеживанием событий служит оператор WithEvents (может применяться только в модулях классов).
Если необходимо отследить изменения не TextBox, а ComboBox, то соответственно объявляем объект нужного типа:
Public WithEvents oCmbBx As MSForms.ComboBox
Сами события для контролов не берутся из головы и не пишутся вручную — они уже есть и следует использовать именно те, которые доступны. Чтобы для конкретного элемента создать событие, необходимо перейти в модуль класса, вверху в левой части выбрать из списка нужный объект(в нашем случае это oTxtBx) и после этого в правом списке выбрать событие(в этом списке перечисляются все процедуры, доступные для выбранного объекта):
Создание события
Для выбранного события в модуле будет автоматически создана новая процедура.
Процедуры, события для которых уже созданы, выделяются в списке жирным шрифтом и при выборе их из списка происходит переход в процедуру выбранного события.

Завершающий этап — создаем код в модуле формы frmTest, который создаст недостающие ТекстБоксы и свяжет их и ранее созданные с модулем класса:

Option Explicit
 
Private Sub UserForm_Initialize()
    Dim i As Integer
    'Присваиваем последовательно значениям массива aoTxtBxes значения объектов, существующих на форме
    For i = 1 To 4
        Set aoTxtBxes(i).oTxtBx = Me.Controls("TextBox" & i)
    Next i
    'создаем 4 своих TrxtBox-а помимо имеющихся на форме и так же заносим в массив aoTxtBxes
    For i = 5 To 8
        Set aoTxtBxes(i).oTxtBx = Me.Controls.Add("Forms.TextBox.1", "TextBox" & i)
        'задаем позицию нового TextBox
        aoTxtBxes(i).oTxtBx.Left = 100 
        aoTxtBxes(i).oTxtBx.Top = Me.Controls("TextBox" & i - 4).Top
    Next i
End Sub

Кратко описать, что делает эта процедура, можно так:

  1. при запуске формы в массив aoTxtBxes запоминаются сначала те ТекстБоксы, которые мы предусмотрительно заранее создали на форме
  2. затем создаются еще 4 новых ТекстБокса, которые также записываются в массив aoTxtBxes
  3. Т.к. массив aoTxtBxes у нас является новым экземпляром класса, то обращаться к его содержимому мы можем только по законам работы с классами, т.е. только к тем объектам и методам, которые в классе прописаны. А у нас там пока только один объект прописан — oTxtBx(Public WithEvents oTxtBx As MSForms.TextBox). Его и используем. Ничего другого использовать VBE нам и не позволит
  4. т.к. класс мы создали, событие объекта прописали, объектам значения ТекстБоксов присвоили — остается только наслаждаться. Теперь любое изменение в любом из ТекстБоксов будет обработано и появится сообщение — «Вы изменили значение » + имя ТекстБокса

Если необходимо больше ТекстБоксов обработать — увеличиваем верхнюю границу массива aoTxtBxes(если хотим вместить 20 текстбоксов — Public aoTxtBxes(1 To 20) As New clsmTxtBxes). Если заранее неизвестно количество — либо задаем с запасом, либо объявляем aoTxtBxes как динамический массив(Public aoTxtBxes() As New clsmTxtBxes), а границы определяем в процессе(посредством ReDim Preserve). Но это уже совершенно другая тема.


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

Me.Controls.Add("Forms.TextBox.1", "TextBox" & i)

и соответственно изменить/добавить тип переменной в модуле класса:

Public WithEvents oCmbBx As MSForms.ComboBox

Всего для создания доступно 11 встроенных типов контролов:
ComboBox — MSForms.ComboBox
CheckBox — MSForms.CheckBox
CommandButton — MSForms.CommandButton
Frame — MSForms.Frame
Image — MSForms.Image
Label — MSForms.Label
ListBox — MSForms.ListBox
MultiPage — MSForms.MultiPage
SpinButton — MSForms.SpinButton
TabStrip — MSForms.TabStrip
ToggleButton — MSForms.ToggleButton

И небольшая ложка дегтя: из модулей классов доступны не все события. Например, для TextBox-ов нет события Exit. Это порой расстраивает. И никак это не исправить — нет его и все…

Скачать пример:

  Tips_Macro_UseClassModules.xls (63,5 KiB, 5 960 скачиваний)

Также см.:
Что такое модуль? Какие бывают модули?
Что такое переменная и как правильно её объявить?
Variable not defined или что такое Option Explicit и зачем оно нужно?


Статья помогла? Поделись ссылкой с друзьями!

  Плейлист   Видеоуроки


Поиск по меткам



Access
apple watch
Multex
Power Query и Power BI
VBA управление кодами
Бесплатные надстройки
Дата и время
Записки
ИП
Надстройки
Печать
Политика Конфиденциальности
Почта
Программы
Работа с приложениями
Разработка приложений
Росстат
Тренинги и вебинары
Финансовые
Форматирование
Функции Excel
акции MulTEx
ссылки
статистика

На чтение 24 мин. Просмотров 22.7k.

VBA Class Modules

Председатель Мао

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

Содержание

  1. Краткое руководство по VBA Class Module
  2. Введение
  3. Почему мы используем объекты
  4. Преимущества использования объектов
  5. Недостатки использования объектов
  6. Создание простого Class Module
  7. Class Module VBA против обычных модулей VBA
  8. Части Class Module
  9. События Class Module
  10. Class Module. Пример 1
  11. Class Module. Пример 2
  12. Заключение

Краткое руководство по VBA Class Module

Пункт Пояснение
Class Module Позволяет пользователю
создавать свои собственные
объекты.
Метод Открытая функция или
подпрограмма в Class Module .
Переменная Переменная, объявленная в Class Module.
Свойство Специальная функция /
подпрограммы, которые ведут
себя как переменные при
использовании
Типы свойств Get, Let и Set.
Событие — инициализация Sub, который автоматически
запускается при создании
объекта Class Module.
Событие — завершение Sub, который автоматически
запускается при удалении
объекта Class Module.
Объявление и создание —
статический
Dim o As New Class1
Объявление и создание —
динамический
Dim o As Class1 
Set o = New Class1 
Вызов подпрограммы Class
Module
o.WriteValues Total
Вызов функции Class Module Amount = o.Calculate()
Использование свойства Class
Module
o.Amount = 1
Total = o.Amount

Введение

Class Module VBA позволяют пользователю создавать свои собственные объекты. Если вы не знакомы с объектами, я настоятельно рекомендую вам сначала ознакомиться с моей статьей Все об объектах VBA Objects.

В таких языках, как C # и Java, классы используются для создания объектов. Class Module являются VBA-эквивалентом этих классов. Основное отличие состоит в том, что Class Module VBA имеют очень ограниченный тип наследования * по сравнению с классами на других языках. В VBA наследование работает аналогично интерфейсам ** в C # Java.

В VBA у нас есть встроенные объекты, такие как Collection, Workbook, Worksheet и так далее. Целью Class Module VBA является предоставление нам возможности создавать собственные объекты.

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

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

Почему мы используем объекты

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

Идея состоит в том, что код каждого объекта является автономным. Он полностью независим от любого другого кода в нашем приложении.

Это похоже на то, как все строится с использованием кирпичей Lego. Существует много различных типов компонентов Lego. Например, блок, руль и лазер — это разные вещи. Они ведут себя совершенно независимо друг от друга. Но мы можем соединить их вместе, чтобы создать здание, транспортное средство, космическую станцию ​​и так далее.

Если вам все еще неясно, не волнуйтесь. В остальной части этой статьи мы разберем все это на простые термины.

Преимущества использования объектов

Рассматривая части нашего кода как блоки, мы получаем много больших преимуществ.

  1. Это позволяет нам создавать приложения по одному блоку за раз.
  2. Намного проще протестировать отдельные части приложения.
  3. Обновление кода не вызовет проблем в других частях приложения.
  4. Легко добавлять объекты между приложениями.

VBA Class Modules

Недостатки использования объектов

У большинства вещей в жизни есть свои плюсы и минусы. Использование Class Module VBA ничем не отличается. Ниже приведены недостатки использования Class Module для создания объектов.

  1. Первоначально для создания приложений требуется больше времени *.
  2. Не всегда легко четко определить, что такое объект.
  3. Люди, плохо знакомые с классами и предметами, могут сначала найти их трудными для понимания.

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

Создание простого Class Module

Давайте рассмотрим очень простой пример создания Class Module и использования его в нашем коде.

Чтобы создать Class Module, мы щелкаем правой кнопкой мыши в окне Project и затем выбираем Insert и Class Module.

VBA Class Module

Наш новый класс называется Class1. Мы можем изменить имя в окне свойств, как показано на следующем скриншоте.

VBA Application

Давайте изменим имя модуля класса на clsCustomer. Затем мы добавим переменную в Class Module следующим образом.

VBA Class Module

Теперь мы можем использовать этот Class Module в любом модуле (стандартном или классе) в нашей рабочей книге. Например:

' Создать объект из Class Module
Dim oCustomer As New clsCustomer

' Установите имя клиента
oCustomer.Name = "Иван"

' Напечатайте имя в Immediate Window(Ctrl + G)
Debug.Print oCustomer.Name

Class Module против Объектов

Люди, которые плохо знакомы с использованием классов и модулей классов VBA, часто путаются между тем, что такое класс и что является объектом.

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

Это похоже на работу Class Module и объектов.

Class Module можно рассматривать как дизайн.

Объект можно рассматривать как элемент, созданный из дизайна.

Ключевое слово New в VBA — это то, что мы используем для создания объекта из Class Module. Например:

' Создание объектов с использованием New
Dim oItem As New Class1
Dim oCustomer1 As New clsCustomer
Dim coll As New Collection

Примечание. Мы не используем New для таких элементов, как Workbooks и Worksheets. См. Когда New не требуется для получения дополнительной информации.

Class Module VBA против обычных модулей VBA

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

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

Разница 1 — Как используются модули

Если вы хотите использовать подпрограмму / функцию и т.д. Из
Class Module, вы должны сначала создать объект.

Например, представьте, что у нас есть два идентичных Sub PrintCustomer. Один находится в Class Module, а другой — в обычном модуле…

' CLASS MODULE Код - clsCustomer
Public Sub PrintCustomer()
    Debug.Print "Пример вывода"
End Sub
' Код обычного модуля 
Public Sub PrintCustomer()
    Debug.Print "Пример вывода"
End Sub

Вы заметите, что коды абсолютно одинаковые.

Чтобы использовать подпрограмму PrintCustomer из Class Module, вы должны сначала создать объект этого типа

' Другой модуль
Sub UseCustomer()

    Dim oCust As New clsCustomer
    oCust.PrintCustomer

End Sub

Чтобы использовать PrintCustomer из обычного модуля, вы можете вызвать его напрямую

' Другой модуль
Sub UseCustomer()

    PrintCustomer

End Sub

Разница 2 — Количество копий

Когда вы создаете переменную в обычном модуле, существует только одна ее копия. Для Class Module существует одна копия переменной для каждого создаваемого вами объекта.

Например, представьте, что мы создаем переменную StudentName как в классе, так и в обычном модуле.

' Обычный модуль
Public StudentName As String
' CLASS MODULE
Public StudentName As String

Для обычной переменной модуля в нашем приложении будет только одна копия этой переменной.

Для Class Module новая копия переменной StudentName создается каждый раз, когда создается новый объект.

Dim student1 As New clsStudent
Dim student2 As New clsStudent

student1.StudentName = "Петр"
student2.StudentName = "Василий"

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

Части Class Module

В Class Module есть четыре разных предмета. Это:

  1. Методы — функции / подводные лодки.
  2. Переменные-члены — переменные.
  3. Свойства — типы функций / подпрограмм, которые ведут себя как переменные.
  4. События — подводные лодки, которые запускаются событием

Вы можете видеть, что они все или функции, подпрограммы или переменные.

Давайте кратко рассмотрим некоторые примеры, прежде чем разбираться с ними по очереди.

' CLASS MODULE Код

' Переменная
Private dBalance As Double

' Свойства
Property Get Balance() As Double
    Balance = dBalance
End Property

Property Let Balance(dValue As Double)
    dBalance = dValue 
End Property

' Событие - срабатывает при создании класса
Private Sub Class_Initialize()
    dBalance = 100
End Sub

' Методы
Public Sub Withdraw(dAmount As Double)
    dBalance = dBalance - dAmount
End Sub

Public Sub Deposit(dAmount As Double)
    dBalance = dBalance + dAmount
End Sub

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

Методы Class Module

Методы относятся к процедурам класса. В VBA есть процедуры и функции. Как и переменные-члены, они могут быть Public или Private.

Давайте посмотрим на пример:

' CLASS MODULE Код

' Имя класса: clsSimple

' Публичные процедуры могут быть вызваны извне объекта
Public Sub PrintText(sText As String)
    Debug.Print sText
End Sub

Public Function Calculate(dAmount As Double) As Double
    Calculate = dAmount - GetDeduction
End Function

' частные процедуры могут быть вызваны только из Class Module
Private Function GetDeduction() As Double
    GetDeduction = 2.78
End Function

Мы можем использовать Class Module clsSimple следующим образом

Sub ClassMembers()
    
    Dim oSimple As New clsSimple
    
    oSimple.PrintText "Hello"
     
    Dim dTotal As Double
    dTotal = oSimple.Calculate(22.44)
     
    Debug.Print dTotal

End Sub

Переменные-члены Class Module

Переменная-член очень похожа на обычную переменную, которую мы используем в VBA. Разница в том, что мы используем Public или Private вместо Dim.

' CLASS MODULE Код

Private Balance As Double
Public AccountID As String

Примечание: Dim и Private делают одно и то же, но соглашение заключается в том, чтобы использовать Dim в sub / functions и использовать Private за пределами sub / functions.

Ключевое слово Public означает, что переменная может быть доступна вне Class Module. Например:

Dim oAccount As New clsAccount

' Действительный - AccountID открыт
oAccount.AccountID = "499789"

' Ошибка - Баланс является частным
oAccount.Balance = 678.90

В приведенном выше примере мы не можем получить доступ к Балансу, потому что он объявлен, как Частный. Мы можем использовать только приватную переменную внутри Class Module. Мы можем использовать функцию / подпрограмму в Class Module, например:

' CLASS MODULE Код
Private Balance As Double

Public Sub SetBalance()
    Balance = 100
    Debug.Print Balance
End Sub

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

Чтобы пользователь не общался напрямую с нашими переменными-членами, мы используем Свойства.

Свойства Class Module

  1. Get — возвращает объект или значение из класса
  2. Let — устанавливает значение в классе
  3. Set — устанавливает объект в классе

Формат свойств VBA

Обычный формат для свойств выглядит следующим образом:

Public Property Get () As Type
End Property

Public Property Let (varname As Type )
End Property

Public Property Set (varname As Type )
End Property

Мы уже видели, что свойство это просто тип sub. Назначение свойства — позволить вызывающей стороне получать и устанавливать значения.

Почему мы используем свойства

Почему мы не можем просто сделать переменные общедоступными и использовать их напрямую?

Давайте объясним с некоторыми примерами. Представьте, что у нас есть класс, который ведет список стран. Мы могли бы сохранить список в виде массива:

' Использовать массив для хранения стран
Public arrCountries As Variant

' Установить размер массива при инициализации класса
Private Sub Class_Initialize()
    ReDim arrCountries(1 To 1000)
End Sub

Когда пользователь хочет получить количество стран в списке, он может сделать это:

' Код обычного модуля
Dim oCountry As New clsCountry

' Получить количество элементов
NumCountries = UBound(oCountry.arrCountries) + 1

С приведенным выше кодом есть две основные проблемы

  1. Чтобы узнать количество стран, вам необходимо знать, как хранится список, например, Массив.
  2. Если мы изменим массив на коллекцию, нам нужно будет изменить весь код, который напрямую ссылается на массив

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

' CLASS MODULE Код - clsCountryList
' Массив
Private arrCountries() As String

Public Function Count() As Long
    Count = UBound(arrCountries) + 1
End Function

Затем мы используем это так

' Код модуля
Dim oCountries As New clsCountries

Debug.Print "Количество стран " & oCountries.Count

Этот код решает две проблемы, которые мы перечислили выше. Мы можем изменить наш массив на коллекцию, и код вызывающего абонента все равно будет работать. Например:

' CLASS MODULE Код
' Коллекция
Private collCountries() As Collection

Public Function Count() As Long
    Count = collCountries.Count
End Function

Вызывающий не замечает, как хранятся страны. Все, что нужно знать вызывающему — это то, что функция Count будет возвращать количество стран.

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

Использование свойства вместо Function/Sub

Вместо создания функции Count мы можем создать свойство Count. Как вы можете видеть ниже, они очень похожи:

' Замени это 
Public Function Count() As Long
    Count = UBound(arrCountries) + 1
End Function

' На это
Property Get Count() As Long
    Count = UBound(arrCountries) + 1
End Function

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

' CLASS MODULE Код - clsAccount
Private dTotalCost As Double

Property Get TotalCost() As Long
     TotalCost= dTotalCost 
End Property

Property Let TotalCost(dValue As Long) 
     dTotalCost = dValue 
End Property

Использование Let позволяет нам рассматривать свойство, как переменную. Таким образом, мы можем сделать это:

Второе отличие состоит в том, что использование Let и Get позволяет нам использовать одно и то же имя при обращении к свойству Get или Let. Таким образом, мы можем использовать свойство, как переменную. Это цель использования свойств над подпрограммой и функцией.

oAccount.TotalCost = 6
dValue = oAccount.TotalCost 

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

oAccount.SetTotalCost 6
dValue = oAccount.GetTotalCost

Вы также можете видеть, что когда мы использовали Let, мы можем присвоить значение, как переменную. Когда мы используем SetTotalCost, мы должны были передать его в качестве параметра.

О Свойствах в двух словах

  1. Свойство скрывает детали реализации от вызывающей стороны.
  2. Свойство позволяет нам обеспечивать то же поведение, что и переменная.

Типы свойств VBA

Есть три типа свойств. Мы уже видели Get и Let. Но мы еще не рассмотрели Set.

Set похож на Let, но он используется для объекта (подробнее об этом см. Назначение объектов VBA).

Первоначально в Visual Basic ключевое слово Let использовалось для назначения переменной. На самом деле, мы можем использовать его, как захотим.

' Эти строки эквивалентны
Let a = 7
a = 7

Поэтому мы используем Let, чтобы присвоить значение переменной, и мы используем Set, чтобы назначить объект переменной объекта.

' Используем Let
Dim a As Long
Let a = 7

' Используем Set
Dim coll1 As Collection, coll2 As Collection
Set coll1 = New Collection
Set coll2 = coll1
  • Let используется для присвоения значения базовому типу переменной.
  • Set используется для назначения объекта переменной объекта

В следующем примере мы используем свойства Get и Let для строковой переменной

' CLASS MODULE Код

' Свойства SET/LET для переменной
Private m_sName As String

' свойства Get/Let
Property Get Name() As String
    Name = m_sName
End Property

Property Let Name(sName As String)
    m_sName = sName
End Property

Затем мы можем использовать свойства Name так:

Sub TestLetSet()

    Dim sName As String    
    Dim coll As New Collection
    Dim oCurrency As New clsCurrency
    
    ' Свойство Let 
    oCurrency.Name = "USD"
    
    ' Свойство Get 
    sName = oCurrency.Name

End Sub

В следующем примере мы используем свойства Get и Set для переменной объекта

' CLASS MODULE Код

Private m_collPrices As Collection

' Свойства Get/Set 
Property Get Prices() As Collection
    Set Prices = m_collPrices 
End Property

Property Set Prices(collPrices As Collection)
    Set m_collPrices = collPrices
End Property

Затем мы можем использовать свойства так:

Sub TestLetSet()
    
    Dim coll1 As New Collection
    Dim oCurrency As New clsCurrency
    
    ' Свойство Set 
    Set oCurrency.Prices = coll1

    ' Свойство Get 
    Dim coll2 As Collection 
    Set Coll2 = oCurrency.Prices
    
End Sub

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

События Class Module

Class Module имеет два события:

  1. Инициализировать — происходит при создании нового объекта класса.
  2. Завершить — происходит, когда объект класса удален.

В объектно-ориентированных языках, таких как C ++, эти события называются Конструктором и Деструктором. В большинстве языков вы можете передавать параметры конструктору, но не в VBA. Мы можем использовать Class Factory, чтобы обойти эту проблему, как показано ниже.

Инициализация

Давайте создадим очень простой Class Module с именем clsSimple с событиями Initialize и Terminate.

' CLASS MODULE Код
Private Sub Class_Initialize()
    MsgBox "Класс инициализируется"
End Sub

Private Sub Class_Terminate()
    MsgBox "Класс прекращается"
End Sub

Public Sub PrintHello()
    Debug.Print "Привет"
End Sub

В следующем примере мы используем Dim и New для создания объекта.

В этом случае oSimple не создается, пока мы не ссылаемся на него в первый раз, например:

Sub ClassEventsInit2()

    Dim oSimple As New clsSimple
    
    ' Инициализация происходит здесь
    oSimple.PrintHello

End Sub

Когда мы используем Set и New вместе, поведение отличается. В этом случае объект создается при использовании Set, например:

Sub ClassEventsInit()

    Dim oSimple As clsSimple
    
    ' Инициализация происходит здесь
    Set oSimple = New clsSimple
    
    oSimple.PrintHello
   
End Sub

Примечание: Для получения дополнительной информации о различиях между использованием New с Dim и использованием New с Set см. Тонкие различия Dim и Set

Как я уже говорил ранее, вы не можете передать параметр в Initialize. Если вам нужно сделать это, вам нужна функция, чтобы сначала создать объект.

' CLASS MODULE - clsSimple
Public Sub Init(Price As Double) 

End Sub 

' обычный модуль
Public Sub Test()

    ' использование функции CreateSimpleObject 
    Dim oSimple As clsSimple
    Set oSimple = CreateSimpleObject(199.99)

End Sub

Public Function CreateSimpleObject(Price As Double) As clsSimple

    Dim oSimple As New clsSimple
    oSimple.Init Price

    Set CreateSimpleObject = oSimple

End Function 

Мы расширим CreateSimpleObject в Примере 2, чтобы создать фабрику классов.

Завершение

Событие Terminate наступает при удалении класса. Это происходит, когда мы устанавливаем значение Nothing.

Sub ClassEventsTerm()

    Dim oSimple As clsSimple
    Set oSimple = New clsSimple
    
    ' Завершение происходит здесь
    Set oSimple = Nothing
   
End Sub

Если мы не установим объект в Nothing, VBA автоматически удалит его, когда он выйдет из области видимости.

Это означает, что если мы создадим объект в процедуре, когда эта процедура завершится, VBA удалит все созданные объекты.

Sub ClassEventsTerm2()

    Dim oSimple As New clsSimple
    
    ' Инициализация происходит здесь
    oSimple.PrintHello

   ' oSimple удаляется, когда мы выходим из этого Sub-вызова Terminate
End Sub

Class Module. Пример 1

В этом примере мы рассмотрим очень распространенное использование Class Module.

Представьте, что у нас есть следующие данные:

Movie Data

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

Мы могли бы использовать для этого 2D-массив или коллекцию коллекций, например:

For i = 2 To rg.Rows.Count
    
    Year = rg.Cells(i, 3)
    If startYear <= Year And endYear >= Year Then
        
        ' Создать новую коллекцию для каждой строки
        Set rowColl = New Collect
 
        ' Добавить исполнителя 
        rowColl.Add rg.Cells(i, 1).Value
        ' Добавить заголовок
        rowColl.Add rg.Cells(i, 2).Value
        
        ' и так далее

        ' Добавить коллекцию строк в основную коллекцию
        coll.Add rowColl

    End If
    
Next i

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

VBA Class Module record

К счастью для нас, у нас есть Class Module VBA, чтобы сделать нашу жизнь проще. Мы можем создать Class Module для хранения элементов.

' clsAlbum class module
Private m_sArtist As String
Private m_sTitle As String
Private m_sYear As String
Private m_sGenre As String
Private m_sSales As String

' Свойства
Public Property Get Artist() As String
    Artist = m_sArtist
End Property
Public Property Let Artist(ByVal sArtist As String)
    m_sArtist = sArtist
End Property

' и т.д.

Каждый раз, когда мы хотим добавить запись, мы можем сделать это следующим образом:

' Объявить переменную
Dim oAlbum As clsAlbum

' Создать новый альбом
Set oAlbum = New clsAlbum

' Добавить детали
oAlbum.Artist = rg.Cells(i, 1)
oAlbum.Title = rg.Cells(i, 2)
oAlbum.Year = rg.Cells(i, 3)
oAlbum.Genre = rg.Cells(i, 4)
oAlbum.Sales = rg.Cells(i, 5)

' Добавить объект альбома в коллекцию
coll.Add oAlbum

Как видите, это делает наш код более читабельным. Понятно, для чего используются Artist, Title и т.д.

Затем мы можем легко использовать эти данные для создания отчетов, записи в файлы и т.д.

Sub PrintAlbum(coll As Collection)
    
    Dim oAlbum As clsAlbum

    For Each oAlbum In coll
        ' Распечатайте название и исполнителя для каждого альбома
        Debug.Print oAlbum.Title, oAlbum.Artist
    Next
    
End Sub

Ниже приведен полный код для этого примера

Sub CreateReport()

    Dim coll As Collection
    ' читать данные
    Set coll = ReadAlbums(1990, 2001)
    
    ' Распечатать информацию об альбоме
    PrintAlbum coll

    ' Распечатать общий объем продаж
    PrintTotalSales coll
    
End Sub

Function ReadAlbums(startYear As Long, endYear As Long) _
              As Collection
    
    Dim rg As Range
    Set rg = Sheet1.Range("A1").CurrentRegion
    
    ' Создать коллекцию для хранения альбомов
    Dim coll As New Collection
    Dim oAlbum As clsAlbum
    
    Dim i As Long, Year As Long
    For i = 2 To rg.Rows.Count
        
        Year = rg.Cells(i, 3)
        If startYear <= Year And endYear >= Year Then
            ' Создать новый альбом
            Set oAlbum = New clsAlbum
            ' Добавить детали
            oAlbum.Artist = rg.Cells(i, 1)
            oAlbum.Title = rg.Cells(i, 2)
            oAlbum.Year = Year
            oAlbum.Genre = rg.Cells(i, 4)
            oAlbum.sales = rg.Cells(i, 5)
            ' Добавить объект альбома в коллекцию
            coll.Add oAlbum
        End If
        
    Next i
    
    Set ReadAlbums = coll
    
End Function

Sub PrintAlbum(coll As Collection)
    
    Dim oAlbum As clsAlbum
    For Each oAlbum In coll
        Debug.Print oAlbum.Title, oAlbum.Artist
    Next
    
End Sub

Sub PrintTotalSales(coll As Collection)
    
    Dim oAlbum As clsAlbum, sales As Double
    For Each oAlbum In coll
        sales = sales + oAlbum.sales
    Next
    
    Debug.Print "Общее количество продаж составляет " & sales
    
End Sub

Class Module. Пример 2

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

Представьте, что у вас есть список продуктов, как на картинке ниже.

data film

Продукты имеют разные поля, поэтому нам нужно использовать разные модули классов для каждого типа продуктов. Один тип для строки Книги, один тип для строки Фильмы.

Сначала мы создадим наши модули классов. Они очень похожи для обоих типов продуктов.

' CLASS MODULE - clsBook
' Переменные
Private m_Title As String
Private m_Year As Long

' Свойства
Property Get ItemType() As String
    ItemType = "Book"
End Property
Property Get Title() As String
    Title = m_Title
End Property
Property Get Year() As Long
    Year = m_Year
End Property

' Методы
Public Sub Init(rg As Range)
    m_Title = rg.Cells(1, 2)
    m_Year = CLng(rg.Cells(1, 4))
End Sub

Public Sub PrintToImmediate()
    Debug.Print ItemType, m_Title, m_Year
End Sub
' CLASS MODULE - clsFilm
' Переменные
Private m_Title As String
Private m_Year As Long

' Свойства
Property Get ItemType() As String
    ItemType = "Film"
End Property
Property Get Title() As String
    Title = m_Title
End Property
Property Get Year() As Long
    Year = m_Year
End Property

' Методы
Sub Init(rg As Range)
    m_Title = rg.Cells(1, 2)
    m_Year = CLng(rg.Cells(1, 5))
End Sub

Public Sub PrintToImmediate()
    Debug.Print ItemType, m_Title, m_Year
End Sub

Как видите, единственная реальная разница — это инициализация.

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

' Для каждого типа требуется одна переменная
Dim oBook As clsBook
Dim oFilm As clsFilm

' Если Книга сделать это
Set oBook = New clsBook

' Если фильм сделать это
Set oFilm = New clsFilm

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

В VBA мы можем объявить переменную как вариант. Когда мы используем Variant, мы, по сути, говорим: «Мы определим тип переменной во время выполнения кода».

Это очень полезно при работе с объектами и позволяет нам избежать использования одной переменной, например:

' Требуется только одна переменная
Dim oItem As Variant

' Если книга, указать тип clsBook
Set oItem = New clsBook

' Если фильм, указать тип clsFilm
Set oItem = New clsFilm

Это действительно полезно, так как нам нужна только одна переменная, независимо от того, сколько у нас объектов.

Второе преимущество использования Variant заключается в следующем. Если у каждого Class Module есть подпрограмма / функция с одинаковым именем и параметрами, мы можем использовать одну и ту же переменную для ее вызова.

Представьте, что в clsBook есть функция InitBook, а в clsFilm есть функция InitFilm. Нам нужно сделать это:

' Если clsBook
If Type = "Book" Then
    oItem.InitBook
ElseIf Type = "Film" Then
    oItem.InitFilm

Однако, если они имеют одинаковое имя, например, Init, мы можем заменить строки кода If ElseIf одной строкой:

  ' это вызовет подпрограмму Init любого типа oItem, установленного в
    oItem.Init

Теперь мы можем создать функцию для создания соответствующего объекта. В объектно-ориентированном программировании мы имеем то, что называется фабрикой классов. Это просто функция, которая создает объект на основе заданного типа.

Ранее мы видели, что событие Initialize не принимает параметры. Мы можем позвонить в Init на фабрике классов, чтобы обойти эту проблему.

Полный код для функции ClassFactory здесь:

Function ClassFactory(rg As Range) As Variant

    ' Получить тип продукта
    Dim sType As String
    sType = rg.Cells(1, 1)

    ' Создать объект на основе типа
    Dim oItem As Variant
    Select Case sType
    
        Case "Book":
            Set oItem = New clsBook
        Case "Film":
            Set oItem = New clsFilm
        Case Else
            MsgBox "Invalid type"
    
    End Select
    
    ' Разобрать поля на правильные переменные класса
    oItem.Init rg
    
    ' Вернуть объект продукта
    Set ClassFactory = oItem
        
End Function

Это следующее наше начало. В этом разделе мы читаем таблицу и передаем диапазон в ClassFactory.

Создает объект, передает диапазон в метод Parse объекта. Затем он возвращает объект, который мы добавляем в нашу коллекцию.

Sub ReadProducts()
    
    ' Создать коллекцию
    Dim coll As New Collection
    Dim product As Variant
    
    Dim rg As Range


    ' Читайте продукты с листа
    Dim i As Long
    For i = 1 To 2
        Set rg = Sheet1.Range("A" & i & ":E" & i)
        Set product = ClassFactory(rg)
        coll.Add product
    Next

    ' Распечатать информацию о продукте в Immediate Window(Ctrl + G)
    PrintCollection coll

End Sub

Мы также можем использовать вариант объекта для печати элементов. Пока оба объекта имеют подпрограмму с одинаковым именем и параметрами (например, PrintToImmediate), мы можем вызывать ее, используя тип Variant.

Public Sub PrintCollection(ByRef coll As Collection)
    
    Dim v As Variant
    For Each v In coll
        ' Печать элементов
        v.PrintToImmediate
    Next
    
End Sub

Заключение

На этом я заканчиваю свою статью о Class Module VBA. В этой статье мы рассмотрели части Class Module VBA и два примера, в которых вы могли бы их использовать.

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

Если вы планируете использовать Class Module, то я советую начать с основ и ознакомиться с тем, как создать простой. Как только вы освоите основы, вам будет намного легче переходить к более сложным сценариям.

      Создание приложения на vba путем написания процедур и функций стоит называть процедурным программированием. Написание участков кода, называемых процедурами и функциями, которые описывают какое-либо действие, и последующее последовательное исполнение этих кодов, является основным методом программирования на vba. При этом используется доступ к объектной модели Excel или других приложений. Данные и подпрограммы (функции и процедуры) функционально не связаны между собой. Это значит, что объявляя переменную «длина хвоста» на уровне модуля, нельзя задать ей различные значения для двух процедур.

При объектно-ориентированном программировании (ООП) подход иной. Данные и подпрограммы связаны между собой и описываются в классе.

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

Класс имеет следующую структуру:

Поле – элемент класса для хранения данных,

Свойство – элемент класса для хранения данных с возможностью их обработки,

Метод – аналог процедуры или функции,

Событие – сигнал при изменении состояния объекта, например исполнения метода или изменения данных.

Из всех принципов ООП в vba реализуемы только два: Абстрагирование и инкапсуляция.

Инкапсуляция позволяет скрыть механизм работы класса, оставив открытыми для других процедур только необходимые для работы с экземпляром класса.
Абстракция позволяет создать объект, наиболее приближенный к некомпьютерному прототипу. Например, создать объект «Кошка» со свойствами «количество лап» и «цвет хвоста».

Создание класса

      Для создания класса в vba редакторе выберите в меню Insert строку Class Module. Назовите созданный класс путем переименования созданного модуля. В файле с примером он называется ExampleClass. А модуль, демонстрирующий использование этого класса называется ExClassManagement. В свойствах класса, кроме имени так же есть параметр Instancing. Указывается, будет ли виден класс из другой книги при установке ссылки на данную книгу. При установке Private (по умолчанию) класс виден только в данной книге, при установке PublicNotCreatable, класс не будет доступен из другой книги, однако экземпляр класса доступен будет, если он создан в данной книге.

Создание экземпляра класса

     Класс — это всего лишь описание объекта. Для использования возможностей класса, необходимо создать экземпляр класса (объект). Существует несколько способов:

Способ 1:

Private Sub TestClass()
Dim cl As ExampleClass 
Set cl = New ExampleClass End Sub

    
Данный способ корректен абсолютно

Способ 2:

Dim cl As ExampleClass 
Private Sub TestClass() 
Set cl = New ExampleClass End Sub 

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

Способ 3:

Dim cl WithEvents As ExampleClass 
Private Sub TestClass() 
Set cl = New ExampleClass End Sub 

    

Экземпляр класса объявляется с событиями, и если в классе описаны события объекта, они будут доступны. Работает только при объявлении в объектном модуле (модуль класса, формы, листа, книги)

Способ 4:

Private Sub TestClass() 
Dim cl As New ExampleClass 
End Sub 

     
Так называемый неявный метод создания экземпляра класса. В этом случае объект создается при первом обращении к переменной cl. Наверное, предпочтительнее сначала объявлять переменную (выделяется память), а затем явно создавать объект.

Уничтожение экземпляра класса

Естественно, после использования экземпляра класса, необходимо очистить память. Делается это одним способом:

Set cl = Nothing 

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

Создание полей класса

Созданный класс ExampleClass не имеет полей, свойств, методов, событий, поэтому и созданный на его основе объект (экземпляр класса) cl, так же бесполезен.

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

Создание закрытого поля:

Dim sBody As String 

или

Private sBody As String

Создание открытого поля:

Public Head As String 

И теперь свойство Head доступно у экземпляра класса cl.

В него можно записать

cl.Head = "FHead" 

и прочитать

Debug.Print cl.Head

Создать поле с пользовательским типом данных не удастся.

     Поля используются для хранения данных в объекте. Данные будут доступны, пока объект существует. Однако нельзя указать значение по-умолчанию для поля и нельзя сделать поле только для чтения.

Создание свойства класса

      Свойство это способ доступа к данным внутри объекта. Выглядят как поля, однако, это функции (назовем их методами). Называются они Property Get для чтения данных из объекта, и Property Let для записи данных в объект. Есть еще третье Property Set для установки ссылки на другой объект. Но это можно сделать и при помощи Property Let, поэтому Property Set вещь бесполезная.

Синтаксис Property Get

[Public | Private | Friend] [Static] Property Get имя [(аргументы)] [As тип]

[произвольный код]
[имя=выражение]
[Exit Property]

[произвольный код]
[имя=выражение]
[End Property]

Элемент Описание
Public Не обязательно. Делает метод открытым во всех модулях проекта
Private Не обязательно. Метод будет доступен только в модуле класса, то есть не будет виден как свойство
Friend Не обязательно. Метод будет виден во всех модулях проекта, но не будет
виден по ссылке на класс. то есть при конструкции Dim cl as new
ExampleClass; Dim cll as object; Set cll=cl в объекте cll свойство с
модификатором Friend видно не будет, а в объекте cl будет
Static Не обязательно. Значения объявленных локальных переменных внутри метода
сохраняются между обращениями. Не распространяется на переменные,
объявленные на уровне модуля класса
имя Обязательно. Имя метода будет являться и именем свойства. при этом оно
должно совпадать для методов Property Let или Property Set
аргументы Не обязательно. По сути, это аргументы функции. Имена и типы аргументов должны совпадать с аргументами метода Property Let
тип Не обязательно. Тип данных, возвращаемый функцией может быть Byte,
Boolean, Integer, Long, Currency, Single, Double, Date, String, Object,
Variant, пользовательским типом. Должен совпадать с типом данных
выражения
произвольный код Не обязательно. Любое количество строк кода, например математические операции с возвращаемым значением
выражение Значение, возвращаемое методом Property Get. Если не указать, метод
вернет пустую строку для типа String, False для Boolean, 0 для Integer и
так далее

Замечания:
     — Элемент Exit Property означает выход из метода, и по сути аналог Exit Function. В методе их может быть несколько.
     — Внутри метода Property Get может быть сколько угодно процедур и
функций, но сам метод не может входить в состав других процедур и
функций, одноименных методов Property Get в модуле класса быть не может.

Синтаксис Property Let

[Public | Private | Friend] [Static] Property Let имя ([аргументы,] значение)
[произвольный код]
[Exit Property]

[произвольный код]
[End Property]

Элемент Описание
Public Не обязательно. Делает метод открытым во всех модулях проекта
Private Не обязательно. Метод будет доступен только в модуле класса, то есть не будет виден как свойство
Friend Не обязательно. Метод будет виден во всех модулях проекта, но не будет
виден по ссылке на класс. то есть при конструкции Dim cl as new
ExampleClass; Dim cll as object; Set cll=cl в объекте cll свойство с
модификатором Friend видно не будет, а в объекте cl будет
Static Не обязательно. Значения объявленных локальных переменных внутри метода
сохраняются между обращениями. Не распространяется на переменные,
объявленные на уровне модуля класса
аргументы Не обязательно. По сути, это аргументы функции. Имена и типы аргументов должны совпадать с аргументами метода Property Get
значение Не обязательно. Имя переменной метода, которой будет присваиваться
значения свойства. Тип данных значения должен совпадать с типом метода
Property Get
произвольный код Не обязательно. Любое количество строк кода, например проверка
указываемого значения. И конечно необходимо здесь передать значение из
локальной переменной (значение) метода во внешнюю переменную модуля
класса. А значение внешней переменной передать в выражение метода
Property Get

Замечание:
     — Аналогично методу Property Get

Синтаксис Property Set

[Public | Private | Friend] [Static] Property Set имя ([аргументы,] ссылка)
[произвольный код]
[Exit Property]
[произвольный код]
[End Property]

Элемент Описание
Public Не обязательно. Делает метод открытым во всех модулях проекта
Private Не обязательно. Метод будет доступен только в модуле класса, то есть не будет виден как свойство
Friend Не обязательно. Метод будет виден во всех модулях проекта, но не будет
виден по ссылке на класс. то есть при конструкции Dim cl as new
ExampleClass; Dim cll as object; Set cll=cl в объекте cll свойство с
модификатором Friend видно не будет, а в объекте cl будет
Static Не обязательно. Значения объявленных локальных переменных внутри метода
сохраняются между обращениями. Не распространяется на переменные,
объявленные на уровне модуля класса
аргументы Не обязательно. По сути, это аргументы функции. Имена и типы аргументов должны совпадать с аргументами метода Property Get
ссылка Не обязательно. Имя переменной метода, которая будет ссылкой на объект. Тип данных ссылки должен быть object
произвольный код Не обязательно. Любое количество строк кода, например работа со свойствами объекта. И конечно необходимо здесь передать значение из
локальной переменной (значение) метода во внешнюю переменную модуля
класса. А значение внешней переменной передать в выражение метода
Property Get

Замечание:
     — Не забывайте, передача ссылки на объект происходит при помощи Set.

Примеры создания свойства в модуле класса Pacient:

Private sHeight As Single 
Public Property Get Height() 
 As Single Height = sHeight 
End Property 

Public Property 
Let Height(ByVal sHeightValue As Single) 
 sHeight = sHeightValue 
End Property 

     
Свойство Height доступно для чтения и записи. при этом при записи и чтении никаких модификаций с данными не производится, то есть такая конструкция аналогична открытому полю (Public Height as Single), а потому создание ее смысла не имеет. Просто лишний код.

Private sHeight As Single 
Public Property Get Height() As Single 
 Height = sHeight 
End Property 

 

     Свойство доступно только для чтения. Метод Property Let не создавался. Предполагается, что данные переменной sHeight заданы в какой-либо процедуре модуля класса.

Const sHeight As Single = 2 
Public Property 
Get Height() As Single 
 Height = sHeight 
End Property 

     

Свойство доступно только для чтения и содержит константу. Единственный способ открытия константы из объектного модуля. Конструкция Public Const sHeight As Single = 2 работать не будет.

Private sHeight As Single 
Public Property 
Let Height(ByVal sHeightValue As Single) 
 sHeight = sHeightValue 
End Property 

     

Свойство только для записи. при попытке его прочитать, появится ошибка.

Private sHeight As Single 
Public Property 
Get Height() As Single 
 Height = sHeight 
End Property 
Public Property 
Let Height(ByVal sHeightValue As Single) 
Select Case sHeightValue 
Case Is > 250 
sHeight = 250 
Case Is < 50 
sHeight = 50 
Case Else 
sHeight = sHeightValue 
End Select End Property 

     
Происходит проверка данных при записи значения.

Private sHeight As Single 
Public Property 
Get Height() As Single 
Height = sHeight / 100 
End Property

Public Property 
Let Height(ByVal sHeightValue As Single) 
sHeight = 
sHeightValue 
End Property 

     
Пример произвольного кода в методе Property Get. В свойство Height записывается значение в сантиметрах, а читается в метрах.

Private sHeight As Single 
Public Property Get Height(ByVal Scales As Integer) As Single 
Select Case Scales 
Case 1 
Height = sHeight 
Case 2 
Height = sHeight / 10 
Case 3 Height = sHeight / 1000 
End Select 
End Property 

Public Property Let Height(ByVal Scales As Integer, ByVal sHeightValue As Single) 
Select Case Scales 
Case 1 
sHeight = sHeightValue 
Case 2 
sHeight = sHeightValue * 10 
Case 3 sHeight = sHeightValue * 1000 
End Select 
End Property 

     
Свойство со аргументом. Аргумент Scales указывает, в каких единицах измерения записывается рост, а в каких читается.

Private sHeight As Single 
Private sWeight As Single 
Public Property Let Height(ByVal sHeightValue As Single) 
sHeight = sHeightValue 
End Property 

Public Property Let Weight(ByVal sWeightValue As Single) 
sWeight = sWeightValue 
End Property

Property Get IMT() As Single 
If sHeight <> 0 
Then IMT = Round(sWeight / sHeight ^ 2, 1) 
End Property 

      Свойства Height и Weight только для записи, свойство IMT только для чтения и содержит индекс массы тела, рассчитанный на основе веса и роста. Расчет происходит всякий раз при обращении к свойству IMT.

Методы класса

     В созданном классе можно создавать процедуры и функции. Они будут видны в экземпляре класса как методы, если указаны как Public и не видны, если указаны как Private. Все процедуры и функции, которые не планируется использовать как методы, должны быть Private. Этого требует принцип инкапсуляции.

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

Код в классе Вызов метод
Public Function IMT(sWeight As Single, sHeight As Single) As Single 
If sHeight <> 0 
Then IMT = Round(sWeight / sHeight ^ 2, 1) 
End Function 
Dim oPac As Pacient 
Set oPac = New Pacient 
Debug.Print oPac.IMT(113, 1.83) 
Set oPac = Nothing 
Public IMT As Single 
Public Sub CalcIMT(sWeight As Single, sHeight As Single) 
If sHeight <> 0 
Then IMT = Round(sWeight / sHeight ^ 2, 1) 
End Sub 
Dim oPac As Pacient 
Set oPac = New Pacient 
oPac.CalcIMT sWeight:=113, sHeight:=1.83 
Debug.Print oPac.IMT 
Set oPac = Nothing 

В первом случае значение индекса массы тела возвращает функция, во втором открытое поле IMT.

Классический вариант:

Код в классе Вызов метод
Private sHeight As Single 
Private sWeight As Single 
Private sIMT As Single 
Public Property Get Height() As Single 
Height = sHeight 
End Property 

Public Property Let Height(ByVal sHeightValue As Single) 
sHeight = sHeightValue 
End Property 

Public Property Get Weight() As Single 
Weight = sWeight 
End Property 

Public Property Let Weight(ByVal sWeightValue As Single) 
sWeight = sWeightValue 
End Property 

Public Property Get IMT() As Single 
IMT = sIMT 
End Property

Public Sub CalcIMT() 
If sHeight <> 0 
Then sIMT = Round(sWeight / sHeight ^ 2, 1) 
End Sub 
Dim oPac As Pacient 
Set oPac = New Pacient 
oPac.Height = 1.83 
oPac.Weight = 113 
oPac.CalcIMT 
Debug.Print oPac.IMT 
Set oPac = Nothing 

 Свойства Height и Weight для чтения и записи, свойство IMT только для чтения. Индекс массы тела рассчитывается методом CalcIMT.

События класса

      Созданный класс уже имеет два скрытых события:
Class_Initialize  — Происходит при создании экземпляра класса. В этом событии удобно указывать значения свойств и переменных по-умолчанию.
Class_Terminate — Происходит при уничтожении экземпляра класса. Экземпляр класса уничтожается, когда процедура, в которой он был объявлен, завершает свою работу. Или после явной деинициализации экземпляра класса:
Set oPac=Nothing

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

Private WithEvents 
oPac As Pacient 

А в самом модуле класса указывается событие:

Синтаксис:
[Public] Event имя[(аргументы)]

Элемент Описание
Public Не обязательно. Делает событие открытым во всех объектных модулях проекта. По-умолчанию, все пользовательские события Public
имя Имя события, которое будет видно в экземпляре класса
аргументы Не обязательно. События могут иметь аргументы, которым можно передавать значения или ссылку на объект

Замечание:Событие должно указываться в самом начале модуля класса

     В месте, где должно происходить событие указывается триггер с синтаксисом:

RaiseEvent имя[(значение аргументов)]

Элемент Описание
имя Обязательно. Имя события, для которого указывается триггер
значение аргументов Не обязательно. Если при указании события были указаны аргументы, здесь через запятую указываются их значения

      Вот пример кода расчета индекса массы тела в форме.

Код в классе:

Public Event IMTCalculated(IMTValue As Single) 
Private sHeight As Single 
Private sWeight As Single 
Private sIMT As Single 
Public Property Get Height() As Single 
Height = sHeight 
End Property 

Public Property Let Height(ByVal sHeightValue As Single) 
sHeight = sHeightValue 
End Property 

Public Property Get Weight() As Single 
Weight = sWeight 
End Property 

Public Property Let Weight(ByVal sWeightValue As Single) 
sWeight = sWeightValue 
End Property 
Public Sub CalcIMT() 
If sHeight <> 0 Then sIMT = Round(sWeight / sHeight ^ 2, 1) 
RaiseEvent IMTCalculated(sIMT) 
End Sub

Вызов метода в форме:

Private WithEvents oPac As Pacient 
Private Sub CommandButton1_Click() 
Set oPac = New Pacient 
oPac.Height = 1.83 
oPac.Weight = 113 
oPac.CalcIMT 
End Sub 

Private Sub oPac_IMTCalculated(IMTValue As Single) 
 Me.Label1.Caption = IMTValue 
End Sub 

     
Событие IMTCalculated срабатывает после расчета индекса массы тела, и в форме значение аргумента IMTValue присваивается надписи. Таким образом, отпадает необходимость в создании отдельного свойства IMT.

Практическое применение модулей классов

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

           Есть случаи, когда использовать пользовательские классы удобнее, чем процедуры и функции:

Содержание

  • Модули классов VBA — Введение
  • Вставка модуля класса
  • Создание предмета
  • Создание коллекции
  • Использование вашего нового объекта
  • Резюме создания объекта с использованием модуля класса
  • Использование модуля класса для создания репозитория переменных
  • Превращение вашего объекта в надстройку

В этом руководстве вы узнаете о модулях классов в VBA. Вы узнаете, что это такое и как их использовать.

Когда вы вставляете модули в редактор Visual Basic (VBE), чтобы ввести свой код, вы могли заметить, что вы также можете вставить так называемый «модуль класса».

Модули класса против модулей

Модули классов работают совершенно иначе, чем обычные модули, поскольку они облегчают создание модели компонентных объектов (COM), которую затем можно использовать в обычном коде VBA.

Фактически, вы создаете объект, который работает так же, как встроенный объект Excel, такой как «Рабочие листы». В объекте Worksheets у вас есть ряд свойств и методов, которые позволяют получить количество рабочих листов в рабочей книге или каждое отдельное имя рабочего листа или множество другой информации.

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

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

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

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

Excel — это многоуровневое приложение. Существует уровень клиентских сервисов, который управляет фактическим окном рабочего листа, с которым пользователь знаком. Следующий уровень — объектная модель Excel. Нажмите F2 в модуле VBA, и вы сможете увидеть огромное количество объектов и членов тех объектов, которые являются движком Excel. Обратите внимание, что ваш новый объект также будет отображаться здесь.

Наконец, под всем этим у вас есть уровень служб данных, который содержит все данные, которые вы ввели в рабочие листы и ячейки. Excel обращается к этому с помощью объектной модели Excel.

Создание модуля класса позволяет расширить объектный модуль Excel своими собственными настраиваемыми объектами и членами.

В этой статье объясняется, как создать простую иерархию объектов с помощью модулей классов.

Преимущества использования модулей классов

  1. Вы можете разработать надежный строительный блок, который можно использовать в любом количестве различных приложений Excel.
  2. После того, как он будет тщательно протестирован, можно положиться на то, что он всегда будет давать правильные результаты так же, как встроенные объекты Excel.
  3. Если в код в другом месте приложения внесены обновления, новый объект по-прежнему будет работать таким же образом.
  4. Вы можете использовать свой новый объект в других приложениях Excel в качестве надстройки.
  5. Объекты могут быть повторно использованы в других приложениях и помогают в отладке.

Недостатки использования модулей классов

  1. Их бывает сложно создать и понять.
  2. Соглашения об именах очень важны, потому что это то, что вы увидите, когда используете свой объект в обычном модуле.
  3. Если вы раньше не создавали модуль класса, его может быть трудно понять, и вам придется изучить его.
  4. Невозможно внести изменения во время выполнения — необходимо заново настроить проект.
  5. Если свойства и частные переменные имеют одинаковое имя, могут возникать бесконечные циклы, приводящие к ошибкам.

Вставка модуля класса

Выберите Вставить | Модуль класса из меню VBE (редактор Visual Basic). Новый модуль класса будет автоматически называться «Класс 1», но его необходимо немедленно изменить на имя, которое вы собираетесь использовать для своего объекта.

Вы меняете имя в окне «Свойства», на которое указывает стрелка. Вы просто вводите свое новое имя, и оно изменится в коллекции модулей классов.

Если окно «Свойства» не отображается, выберите «Просмотр» | Свойства в меню VBE или нажмите F4

Вызовите новый модуль класса MyItem и дважды щелкните имя в древовидной структуре в Project Explorer, чтобы отобразить окно кода для него.

Создание предмета

В этом примере будет создан объект верхнего уровня под названием «MyItems» с объектом-членом под ним под названием «MyItem», который будет содержать индивидуальные данные для каждого элемента. После создания он будет работать так же, как встроенный объект Excel. Например, есть объект под названием «Рабочие листы», который представляет собой набор всех рабочих листов в вашей книге. Существует также объект под названием «Лист», который представляет каждый отдельный рабочий лист в вашей книге и содержит все свойства и методы для каждого рабочего листа. Этот объект относится к объекту коллекции «Рабочие листы».

Вы можете перемещаться по коллекции «Рабочие листы», просматривая каждый «Лист» по очереди. Таким же образом вы сможете перебирать коллекцию «MyItems», просматривая свойства, созданные вами в элементе «MyItems».

Первое, что нужно сделать, — это создать подобъект для уровня члена, который будет содержать фактические элементы в коллекции объекта верхнего уровня. Это эквивалент элементов (например, name, visible, count) в объекте Sheet в Excel. Этот код вводится в модуль класса под названием «MyItem».

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

В подобъекте мы собираемся создать два свойства для объекта — Item и Detail.

Первоначально необходимо объявить две строковые переменные для хранения значений свойств:

12 Частный mItem как строкаЧастный mDetail как строка

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

Им нужно дать уникальные имена, чтобы они отличались от свойств, которые мы собираемся создать, поэтому перед каждым именем стоит буква «m» (для члена).

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

Следующим шагом является настройка кода для предоставления доступа к двум свойствам. Это делается с помощью операторов Property Let и Property Get для каждого свойства. Они должны быть общедоступными, иначе объект верхнего уровня не будет иметь никаких видимых свойств.

123456789101112131415 Публичное свойство Let Item (vdata As String)mItem = vdataКонечная собственностьОбщедоступное свойство Get Item () As StringItem = mItemКонечная собственностьСведения о публичной собственности (vdata как строка)mDetail = vdataКонечная собственностьПубличное свойство Получить детали () в виде строкиДеталь = mDetailКонечная собственность

Этот код создает средства для чтения и записи значений двух свойств (Item и Detail) с использованием двух частных переменных, которые были определены в разделе объявлений модуля.

Параметр vdata используется для передачи данных в соответствующее свойство.

Важно, чтобы каждое свойство имело инструкции «Let» и «Get» и чтобы имя свойства было одинаковым в каждом случае. При неправильном написании вы можете получить два разных свойства — одно, из которого можно читать, и другое, в которое можно писать!

Чтобы помочь с созданием этого кода, вы можете использовать Insert | Процедура в меню VBE для создания скелета кода, который будет создавать начальный код для свойств «Get» и «Let» для данного имени свойства.

Появится всплывающее окно, в котором вы вводите имя свойства и выбираете «Свойство» на переключателях:

Нажмите «ОК», и скелетный код будет добавлен в модуль класса:

1234567 Публичное свойство Get MyProperty () как вариантКонечная собственностьПубличное свойство Let MyProperty (ByVal vNewValue как вариант)Конечная собственность

Это предотвращает любые ошибки в названиях свойств. Вы просто добавляете свой код между операторами Public Property и End Property.

Теперь у вас есть объект MyItem, который будет содержать все данные для этого упражнения.

Устали искать примеры кода VBA? Попробуйте AutoMacro!

Создание коллекции

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

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

1 Частные предметы как коллекция

Опять же, это должно иметь уникальное имя, поэтому перед именем стоит «m» (объект-член), и оно также объявлено как «Private», чтобы оно не появлялось при создании нового объекта. использовал

Затем вам нужно заполнить код Class_Initialize. Это запускается, когда вы впервые используете объект в своем коде, и определяет, какие значения будут загружены в объект.

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

12345678910 Частный подкласс_Initialize ()Dim objItem как MyItemУстановить mItems = New CollectionДля n = от 1 до 3Установить objItem = New MyItemobjItem.Item = Worksheets («Sheet1»). Range («a» & n) .ValueobjItem.Detail = Worksheets («Sheet1»). Range («b» & n) .ValuemItems.Add objItemСледующий nКонец подписки

Код устанавливает объект с именем «objItem», используя определение «MyItem», которое мы создали ранее как модуль класса.

Затем он создает новую коллекцию на основе объекта mItems, определенного ранее.

Он перебирает значения, хранящиеся на Sheet1 книги, и помещает их в свойства, которые мы создали для объекта MyItem. Обратите внимание, что при использовании «objitem» появляется раскрывающийся список с двумя свойствами, как если бы вы использовали встроенный объект Excel.

Затем объект элемента добавляется в объект коллекции, который теперь содержит все данные в значениях свойств.

Входные данные не обязательно должны быть взяты с рабочего листа. Это могут быть статические значения, или они могут поступать из подключения к базе данных, такой как Microsoft Access или SQL Server, или могут поступать из другого рабочего листа.

Затем вам нужно добавить общедоступную функцию под названием «Item».

123 Элемент публичной функции (индекс как целое число) как MyItemУстановить Item = mItems.Item (index)Конечная функция

Это позволяет вам ссылаться на отдельные объекты в объекте коллекции по их порядковому номеру. Эта функция обеспечивает «зеркало» того, что происходит в коллекции «mMyItems» в фоновом режиме.

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

123 Публичная собственность получает счет () до тех пор, покаCount = mItems.CountКонечная собственность

В этом случае вам нужно только свойство «Get», потому что оно доступно только для чтения. Он использует коллекцию mItems, потому что в нее уже встроено свойство count.

Теперь у вас есть объект (MyItems) с полной иерархией, определяемой объектом MyItem.

Чтобы все это работало, теперь вам нужно заполнить рабочий лист (Sheet1) данными, чтобы процедура инициализации класса могла собрать их в объект.

Ваша таблица должна выглядеть так:

Использование вашего нового объекта

Теперь вы можете использовать свой объект Collection (MyItems) в стандартном модуле Excel VBA. Введите следующий код:

12345678 Sub test_object ()Dim MyClass как новые объекты MyItems, n как целое числоMsgBox MyClass.CountДля n = 1 в MyClass.CountMsgBox MyClass.Item (n) .ItemMsgBox MyClass.Item (n) .DetailСледующий nКонец подписки

Этот код создает объект под названием «MyClass» на основе созданного вами объекта коллекции под названием «MyItems». Это запускает процедуру «Initialize», которая извлекает все данные из рабочего листа в объект.

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

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

Если вы используете слово «Static» вместо «Dim», процедура инициализации не запускается, а старые значения сохраняются, пока код выполняется непрерывно. Если данные в электронной таблице изменятся, это не отразится на объекте.

1234567 Sub Test_Static ()Статический Myclass как новые MyItems, n как целое числоДля n = 1 в Myclass.CountMsgBox Myclass.Item (n) .ItemMsgBox Myclass.Item (n) .DetailСледующий nКонец подписки

Резюме создания объекта с использованием модуля класса

Как вы видели, создание иерархии модулей классов для использования в качестве объекта — довольно сложное дело, даже для такой простой структуры, как приведенный здесь пример. Возможности для ошибок огромны!

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

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

С вашим новым объектом вы можете просто сослаться на него и членов, которые вы создали под ним, для хранения данных.

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

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

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

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

Дополнительным преимуществом является то, что при обращении к объекту переменной вы увидите список всех имен переменных, содержащихся в объекте, отсортированных в порядке возрастания.

Чтобы создать репозиторий, вам нужно вставить новый модуль класса. Вы делаете это с помощью Insert | Модуль класса из меню редактора VB

Измените имя на «MyVariables», используя ту же методологию, что и ранее в этой статье.

Введите следующий код:

12345678910111213141516 Частный мВ как вариантОбщедоступное свойство Get Variable1 () As VariantПеременная1 = мВКонечная собственностьОткрытое свойство Let Variable1 (ByVal vNewValue как вариант)mV = vNewValueКонечная собственностьОбщедоступное свойство Get Variable2 () As VariantПеременная1 = мВКонечная собственностьОткрытое свойство Let Variable2 (ByVal vNewValue как вариант)mV = vNewValueКонечная собственность

Этот код устанавливает свойства «Let» и «Get» для двух переменных («Variable1» и «Variable2»). Свойства Let и Get требуются для каждой из ваших переменных, чтобы они были доступны для чтения / записи.

Вы можете использовать свои собственные имена для переменных вместо примеров в этом коде, и вы можете добавить другие переменные, убедившись, что каждая новая переменная имеет инструкции «Let» и «Get».

Частное объявление переменной «mV» предназначено для создания рабочей переменной, которая используется только в модуле класса для передачи значений.

Чтобы использовать репозиторий переменных, введите следующий код в стандартный модуль:

123456 Global VarRepo как новые переменные MyVariablesSub TestVariableRepository ()MsgBox VarRepo.Variable1VarRepo.Variable1 = 10MsgBox VarRepo.Variable1Конец подписки

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

Код сначала отображает значение «Variable1», чтобы показать, что оно пустое.

Значение 10 присваивается «Variable1», и затем отображается новое значение в объекте, чтобы показать, что это свойство теперь содержит это значение.

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

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

Превращение вашего объекта в надстройку

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

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

В раскрывающемся списке типов файлов выберите тип файла «Надстройка (.xlam)» и нажмите «ОК». По умолчанию файл будет сохранен в папке надстройки, но вы можете изменить его местоположение.

Затем вы можете включить файл надстройки в свои приложения Excel, что даст вам гибкость в использовании вашего нового объекта.

Чтобы включить новую надстройку в Excel, щелкните Файл на ленте Excel, а затем щелкните Параметры в нижней части левой панели.

Нажмите «Надстройки» на левой панели в появившемся всплывающем окне. Внизу окна находится кнопка с надписью «Go».

Нажмите на нее, и появится всплывающее окно «Надстройка». Нажмите «Обзор» и найдите файл надстройки. После этого вы сможете ссылаться на свой объект в своем коде.

Макросы – это полноценная программа, просто она выполняется на базе другого приложения. Следовательно, все принципы программирования соблюдаются. А все языки программирования разделяются на две категории: процедурные и объектно-ориентированные.

Макросы разрабатываются на базе языка VBA, который является скорее процедурным с возможностью обращения к отдельным объектам. Недостатком такого подхода есть то, что одна процедура не связана тесно с другой. 

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

Содержание

  1. Общие сведения и определения
  2. Как создать класс в VBA
  3. Способы создания экземпляра класса
  4. Способ №1
  5. Способ №2
  6. Способ №3
  7. Способ №4
  8. Как удалить экземпляр класса?
  9. Как создать поля класса
  10. Процесс определения свойств класса
  11. Пример создания свойства в модуле класса
  12. Методы класса
  13. События класса
  14. Выводы

Общие сведения и определения

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

Под классом подразумевается тот тип данных, в котором приводится описание структуры объекта. Все программы, написанные с помощью объектно-ориентированных языков, основываются на объектах. В случае с Excel такими служат формы, лист, книги, диаграммы и другие. Класс может объединять в себя целую кучу объектов, каждый из которых является своеобразной его копией с некоторыми доработками.

Выражаясь проще, класс – это шаблон, по которому генерируется объект.

Структура класса:

  1. Поле. Это такой элемент класса, в котором непосредственно содержатся данные.
  2. Свойство. Это составная часть класса, в которой также хранятся данные, которые в дальнейшем могут быть обработаны.
  3. Метод. По сути та же процедура или функция, просто в рамках класса. Методы – одно из главных понятий любого объектно-ориентированного языка программирования. 
  4. Событие. Это ситуация, при которой активируется метод. Это может быть изменение содержимого ячеек или выполнение другого метода. 

VBA не является полноценным объектно-ориентированным языком программирования, поскольку в нем есть только два принципа:

  1. Абстракция. С помощью этого метода можно создать объект, который больше всего напоминает некомпьютерный. Например, можно создать объект «собака», в котором будут такие свойства, как «количество ушей», «длина шерсти» и другие. 
  2. Инкапсуляция. Она дает возможность спрятать класс от других процедур, которые не используют его в своей работе. Но этот принцип допускает использование отдельных свойств класса другими процедурами, если это требуется. 

Как создать класс в VBA

Переходим к практическим аспектам использования классов в языке программирования VBA. Чтобы создать класс, в редакторе нужно нажать на пункт «Class Module», который можно найти в меню «Insert». Далее нужно классу дать определенное имя. Это можно сделать только одним способом – изменить имя, сгенерированное автоматически.

VBA предусматривает возможность спрятать класс от другой книги, если она ссылается на эту. За эту возможность отвечает свойство Instancing. Если его значение выставлено на Private, то этот класс будет обнаруживаться только той книгой, в которой макрос открыт. Если такой макрос открыт на нескольких документах, то класс будет доступен только той книге, к которой он привязан. Если же установить параметр PublicNotCreateable, то класс будет обнаруживаться в том случае, если он создан в книге, но все равно не будет доступен для других книг. 

Способы создания экземпляра класса

Итак, под классом подразумевается описание объекта, но не сам объект. Чтобы на основе класса его создать, необходимо воспользоваться одним из доступных способов. 

Способ №1

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

Private Sub TestClass()  

Dim cl As ExampleClass   

Set cl = New ExampleClass End Sub 

Способ №2

Dim cl As ExampleClass   

Private Sub TestClass()   

Set cl = New ExampleClass End Sub 

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

Способ №3

Dim cl WithEvents As ExampleClass   

Private Sub TestClass()   

Set cl = New ExampleClass End Sub

В данном примере объект создается с событиями, и только в связи с ними они становятся доступными. Этот метод имеет ограниченную сферу использования, его можно применять лишь в рамках объектного модуля (например, листа или книги).

Способ №4

Private Sub TestClass()   

Dim cl As New ExampleClass   

End Sub

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

Как удалить экземпляр класса?

Хорошая привычка – очистить выделенную память, если этот объект уже не нужен. Это позволит значительно увеличить быстродействие макроса. Особенно это важно, если он будет использоваться на слабом компьютере. 

Чтобы это сделать, нужно воспользоваться следующей строкой кода.

Set cl = Nothing

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

Как создать поля класса

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

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

Но отличаются другим – невозможно указать значение, которое будет отображаться в поле по умолчанию, а также невозможно запретить средствами VBA его редактирование. Информация в поле хранится до того момента, как она не будет перезаписана или же пока существует соответствующая переменная. 

Чтобы создать поле, необходимо выполнить такой код.

Dim sBody As String 

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

Private sBody As String 

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

Public Head As String   

После этих манипуляций становится возможным использование свойства Head у объекта. С ним можно выполнять разные операции, включая запись:

cl.Head = «FHead»   

и чтение

Debug.Print cl.Head  

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

Процесс определения свойств класса

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

Для чтения данных используется функция Property Get, а для записи – Property Let. Конечно, языком предусмотрена еще и третья конструкция –– Property Set, но на практике она используется редко из-за того, что ссылку на другой объект (а именно для этого и нужна указанная функция) можно записать и с помощью процедуры Property Let.

Давайте детальнее опишем синтаксис всех описанных функций. Итак, вот скриншот, описывающий синтаксис функции Property Get.

Основы работы с модулями классов

1

А вот таблица, в которой детально описываются элементы свойств класса.

Основы работы с модулями классов

2

При чтении свойства класса нужно учитывать такие нюансы:

  1. С помощью элемента Exit Property осуществляется выход из метода. Допустимо использование сразу нескольких подобных элементов в рамках одного метода. Это может быть полезным, например, если используются циклы и условные операторы. 
  2. Несмотря на то, что сама функция Property Get может содержать неограниченное количество процедур внутри, она сама не может быть составной частью других функций.

Теперь давайте более подробно рассмотрим синтаксис функции Property Let. Он виден на этом скриншоте.

Основы работы с модулями классов

3

Этой функции касаются все замечания, рассмотренные выше.

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

Основы работы с модулями классов

4

Пример создания свойства в модуле класса

А теперь давайте детальнее рассмотрим некоторые практические примеры. Будем приводить фрагменты кода с краткими пояснениями.

Основы работы с модулями классов

5

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

Теперь давайте взглянем немного на другой фрагмент кода.

Основы работы с модулями классов

6

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

Основы работы с модулями классов

7

В этом примере свойство не может быть отредактировано, и в нем содержится постоянное значение (константа). Ее можно открыть только из модуля, который находится в данном объекте. Если мы попробуем использовать конструкцию Public Const sHeight As Single = 2, то у нас ничего не получится.

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

Методы класса

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

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

Основы работы с модулями классов

8

События класса

Внутри каждого класса содержится уже несколько событий:

  1. Class_Initialize. Событие, которое осуществляется в момент создания класса. Здесь можно легко настроить значения переменных и свойств, которые будут использоваться всегда, если не указано обратное. 
  2. Class_Terminate. Это событие происходит, когда объект удаляется. Это происходит в двух случаях. Первый – деинициализация объекта. Второй – прекращение работы той процедуры, в рамках которой был объявлен класс.

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

Само же событие указывается в рамках самого модуля. 

Синтаксис конструкции, с помощью которой происходит объявление события, следующий.

Основы работы с модулями классов

9

Выводы

Конечно, это нелегкая тема для понимания. Но это всего лишь два принципа объектно-ориентированного программирования. В случае же с другими языками все еще сложнее. VBA вообще-то считается очень легким языком. Поэтому если приложить должное упорство, все должно получиться. Успехов. 

Оцените качество статьи. Нам важно ваше мнение:

Понравилась статья? Поделить с друзьями:
  • Что такое модуль в visual basic excel
  • Что такое модуль vba excel
  • Что такое модули в excel
  • Что такое модели в excel
  • Что такое меню в excel 2007