Создание интерфейса документа - одна из важных задач, стоящих при разработке практически любых документов. Создание меню, командных кнопок и других элементов управления, вынесенных на
В Office команды меню и CommandBars. Эти панели могут быть причалены или пристыкованы (docked) к левому, правому, верхнему или нижнему краю документа, а могут находиться и в плавающем состоянии. Каждая панель (объект CommandBar ) содержит набор меню или командных кнопок - коллекцию CommandBarControls. Элементы этой коллекции (объекты CommandBarControl ) в зависимости от их типа представляют отдельные меню, команды меню или
Большая часть задач по созданию собственных инструментальных панелей и элементов, располагаемых на них, обычно решается вручную без использования программирования, хотя понятно, что невозможно обойтись без написания макросов, вызываемых в ответ на нажатие командных кнопок или выбор команд меню, созданных собственноручно. Но почти все эти задачи (к сожалению, есть некоторые исключения) можно решать и программно, чем мы сейчас и займемся.
Коллекция 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, если поиск был безуспешным.В Office 2000 у этой коллекции появились новый метод, новое свойство и новое событие:
Function FindControls([Type], [Id], [Tag], [Visible]) As CommandBarControls. Является обобщением метода FindControl, возвращая в случае успеха коллекцию CommandBarControls, содержащую все элементы, удовлетворяющие критериям поиска.Property AdaptiveMenus As Boolean. Позволяет включить или выключить свойство адаптивности встроенных меню, автоматически настраивающее их видимый состав в зависимости от частоты использования той или иной команды меню.Event OnUpdate(). Событие возникает при любых изменениях командной панели объекта 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
В эту коллекцию входят элементы, располагаемые на одной инструментальной панели. Коллекция обладает только традиционными свойствами и методами. Единственное, что следует рассмотреть - это метод Add и его параметры:
Function Add([Type], [Id], [Parameter], [Before], [Temporary]) As CommandBarControl
Метод позволяет добавлять новые элементы в коллекцию. Его параметры:
Add. Если значением Type является константа msoControlButton, то возвращается объект класса CommandBarButton, для значений: msoControlEdit, msoControlDropdown, msoControlComboBox - возвращается объект класса CommandBarComboBox и, наконец, если тип имеет значение msoControlPopup, то возвращаемый класс объекта - CommandBarPopupХотя все создаваемые элементы панели относятся к одному из трех различных классов, все они принадлежат также объединяющему классу CommandBarControl. Мы не будем подробно останавливаться на всех свойствах и методах, как отдельных классов, так и объединяющего элементы класса. Рассмотрим только, как решить главную для команд задачу - выполнить некоторую процедуру в ответ на выбор команды меню или щелчок кнопки. Действия, выполняемые командой, должны быть запрограммированы в виде макроса на языке VBA. Напомним, что макрос - это процедура без параметров. Однако есть некоторый способ передачи информации в исполняемый макрос. Для этого можно использовать параметр Parameter, задаваемый при создании элемента или, что удобнее, свойство Parameter. Конечно, для всех встроенных элементов макрос, задающий команду, уже написан. Но для собственных элементов его нужно написать самому, а затем макрос связать с элементом. Свойство OnAction, которым обладают все элементы панели, позволяет связать элемент с исполняемым макросом. Формально свойство является строкой, задающей имя макроса, который и будет вызываться в ответ на выбор пользователя.
Давайте перейдем теперь к рассмотрению задач, возникающих при создании интерфейса. Я начну c рассмотрения процедур, реализующих некоторые дополнительные свойства изучаемых нами объектов.
К сожалению, у объектов 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. Тем самым будут охвачены практически все возможные варианты работы с
Создадим документ, в котором:
Весь инструментарий, необходимый для создания головного меню, уже создан и приведен в предыдущем разделе. Поэтому мне остается привести достаточно понятную процедуру, которая шаг за шагом создает главное меню в соответствии с приведенным планом:
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) Документ, имеющий собственное главное меню
Макросы, отвечающие за вызов команд меню, встроенных или собственных, могут быть сколь угодно сложными. Зачастую, выбор команды меню является, по существу, приглашением к началу диалога с пользователем. Соответствующий макрос открывает форму (диалоговое окно), содержащее многочисленные кнопки, вкладки и другие элементы интерфейса. Типичным примером являются CommandBarCombobox, позволяющими пользователю при работе с меню задать самостоятельно или выбрать подходящее значение из раскрывающегося списка. Примеры таких элементов хорошо знакомы по встроенному меню. С ними приходится, например, встречаться при выборе стиля, шрифта, подходящего размера шрифта или масштаба.
Давайте создадим панель с собственными элементами подобного типа. Я помещу на панель три элемента разных типов, но принадлежащих одному классу - CommandBarComboBox:
Edit. При работе с таким элементом пользователь имеет возможность ввести свой текст в окно редактирования или согласиться с предложенным ему текстом. Макрос, вызываемый в ответ на действия пользователя, может проанализировать текст, введенный пользователем и реагировать на это соответствующим образом.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-го элемента списка.
Напомню, класс 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) Документ с тремя панелями, реализующими собственный интерфейс
Создание интерфейса документа - одна из важных задач, стоящих при разработке практически любых документов. Создание меню, командных кнопок и других элементов управления, вынесенных на
В Office команды меню и CommandBars. Эти панели могут быть причалены или пристыкованы (docked) к левому, правому, верхнему или нижнему краю документа, а могут находиться и в плавающем состоянии. Каждая панель (объект CommandBar ) содержит набор меню или командных кнопок - коллекцию CommandBarControls. Элементы этой коллекции (объекты CommandBarControl ) в зависимости от их типа представляют отдельные меню, команды меню или
Большая часть задач по созданию собственных инструментальных панелей и элементов, располагаемых на них, обычно решается вручную без использования программирования, хотя понятно, что невозможно обойтись без написания макросов, вызываемых в ответ на нажатие командных кнопок или выбор команд меню, созданных собственноручно. Но почти все эти задачи (к сожалению, есть некоторые исключения) можно решать и программно, чем мы сейчас и займемся.
Коллекция 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, если поиск был безуспешным.В Office 2000 у этой коллекции появились новый метод, новое свойство и новое событие:
Function FindControls([Type], [Id], [Tag], [Visible]) As CommandBarControls. Является обобщением метода FindControl, возвращая в случае успеха коллекцию CommandBarControls, содержащую все элементы, удовлетворяющие критериям поиска.Property AdaptiveMenus As Boolean. Позволяет включить или выключить свойство адаптивности встроенных меню, автоматически настраивающее их видимый состав в зависимости от частоты использования той или иной команды меню.Event OnUpdate(). Событие возникает при любых изменениях командной панели объекта 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
В эту коллекцию входят элементы, располагаемые на одной инструментальной панели. Коллекция обладает только традиционными свойствами и методами. Единственное, что следует рассмотреть - это метод Add и его параметры:
Function Add([Type], [Id], [Parameter], [Before], [Temporary]) As CommandBarControl
Метод позволяет добавлять новые элементы в коллекцию. Его параметры:
Add. Если значением Type является константа msoControlButton, то возвращается объект класса CommandBarButton, для значений: msoControlEdit, msoControlDropdown, msoControlComboBox - возвращается объект класса CommandBarComboBox и, наконец, если тип имеет значение msoControlPopup, то возвращаемый класс объекта - CommandBarPopupХотя все создаваемые элементы панели относятся к одному из трех различных классов, все они принадлежат также объединяющему классу CommandBarControl. Мы не будем подробно останавливаться на всех свойствах и методах, как отдельных классов, так и объединяющего элементы класса. Рассмотрим только, как решить главную для команд задачу - выполнить некоторую процедуру в ответ на выбор команды меню или щелчок кнопки. Действия, выполняемые командой, должны быть запрограммированы в виде макроса на языке VBA. Напомним, что макрос - это процедура без параметров. Однако есть некоторый способ передачи информации в исполняемый макрос. Для этого можно использовать параметр Parameter, задаваемый при создании элемента или, что удобнее, свойство Parameter. Конечно, для всех встроенных элементов макрос, задающий команду, уже написан. Но для собственных элементов его нужно написать самому, а затем макрос связать с элементом. Свойство OnAction, которым обладают все элементы панели, позволяет связать элемент с исполняемым макросом. Формально свойство является строкой, задающей имя макроса, который и будет вызываться в ответ на выбор пользователя.
Давайте перейдем теперь к рассмотрению задач, возникающих при создании интерфейса. Я начну c рассмотрения процедур, реализующих некоторые дополнительные свойства изучаемых нами объектов.
К сожалению, у объектов 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. Тем самым будут охвачены практически все возможные варианты работы с
Создадим документ, в котором:
Весь инструментарий, необходимый для создания головного меню, уже создан и приведен в предыдущем разделе. Поэтому мне остается привести достаточно понятную процедуру, которая шаг за шагом создает главное меню в соответствии с приведенным планом:
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) Документ, имеющий собственное главное меню
Макросы, отвечающие за вызов команд меню, встроенных или собственных, могут быть сколь угодно сложными. Зачастую, выбор команды меню является, по существу, приглашением к началу диалога с пользователем. Соответствующий макрос открывает форму (диалоговое окно), содержащее многочисленные кнопки, вкладки и другие элементы интерфейса. Типичным примером являются CommandBarCombobox, позволяющими пользователю при работе с меню задать самостоятельно или выбрать подходящее значение из раскрывающегося списка. Примеры таких элементов хорошо знакомы по встроенному меню. С ними приходится, например, встречаться при выборе стиля, шрифта, подходящего размера шрифта или масштаба.
Давайте создадим панель с собственными элементами подобного типа. Я помещу на панель три элемента разных типов, но принадлежащих одному классу - CommandBarComboBox:
Edit. При работе с таким элементом пользователь имеет возможность ввести свой текст в окно редактирования или согласиться с предложенным ему текстом. Макрос, вызываемый в ответ на действия пользователя, может проанализировать текст, введенный пользователем и реагировать на это соответствующим образом.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-го элемента списка.
Напомню, класс 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) Документ с тремя панелями, реализующими собственный интерфейс
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.