Своя интеграция Microsoft Word с DIRECTUM

43 2

Добрый день. По следам недавно выполненной разработки хочу рассказать, как можно сделать собственные действия интеграции в Microsoft Word, позволяющие расширить возможности взаимодействия этого редактора с DIRECTUM. Чем мы будем заниматься: мы создадим собственную надстройку, которая позволит связывать фрагменты текста документа с записями справочника DIRECTUM. Надстройку будем создавать в MS Visual Studio на Visual Basic, а для ее работы необходимо наличие установленной среды выполнения Visual Studio Tools for Office Runtime. Сразу оговорюсь: возможности надстройки будут небогатыми, а практическая ценность сомнительной. Наша с вами задача — увидеть, как это работает, чтобы потом смочь по аналогии создать что-то более сложное, полезное и, возможно, дорогое.

Задача

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

Для каждого из пунктов рецепта мне нужно с помощью кнопки на ленте назначить работника (или даже нескольких), ответственного за его выполнение. Ответственные должны быть видны в документе. Более того, мне нужна возможность прямо из документа открывать карточки ответственных работников — вторая кнопка на ленте. А еще я хочу, находясь в карточке работника, быстро находить соответствующий ему пункт рецепта в документе. Чтобы все это сделать, нам нужно решить следующие задачи:

  • Научить Word подключаться к DIRECTUM.
  • Добавить группу кнопок на ленту и создать две кнопки в ней.
  • Написать обработчики для кнопок, чтобы кнопки не были просто мебелью.
  • Наконец, нам нужно слегка доработать карточку работника — научить ее открывать документ рецепта с переходом сразу на нужный пункт.

Прямо по пунктам и будем делать. 

Создание надстройки

Создаем в Visual Studio новый проект на основе шаблона Word 2010 Add-In (версия может отличаться), даем проекту имя (я назвал SampleIntegration) и указываем расположение файлов проекта, если не устраивает предложенное:

После нажатия на OK в редакторе откроется исходный код, в котором уже будет создан класс надстройки (у меня по умолчанию это ThisAddIn) и заготовки процедур для обработки событий старта и завершения работы надстройки. К ним мы вернемся позже, а пока приступим к выполнению пунктов плана.

Подключаемся к системе DIRECTUM

Параметры подключения к системе предлагаю хранить в строковом параметре 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 Кб).

Сергей Рудин

Алексей, интересный материал.

Правильно ли я понимаю, что таким способом можно и стандартное интеграционное меню подправить? Или только в новом?

Алексей Семакин

Спасибо, Сергей.
Думаю, что можно. Правда, для этого нужно, как минимум, иметь идентификаторы элементов управления (риббон, группа и меню) стандартной интеграции на ленте, чтобы обратиться к ним программно.

Авторизуйтесь, чтобы написать комментарий