четверг, 17 сентября 2020 г.

Урок 19. Динамическая смена внешнего изображения на фиксирующейся кнопке (на примере Access)

На этом уроке мы рассмотрим пример реализации динамической смены изображений (и надписей) на фиксирующейся (toggleButton) и на обычной (button) кнопках при нажатии на них. Для демонстрации используем приложение Access, чтобы осветить также и вопрос динамической загрузки внешних изображений в этом приложении по т.н. «второму способу», поскольку мной ранее был подробно освещён только «первый способ», когда иконки загружались один раз при загрузке ленты и в процессе работы больше не менялись (см. урок 17).

Мы сделаем новую вкладку, на которой расположим две крупные кнопки: toggleButton и button. На первой кнопке изображение будет меняться в зависимости от состояния нажатости (toggleButton), а на второй — в зависимости от количества нажатий (button), поскольку у этого типа кнопки нет настоящего фиксируемого состояния.

Итак, если у вас осталась ещё старая версия редактора Ribbon XML Editor, обновите её на официальном сайте программы. На момент написания этой статьи актуальной была версия 9.1.

Создайте в Access файл базы данных, и сохраните его на диск. Также подготовьте два изображения 32х32 пикселя, которые мы будем переключать между собой, или скачайте их отсюда:



Можно использовать и более крупные изображения, но на кнопках они всё равно будут отображаться либо в режиме 16х16 либо в режиме 32х32.

Шаг 1. Ввод XML-кода интерфейса

Итак, запускаем Ribbon XML Editor 9.1 и перетаскиваем мышкой созданный в Access файл базы данных из проводника в область окна редактора. Файл откроется, и в шапке программы появится его имя. Замечу, что в новой версии Ribbon XML Editor можно открывать файлы перетаскиванием не только самого файла, но также и его ярлыка.

Напомню, что в редакторе можно открывать базы форматов .accdb, .accde, и .accdr, однако в последних двух случаях функционал будет несколько ограничен (нельзя будет автоматически интегрировать в базу модули с кодом). Поэтому всю работу желательно вести с форматом .accdb.

После открытия базы в редакторе вставляем в окно xml-кода на вкладку интерфейса 2007-й версии следующий код:

<?xml version="1.0" standalone="yes"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui"
onLoad="ПолучитьСсылкуНаОбъектЛенты">
<ribbon startFromScratch="false">
<tabs>
<tab id="Таб1"
label="Демонстрационная вкладка"
insertBeforeMso="TabHomeAccess">
<group id="Гр1"
label="Короногруппа">
<toggleButton id="КнФикс1"
size="large"
getImage="ПолучитьИконку"
onAction="Действие_toggleButton"
getLabel="ПолучитьНадпись"
screentip="Демонстрация динамической смены картинки и надписи для toggleButton"
supertip="Нажимайте на кнопку и следите за изображением. Фон изображения должен меняться при каждом нажатии"/>
<button id="Кн1"
size="large"
getImage="ПолучитьИконку"
onAction="Действие_button"
getLabel="ПолучитьНадпись"
screentip="Демонстрация динамической смены картинки и надписи для button"
supertip="Нажимайте на кнопку и следите за изображением. Фон изображения должен меняться при каждом нажатии"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>

Здесь мы формируем новую вкладку, на ней располагаем группу, внутри которой размещаем две кнопки — toggleButton и button, присваивая им большой размер. Также прописываем процедуры обратного вызова для получения иконки, получения надписи и для выполнения действия при нажатии на кнопку. При желании прописываем в эти контролы также и текст всплывающих подсказок. 

В корневой элемент интерфейса (customUI) в атрибут onLoad прописываем процедуру обратного вызова события загрузки ленты. Оно нам понадобится для получения ссылки на объект ленты для её своевременного обновления при смене изображений.

Если целевая версия нашего будущего интерфейса будет выше 2007-й, и мы не стремимся к совместимости с этой старой версией офиса, то вставляем код на вкладку интерфейса 2010, 2013, 2016, 2019 и корректируем соответствующим образом параметр xmlns тега customUI (используем для этого функцию автодополнения Ctrl + Пробел). Внедрить код для обеих версий офиса, как это было в случае с Excel, Word и PowerPoint, в Access не получится — редактор выдаст соответствующее предупреждение.

Не забываем также задать имя нашего интерфейса, вписав его в поле «Имя интерфейса», расположенное в правом нижнем углу редактора:

В дальнейшем оно нам пригодится для указания нашего интерфейса в настройках базы Access.

Для тех, кто ранее создавал интерфейсы для других приложений офиса, замечу, что в присвоении имени интерфейсу мы видим основное отличие Access от других приложений пакета Офиса. В базу Access можно сохранить не два интерфейса (для разных версий офиса), а несколько интерфейсов, но только для одной заранее выбранной версии офиса. При этом каждый интерфейс сохраняется под своим именем.

В случае нескольких интерфейсов в одной базе, Ribbon XML Editor позволяет переключаться между несколькими интерфейсами, добавлять новые, удалять ненужные, переименовывать и т.п. Это можно делать, раскрыв поле имени интерфейса нажатием на стрелку в правой его части:


 

Кстати, если вы хотите автоматически отформатировать любой xml-код так же, как показано в вышеприведённом примере, то есть, когда атрибуты тегов выравниваются в столбик, то нажмите кнопку автоформатирования с зажатой клавишей Ctrl. Также этот режим для кнопки автоформатирования на постоянной основе можно включить в настройках программы.

Теперь переходим к следующему шагу — созданию в базе таблицы для хранения изображений.

Шаг 2. Создание таблицы для хранения изображений 

Теперь нам надо создать в базе Access специальную системную таблицу для хранения изображений. Нажимаем кнопку импорта в блоке работы с внешними изображениями:

Откроется окно работы с таблицей внешних изображений:


Раскроем верхний комбобокс и, просмотрев имена таблиц, находящихся в открытой базе данных, убедимся, что таблицы с изображениями в базе ещё нет. Кнопкой с крестиком очистим поле верхнего комбобокса, если мы случайно в него что-нибудь занесли, и нажимаем кнопку «Создать таблицу». Будет создана таблица с именем по умолчанию USYSRibbons_Images, которое будет занесено в поле комбобокса, а в поле второго комбобокса автоматически подставится название столбца этой таблицы, предназначенного для идентификаторов.

Замечу, что можно создать таблицу и с любым другим именем. Для этого перед нажатием кнопки создания таблицы следует вписать в поле верхнего комбобокса любое подходящее для этой таблицы имя. Если мы создаём системную таблицу, то её имя должно начинаться с «USYS», но для большей информативности лучше начинать его с «USYSRibbons_». В имени таблицы также можно использовать пробелы и кириллицу.

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

Шаг 3. Добавление вспомогательного модуля Att2Pic в базу.

По факту интерфейс Access понимает только jpg-изображения. Чтобы заставить его работать с png, к файлу базы данных требуется подключить специальный VBA-модуль Att2Pic (в старых версиях Ribbon XML Editor он назывался basGDIPlus, поддерживал только 32-битные версии Access и содержал лишние процедуры), код которого можно увидеть, нажав в окне работы с таблицей внешних изображений на кнопку «Модуль Att2Pic». Откроется окно с кодом модуля, текст которого можно скопировать в буфер, сохранить в файл или сразу внедрить в базу. Нажмём на кнопку внедрения модуля в базу, и через пару секунд появится сообщение об успешном внедрении модуля. Имя модуля переводится как «Вложение (Attachment) — в (to или 2) изображение (Picture)». Он поддерживает как 32-битные, так и 64-битные версии Access.

Внимание! Если при внедрении модуля возникли какие-то ошибки, возможно, у вас не совсем корректно установлен Microsoft Office или присутствуют другие проблемы в вашей системе. О способах их устранения можно прочитать во встроенной в Ribbon XML Editor справке, в разделе «Ошибки OLE при внедрении VBA-модуля в документ». Если же ошибку исправить не удалось, то можно сохранить модуль в файл, а затем импортировать его в базу уже из самого Access.

Справа от кнопки модуля Att2Pic расположена кнопка со знаком вопроса. Нажав на неё, мы можем увидеть примеры использования модуля Att2Pic с подробными комментариями. В них показана реализация двух способов вывода изображения в интерфейс. Как я уже говорил выше, на этом уроке мы применим второй способ получения изображений, используя процедуру обратного вызова, прописанную в параметр getImage целевого элемента интерфейса.

Закрываем окна с кодом модулей. Перед закрытием окна поиска идентификаторов ещё раз убеждаемся, что оба комбобокса заполнены правильно. Нижнее поле найденных идентификаторов пока остаётся пустым, поскольку созданная нами таблица ещё не заполнена идентификаторами и изображениями. 

Нажимаем кнопку «ОК». Программа запоминает имя столбца с идентификаторами, и впоследствии при их добавлении в таблицу будет доставать их оттуда в комбобокс внешних изображений основного окна. При описываемом в этом уроке «втором способе» получения изображений этот комбобокс нам не понадобится, но пусть они там будут на будущее.

Шаг 4. Генерация процедур обратного вызова.

Теперь в нашей базе есть xml-код интерфейса, таблица для png-изображений и модуль для доставания png-картинки из вложения таблицы изображений и трансформирования этой картинки в распознаваемый интерфейсом объект StdPicture. Осталось создать прописанные в xml-коде процедуры обратного вызова «ПолучитьИконку» (в которой использовать подключённый модуль Att2Pic для возвращения объекта картинки в интерфейс), «ПолучитьНадпись», «Действие_toggleButton» и «Действие_button».

Для облегчения задачи в Ribbon XML Editor есть генератор шаблонов функций обратного вызова. Сгенерируем шаблоны соответствующей кнопкой или сочетанием клавиш (Alt + F11). У нас получится вот такой шаблонный код модуля, автоматически названного RibbonCallbacks:

Attribute VB_Name = "RibbonCallbacks"	'Имя текущего модуля
Option Explicit	'Потребовать явного объявления всех переменных в файле

'customUI (элемент: customUI, атрибут: onLoad), 2007
Public Sub ПолучитьСсылкуНаОбъектЛенты(ribbon As IRibbonUI)
    'Объявите глобальную переменную объекта ленты: Public myRibbonObject As IRibbonUI
    Set myRibbonObject = ribbon
End Sub

'КнФикс1 (элемент: toggleButton, атрибут: getImage), 2007
'Кн1 (элемент: button, атрибут: getImage), 2007
Public Sub ПолучитьИконку(control As IRibbonControl, ByRef image)
    'image = "HappyFace"
    Dim ImageFilename As String
    ImageFilename = DLookup("Image", "USYSRibbons_Images", "Image_or_control_identifier='" & control.id & "'")
    Set image = Att2Pic.AttachmentToPicture("USYSRibbons_Images", "Image", ImageFilename)
End Sub

'КнФикс1 (элемент: toggleButton, атрибут: onAction), 2007
Public Sub Действие_toggleButton(control As IRibbonControl, pressed As Boolean)
    MsgBox "Сработала процедура, заданная в onAction элемента " + control.ID + " (нажатость = " + CStr(pressed) + ")"
End Sub

'КнФикс1 (элемент: toggleButton, атрибут: getLabel), 2007
'Кн1 (элемент: button, атрибут: getLabel), 2007
Public Sub ПолучитьНадпись(control As IRibbonControl, ByRef label)
    label = "label элемента " + control.ID
End Sub

'Кн1 (элемент: button, атрибут: onAction), 2007
Public Sub Действие_button(control As IRibbonControl)
    MsgBox "Сработала процедура, заданная в onAction элемента " + control.ID
End Sub 

Внедрим этот модуль в базу Access так же, как мы это уже проделывали с модулем Att2Pic.

Шаг 5. Наполнение шаблонов процедур обратного вызова.

Теперь запустим базу на выполнение соответствующей кнопкой или клавишей F9. Ribbon XML Editor закроет базу данных в себе и откроет её в Access. Все остальные шаги будем проделывать уже в Access.

Если при открытии базы появится предупреждение системы безопасности Access об отключении запуска активного содержимого, нажмём кнопку «Включить содержимое».

Поскольку таблица изображений у нас пустая и нужный код в шаблоны ещё не прописан, при запуске базы будут выскакивать различные ошибки. Закроем окно сообщения о первой же ошибке нажатием на кнопку «End» и откроем модуль RibbonCallbacks:

Откроется окно довольно примитивного редактора Microsoft Visual Basic for Applications с кодом модуля. Вы можете либо осмысленно менять содержимое этого модуля, постепенно дорабатывая новый код, либо просто скопировать вместо всего его содержимого вот этот уже готовый исходник:

Option Explicit 'Потребовать явного объявления всех переменных в файле
'Флаг нажатости первой кнопки toggleButton
Public flag_КнФикс1 As Boolean
'Флаг эмуляции нажатости второй кнопки button
Public flag_Кн1 As Boolean
'Переменная для хранения объекта ленты
Public myRibbonObject As IRibbonUI

'customUI (элемент: customUI, атрибут: onLoad), 2007
Public Sub ПолучитьСсылкуНаОбъектЛенты(ribbon As IRibbonUI)
    'Присваиваем объект ленты переменной для дальнейшего обращения к ленте, чтобы её обновить
    Set myRibbonObject = ribbon
End Sub

'Общая процедура получения нужной иконки
'КнФикс1 (элемент: toggleButton, атрибут: getImage), 2007
'Кн1 (элемент: button, атрибут: getImage), 2007
Public Sub ПолучитьИконку(control As IRibbonControl, ByRef Image)
    'Переменная имени файла иконки
    Dim ImageFilename As String
    'Переменная для формирования нужного идентификатора иконки в зависимости от внешних факторов (в частности, состояния нажатости)
    Dim ImageID As String
    'Если нажата кнопка КнФикс1, формируем соответствующее имя идентификатора изображения (или можем сразу задать имя файла в ImageFilename, чтобы пропустить процедуру поиска DLookup)
    If control.Id = "КнФикс1" Then
        If flag_КнФикс1 Then ImageID = "Корона_нажатая" Else ImageID = "Корона"
    'Если нажата кнопка Кн1, задаём соответствующее имя идентификатора изображения (или можем сразу задать имя файла в ImageFilename, чтобы пропустить процедуру поиска DLookup)
    ElseIf control.Id = "Кн1" Then
        If flag_Кн1 Then ImageID = "Корона_нажатая" Else ImageID = "Корона"
    End If
    'Ищем имя файла по иденификатору (если выше мы сразу задали имя файла, то пропускаем эту строку)
    ImageFilename = DLookup("Image", "USYSRibbons_Images", "Image_or_control_identifier='" & ImageID & "'")
    'Достаём изображение по его имени файла
    Set Image = Att2Pic.AttachmentToPicture("USYSRibbons_Images", "Image", ImageFilename)
End Sub

'КнФикс1 (элемент: toggleButton, атрибут: onAction), 2007
Public Sub Действие_toggleButton(control As IRibbonControl, pressed As Boolean)
    'Если нажата кнопка КнФикс1, устанавливаем соответствующий флаг нажатости
    If control.Id = "КнФикс1" Then
        flag_КнФикс1 = pressed
    End If
    'Обновляем иконку на текущем контроле
    myRibbonObject.InvalidateControl control.Id
End Sub

'КнФикс1 (элемент: toggleButton, атрибут: getLabel), 2007
'Кн1 (элемент: button, атрибут: getLabel), 2007
Public Sub ПолучитьНадпись(control As IRibbonControl, ByRef label)
    If control.Id = "КнФикс1" Then
        If flag_КнФикс1 Then label = "Нажатая toggleButton" Else label = "toggleButton"
    ElseIf control.Id = "Кн1" Then
        If flag_Кн1 Then label = "Как бы нажатая button" Else label = "button"
    End If
End Sub

'Кн1 (элемент: button, атрибут: onAction), 2007
Public Sub Действие_button(control As IRibbonControl)
    If control.Id = "Кн1" Then
        flag_Кн1 = Not flag_Кн1
    End If
    'Обновляем иконку на текущем контроле
    myRibbonObject.InvalidateControl control.Id
End Sub

Затем нам необходимо через меню редактора Visual Basic (Tools -> References) включить ссылки на следующие библиотеки, если они ещё не подключены:

  1. Visual Basic For Applications
  2. Microsoft Access XX.0 Object Library
  3. OLE Automation
  4. Microsoft Office XX.0 Access database engine Object Library
  5. Microsoft Office XX.0 Object Library 

где XX - номер версии Access, например: 12 (2007), 14 (2010), 15 (2013), 16 (2016 и 2019).

После этих действий сохраняем модуль, закрываем окно Microsoft Visual Basic for Applications и переходим к следующему шагу.

Шаг 6. Заполнение таблицы изображений в Access.

Теперь нам предстоит заполнить созданную нами в базе данных Access на шаге 2 таблицу изображений. Открываем её непосредственно в Access:


Она имеет 4 столбца, но нам важны лишь 2 из них — Image_or_control_identifier и Image (в режиме таблицы вместо названия столбца показана скрепка). Столбец ID будет заполняться сам по мере добавления строк, а столбец Comment можно заполнять по желанию.

Заполняем таблицу — в поле Image_or_control_identifier заносим идентификаторы изображений, в нашем случае это будут «Корона» и «Корона_нажатая», а в поле Image в виде вложения добавляем подготовленный заранее файл соответствующей картинки. 

Замечу, что в каждое поле вложения Access позволяет добавить по несколько файлов, но нам следует добавлять только по одному файлу на строку таблицы, чтобы правильно соблюсти соответствие картинки и её идентификатора.

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

Шаг 7. Подключение интерфейса в настройках базы Access.

У нас всё готово. Осталось лишь подключить интерфейс в настройках базы Access. Открываем настройку «Файл → Параметры → Текущая база данных → Параметры ленты и панелей инструментов → Имя ленты» и выбираем из комбо-бокса имя нашего интерфейса. Нажимаем кнопку ОК, закрываем и снова открываем базу. Если вы всё сделали правильно, то сразу после запуска в ленте появится наша вкладка с двумя кнопками, при нажатии на которые изображение на них будет динамически меняться:



На этом урок закончен, спасибо за внимание :-) 


1 комментарий:

  1. Завершил курс, большое спасибо автору. Всё написано очень доступным языком и талантливо.
    Есть два момента, которые, возможно, необходимо доработать.
    1. В комментариях к уроку 17, пользователь ыра написала:

    Для отображения таблиц – наводим стрелку на “Все объекты Access”, правый клик и выбираем “Параметры навигации>Показывать системные объекты”.

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

    2. В последнем уроке, информацию о подключаемых библиотеках лучше сделать перед блоком кода, а не после. Программирую на VBA уже 10 лет, для интереса код не копировал, а переписывал, при этом обратил внимание, что автодополнение не работает. Благо, я понимаю, в чём дело, и быстро увидел, что нужно сделать после блока кода. Но возможно, не все читатели сразу обратят на это внимание, и как следствие столкнутся с ошибками.

    ОтветитьУдалить