Опубликован: 13.09.2006 | Уровень: для всех | Доступ: платный | ВУЗ: Тверской государственный университет
Лекция 3:

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

< Лекция 2 || Лекция 3: 1234 || Лекция 4 >

Коллекция 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
Листинг 3.1.

Первая из них проверяет существование панели с заданным именем в коллекции панелей. Обратите внимание, имя можно задавать и по-русски, поскольку проверяются оба свойства 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
Листинг 3.2.

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

Set bar = CommandBars("File")
Листинг 3.3.

Но нельзя получить этот элемент в процессе перебора элементов коллекции 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
Листинг 3.4.

Говоря о коллекции 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
Листинг 3.5.

Поскольку панели добавляются, то их нужно уметь и удалять, - операция 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
Листинг 3.6.

Приведем еще одну обратную операцию. Поскольку в процедуре 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
Листинг 3.7.

Добавлять, конечно, нужно не только панели, но и пункты меню в уже созданную панель, команды в уже созданное меню и так далее. Несмотря на то, что существует метод 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
Листинг 3.8.

В процедуре 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
Листинг 3.9.

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

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
Листинг 3.10.
< Лекция 2 || Лекция 3: 1234 || Лекция 4 >
Андрей Галушко
Андрей Галушко
Украина, Конотоп, КИПТ
Анар Каныбетова
Анар Каныбетова
Казахстан, Кызылорда