Основы офисного программирования и документы Word

Создание интерфейса документа. Объект CommandBars

Разбить на страницы
Показывать лекцию целиком

Создание интерфейса документа. Объект CommandBars

Создание интерфейса документа - одна из важных задач, стоящих при разработке практически любых документов. Создание меню, командных кнопок и других элементов управления, вынесенных на инструментальные панели или встроенных непосредственно в документ, создание диалоговых окон (форм) - все эти задачи успешно решены еще в предыдущих версиях Office, и потому в Office 2000 немногое изменилось в этой области.

В Office команды меню и командные кнопки располагаются на инструментальных панелях, которые составляют коллекцию CommandBars. Эти панели могут быть причалены или пристыкованы (docked) к левому, правому, верхнему или нижнему краю документа, а могут находиться и в плавающем состоянии. Каждая панель (объект CommandBar ) содержит набор меню или командных кнопок - коллекцию CommandBarControls. Элементы этой коллекции (объекты CommandBarControl ) в зависимости от их типа представляют отдельные меню, команды меню или командные кнопки. В Office 2000 имеется большое число встроенных панелей со встроенными элементами. Но одно из достоинств Office состоит в том, что весь этот интерфейс является настраиваемым - любую из панелей, также как и любой из элементов, расположенных на панели, можно отключить или включить в любую минуту, можно изменять элементы, показываемые на панелях. В Office 2000 это может делаться автоматически, на основе "интеллектуальных" соображений, - отключаются те команды, которые давно не использовались. Самое главное, что наряду со встроенными панелями и командами можно создавать собственные панели и команды. Это позволяет полностью отказаться от стандартного интерфейса, создав свой собственный интерфейс документа. Чаще всего, в документе используется комбинация собственных и встроенных элементов интерфейса.

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

Свойства и методы коллекции CommandBars

Коллекция CommandBars обладает не только традиционными свойствами и методами, но и некоторыми специфическими свойствами. Давайте кратко познакомимся с большинством из них:

  • Property ActionControl As CommandBarControl вызывается обычно в OnAction процедуре и возвращает объект CommandBarControl, чье свойство OnAction связано с этой выполняемой процедурой. Если же такого объекта нет, то возвращается значение Nothing, что бывает при вызове этого свойства из обычной процедуры. Позволяет отключить доступ к объекту на время выполнения процедуры.
  • Property ActiveMenuBar As CommandBar возвращает объект CommandBar, представляющий активную панель меню.
  • Property DisplayKeysInTooltips As Boolean, DisplayTooltips As Boolean - если свойства имеют значения True, то на панели отображаются назначения "горячих" клавиш.
  • Property LargeButtons As Boolean - значение True позволяет включить клавиши "большого" размера.
  • Function Add([Name], [Position], [MenuBar], [Temporary]) As CommandBar. Метод Add - основной метод коллекции позволяет программным путем добавить в коллекцию новую собственную панель, дав ей имя Name. Параметр Position указывает расположение панели на экране. Его возможные значения: msoBarLeft, msoBarTop, msoBarRight, msoBarBottom указывают, куда стыковочная панель будет причалена - справа, слева, вверху или снизу экрана. Значение msoBarFloating этого параметра указывает, что панель появится в плавающем состоянии. Значение msoBarPopup указывает, что новая панель создается для контекстного меню. Булев параметр MenuBar имеет значение True, когда новая панель заменяет главную панель меню. Булев параметр Temporary позволяет сделать добавление панели временным (на момент сеанса) или постоянным.
  • Function FindControl([Type], [Id], [Tag], [Visible], [Recursive]) As CommandBarControl Позволяет найти на панелях, входящих в коллекцию, элемент, удовлетворяющий критериям поиска. Параметры, являющиеся ключами поиска, могут быть опущены, достаточно задания одного из них. Булев параметр Recursive указывает, нужно ли вести поиск на всю глубину, включая все подменю. Если критериям поиска удовлетворяет несколько элементов, то в качестве результата возвращается первый из них. Возвращается значение Nothing, если поиск был безуспешным.
  • Sub ReleaseFocus() Все панели теряют фокус.
  • В Office 2000 у этой коллекции появились новый метод, новое свойство и новое событие:

  • Function FindControls([Type], [Id], [Tag], [Visible]) As CommandBarControls. Является обобщением метода FindControl, возвращая в случае успеха коллекцию CommandBarControls, содержащую все элементы, удовлетворяющие критериям поиска.
  • Property AdaptiveMenus As Boolean. Позволяет включить или выключить свойство адаптивности встроенных меню, автоматически настраивающее их видимый состав в зависимости от частоты использования той или иной команды меню.
  • Event OnUpdate(). Событие возникает при любых изменениях командной панели объекта CommandBar.
  • Свойства и методы объекта CommandBar

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

  • Property BuiltIn As Boolean Это булево свойство определено как для панели, так и для ее элементов, значение True указывает, что панель или элемент являются встроенными, False - определены пользователем.
  • Property Context As String - позволяет установить или проверить контекст, определяющий местоположение хранения панели, то ли непосредственно с самим документом, то ли в другом возможном месте, зависящем от приложения.

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

  • Property Controls As CommandBarControls - можно сказать центральное свойство, возвращающее коллекцию элементов, располагаемых на панели.
  • Property Enabled As Boolean, Visible As Boolean - тоже два центральных и широко используемых свойства. Первое позволяет выключить доступ к панели, обычно временно, второе делает панель вообще невидимой и тем более недоступной.
  • Property Height As Long, Left As Long, Top As Long, Width As Long - типичные свойства графических элементов, в том числе для панели и ее элементов. Задают размеры элемента.
  • Property Index As Long - возвращает порядковый номер элемента в коллекции.
  • Property Name As String, NameLocal As String - имя панели или ее элемента, для встроенных панелей (элементов) имеют смысл два имени "родное" английское имя и локализованное.
  • Property Position As MsoBarPosition - такое же свойство, как у коллекции панелей, но применяемое к отдельно взятой панели.
  • Property Protection As MsoBarProtection - защищает панель от тех или иных действий пользователя. Значением свойства являются константы, определяющие, что конкретно нельзя делать с панелью, например, менять ее размеры или передвигать.
  • Property Type As MsoBarType - возвращает тип панели.
  • Sub Delete() - удаляет панель
  • Function FindControl([Type], [Id], [Tag], [Visible], [Recursive]) As CommandBarControl - такой же метод поиска элемента, как и описанный выше для коллекции, но поиск ограничен пределами одной панели.
  • Sub Reset() - Восстанавливает установки, принятые по умолчанию, в частности конфигурацию встроенных панелей.
  • Sub ShowPopup([x], [y]) - отображает контекстную панель в позиции, заданной курсором или параметрами X и Y, если они указаны.
  • Также как и коллекция, этот объект приобрел новое булево свойство AdaptiveMenu

    Коллекция CommandBarControls и ее элементы

    В эту коллекцию входят элементы, располагаемые на одной инструментальной панели. Коллекция обладает только традиционными свойствами и методами. Единственное, что следует рассмотреть - это метод Add и его параметры:

    Function Add([Type], [Id], [Parameter], [Before], [Temporary]) As CommandBarControl

    Метод позволяет добавлять новые элементы в коллекцию. Его параметры:

  • Type устанавливает тип элемента и класс этого элемента, возвращаемый функцией Add. Если значением Type является константа msoControlButton, то возвращается объект класса CommandBarButton, для значений: msoControlEdit, msoControlDropdown, msoControlComboBox - возвращается объект класса CommandBarComboBox и, наконец, если тип имеет значение msoControlPopup, то возвращаемый класс объекта - CommandBarPopup
  • Id задает идентификатор встроенного элемента, добавляемого в коллекцию. Если он опущен, то на панели появится пользовательский, пустой элемент, свойства которого позднее следует определить.
  • Parameter для встроенных элементов используется приложением при запуске команды, для пользовательских элементов может хранить некоторую информацию об элементе.
  • Before задает местоположение элемента на панели, если он опущен, элемент добавляется в конец панели.
  • Temporary является булевым параметром. Его значение True говорит о том, что элемент является временным и будет удален с панели при закрытии приложения. По умолчанию элементы являются постоянными.
  • Хотя все создаваемые элементы панели относятся к одному из трех различных классов, все они принадлежат также объединяющему классу CommandBarControl. Мы не будем подробно останавливаться на всех свойствах и методах, как отдельных классов, так и объединяющего элементы класса. Рассмотрим только, как решить главную для команд задачу - выполнить некоторую процедуру в ответ на выбор команды меню или щелчок кнопки. Действия, выполняемые командой, должны быть запрограммированы в виде макроса на языке VBA. Напомним, что макрос - это процедура без параметров. Однако есть некоторый способ передачи информации в исполняемый макрос. Для этого можно использовать параметр Parameter, задаваемый при создании элемента или, что удобнее, свойство Parameter. Конечно, для всех встроенных элементов макрос, задающий команду, уже написан. Но для собственных элементов его нужно написать самому, а затем макрос связать с элементом. Свойство OnAction, которым обладают все элементы панели, позволяет связать элемент с исполняемым макросом. Формально свойство является строкой, задающей имя макроса, который и будет вызываться в ответ на выбор пользователя.

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

    Exist-функции

    К сожалению, у объектов CommandBars, CommandBar, CommandBarPopUp отсутствует весьма полезное свойство, проверяющее существование элемента в коллекции, на панели или меню. Оно необходимо при добавлении элемента, поскольку прежде чем добавить элемент, следует проверить, не существует ли он уже в коллекции. Эта проверка необходима еще и потому, что добавление выполняется вне зависимости от присутствия добавляемого элемента в коллекции (панели, меню) и тем самым порождает копии элементов, что, конечно же, нежелательно. Я написал три функции, реализующие свойство Exist у рассматриваемых объектов:

    Public Function ExistCommandBar(PanelName As String) As Boolean
    	'Возвращает True, если в коллекции CommandBars
    	'существует панель с именем PanelName
    	Dim bar As CommandBar, Exist As Boolean
    	Exist = False
    	For Each bar In CommandBars
    		If bar.Name = PanelName Or bar.NameLocal = PanelName Then
    			Exist = True
    			Exit For
    		End If
    	Next bar
    	ExistCommandBar = Exist
    End Function
    Public Function ExistControl(Panel As CommandBar, _
    		Capt As String) As Boolean
    	'Возвращает True, если в коллекции Controls
    	' панели с именем Panel существует элемент с заголовком capt
    	Dim Ctrl As CommandBarControl, Exist As Boolean
    	Exist = False
    	For Each Ctrl In Panel.Controls
    		If Ctrl.Caption = Capt Then
    			Exist = True
    			Exit For
    		End If
    	Next Ctrl
    	ExistControl = Exist
    End Function
    Public Function ExistItem(CtrlPopUp As CommandBarPopup, _
    		Capt As String) As Boolean
    	'Возвращает True, если в меню CtrlPopUp
    	'	существует элемент с заголовком capt
    	Dim Ctrl As CommandBarControl, Exist As Boolean
    	Exist = False
    	For Each Ctrl In CtrlPopUp.Controls
    		If Ctrl.Caption = Capt Then
    			Exist = True
    			Exit For
    		End If
    	Next Ctrl
    	ExistItem = Exist
    End Function

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

    Поиск элементов

    Казалось бы, что с поиском элементов проблем нет, поскольку объекты CommandBars и Commandbar имеют метод FindControl, а в Office 2000 появился и новый метод FindControls. Эти методы, к сожалению, не всегда применимы, поскольку имеют весьма существенный недостаток. Ключом поиска является значение ID или Tag, но в момент поиска значения этих свойств, как правило, не известно. Чаще всего, известен лишь заголовок разыскиваемого элемента, не включенный в число параметров стандартных методов поиска. Поэтому я предпочел написать собственную процедуру поиска. Приведу эту процедуру и заодно расскажу об одной не очень приятной особенности коллекции панелей CommandBars:

    Public Function MyFindControl(Capt As String) As CommandBarControl
    	'Поиск по заголовку
    	'Возвращает в качестве результата первый элемент с указанным заголовком
    	Dim bar As CommandBar
    	Dim Ctr As CommandBarControl
    	Dim i As Integer
    	For i = 1 To 9
    		Select Case i
    		 Case 1
    			Set bar = CommandBars("File")
    		 Case 2
    			Set bar = CommandBars("Edit")
    		 Case 3
    			Set bar = CommandBars("View")
    		 Case 4
    			Set bar = CommandBars("Insert")
    		 Case 5
    			Set bar = CommandBars("Format")
    		 Case 6
    			Set bar = CommandBars("Tools")
    		 Case 7
    			Set bar = CommandBars("Table")
    		 Case 8
    			Set bar = CommandBars("Window")
    		 Case 9
    			Set bar = CommandBars("Help")
    		End Select
    		For Each Ctr In bar.Controls
    			If Ctr.Caption = Capt Then
    				Set MyFindControl = Ctr
    				Exit Function
    			End If
    		 Next Ctr
    	Next i
    	For Each bar In CommandBars
    		'Debug.Print bar.name
    		For Each Ctr In bar.Controls
    			If Ctr.Caption = Capt Then
    				Set MyFindControl = Ctr
    				Exit Function
    			End If
    		 Next Ctr
    	Next bar
    End Function

    Эта функция похожа на функцию FindControl коллекции CommandBars, также в случае успеха возвращается первый найденный элемент класса CommandBarControl. Разница состоит в том, что ключом поиска является заголовок искомого элемента. Но я хочу обратить внимание на реализацию данного метода. Заметьте, несмотря на то, что в основной части этой функции организован перебор по всей коллекции CommandBars, этому поиску предшествует поиск на отдельных элементах этой коллекции. Дело в том, что не все панели, реально присутствующие в коллекции, могут быть получены путем перебора в стандартном цикле For Each, индексы этих элементов выходят за диапазон, заданный значением Count этой коллекции. Таким образом, можно получить, например, доступ к панели "File"

    Set bar = CommandBars("File")

    Но нельзя получить этот элемент в процессе перебора элементов коллекции CommandBars. Вместе с тем, многие команды меню находятся именно на этих панелях. Так, команда меню "Сохранить как " присутствует только на панели "File", в то же время другие команды, как, например, "Открыть", находятся одновременно и на панели "Standard". Справедливости ради, стоит заметить, что, если Вы знаете ID элемента, то для его поиска можно использовать стандартный метод поиска FindControl, который ведет поиск на всех панелях коллекции. Если же перебор панелей организуется самостоятельно, то следует считаться с "жучком", не позволяющим автоматически получить все элементы коллекции CommandBars.

    Добавление и удаление панелей и других элементов

    Начнем с процедуры, позволяющей в любом из приложений Office 2000 добавить в коллекцию CommandBars собственную инструментальную панель с заданным именем. Напомним, что хотя эта задача и решается одним оператором, поскольку у коллекции есть специальный метод Add, но корректная реализация требует предварительной проверки существования в коллекции панелей элемента с таким именем. Вот процедура, решающая эту задачу и использующая ранее написанную функцию Exist:

    Public Sub AddPanel(PanelName As String)
    	'Добавляет и делает видимой панель с именем Panelname
    	'в коллекцию Commandbars
    	'Панель расположена вверху документа,
    	'не заменяет главное меню и не является временной
    	If Not ExistCommandBar(PanelName) Then
    		Call CommandBars.Add(name:=PanelName, Position:=msoBarTop, _
    		MenuBar:=False, Temporary:=False)
    	End If
    	CommandBars(PanelName).Enabled = True
    	CommandBars(PanelName).Visible = True
    
    End Sub

    Говоря о коллекции CommandBars и добавлении пользовательских панелей в эту коллекцию, следует иметь в виду одно обстоятельство. В разных приложениях Office 2000 есть свои нюансы использования этих коллекций. В приложении Word можно рассматривать коллекцию панелей, связанную с конкретным документом, и общую коллекцию, связанную с проектом Normal. Поэтому при работе в приложении Word, при рассмотрении коллекции панелей, связанной с документом, следует пользоваться уточненным именем ActiveDocument.CommandBars. В противном случае речь будет идти об общей коллекции, связанной с проектом Normal. При работе в Excel все пользовательские панели будут автоматически добавляться в коллекцию, связанную с документом и не будут появляться при открытии других документов Excel. В приложении Outlook программный проект, частью которого является коллекция, существует в единственном экземпляре, аналогично проекту Normal. Поэтому добавляемые панели будут распространяться на все документы Outlook.

    Говоря о приложении Word и добавлении пользовательских панелей, хочу рассказать еще об одном небольшом "жучке" и как с ним бороться.

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

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

    Public Sub AddMainPanel(MainPanel As String)
    	'Выключаем все панели документа
    	Dim bar As CommandBar
    	For Each bar In CommandBars
    		bar.Enabled = False
    	Next bar
    	'Добавляем и включаем новую панель главного меню
    	If Not ExistCommandBar(MainPanel) Then
    		Call CommandBars.Add(name:=MainPanel, Position:=msoBarTop, _
    		MenuBar:=True, Temporary:=False)
    	End If
    		CommandBars(MainPanel).Enabled = True
    		CommandBars(MainPanel).Visible = True
    
    End Sub

    Поскольку панели добавляются, то их нужно уметь и удалять, - операция Add должна иметь и обратную операцию Remove (Delete). Удаление возможно только для пользовательских панелей и невозможно, естественно, для встроенных панелей. Выполняется удаление достаточно просто, но и здесь желательны предварительные проверки:

    Public Sub DelPanel(PanelName As String)
    	'Удаляет пользовательскую панель
    	'из коллекции CommandBars
    	If ExistCommandBar(PanelName) _
    		And Not CommandBars(PanelName).BuiltIn Then
    		CommandBars(PanelName).Delete
    	End If
    End Sub

    Приведем еще одну обратную операцию. Поскольку в процедуре AddMainPanel все панели документа были отключены (это очень опасная операция), то необходимо иметь процедуру, восстанавливающую отключенные панели:

    Public Sub ResetMainMenu()
    	Dim CstmBar As CommandBar
    	'Включаем все панели
    	For Each CstmBar In CommandBars
    		CstmBar.Enabled = True
    	Next CstmBar
    	
    	Set CstmBar = CommandBars.Item("Menu Bar")
    	CstmBar.Visible = True
    End Sub

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

    Public Sub AddBuiltinMenu(Panel As String, MenuName As String, _
    	Num As Variant, item As CommandBarPopup)
    	'Добавляет встроенный	пункт меню с именем MenuName
    	'на панель Panel
    	Dim bar As CommandBar, ider As Variant
    	Dim Ctrl As CommandBarControl
    	Set bar = CommandBars(Panel)
    	If Not ExistControl(bar, MenuName) Then
    		Set Ctrl = MyFindControl(MenuName)
    		'Если найден
    		If Not (Ctrl Is Nothing) Then
    			ider = Ctrl.ID
    			bar.Controls.Add Type:=msoControlPopup, _
    				ID:=ider, Before:=Num
    		Else: Exit Sub
    		End If
    	End If
    	Set item = bar.Controls(MenuName)
    End Sub

    В процедуре AddBuiltinMenu встроенный пункт меню с именем MenuName вначале разыскивается в коллекции панелей, а затем добавляется на указанную панель Panel. Параметр Num указывает местоположение добавляемого элемента, а параметр item является результатом, возвращая добавленный пункт меню, как объект класса CommandBarPopup. В следующей процедуре в меню добавляется встроенная команда:

    Public Sub AddItem(item As CommandBarPopup, name As String)
    	Dim Ctrl As CommandBarControl
    	If Not ExistItem(item, name) Then
    		'Найти встроенный элемент в коллекции встроенных панелей
    		Set Ctrl = MyFindControl(name)
    		'Если найден
    		If Not (Ctrl Is Nothing) Then
    			item.Controls.Add Type:=msoControlButton, ID:=Ctrl.ID
    		End If
    	End If
    End Sub

    Приведем еще парочку процедур добавления:

    Public Sub AddCustomItem(item As CommandBarPopup, _
    					name As String, Act As String)
    	'Добавляет команду меню
    	'Параметр Act задает имя макроса, реализующего команду
    	Dim Ctrl As CommandBarControl
    	If Not ExistItem(item, name) Then
    		Set Ctrl = item.Controls.Add(Type:=msoControlButton)
    		Ctrl.Caption = name
    		Ctrl.OnAction = Act
    	End If
    End Sub 
    Public Function AddCustomPopup(item As CommandBarPopup, _
    					name As String) As CommandBarPopup
    	'Добавляет подменю в меню,
    	'возвращая объект подменю в качестве результата
    	Dim Ctrl As CommandBarPopup
    	If Not ExistItem(item, name) Then
    		Set Ctrl = item.Controls.Add(Type:=msoControlPopup)
    		Ctrl.Caption = name
    	End If
    	Set AddCustomPopup = item.Controls(name)
    End Function

    Пример создания интерфейса

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

    Создание головного меню

    Создадим документ, в котором:

  • Все встроенные панели отключены,
  • Главное меню содержит пять команд. Каждая команда головного меню является именем вертикально расположенного меню, выпадающего при выборе этой команды. Первые две команды главного меню (Файл, Правка) являются привычными для документов Office 2000 и содержат встроенные команды. Три другие команды главного меню (Документы, Проекты, Презентации) являются пользовательскими и содержат собственные команды, макросы для которых придется написать самостоятельно.
  • Весь инструментарий, необходимый для создания головного меню, уже создан и приведен в предыдущем разделе. Поэтому мне остается привести достаточно понятную процедуру, которая шаг за шагом создает главное меню в соответствии с приведенным планом:

    Public Sub CreateMainMenu()
    	'Создает главное меню
    	Dim newitem As CommandBarPopup
    	Dim Popupitem As CommandBarPopup
    	'Создает панель MainPanel главного меню
    	AddMainPanel ("MainPanel")
    	'Добавляет пункт File
    	Call AddBuiltinMenu("MainPanel", "Файл", 1, newitem)
    		AddItem newitem, "Создать"
    		AddItem newitem, "Открыть..."
    		AddItem newitem, "Закрыть"
    		AddItem newitem, "Сохранить"
    		AddItem newitem, "Печать"
    		AddItem newitem, "Сохранить как..."
    	'Добавляет пункт Edit
    	Call AddBuiltinMenu("MainPanel", "Правка", 2, newitem)
    		AddItem newitem, "Вырезать"
    		AddItem newitem, "Копировать"
    		AddItem newitem, "Вставить"
    		AddItem newitem, "Найти..."
    		AddItem newitem, "Заменить..."
    	'Добавляет собственный пункт "Документы"
    	Call AddMenu("MainPanel", "Документы", 3, newitem)
    		AddCustomItem newitem, "Все документы", "Sorry"
    		AddCustomItem newitem, "Мои документы", "Sorry"
    		'Добавляем подменю
    		Set Popupitem = AddCustomPopup(newitem, "Текущие документы")
    			AddCustomItem Popupitem, "Все текущие документы", "Sorry"
    			AddCustomItem Popupitem, "Мои текущие документы", "Sorry"
    		AddCustomItem newitem, "Последний документ", "Sorry"
    
    	'Добавляет собственный пункт "Проекты"
    	Call AddMenu("MainPanel", "Проекты", 4, newitem)
    		AddCustomItem newitem, "Все проекты", "Sorry"
    		AddCustomItem newitem, "Мои проекты", "Sorry"
    		'Добавляем подменю
    		Set Popupitem = AddCustomPopup(newitem, "Текущие проекты")
    			AddCustomItem Popupitem, "Все текущие проекты", "Sorry"
    			AddCustomItem Popupitem, "Мои текущие проекты", "Sorry"
    		AddCustomItem newitem, "Последний проект", "Sorry"
    
    	'Добавляет собственный пункт "Презентации"
    	Call AddMenu("MainPanel", "Презентации", 5, newitem)
    		AddCustomItem newitem, "Все презентации", "Sorry"
    		AddCustomItem newitem, "Мои презентации", "Sorry"
    		'Добавляем подменю
    		Set Popupitem = AddCustomPopup(newitem, "Текущие презентации")
    			AddCustomItem Popupitem, "Все текущие презентации", "Sorry"
    			AddCustomItem Popupitem, "Мои текущие презентации", "Sorry"
    		AddCustomItem newitem, "Последняя презентация", "Sorry"
    
    End Sub

    Я не стал определять макросы, отвечающие за вызов каждой собственной команды меню, ограничившись стандартной заглушкой "Sorry". В заключение, взгляните на документ, в котором главное меню имеет спроектированный нами вид:

    (рис 3.1) Документ, имеющий собственное главное меню

    Проектирование панели с элементами Combobox

    Макросы, отвечающие за вызов команд меню, встроенных или собственных, могут быть сколь угодно сложными. Зачастую, выбор команды меню является, по существу, приглашением к началу диалога с пользователем. Соответствующий макрос открывает форму (диалоговое окно), содержащее многочисленные кнопки, вкладки и другие элементы интерфейса. Типичным примером являются встроенные команды "Открыть…" и "Сохранить как…", диалоговые окна которых пополнились новыми возможностями в Office 2000. Но не всегда следует применять формы, чтобы организовать диалог с пользователем в процессе выбора команды меню. Нередко можно обойтись специальными элементами класса CommandBarCombobox, позволяющими пользователю при работе с меню задать самостоятельно или выбрать подходящее значение из раскрывающегося списка. Примеры таких элементов хорошо знакомы по встроенному меню. С ними приходится, например, встречаться при выборе стиля, шрифта, подходящего размера шрифта или масштаба.

    Давайте создадим панель с собственными элементами подобного типа. Я помещу на панель три элемента разных типов, но принадлежащих одному классу - CommandBarComboBox:

  • Первый элемент будет представлять окно редактирования Edit. При работе с таким элементом пользователь имеет возможность ввести свой текст в окно редактирования или согласиться с предложенным ему текстом. Макрос, вызываемый в ответ на действия пользователя, может проанализировать текст, введенный пользователем и реагировать на это соответствующим образом.
  • Второй элемент будет представлять выпадающий список (Dropdown List), из которого пользователь может выбрать подходящее значение. На примере я покажу, как можно задать элементы списка и как в макросе, вызываемом в ответ на выбор пользователя, можно проанализировать, какое значение выбрал пользователь из списка.
  • Третий элемент - ComboBox - объединяет достоинства двух предыдущих элементов, обладая как окном редактирования, так и возможностью выбора значения из списка.
  • Вот текст процедуры, решающей поставленную задачу:

    Public Sub CreateComboPanel()
    	'Создание панели с элементами класса CommandBarCombobox
    	'Создаем панель
    	Dim panel As CommandBar
    	Dim Ctrl As CommandBarComboBox
    	AddPanel ("ComboPanel")
    	Set panel = CommandBars("ComboPanel")
    	'Добавляем на панель три Combo кнопки разного типа
    	'Добавление окна редактирования Edit
    	Set Ctrl = AddCustomCombo(panel, "Edititem", msoControlEdit)
    	 'Добавляем text	в окно редактирования
    	 Ctrl.Text = "Один"
    	 Ctrl.OnAction = "EditReaction"
    	'Добавление выпадающего списка
    	Set Ctrl = AddCustomCombo(panel, "DropdownItem", msoControlDropdown)
    	 'Задание элементов списка
    	 Ctrl.AddItem "One", 1
    	 Ctrl.AddItem "Two", 2
    	 Ctrl.AddItem "Three", 3
    	 'Отчеркивание группы элементов
    	 Ctrl.ListHeaderCount = 3
    	 'Добавление элемента в конец списка
    	 Ctrl.AddItem "Others"
    	 Ctrl.OnAction = "DropdownReaction"
    	'Добавление элемента Combobox - окно редактирования и список
    	Set Ctrl = AddCustomCombo(panel, "ComboBoxItem", msoControlComboBox)
    	 Ctrl.Text = "One"
    	 Ctrl.AddItem "One"
    	 Ctrl.AddItem "Five"
    	 Ctrl.OnAction = "DropdownReaction"
    End Sub

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

    Public Function AddCustomCombo(panel As CommandBar, _
    			name As String, tip As Variant) As CommandBarComboBox
    	'Добавляет на панель элемент, тип которого задан параметром tip
    	'возвращая объект CommandBarComboBox	в качестве результата
    	Dim Ctrl As CommandBarComboBox
    	If Not ExistControl(panel, name) Then
    		Set Ctrl = panel.Controls.Add(Type:=tip)
    		Ctrl.Caption = name
    	End If
    	Set AddCustomCombo = panel.Controls(name)
    End Function

    Хотя в процедуре CreateComboPanel есть подробные комментарии, но есть смысл сказать о ней еще несколько слов и остановится на используемых в ней методах и свойствах объектов. Это тем более уместно по той причине, что ранее я не стал описывать свойства и методы объектов класса CommandBarComboBox, ни их общего класса CommandbarControl. Теперь пришла пора в какой-то мере исправить это положение. Прежде всего, напомню, что объекты класса CommandbarComboBox могут быть разного типа. Их тип задается в момент создания элемента и определяется значением соответствующей константы. Так тип наших трех элементов задается соответственно константами: msoControlEdit, msoControlDropdown, msoControlComboBox. Тип определяет, какие свойства и методы класса CommandBarComboBox доступны для использования у данного элемента.

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

    С другой стороны, все свойства и методы, связанные со списком, доступны только для второго и третьего элемента и, естественно, недоступны для окна редактирования. Метод AddItem позволяет добавить новое значение, которое будет показано пользователю в раскрывающемся списке. Параметры этого метода позволяют задать как само значение, так и позицию в списке. Свойство ListHeaderCount позволяет разбить список на группы и отчеркнуть начальную группу значений.

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

    Public Sub EditReaction()
    	 'Обработчик меню - Edit ComboBox
    	Dim Ctrl As CommandBarComboBox
    	'Определяем активный элемент коллекции панелей,
    	'задающий меню, выбранное пользователем
    	Set Ctrl = CommandBars.ActionControl
    	MsgBox ("Привет! В окне редактирования набран (выбран) текст: "  Ctrl.Text)
    	
    	'Далее можно задать реакцию на введенный текст
    	
    End Sub
    
    Public Sub DropdownReaction()
    	'Обработчик кнопки меню - Dropdown ComboBox
    	Dim Ctrl As CommandBarComboBox
    	Set Ctrl = CommandBars.ActionControl
    	'MsgBox ("Hi! Here is Dropdown Box")
    	With Ctrl
    		Select Case .Text
    			Case "One", "Two"
    				MsgBox ("Your Choice : one or two")
    			Case "Three"
    				MsgBox ("Your Choice : three")
    			Case Else
    				MsgBox ("Your Choice : unknown")
    		End Select
    		
    		Select Case .ListIndex
    			Case 1, 2
    				MsgBox ("Your Choice : one or two")
    			Case 3
    				MsgBox ("Your Choice : three")
    			Case Else
    				MsgBox ("Your Choice : unknown")
    		End Select
    
    	End With
    End Sub

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

    С окном редактирования все достаточно просто, поскольку уже знакомое свойство Text позволяет понять, какое значение введено пользователем или он согласился с предложенным ему значением. Для списков, как правило, устраивается разбор случаев. Заметьте, я привел два альтернативных способа, позволяющих узнать, какой элемент списка был выбран пользователем. В первом случае используется свойство текст. Альтернативой является использование свойства ListIndex, фиксирующего номер элемента списка, выбранного пользователем. Замечу, что часто приходится пользоваться и свойством List(i), хранящим значение i-го элемента списка.

    Создание панели с кнопками - объектами класса CommandBarButton

    Напомню, класс CommandBarControl объединяет три класса объектов - CommandBarPopup, CommandBarComboBox и CommandBarButton. О двух первых из них мы уже поговорили, осталось рассмотреть самый простой класс, когда элементами панели являются кнопки. Создание панелей с кнопками - это наиболее распространенная задача, которую приходится решать при проектировании интерфейса документа. Чтобы расширить возможности стандартного документа Office 2000, добавить собственные функции, учитывающие специфику документа, достаточно написать соответствующие макросы, реализующие эти функции, связать макросы с кнопками и разместить эти кнопки на инструментальной панели.

    Когда я работаю с документом Word, то, как правило, включаю свою панель с кнопками, реализующими дополнительные функции, которые я привык использовать в своей работе. В следующем примере я построю панель, содержащую четыре такие кнопки. Макросы, связанные с первыми двумя кнопками, устраняют ошибки переключения клавиатуры, возникающие в ситуации, когда я забываю изменить раскладку клавиатуры. Я предпочитаю пользоваться собственными макросами, несмотря на то, что подобная стандартная функция включена в состав Word 2000. Мои функции мне представляются более удобными. Третий макрос производит посимвольное преобразование текста из кириллицы в латиницу. Им я часто пользуюсь при пересылке русских текстов за рубеж в случае, когда у получателя отсутствует возможность чтения кириллических текстов. Четвертый макрос служит для форматирования программного текста, копируемого из Редактора VBA, заменяя пробелы символами табуляции, изменяя стиль и выполняя другие полезные преобразования.

    Вот процедура, создающая инструментальную панель с кнопками:

    Public Sub CreateButtonPanel()
    	'Создание панели с элементами класса CommandBarButton
    	'Создаем панель
    	Dim panel As CommandBar
    	Dim ctrl As CommandBarButton
    	Dim Ctrl1 As CommandBarButton
    	AddPanel ("ButtonPanel")
    	Set panel = CommandBars("ButtonPanel")
    	'Добавляем на панель четыре кнопки
    	'Добавление кнопки ER
    	Set ctrl = AddCustomButton(panel, "ER")
    	'Свойства кнопки
    	With ctrl
    		.OnAction = "Translate.FromEtoR"
    		.DescriptionText = _
    			"Перевод в русскую раскладку клавиатуры"
    		.TooltipText = "English -> Russian"
    		.Style = msoButtonIconAndCaption
    		.Tag = "English -> Russian"
    		.FaceId = 134
    	End With
    	 'Добавление кнопки RE
    	Set ctrl = AddCustomButton(panel, "RE")
    	'Свойства кнопки
    	With ctrl
    		.OnAction = "Translate.FromRtoE"
    		.DescriptionText = _
    			"Перевод в английскую раскладку клавиатуры"
    		.TooltipText = "Russian	-> English"
    		.Style = msoButtonIconAndCaption
    		.Tag = "Russian	-> English"
    		.FaceId = 135
    	End With
    	 'Добавление кнопки RL
    	Set ctrl = AddCustomButton(panel, "RL")
    	'Свойства кнопки
    	With ctrl
    		.OnAction = "Translate.FromRuToLat"
    		.DescriptionText = _
    			"Перевод в латиницу"
    		.TooltipText = "Кириллица -> Латиница"
    		.Style = msoButtonIconAndCaption
    		.Tag = "Кириллица -> Латиница"
    		.FaceId = 137
    	End With
    		'Добавление кнопки RepNew
    	Set ctrl = AddCustomButton(panel, "RepNew")
    	'Свойства кнопки
    	With ctrl
    		.OnAction = "Translate.RepNew"
    		.DescriptionText = _
    			"Форматирование программного текста"
    		.TooltipText = "Форматирование"
    		.Style = msoButtonIconAndCaption
    		.Tag = "Форматирование программного текста"
    		.BeginGroup = True
    		'Поиск кнопки с подходящим рисунком
    			Set Ctrl1 = MyFindControl("Разметка страницы")
    			Ctrl1.CopyFace
    		.PasteFace
    	End With
    End Sub

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

    Public Function AddCustomButton(panel As CommandBar, _
    			name As String) As CommandBarButton
    	'Добавляет на панель кнопку,
    	'возвращая объект CommandBarButton в качестве результата
    	Dim ctrl As CommandBarButton
    	If Not ExistControl(panel, name) Then
    		Set ctrl = panel.Controls.Add(Type:=msoControlButton)
    		ctrl.Caption = name
    	End If
    	Set AddCustomButton = panel.Controls(name)
    End Function

    Вернемся к процедуре CreateButtonPanel и прокомментируем использованные в ней методы и свойства объектов класса CommandBarButton. Помимо общего для кнопок и команд меню метода OnAction, связывающего с кнопкой макрос, я использовал ряд специальных свойств и методов:

  • Свойство DescriptionText позволяет задать описание функции, выполняемой макросом.
  • Свойство TooltipText задает такое же описание, но в более краткой форме, появляющееся в виде подсказки при подведении курсора к кнопке.
  • Свойство Style задает внешний вид кнопки. В данном случае, когда стиль задан константой msoButtonIconAndCaption, на кнопке будет отображаться рисунок, сопровождаемый текстом.
  • Свойство Tag также задает описание элемента. Если элемент обладает свойством Tag, то его можно найти, используя значение этого свойства в качестве ключа в стандартном методе поиска FindControl. Напомню, что в этом методе в качестве ключа нельзя использовать свойство Caption.
  • Свойство FaceId позволяет задать идентификатор картинки, размещаемой на кнопке. Конечно, есть определенная трудность в том, чтобы знать какое число соответствует желаемой картинке. С этой проблемой частично можно справиться, используя методы CopyFace и PasteFace.
  • Метод CopyFace копирует в буфер картинку, размещаемую на кнопке.
  • Метод PasteFace переносит картинку из буфера на кнопку. Заметьте, на кнопку можно поместить любой рисунок в формате, допускающем сжатие. Для этого следует скопировать рисунок в буфер, а затем применить метод PasteFace.
  • Таким образом, как видите, программные возможности по созданию красивых кнопок на инструментальной панели ничуть не ниже, чем при выполнении этой работы вручную.

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

    (рис 3.2) Документ с тремя панелями, реализующими собственный интерфейс
    Страницы:

    Создание интерфейса документа. Объект CommandBars

    Создание интерфейса документа - одна из важных задач, стоящих при разработке практически любых документов. Создание меню, командных кнопок и других элементов управления, вынесенных на инструментальные панели или встроенных непосредственно в документ, создание диалоговых окон (форм) - все эти задачи успешно решены еще в предыдущих версиях Office, и потому в Office 2000 немногое изменилось в этой области.

    В Office команды меню и командные кнопки располагаются на инструментальных панелях, которые составляют коллекцию CommandBars. Эти панели могут быть причалены или пристыкованы (docked) к левому, правому, верхнему или нижнему краю документа, а могут находиться и в плавающем состоянии. Каждая панель (объект CommandBar ) содержит набор меню или командных кнопок - коллекцию CommandBarControls. Элементы этой коллекции (объекты CommandBarControl ) в зависимости от их типа представляют отдельные меню, команды меню или командные кнопки. В Office 2000 имеется большое число встроенных панелей со встроенными элементами. Но одно из достоинств Office состоит в том, что весь этот интерфейс является настраиваемым - любую из панелей, также как и любой из элементов, расположенных на панели, можно отключить или включить в любую минуту, можно изменять элементы, показываемые на панелях. В Office 2000 это может делаться автоматически, на основе "интеллектуальных" соображений, - отключаются те команды, которые давно не использовались. Самое главное, что наряду со встроенными панелями и командами можно создавать собственные панели и команды. Это позволяет полностью отказаться от стандартного интерфейса, создав свой собственный интерфейс документа. Чаще всего, в документе используется комбинация собственных и встроенных элементов интерфейса.

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

    Свойства и методы коллекции CommandBars

    Коллекция CommandBars обладает не только традиционными свойствами и методами, но и некоторыми специфическими свойствами. Давайте кратко познакомимся с большинством из них:

  • Property ActionControl As CommandBarControl вызывается обычно в OnAction процедуре и возвращает объект CommandBarControl, чье свойство OnAction связано с этой выполняемой процедурой. Если же такого объекта нет, то возвращается значение Nothing, что бывает при вызове этого свойства из обычной процедуры. Позволяет отключить доступ к объекту на время выполнения процедуры.
  • Property ActiveMenuBar As CommandBar возвращает объект CommandBar, представляющий активную панель меню.
  • Property DisplayKeysInTooltips As Boolean, DisplayTooltips As Boolean - если свойства имеют значения True, то на панели отображаются назначения "горячих" клавиш.
  • Property LargeButtons As Boolean - значение True позволяет включить клавиши "большого" размера.
  • Function Add([Name], [Position], [MenuBar], [Temporary]) As CommandBar. Метод Add - основной метод коллекции позволяет программным путем добавить в коллекцию новую собственную панель, дав ей имя Name. Параметр Position указывает расположение панели на экране. Его возможные значения: msoBarLeft, msoBarTop, msoBarRight, msoBarBottom указывают, куда стыковочная панель будет причалена - справа, слева, вверху или снизу экрана. Значение msoBarFloating этого параметра указывает, что панель появится в плавающем состоянии. Значение msoBarPopup указывает, что новая панель создается для контекстного меню. Булев параметр MenuBar имеет значение True, когда новая панель заменяет главную панель меню. Булев параметр Temporary позволяет сделать добавление панели временным (на момент сеанса) или постоянным.
  • Function FindControl([Type], [Id], [Tag], [Visible], [Recursive]) As CommandBarControl Позволяет найти на панелях, входящих в коллекцию, элемент, удовлетворяющий критериям поиска. Параметры, являющиеся ключами поиска, могут быть опущены, достаточно задания одного из них. Булев параметр Recursive указывает, нужно ли вести поиск на всю глубину, включая все подменю. Если критериям поиска удовлетворяет несколько элементов, то в качестве результата возвращается первый из них. Возвращается значение Nothing, если поиск был безуспешным.
  • Sub ReleaseFocus() Все панели теряют фокус.
  • В Office 2000 у этой коллекции появились новый метод, новое свойство и новое событие:

  • Function FindControls([Type], [Id], [Tag], [Visible]) As CommandBarControls. Является обобщением метода FindControl, возвращая в случае успеха коллекцию CommandBarControls, содержащую все элементы, удовлетворяющие критериям поиска.
  • Property AdaptiveMenus As Boolean. Позволяет включить или выключить свойство адаптивности встроенных меню, автоматически настраивающее их видимый состав в зависимости от частоты использования той или иной команды меню.
  • Event OnUpdate(). Событие возникает при любых изменениях командной панели объекта CommandBar.
  • Свойства и методы объекта CommandBar

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

  • Property BuiltIn As Boolean Это булево свойство определено как для панели, так и для ее элементов, значение True указывает, что панель или элемент являются встроенными, False - определены пользователем.
  • Property Context As String - позволяет установить или проверить контекст, определяющий местоположение хранения панели, то ли непосредственно с самим документом, то ли в другом возможном месте, зависящем от приложения.

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

  • Property Controls As CommandBarControls - можно сказать центральное свойство, возвращающее коллекцию элементов, располагаемых на панели.
  • Property Enabled As Boolean, Visible As Boolean - тоже два центральных и широко используемых свойства. Первое позволяет выключить доступ к панели, обычно временно, второе делает панель вообще невидимой и тем более недоступной.
  • Property Height As Long, Left As Long, Top As Long, Width As Long - типичные свойства графических элементов, в том числе для панели и ее элементов. Задают размеры элемента.
  • Property Index As Long - возвращает порядковый номер элемента в коллекции.
  • Property Name As String, NameLocal As String - имя панели или ее элемента, для встроенных панелей (элементов) имеют смысл два имени "родное" английское имя и локализованное.
  • Property Position As MsoBarPosition - такое же свойство, как у коллекции панелей, но применяемое к отдельно взятой панели.
  • Property Protection As MsoBarProtection - защищает панель от тех или иных действий пользователя. Значением свойства являются константы, определяющие, что конкретно нельзя делать с панелью, например, менять ее размеры или передвигать.
  • Property Type As MsoBarType - возвращает тип панели.
  • Sub Delete() - удаляет панель
  • Function FindControl([Type], [Id], [Tag], [Visible], [Recursive]) As CommandBarControl - такой же метод поиска элемента, как и описанный выше для коллекции, но поиск ограничен пределами одной панели.
  • Sub Reset() - Восстанавливает установки, принятые по умолчанию, в частности конфигурацию встроенных панелей.
  • Sub ShowPopup([x], [y]) - отображает контекстную панель в позиции, заданной курсором или параметрами X и Y, если они указаны.
  • Также как и коллекция, этот объект приобрел новое булево свойство AdaptiveMenu

    Коллекция CommandBarControls и ее элементы

    В эту коллекцию входят элементы, располагаемые на одной инструментальной панели. Коллекция обладает только традиционными свойствами и методами. Единственное, что следует рассмотреть - это метод Add и его параметры:

    Function Add([Type], [Id], [Parameter], [Before], [Temporary]) As CommandBarControl

    Метод позволяет добавлять новые элементы в коллекцию. Его параметры:

  • Type устанавливает тип элемента и класс этого элемента, возвращаемый функцией Add. Если значением Type является константа msoControlButton, то возвращается объект класса CommandBarButton, для значений: msoControlEdit, msoControlDropdown, msoControlComboBox - возвращается объект класса CommandBarComboBox и, наконец, если тип имеет значение msoControlPopup, то возвращаемый класс объекта - CommandBarPopup
  • Id задает идентификатор встроенного элемента, добавляемого в коллекцию. Если он опущен, то на панели появится пользовательский, пустой элемент, свойства которого позднее следует определить.
  • Parameter для встроенных элементов используется приложением при запуске команды, для пользовательских элементов может хранить некоторую информацию об элементе.
  • Before задает местоположение элемента на панели, если он опущен, элемент добавляется в конец панели.
  • Temporary является булевым параметром. Его значение True говорит о том, что элемент является временным и будет удален с панели при закрытии приложения. По умолчанию элементы являются постоянными.
  • Хотя все создаваемые элементы панели относятся к одному из трех различных классов, все они принадлежат также объединяющему классу CommandBarControl. Мы не будем подробно останавливаться на всех свойствах и методах, как отдельных классов, так и объединяющего элементы класса. Рассмотрим только, как решить главную для команд задачу - выполнить некоторую процедуру в ответ на выбор команды меню или щелчок кнопки. Действия, выполняемые командой, должны быть запрограммированы в виде макроса на языке VBA. Напомним, что макрос - это процедура без параметров. Однако есть некоторый способ передачи информации в исполняемый макрос. Для этого можно использовать параметр Parameter, задаваемый при создании элемента или, что удобнее, свойство Parameter. Конечно, для всех встроенных элементов макрос, задающий команду, уже написан. Но для собственных элементов его нужно написать самому, а затем макрос связать с элементом. Свойство OnAction, которым обладают все элементы панели, позволяет связать элемент с исполняемым макросом. Формально свойство является строкой, задающей имя макроса, который и будет вызываться в ответ на выбор пользователя.

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

    Exist-функции

    К сожалению, у объектов CommandBars, CommandBar, CommandBarPopUp отсутствует весьма полезное свойство, проверяющее существование элемента в коллекции, на панели или меню. Оно необходимо при добавлении элемента, поскольку прежде чем добавить элемент, следует проверить, не существует ли он уже в коллекции. Эта проверка необходима еще и потому, что добавление выполняется вне зависимости от присутствия добавляемого элемента в коллекции (панели, меню) и тем самым порождает копии элементов, что, конечно же, нежелательно. Я написал три функции, реализующие свойство Exist у рассматриваемых объектов:

    Public Function ExistCommandBar(PanelName As String) As Boolean
    	'Возвращает True, если в коллекции CommandBars
    	'существует панель с именем PanelName
    	Dim bar As CommandBar, Exist As Boolean
    	Exist = False
    	For Each bar In CommandBars
    		If bar.Name = PanelName Or bar.NameLocal = PanelName Then
    			Exist = True
    			Exit For
    		End If
    	Next bar
    	ExistCommandBar = Exist
    End Function
    Public Function ExistControl(Panel As CommandBar, _
    		Capt As String) As Boolean
    	'Возвращает True, если в коллекции Controls
    	' панели с именем Panel существует элемент с заголовком capt
    	Dim Ctrl As CommandBarControl, Exist As Boolean
    	Exist = False
    	For Each Ctrl In Panel.Controls
    		If Ctrl.Caption = Capt Then
    			Exist = True
    			Exit For
    		End If
    	Next Ctrl
    	ExistControl = Exist
    End Function
    Public Function ExistItem(CtrlPopUp As CommandBarPopup, _
    		Capt As String) As Boolean
    	'Возвращает True, если в меню CtrlPopUp
    	'	существует элемент с заголовком capt
    	Dim Ctrl As CommandBarControl, Exist As Boolean
    	Exist = False
    	For Each Ctrl In CtrlPopUp.Controls
    		If Ctrl.Caption = Capt Then
    			Exist = True
    			Exit For
    		End If
    	Next Ctrl
    	ExistItem = Exist
    End Function

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

    Поиск элементов

    Казалось бы, что с поиском элементов проблем нет, поскольку объекты CommandBars и Commandbar имеют метод FindControl, а в Office 2000 появился и новый метод FindControls. Эти методы, к сожалению, не всегда применимы, поскольку имеют весьма существенный недостаток. Ключом поиска является значение ID или Tag, но в момент поиска значения этих свойств, как правило, не известно. Чаще всего, известен лишь заголовок разыскиваемого элемента, не включенный в число параметров стандартных методов поиска. Поэтому я предпочел написать собственную процедуру поиска. Приведу эту процедуру и заодно расскажу об одной не очень приятной особенности коллекции панелей CommandBars:

    Public Function MyFindControl(Capt As String) As CommandBarControl
    	'Поиск по заголовку
    	'Возвращает в качестве результата первый элемент с указанным заголовком
    	Dim bar As CommandBar
    	Dim Ctr As CommandBarControl
    	Dim i As Integer
    	For i = 1 To 9
    		Select Case i
    		 Case 1
    			Set bar = CommandBars("File")
    		 Case 2
    			Set bar = CommandBars("Edit")
    		 Case 3
    			Set bar = CommandBars("View")
    		 Case 4
    			Set bar = CommandBars("Insert")
    		 Case 5
    			Set bar = CommandBars("Format")
    		 Case 6
    			Set bar = CommandBars("Tools")
    		 Case 7
    			Set bar = CommandBars("Table")
    		 Case 8
    			Set bar = CommandBars("Window")
    		 Case 9
    			Set bar = CommandBars("Help")
    		End Select
    		For Each Ctr In bar.Controls
    			If Ctr.Caption = Capt Then
    				Set MyFindControl = Ctr
    				Exit Function
    			End If
    		 Next Ctr
    	Next i
    	For Each bar In CommandBars
    		'Debug.Print bar.name
    		For Each Ctr In bar.Controls
    			If Ctr.Caption = Capt Then
    				Set MyFindControl = Ctr
    				Exit Function
    			End If
    		 Next Ctr
    	Next bar
    End Function

    Эта функция похожа на функцию FindControl коллекции CommandBars, также в случае успеха возвращается первый найденный элемент класса CommandBarControl. Разница состоит в том, что ключом поиска является заголовок искомого элемента. Но я хочу обратить внимание на реализацию данного метода. Заметьте, несмотря на то, что в основной части этой функции организован перебор по всей коллекции CommandBars, этому поиску предшествует поиск на отдельных элементах этой коллекции. Дело в том, что не все панели, реально присутствующие в коллекции, могут быть получены путем перебора в стандартном цикле For Each, индексы этих элементов выходят за диапазон, заданный значением Count этой коллекции. Таким образом, можно получить, например, доступ к панели "File"

    Set bar = CommandBars("File")

    Но нельзя получить этот элемент в процессе перебора элементов коллекции CommandBars. Вместе с тем, многие команды меню находятся именно на этих панелях. Так, команда меню "Сохранить как " присутствует только на панели "File", в то же время другие команды, как, например, "Открыть", находятся одновременно и на панели "Standard". Справедливости ради, стоит заметить, что, если Вы знаете ID элемента, то для его поиска можно использовать стандартный метод поиска FindControl, который ведет поиск на всех панелях коллекции. Если же перебор панелей организуется самостоятельно, то следует считаться с "жучком", не позволяющим автоматически получить все элементы коллекции CommandBars.

    Добавление и удаление панелей и других элементов

    Начнем с процедуры, позволяющей в любом из приложений Office 2000 добавить в коллекцию CommandBars собственную инструментальную панель с заданным именем. Напомним, что хотя эта задача и решается одним оператором, поскольку у коллекции есть специальный метод Add, но корректная реализация требует предварительной проверки существования в коллекции панелей элемента с таким именем. Вот процедура, решающая эту задачу и использующая ранее написанную функцию Exist:

    Public Sub AddPanel(PanelName As String)
    	'Добавляет и делает видимой панель с именем Panelname
    	'в коллекцию Commandbars
    	'Панель расположена вверху документа,
    	'не заменяет главное меню и не является временной
    	If Not ExistCommandBar(PanelName) Then
    		Call CommandBars.Add(name:=PanelName, Position:=msoBarTop, _
    		MenuBar:=False, Temporary:=False)
    	End If
    	CommandBars(PanelName).Enabled = True
    	CommandBars(PanelName).Visible = True
    
    End Sub

    Говоря о коллекции CommandBars и добавлении пользовательских панелей в эту коллекцию, следует иметь в виду одно обстоятельство. В разных приложениях Office 2000 есть свои нюансы использования этих коллекций. В приложении Word можно рассматривать коллекцию панелей, связанную с конкретным документом, и общую коллекцию, связанную с проектом Normal. Поэтому при работе в приложении Word, при рассмотрении коллекции панелей, связанной с документом, следует пользоваться уточненным именем ActiveDocument.CommandBars. В противном случае речь будет идти об общей коллекции, связанной с проектом Normal. При работе в Excel все пользовательские панели будут автоматически добавляться в коллекцию, связанную с документом и не будут появляться при открытии других документов Excel. В приложении Outlook программный проект, частью которого является коллекция, существует в единственном экземпляре, аналогично проекту Normal. Поэтому добавляемые панели будут распространяться на все документы Outlook.

    Говоря о приложении Word и добавлении пользовательских панелей, хочу рассказать еще об одном небольшом "жучке" и как с ним бороться.

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

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

    Public Sub AddMainPanel(MainPanel As String)
    	'Выключаем все панели документа
    	Dim bar As CommandBar
    	For Each bar In CommandBars
    		bar.Enabled = False
    	Next bar
    	'Добавляем и включаем новую панель главного меню
    	If Not ExistCommandBar(MainPanel) Then
    		Call CommandBars.Add(name:=MainPanel, Position:=msoBarTop, _
    		MenuBar:=True, Temporary:=False)
    	End If
    		CommandBars(MainPanel).Enabled = True
    		CommandBars(MainPanel).Visible = True
    
    End Sub

    Поскольку панели добавляются, то их нужно уметь и удалять, - операция Add должна иметь и обратную операцию Remove (Delete). Удаление возможно только для пользовательских панелей и невозможно, естественно, для встроенных панелей. Выполняется удаление достаточно просто, но и здесь желательны предварительные проверки:

    Public Sub DelPanel(PanelName As String)
    	'Удаляет пользовательскую панель
    	'из коллекции CommandBars
    	If ExistCommandBar(PanelName) _
    		And Not CommandBars(PanelName).BuiltIn Then
    		CommandBars(PanelName).Delete
    	End If
    End Sub

    Приведем еще одну обратную операцию. Поскольку в процедуре AddMainPanel все панели документа были отключены (это очень опасная операция), то необходимо иметь процедуру, восстанавливающую отключенные панели:

    Public Sub ResetMainMenu()
    	Dim CstmBar As CommandBar
    	'Включаем все панели
    	For Each CstmBar In CommandBars
    		CstmBar.Enabled = True
    	Next CstmBar
    	
    	Set CstmBar = CommandBars.Item("Menu Bar")
    	CstmBar.Visible = True
    End Sub

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

    Public Sub AddBuiltinMenu(Panel As String, MenuName As String, _
    	Num As Variant, item As CommandBarPopup)
    	'Добавляет встроенный	пункт меню с именем MenuName
    	'на панель Panel
    	Dim bar As CommandBar, ider As Variant
    	Dim Ctrl As CommandBarControl
    	Set bar = CommandBars(Panel)
    	If Not ExistControl(bar, MenuName) Then
    		Set Ctrl = MyFindControl(MenuName)
    		'Если найден
    		If Not (Ctrl Is Nothing) Then
    			ider = Ctrl.ID
    			bar.Controls.Add Type:=msoControlPopup, _
    				ID:=ider, Before:=Num
    		Else: Exit Sub
    		End If
    	End If
    	Set item = bar.Controls(MenuName)
    End Sub

    В процедуре AddBuiltinMenu встроенный пункт меню с именем MenuName вначале разыскивается в коллекции панелей, а затем добавляется на указанную панель Panel. Параметр Num указывает местоположение добавляемого элемента, а параметр item является результатом, возвращая добавленный пункт меню, как объект класса CommandBarPopup. В следующей процедуре в меню добавляется встроенная команда:

    Public Sub AddItem(item As CommandBarPopup, name As String)
    	Dim Ctrl As CommandBarControl
    	If Not ExistItem(item, name) Then
    		'Найти встроенный элемент в коллекции встроенных панелей
    		Set Ctrl = MyFindControl(name)
    		'Если найден
    		If Not (Ctrl Is Nothing) Then
    			item.Controls.Add Type:=msoControlButton, ID:=Ctrl.ID
    		End If
    	End If
    End Sub

    Приведем еще парочку процедур добавления:

    Public Sub AddCustomItem(item As CommandBarPopup, _
    					name As String, Act As String)
    	'Добавляет команду меню
    	'Параметр Act задает имя макроса, реализующего команду
    	Dim Ctrl As CommandBarControl
    	If Not ExistItem(item, name) Then
    		Set Ctrl = item.Controls.Add(Type:=msoControlButton)
    		Ctrl.Caption = name
    		Ctrl.OnAction = Act
    	End If
    End Sub 
    Public Function AddCustomPopup(item As CommandBarPopup, _
    					name As String) As CommandBarPopup
    	'Добавляет подменю в меню,
    	'возвращая объект подменю в качестве результата
    	Dim Ctrl As CommandBarPopup
    	If Not ExistItem(item, name) Then
    		Set Ctrl = item.Controls.Add(Type:=msoControlPopup)
    		Ctrl.Caption = name
    	End If
    	Set AddCustomPopup = item.Controls(name)
    End Function

    Пример создания интерфейса

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

    Создание головного меню

    Создадим документ, в котором:

  • Все встроенные панели отключены,
  • Главное меню содержит пять команд. Каждая команда головного меню является именем вертикально расположенного меню, выпадающего при выборе этой команды. Первые две команды главного меню (Файл, Правка) являются привычными для документов Office 2000 и содержат встроенные команды. Три другие команды главного меню (Документы, Проекты, Презентации) являются пользовательскими и содержат собственные команды, макросы для которых придется написать самостоятельно.
  • Весь инструментарий, необходимый для создания головного меню, уже создан и приведен в предыдущем разделе. Поэтому мне остается привести достаточно понятную процедуру, которая шаг за шагом создает главное меню в соответствии с приведенным планом:

    Public Sub CreateMainMenu()
    	'Создает главное меню
    	Dim newitem As CommandBarPopup
    	Dim Popupitem As CommandBarPopup
    	'Создает панель MainPanel главного меню
    	AddMainPanel ("MainPanel")
    	'Добавляет пункт File
    	Call AddBuiltinMenu("MainPanel", "Файл", 1, newitem)
    		AddItem newitem, "Создать"
    		AddItem newitem, "Открыть..."
    		AddItem newitem, "Закрыть"
    		AddItem newitem, "Сохранить"
    		AddItem newitem, "Печать"
    		AddItem newitem, "Сохранить как..."
    	'Добавляет пункт Edit
    	Call AddBuiltinMenu("MainPanel", "Правка", 2, newitem)
    		AddItem newitem, "Вырезать"
    		AddItem newitem, "Копировать"
    		AddItem newitem, "Вставить"
    		AddItem newitem, "Найти..."
    		AddItem newitem, "Заменить..."
    	'Добавляет собственный пункт "Документы"
    	Call AddMenu("MainPanel", "Документы", 3, newitem)
    		AddCustomItem newitem, "Все документы", "Sorry"
    		AddCustomItem newitem, "Мои документы", "Sorry"
    		'Добавляем подменю
    		Set Popupitem = AddCustomPopup(newitem, "Текущие документы")
    			AddCustomItem Popupitem, "Все текущие документы", "Sorry"
    			AddCustomItem Popupitem, "Мои текущие документы", "Sorry"
    		AddCustomItem newitem, "Последний документ", "Sorry"
    
    	'Добавляет собственный пункт "Проекты"
    	Call AddMenu("MainPanel", "Проекты", 4, newitem)
    		AddCustomItem newitem, "Все проекты", "Sorry"
    		AddCustomItem newitem, "Мои проекты", "Sorry"
    		'Добавляем подменю
    		Set Popupitem = AddCustomPopup(newitem, "Текущие проекты")
    			AddCustomItem Popupitem, "Все текущие проекты", "Sorry"
    			AddCustomItem Popupitem, "Мои текущие проекты", "Sorry"
    		AddCustomItem newitem, "Последний проект", "Sorry"
    
    	'Добавляет собственный пункт "Презентации"
    	Call AddMenu("MainPanel", "Презентации", 5, newitem)
    		AddCustomItem newitem, "Все презентации", "Sorry"
    		AddCustomItem newitem, "Мои презентации", "Sorry"
    		'Добавляем подменю
    		Set Popupitem = AddCustomPopup(newitem, "Текущие презентации")
    			AddCustomItem Popupitem, "Все текущие презентации", "Sorry"
    			AddCustomItem Popupitem, "Мои текущие презентации", "Sorry"
    		AddCustomItem newitem, "Последняя презентация", "Sorry"
    
    End Sub

    Я не стал определять макросы, отвечающие за вызов каждой собственной команды меню, ограничившись стандартной заглушкой "Sorry". В заключение, взгляните на документ, в котором главное меню имеет спроектированный нами вид:

    (рис 3.1) Документ, имеющий собственное главное меню

    Проектирование панели с элементами Combobox

    Макросы, отвечающие за вызов команд меню, встроенных или собственных, могут быть сколь угодно сложными. Зачастую, выбор команды меню является, по существу, приглашением к началу диалога с пользователем. Соответствующий макрос открывает форму (диалоговое окно), содержащее многочисленные кнопки, вкладки и другие элементы интерфейса. Типичным примером являются встроенные команды "Открыть…" и "Сохранить как…", диалоговые окна которых пополнились новыми возможностями в Office 2000. Но не всегда следует применять формы, чтобы организовать диалог с пользователем в процессе выбора команды меню. Нередко можно обойтись специальными элементами класса CommandBarCombobox, позволяющими пользователю при работе с меню задать самостоятельно или выбрать подходящее значение из раскрывающегося списка. Примеры таких элементов хорошо знакомы по встроенному меню. С ними приходится, например, встречаться при выборе стиля, шрифта, подходящего размера шрифта или масштаба.

    Давайте создадим панель с собственными элементами подобного типа. Я помещу на панель три элемента разных типов, но принадлежащих одному классу - CommandBarComboBox:

  • Первый элемент будет представлять окно редактирования Edit. При работе с таким элементом пользователь имеет возможность ввести свой текст в окно редактирования или согласиться с предложенным ему текстом. Макрос, вызываемый в ответ на действия пользователя, может проанализировать текст, введенный пользователем и реагировать на это соответствующим образом.
  • Второй элемент будет представлять выпадающий список (Dropdown List), из которого пользователь может выбрать подходящее значение. На примере я покажу, как можно задать элементы списка и как в макросе, вызываемом в ответ на выбор пользователя, можно проанализировать, какое значение выбрал пользователь из списка.
  • Третий элемент - ComboBox - объединяет достоинства двух предыдущих элементов, обладая как окном редактирования, так и возможностью выбора значения из списка.
  • Вот текст процедуры, решающей поставленную задачу:

    Public Sub CreateComboPanel()
    	'Создание панели с элементами класса CommandBarCombobox
    	'Создаем панель
    	Dim panel As CommandBar
    	Dim Ctrl As CommandBarComboBox
    	AddPanel ("ComboPanel")
    	Set panel = CommandBars("ComboPanel")
    	'Добавляем на панель три Combo кнопки разного типа
    	'Добавление окна редактирования Edit
    	Set Ctrl = AddCustomCombo(panel, "Edititem", msoControlEdit)
    	 'Добавляем text	в окно редактирования
    	 Ctrl.Text = "Один"
    	 Ctrl.OnAction = "EditReaction"
    	'Добавление выпадающего списка
    	Set Ctrl = AddCustomCombo(panel, "DropdownItem", msoControlDropdown)
    	 'Задание элементов списка
    	 Ctrl.AddItem "One", 1
    	 Ctrl.AddItem "Two", 2
    	 Ctrl.AddItem "Three", 3
    	 'Отчеркивание группы элементов
    	 Ctrl.ListHeaderCount = 3
    	 'Добавление элемента в конец списка
    	 Ctrl.AddItem "Others"
    	 Ctrl.OnAction = "DropdownReaction"
    	'Добавление элемента Combobox - окно редактирования и список
    	Set Ctrl = AddCustomCombo(panel, "ComboBoxItem", msoControlComboBox)
    	 Ctrl.Text = "One"
    	 Ctrl.AddItem "One"
    	 Ctrl.AddItem "Five"
    	 Ctrl.OnAction = "DropdownReaction"
    End Sub

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

    Public Function AddCustomCombo(panel As CommandBar, _
    			name As String, tip As Variant) As CommandBarComboBox
    	'Добавляет на панель элемент, тип которого задан параметром tip
    	'возвращая объект CommandBarComboBox	в качестве результата
    	Dim Ctrl As CommandBarComboBox
    	If Not ExistControl(panel, name) Then
    		Set Ctrl = panel.Controls.Add(Type:=tip)
    		Ctrl.Caption = name
    	End If
    	Set AddCustomCombo = panel.Controls(name)
    End Function

    Хотя в процедуре CreateComboPanel есть подробные комментарии, но есть смысл сказать о ней еще несколько слов и остановится на используемых в ней методах и свойствах объектов. Это тем более уместно по той причине, что ранее я не стал описывать свойства и методы объектов класса CommandBarComboBox, ни их общего класса CommandbarControl. Теперь пришла пора в какой-то мере исправить это положение. Прежде всего, напомню, что объекты класса CommandbarComboBox могут быть разного типа. Их тип задается в момент создания элемента и определяется значением соответствующей константы. Так тип наших трех элементов задается соответственно константами: msoControlEdit, msoControlDropdown, msoControlComboBox. Тип определяет, какие свойства и методы класса CommandBarComboBox доступны для использования у данного элемента.

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

    С другой стороны, все свойства и методы, связанные со списком, доступны только для второго и третьего элемента и, естественно, недоступны для окна редактирования. Метод AddItem позволяет добавить новое значение, которое будет показано пользователю в раскрывающемся списке. Параметры этого метода позволяют задать как само значение, так и позицию в списке. Свойство ListHeaderCount позволяет разбить список на группы и отчеркнуть начальную группу значений.

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

    Public Sub EditReaction()
    	 'Обработчик меню - Edit ComboBox
    	Dim Ctrl As CommandBarComboBox
    	'Определяем активный элемент коллекции панелей,
    	'задающий меню, выбранное пользователем
    	Set Ctrl = CommandBars.ActionControl
    	MsgBox ("Привет! В окне редактирования набран (выбран) текст: "  Ctrl.Text)
    	
    	'Далее можно задать реакцию на введенный текст
    	
    End Sub
    
    Public Sub DropdownReaction()
    	'Обработчик кнопки меню - Dropdown ComboBox
    	Dim Ctrl As CommandBarComboBox
    	Set Ctrl = CommandBars.ActionControl
    	'MsgBox ("Hi! Here is Dropdown Box")
    	With Ctrl
    		Select Case .Text
    			Case "One", "Two"
    				MsgBox ("Your Choice : one or two")
    			Case "Three"
    				MsgBox ("Your Choice : three")
    			Case Else
    				MsgBox ("Your Choice : unknown")
    		End Select
    		
    		Select Case .ListIndex
    			Case 1, 2
    				MsgBox ("Your Choice : one or two")
    			Case 3
    				MsgBox ("Your Choice : three")
    			Case Else
    				MsgBox ("Your Choice : unknown")
    		End Select
    
    	End With
    End Sub

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

    С окном редактирования все достаточно просто, поскольку уже знакомое свойство Text позволяет понять, какое значение введено пользователем или он согласился с предложенным ему значением. Для списков, как правило, устраивается разбор случаев. Заметьте, я привел два альтернативных способа, позволяющих узнать, какой элемент списка был выбран пользователем. В первом случае используется свойство текст. Альтернативой является использование свойства ListIndex, фиксирующего номер элемента списка, выбранного пользователем. Замечу, что часто приходится пользоваться и свойством List(i), хранящим значение i-го элемента списка.

    Создание панели с кнопками - объектами класса CommandBarButton

    Напомню, класс CommandBarControl объединяет три класса объектов - CommandBarPopup, CommandBarComboBox и CommandBarButton. О двух первых из них мы уже поговорили, осталось рассмотреть самый простой класс, когда элементами панели являются кнопки. Создание панелей с кнопками - это наиболее распространенная задача, которую приходится решать при проектировании интерфейса документа. Чтобы расширить возможности стандартного документа Office 2000, добавить собственные функции, учитывающие специфику документа, достаточно написать соответствующие макросы, реализующие эти функции, связать макросы с кнопками и разместить эти кнопки на инструментальной панели.

    Когда я работаю с документом Word, то, как правило, включаю свою панель с кнопками, реализующими дополнительные функции, которые я привык использовать в своей работе. В следующем примере я построю панель, содержащую четыре такие кнопки. Макросы, связанные с первыми двумя кнопками, устраняют ошибки переключения клавиатуры, возникающие в ситуации, когда я забываю изменить раскладку клавиатуры. Я предпочитаю пользоваться собственными макросами, несмотря на то, что подобная стандартная функция включена в состав Word 2000. Мои функции мне представляются более удобными. Третий макрос производит посимвольное преобразование текста из кириллицы в латиницу. Им я часто пользуюсь при пересылке русских текстов за рубеж в случае, когда у получателя отсутствует возможность чтения кириллических текстов. Четвертый макрос служит для форматирования программного текста, копируемого из Редактора VBA, заменяя пробелы символами табуляции, изменяя стиль и выполняя другие полезные преобразования.

    Вот процедура, создающая инструментальную панель с кнопками:

    Public Sub CreateButtonPanel()
    	'Создание панели с элементами класса CommandBarButton
    	'Создаем панель
    	Dim panel As CommandBar
    	Dim ctrl As CommandBarButton
    	Dim Ctrl1 As CommandBarButton
    	AddPanel ("ButtonPanel")
    	Set panel = CommandBars("ButtonPanel")
    	'Добавляем на панель четыре кнопки
    	'Добавление кнопки ER
    	Set ctrl = AddCustomButton(panel, "ER")
    	'Свойства кнопки
    	With ctrl
    		.OnAction = "Translate.FromEtoR"
    		.DescriptionText = _
    			"Перевод в русскую раскладку клавиатуры"
    		.TooltipText = "English -> Russian"
    		.Style = msoButtonIconAndCaption
    		.Tag = "English -> Russian"
    		.FaceId = 134
    	End With
    	 'Добавление кнопки RE
    	Set ctrl = AddCustomButton(panel, "RE")
    	'Свойства кнопки
    	With ctrl
    		.OnAction = "Translate.FromRtoE"
    		.DescriptionText = _
    			"Перевод в английскую раскладку клавиатуры"
    		.TooltipText = "Russian	-> English"
    		.Style = msoButtonIconAndCaption
    		.Tag = "Russian	-> English"
    		.FaceId = 135
    	End With
    	 'Добавление кнопки RL
    	Set ctrl = AddCustomButton(panel, "RL")
    	'Свойства кнопки
    	With ctrl
    		.OnAction = "Translate.FromRuToLat"
    		.DescriptionText = _
    			"Перевод в латиницу"
    		.TooltipText = "Кириллица -> Латиница"
    		.Style = msoButtonIconAndCaption
    		.Tag = "Кириллица -> Латиница"
    		.FaceId = 137
    	End With
    		'Добавление кнопки RepNew
    	Set ctrl = AddCustomButton(panel, "RepNew")
    	'Свойства кнопки
    	With ctrl
    		.OnAction = "Translate.RepNew"
    		.DescriptionText = _
    			"Форматирование программного текста"
    		.TooltipText = "Форматирование"
    		.Style = msoButtonIconAndCaption
    		.Tag = "Форматирование программного текста"
    		.BeginGroup = True
    		'Поиск кнопки с подходящим рисунком
    			Set Ctrl1 = MyFindControl("Разметка страницы")
    			Ctrl1.CopyFace
    		.PasteFace
    	End With
    End Sub

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

    Public Function AddCustomButton(panel As CommandBar, _
    			name As String) As CommandBarButton
    	'Добавляет на панель кнопку,
    	'возвращая объект CommandBarButton в качестве результата
    	Dim ctrl As CommandBarButton
    	If Not ExistControl(panel, name) Then
    		Set ctrl = panel.Controls.Add(Type:=msoControlButton)
    		ctrl.Caption = name
    	End If
    	Set AddCustomButton = panel.Controls(name)
    End Function

    Вернемся к процедуре CreateButtonPanel и прокомментируем использованные в ней методы и свойства объектов класса CommandBarButton. Помимо общего для кнопок и команд меню метода OnAction, связывающего с кнопкой макрос, я использовал ряд специальных свойств и методов:

  • Свойство DescriptionText позволяет задать описание функции, выполняемой макросом.
  • Свойство TooltipText задает такое же описание, но в более краткой форме, появляющееся в виде подсказки при подведении курсора к кнопке.
  • Свойство Style задает внешний вид кнопки. В данном случае, когда стиль задан константой msoButtonIconAndCaption, на кнопке будет отображаться рисунок, сопровождаемый текстом.
  • Свойство Tag также задает описание элемента. Если элемент обладает свойством Tag, то его можно найти, используя значение этого свойства в качестве ключа в стандартном методе поиска FindControl. Напомню, что в этом методе в качестве ключа нельзя использовать свойство Caption.
  • Свойство FaceId позволяет задать идентификатор картинки, размещаемой на кнопке. Конечно, есть определенная трудность в том, чтобы знать какое число соответствует желаемой картинке. С этой проблемой частично можно справиться, используя методы CopyFace и PasteFace.
  • Метод CopyFace копирует в буфер картинку, размещаемую на кнопке.
  • Метод PasteFace переносит картинку из буфера на кнопку. Заметьте, на кнопку можно поместить любой рисунок в формате, допускающем сжатие. Для этого следует скопировать рисунок в буфер, а затем применить метод PasteFace.
  • Таким образом, как видите, программные возможности по созданию красивых кнопок на инструментальной панели ничуть не ниже, чем при выполнении этой работы вручную.

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

    (рис 3.2) Документ с тремя панелями, реализующими собственный интерфейс
    Вернуться к учебному плану