четверг, 25 августа 2016 г.

Обращение к объектам в VBA - восклицательый знак или точка?

Один из самых часто возникающих вопросов у новых пользователей Access (равно как и у ветеранов) это написание выражений для обращения к коллекциям (collections), объектам (objects) и свойствам (properties). Когда же всё-таки стоит использовать восклицательный знак ! (bang operator), а когда точку . (dot operator), и зачем всё это нужно?

Допустим, вы пишите код на VBA и и хотите обратиться к элементу управления (control) на открытой форме (form). Длинный вариант будет выглядеть так:

Forms(“FormName”).Controls(“TextboxName”)

А короткий так:

Forms!FormName!TextboxName

Сейчас я объясню, что тут на самом деле происходит.  Восклицательный знак !, равно как и скобки с кавычками ("") отделяют объект от коллекции, к которой он принадлежит. В данном случае форма FormName принадлежит коллекции Forms, содержащей все открытые в настоящий момент формы. А элемент управления TextboxName входит в коллекцию Controls - все элементы управления формы FormName

Восклицательный знак показывает, что следующий за ним объект это элемент коллекции. Он как бы говорит: "получи объект TextboxName из коллекции по умолчанию для родительского объекта". Родительским объектом в данном случае является FormName, а коллекцией по умолчанию - Controls. Коллекция по умолчанию это та коллекция, которая подразумевается в первую очередь, когда мы опускаем явное обращение к ней (т.е. не указываем, что это именно Controls, а просто ставим !). Это преимущественно делается для сокращения количества используемых слов в коде и времени кодирования.
Можно было бы даже написать FormName.Controls!ControlName (просто используем ! вместо ("")), но восклицательный знак позволяет вообще избавиться от слова Controls, подразумевая его по умолчанию: FormName!ControlName

С другой стороны вы всегда можете использовать точку для обращения к свойству, коллекции или методу. Так как коллекция Controls является коллекцией по умолчанию, то точку тоже можно опустить. Сравните эти три варианта:

Forms(“FormName”).Controls(“TextboxName”)
Forms(“FormName”)(“TextboxName”)
Forms(“FormName”)!TextboxName


Есть мнение, что точки надо использовать с элементами, созданными самим Access, а восклицательный знак - с элементами, созданными пользователем. Это совсем не стопроцентное правило и имеет много исключений. К примеру, восклицательный знак используется в параметрах запроса (query), обращающихся к полю формы (form field):

SELECT *
FROM MyTable
WHERE TableField = Forms!FormName!FieldName

Такой запрос будет брать значение параметра для WHERE прямо из поля FieldName формы FormName
Кстати говоря, у этого синтаксиса есть одна проблема: его можно использовать только явно прописывая обращение к форме в запросе, открыв запрос в окне Access в режиме редактирования SQL.
Обращение к полю формы из SQL
При создании запроса с таким же выражением в VBA, запрос не будет выполнен:
strSQL = "SELECT * " & _
"FROM MyTable " & _
"WHERE TableField = Forms!FormName!FieldName"
DoCmd.RunSQL strSQL
Это связано с тем, что выражение SQL, сформированное в режиме SQL, проходит через интерпретатор, который преобразует его в традиционный Jet синтаксис, то есть просто подставляет значение поля вместо ссылки. При запуске из кода VBA выражение SQL передаётся в Jet-машину без изменений и не исполняется, т.к. происходит нарушение синтаксиса.

Самое лучшее правило - использовать восклицательный знак при обращении к элементам коллекции. FormName!ControlName это короткий вариант FormName.Controls!ControlName. При этом точку оставить для свойств и методов. Одно из явных преимуществ точки состоит в том, что при её использовании в редакторе VBA срабатывает технология Intellisense - та самая, которая показывает выпадающий список с предложениями свойств объекта. Вы просто можете написать Me. и дальше выбирать из списка. Это поможет немного ускорить процесс написания кода и избежать опечаток.



Intellisense в действии

Использование восклицательного знака имеет одну особенность: в конечном счёте Access транслирует этот код в "скобочно-кавычечный" синтаксис, поэтому некоторые считают, что сэкономленное на написании кода время будет нивелировано дополнительными вычислениями интерпретатора. Вполне возможно, хотя мне пока не приходилось писать такой код на VBA, который требовал бы сколько-нибудь ощутимое время для интерпретации.

И, кстати, напоминаю, что имя объекта, содержащее пробелы, при использовании ! требует участия квадратных скобок []:
Forms("Form Name").Controls("Text box")
Forms![Form Name]![Text box]