Добрый день. По следам недавно выполненной разработки хочу рассказать, как можно сделать собственные действия интеграции в Microsoft Word, позволяющие расширить возможности взаимодействия этого редактора с DIRECTUM. Чем мы будем заниматься: мы создадим собственную надстройку, которая позволит связывать фрагменты текста документа с записями справочника DIRECTUM. Надстройку будем создавать в MS Visual Studio на Visual Basic, а для ее работы необходимо наличие установленной среды выполнения Visual Studio Tools for Office Runtime. Сразу оговорюсь: возможности надстройки будут небогатыми, а практическая ценность сомнительной. Наша с вами задача — увидеть, как это работает, чтобы потом смочь по аналогии создать что-то более сложное, полезное и, возможно, дорогое.
Для примера я выдумал такую простую задачу. Допустим, у меня есть документ с рецептом приготовления супа — рабочая инструкция, в которой по пунктам расписан порядок действий, приводящий к появлению блюда.
Для каждого из пунктов рецепта мне нужно с помощью кнопки на ленте назначить работника (или даже нескольких), ответственного за его выполнение. Ответственные должны быть видны в документе. Более того, мне нужна возможность прямо из документа открывать карточки ответственных работников — вторая кнопка на ленте. А еще я хочу, находясь в карточке работника, быстро находить соответствующий ему пункт рецепта в документе. Чтобы все это сделать, нам нужно решить следующие задачи:
Прямо по пунктам и будем делать.
Создаем в Visual Studio новый проект на основе шаблона Word 2010 Add-In (версия может отличаться), даем проекту имя (я назвал SampleIntegration) и указываем расположение файлов проекта, если не устраивает предложенное:
После нажатия на OK в редакторе откроется исходный код, в котором уже будет создан класс надстройки (у меня по умолчанию это ThisAddIn) и заготовки процедур для обработки событий старта и завершения работы надстройки. К ним мы вернемся позже, а пока приступим к выполнению пунктов плана.
Параметры подключения к системе предлагаю хранить в строковом параметре ConnectionString в реестре по адресу
HKCU\Software\Computer\Directum\SampleIntegration
В коде класса надстройки создадим для этого две константы:
' Параметры подключения к DIRECTUM Private Const REG_SECTION As String = "Software\Computer\DIRECTUM\SampleIntegration" Private Const CONNECTION_PARAMS_KEY As String = "ConnectionString"
переменную для хранения ссылки на объект IApplication системы DIRECTUM:
Private DirectumApplication As Object
и функцию, которая свяжет все это воедино:
Private Function GetApplication() As Object Dim LoginPoint As Object Dim ConnectionParams As String If DirectumApplication Is Nothing Then Try Dim Settings As RegistryKey Settings = Registry.CurrentUser.OpenSubKey(REG_SECTION, False) ConnectionParams = Settings.GetValue(CONNECTION_PARAMS_KEY, "").ToString If ConnectionParams = "" Then GetApplication = Nothing Else LoginPoint = CreateObject("SBLogon.LoginPoint") GetApplication = LoginPoint.GetApplication(ConnectionParams) End If Catch ex As Exception GetApplication = Nothing MsgBox("Ошибка интеграции с DIRECTUM: " & ex.Message) End Try Else GetApplication = DirectumApplication End If End Function
Вкратце: функция читает из реестра параметры подключения к системе DIRECTUM и пытается подключиться к ней. Если подключиться удалось, функция возвращает ссылку на объект IApplication, иначе — значение Nothing. Поскольку функция обращается к свойствам и методам классов для работы с реестром (Registry) и ключом реестра (RegistryKey), нужно импортировать соответствующее пространство имен, дописав в самом начале исходного кода надстройки:
Imports Microsoft.Win32
Эту функцию мы сможем вызывать из любого места внутри класса надстройки, когда нам потребуется подключение к DIRECTUM. Теперь вернемся к обработчикам событий старта и завершения работы надстройки и реализуем в них явную инициализацию и очистку переменной DirectumApplication:
Private Sub ThisAddIn_Startup() Handles Me.Startup DirectumApplication = Nothing End Sub Private Sub ThisAddIn_Shutdown() Handles Me.Shutdown DirectumApplication = Nothing End Sub
На этом с первым пунктом плана покончено.
Добавляем в наш проект новый элемент — ленту:
После добавления ленты откроется вкладка с визуальным редактором ленты. Свойства ленты оставляем по умолчанию, а вот у вкладки есть смысл поправить значение свойства ControlId на "TabHome". Это приведет к тому, что наша группа кнопок будет размещена на вкладке "Главная", а не на вкладке "Надстройки". Далее, у группы кнопок меняем надпись на "Ответственные". Теперь нужно добавить в группу две кнопки. Для этого в меню View выбираем пункт Toolbox из появившегося диалога добавляем в нашу группу кнопок на ленте элемент Button из группы Office Ribbon Controls. Задаем имя кнопки: "ButtonAssign", и надпись: "Назначить ответственного". В свойстве ControlSize (размер кнопки на ленте) выбираем значение "RibbonControlSizeLarge". Аналогичным образом добавляем в группу вторую кнопку — с именем "ButtonOpenCard", надписью "Открыть карточку" и размер кнопки тоже RibbonControlSizeLarge. Кнопкам можно назначить иконки. Должно получиться что-то такое:
Мы выполнили второй пункт плана.
Сначала напишем обработчик для кнопки "Назначить ответственного". Для этого создадим процедуру AssignResponsible. Саму процедуру пишем внутри класса надстройки. Суть кода вкратце: выбираем работника из справочника, после чего на выделенный фрагмент текста в документе ставим закладку с ID выбранного работника и добавляем примечание.
' Процедура: назначение ответственного Sub AssignResponsible() DirectumApplication = GetApplication() If Not (DirectumApplication Is Nothing) Then Try ' Объекты для работы со справочником работников Dim ReferenceFactory As Object Dim ReferenceComponent As Object Dim View As Object ' Открыть справочник для выбора ReferenceFactory = DirectumApplication.ReferencesFactory.ReferenceFactory("РАБ") ReferenceComponent = ReferenceFactory.GetComponent() View = ReferenceComponent.CreateView(ReferenceComponent.MainViewCode) View.ViewMode = 1 ' vmSelect View.MultiSelection = False CreateObject("WScript.Shell").AppActivate(DirectumApplication.PID) ' Вытащить окно приложения DIRECTUM с заднего плана наверх View.MainForm.ShowModal() ' Если запись выбрана, создать закладку и примечание If View.MainForm.Result = 1 Then ' mrOk Dim EmployeeID As Integer = View.SelectedRecordsID(0) Dim Selection As Microsoft.Office.Interop.Word.Selection = Application.Selection Application.ActiveDocument.Bookmarks.Add("Employee_" & EmployeeID, Selection.Range) Dim EmployeeName As String = ReferenceFactory.ObjectInfo(EmployeeID).Name Selection.Comments.Add(Selection.Range, "Ответственный: " & EmployeeName) End If Catch ex As Exception MsgBox(ex.Message) End Try End If End Sub
А чтобы связать процедуру с кнопкой, добавим вызов процедуры в обработчик нажатия кнопки (в дизайнере ленты кликаем два раза по кнопке "Назначить ответственного" и вписываем одну строчку в тело обработчика):
Private Sub ButtonAssign_Click() Handles ButtonAssign.Click Globals.ThisAddIn.AssignResponsible() End Sub
Аналогичным образом оживляем вторую кнопку на ленте — создаем в коде класса ThisAddIn процедуру OpenCard, которая должна открывать карточку ответственного работника:
' Открыть карточку ответственного работника Public Sub OpenCard() ' Вычислить ID работника из имени закладки, на которой стоит курсор Dim EmployeeID As Integer = 0 For Each Bookmark As Word.Bookmark In Application.Selection.Bookmarks If Bookmark.Name.Contains("Employee_") Then EmployeeID = Bookmark.Name.Replace("Employee_", "") Exit For End If Next ' Если вычислился ID работника, открыть его карточку If EmployeeID > 0 Then Try DirectumApplication = GetApplication() If Not DirectumApplication Is Nothing Then Dim ReferenceFactory As Object = DirectumApplication.ReferencesFactory.ReferenceFactory("РАБ") ReferenceFactory.GetObjectByID(EmployeeID).Form.ShowModal() End If Catch ex As Exception MsgBox(ex.Message) End Try End If End Sub
и пишем ее вызов в обработчике нажатия кнопки:
Private Sub ButtonOpenCard_Click() Handles ButtonOpenCard.Click Globals.ThisAddIn.OpenCard() End Sub
Третий пункт плана готов. Уже можно собирать надстройку (меню Build > Build SampleIntegration) и тестировать разработанный функционал. При сборке надстройка автоматически регистрируется в Word, загрузится при его следующем запуске, и можно будет назначить ответственных за приготовление картофельного супа:
Кнопки "Назначить ответственного" и "Открыть карточку" присутствуют на ленте и работают в полном соответствии с задуманной логикой.
Чтобы открыть из карточки работника его пункт в документе рецепта, мы должны сообщить документу ID работника. Проще всего сделать это через параметр в той же ветке реестра, где мы храним параметры подключения к DIRECTUM. После того как ID работника окажется в реестре, нужно открыть сам документ. Что это будет за документ и как его найти, нам не интересно в данном случае. У нас для простоты ID документа будет храниться непосредственно в карточке работника. В реальной жизни это может быть связанный с записью справочника документ, имеющий определенный тип карточки, например. Добавим на форму карточки работника кнопку и связанное с ней действие:
// Ветка реестра для интеграции с Word REGISTRY_KEY = "Software\Computer\DIRECTUM\SampleIntegration" EDocID = Object.DocumentID EDocument = EDocuments.GetObjectByID(EDocID) // Передать ID текущего работника в реестр и открыть документ RegWrite(REGISTRY_KEY; "EmployeeID"; Object.SYSREQ_ID) EDocument.Open(true)
Документ при открытии должен прочитать из реестра ID работника, найти соответствующую закладку и перейти на нее. Для этого в код класса надстройки добавим обработчик открытия документа:
Private Sub Application_DocumentOpen(ByVal Doc As Word.Document) Handles Application.DocumentOpen Dim EmployeeID As Integer ' Прочитать ID работника из реестра Try Dim Settings As RegistryKey Settings = Registry.CurrentUser.OpenSubKey(REG_SECTION, True) EmployeeID = Settings.GetValue("EmployeeID", 0).ToString Settings.DeleteValue("ObjectID", False) Settings = Nothing Catch ex As Exception EmployeeID = 0 End Try ' Перейти на соответствующую работнику закладку в документе If EmployeeID > 0 Then For Each Bookmark As Word.Bookmark In Doc.Bookmarks If Bookmark.Name.Contains("Employee_" & EmployeeID) Then Application.Selection.GoTo(What:=Word.WdGoToItem.wdGoToBookmark, Name:=Bookmark.Name) Exit For End If Next End If End Sub
Это был четвертый и последний пункт нашего плана. Теперь надстройка полностью готова к работе, а задача решена.
Программного кода в материале получилось едва ли не больше, чем собственно текста. Но это делалось намеренно — чтобы можно было проследить, что и откуда появилось в надстройке. Следующий шаг — создание инсталлятора для установки надстройки на рабочие места. Можно и без инсталлятора, но удобнее все же с ним. Я делал вот по этой инструкции.
Прикладываю мой проект надстройки, на основе которого написан материал: SampleIntegration.zip (20,62 Кб).
Алексей, интересный материал.
Правильно ли я понимаю, что таким способом можно и стандартное интеграционное меню подправить? Или только в новом?
Спасибо, Сергей.
Думаю, что можно. Правда, для этого нужно, как минимум, иметь идентификаторы элементов управления (риббон, группа и меню) стандартной интеграции на ленте, чтобы обратиться к ним программно.
Авторизуйтесь, чтобы написать комментарий