chapter 10

27
Перевод Loriem, 2010 Глава 10. The Ribbon 1 В начале 80-х, одновременно с появлением первых персональных компьютеров, компания IBM решила определить стандарты построения пользовательского интерфейса PC-приложений (в то время для DOS). Эта спецификация была названа «типовой пользовательский интерфейс» — Common User Access (CUA), и Microsoft в течение многих лет активно поддерживала его в программах DOS и во всех первых версиях Windows, определив, по сути, естественный способ взаимодействия с большинством программ. Вы знаете, что команды Copy и Paste находятся в меню Edit, а для сохранения или загрузки документа нужно взглянуть в меню File, так что при изучении всякой новой программы вам необходимо знать на несколько вещей меньше. Пусть даже впоследствии было сделано много добавлений к CUA, включая панели инструментов, контекстные меню, панели команд и многое другое, его основной каркас сохранялся около 20 лет. В первый раз Microsoft отступила от этого стандарта в Office 2007 (и отчасти в Windows Vista), предложив новую парадигму пользовательского интерфейса, названного Fluent User Interface. Этот интерфейс также известен как Интерфейс Ribbon, по своему основному визуальному элементу. Не хочу здесь вдаваться в споры относительно правильности отступления от стандарта (лично у меня есть некоторые сомнения), только обращу внимание на тот факт, что без некоторых, готовых к использованию, визуальных компонентов реализация этого пользовательского интерфейса с его набором строгих правил является нетривиальной задачей. В Delphi 2009 имеется такой набор визуальных элементов управления, обсуждение которого и является темой настоящей главы. Он был разработан для CodeGear Джереми Нортом (Jeremy North) 2 . Введение в Fluent User Interface Как выше было упомянуто, Fluent User Interface был изобретен компанией Microsoft, ныне добивающейся патента на него 3 . Этот патент сосредоточен не на коде, реализующем пользовательский интерфейс (элементы управления Ribbon использованы в Office 2007), а на дизайне самого интерфейса. В ссылках Microsoft на этот интерфейс присутствуют слова «Microsoft for the Office UI». 1 англ. лента. 2 Джереми Норт является автором нескольких Дельфи-компонентов, расширений среды разработки и инструментов для разработчиков (включая расширенный клиент Quality Central). Он также оказывал помощь в разработке программ Compact Framework, написанных на Delphi for .NET. О Джереми Норте вы можете узнать больше на его сайте http://www.jed-software.com. 3 Информация об этой заявке на патент и соответствующее обсуждение могут быть найдены в Википедии: http://en.wikipedia.org/wiki/Ribbon_(computing) ..

Upload: andrewtishkin

Post on 22-Nov-2014

496 views

Category:

Documents


10 download

TRANSCRIPT

Page 1: Chapter 10

Перевод Loriem, 2010

Глава 10. The Ribbon1

В начале 80-х, одновременно с появлением первых персональных

компьютеров, компания IBM решила определить стандарты построения

пользовательского интерфейса PC-приложений (в то время для DOS). Эта

спецификация была названа «типовой пользовательский интерфейс» —

Common User Access (CUA), и Microsoft в течение многих лет активно

поддерживала его в программах DOS и во всех первых версиях Windows,

определив, по сути, естественный способ взаимодействия с большинством

программ. Вы знаете, что команды Copy и Paste находятся в меню Edit, а для

сохранения или загрузки документа нужно взглянуть в меню File, так что при

изучении всякой новой программы вам необходимо знать на несколько

вещей меньше.

Пусть даже впоследствии было сделано много добавлений к CUA,

включая панели инструментов, контекстные меню, панели команд и многое

другое, его основной каркас сохранялся около 20 лет. В первый раз Microsoft

отступила от этого стандарта в Office 2007 (и отчасти в Windows Vista),

предложив новую парадигму пользовательского интерфейса, названного

Fluent User Interface. Этот интерфейс также известен как Интерфейс Ribbon,

по своему основному визуальному элементу.

Не хочу здесь вдаваться в споры относительно правильности отступления

от стандарта (лично у меня есть некоторые сомнения), только обращу

внимание на тот факт, что без некоторых, готовых к использованию,

визуальных компонентов реализация этого пользовательского интерфейса с

его набором строгих правил является нетривиальной задачей. В Delphi 2009

имеется такой набор визуальных элементов управления, обсуждение

которого и является темой настоящей главы. Он был разработан для

CodeGear Джереми Нортом (Jeremy North)2.

Введение в Fluent User Interface

Как выше было упомянуто, Fluent User Interface был изобретен компанией

Microsoft, ныне добивающейся патента на него3. Этот патент сосредоточен не

на коде, реализующем пользовательский интерфейс (элементы управления

Ribbon использованы в Office 2007), а на дизайне самого интерфейса. В

ссылках Microsoft на этот интерфейс присутствуют слова «Microsoft for the

Office UI».

1 англ. лента.

2 Джереми Норт является автором нескольких Дельфи-компонентов, расширений среды разработки и

инструментов для разработчиков (включая расширенный клиент Quality Central). Он также оказывал

помощь в разработке программ Compact Framework, написанных на Delphi for .NET. О Джереми Норте вы

можете узнать больше на его сайте http://www.jed-software.com. 3 Информация об этой заявке на патент и соответствующее обсуждение могут быть найдены в Википедии:

http://en.wikipedia.org/wiki/Ribbon_(computing)..

Page 2: Chapter 10

Перевод Loriem, 2010

Это означает, что патент Microsoft (если он будет получен) будет все еще

применяться, даже если доступная в Delphi 2009 реализация VCL является

совершенно новой версией элементов управления (никоим образом не

связанной с кодом, который использует Microsoft в Office и других

приложениях, и который Microsoft не лицензирует). Вот почему мы должны

обратить внимание на «юридическую сторону» этого компонента, прежде

чем рассмотрим его применение.

Обратите внимание, что в отличие от других руководств, Office Fluent UI

Design Guidelines, описывающее как должны работать приложения,

основанные на Ribbon, не является общедоступным, но «конфиденциальной

информацией компании Microsoft».

Юридическая сторона Ribbon

В процессе установки Delphi 2009 вы увидите такое, немного необычное,

сообщение (добавленное по требованию компании Microsoft):

Как видите, компания Microsoft просит любого, кто хочет использовать

Fluent User Interface, принять условия лицензионного соглашения об

использовании пользовательского интерфейса Office. Эта лицензия не

предписывает каких-либо денежных отчислений, но озвучивает нормативы и

ограничения в отношении того, что вы можете делать. Самым важным

обстоятельством является то, что вам не разрешено создавать программы-

конкуренты Microsoft Office. Существуют также принципы интерфейса,

которым необходимо следовать: например, вы не можете приспосабливать

интерфейс Ribbon к вашим желаниям, но обязаны использовать его

способом, согласным с предписанным компанией Microsoft подходом. Более

полную информацию вы можете получить, посетив ссылки, указанные в

вышеприведенном диалоговом окне:

http://msdn.microsoft.com/officeui

http://msdn.microsoft.com/en-us/office/aa973809.aspx

После того, как вы примете лицензионное соглашение и зарегистрируете

свое приложение, вы сможете загрузить 119-ти страничный PDF-документ, с

описанием принципов дизайна Office UI.

Page 3: Chapter 10

Перевод Loriem, 2010

Первый простой Ribbon

Мой первый пример использования Ribbon является просто «голой»

демонстрацией работы этого компонента, на самом же деле не представляя

реального интерфейса пользователя. Как мы увидим в следующем разделе,

единственным способом создания интерфейса, полностью основанного на

Ribbon, будет совместное использование его с архитектурой Менеджера

команд. Использование компонента Ribbon без действий (actions) возможно,

но весьма неудобно и чрезвычайно ограниченно,… так что после этого

первого примера я буду двигаться во втором направлении.

Итак, мы можем начать первые эксперименты с простого компонента

Ribbon, создав вкладки и группы и разместив в них несколько стандартных

компонентов. Начните новое приложение и на его основной форме поместите

компонент Ribbon. Как только компонент окажется на своем месте, вы

можете использовать его контекстное меню (доступное на форме или в

панели Structure) для добавления новой вкладки. То же меню позволит вам

удалить вкладку, или добавить Меню приложения (Application menu) и

Панель быстрого доступа (Quick Access toolbar), как мы увидим позднее. Вы

можете работать с вкладками Ribbon, используя также коллекцию Tabs

компонента Ribbon (технически это коллекция объектов TRibbonTabItem,

каждый из которых связан с TRibbonPage — разновидность панели) и

команду AddItem. Вот как, например, может выглядеть панель Structure:

Во время проектирования в среде разработки Delphi 2009 заголовок

Ribbon с двумя вкладками (страницами) выглядит так:

В этом примере я оставил включенным (по умолчанию) свойство

ShowHelpButton, отвечающее за отображение знака вопроса в правом

верхнем углу. Включенным я также оставил свойство UseCustomFrame (его

значение будет объяснено позже).

Здесь представлены некоторые другие свойства элемента управления

Ribbon приложения BareBoneRibbon:

object Ribbon1: TRibbon Caption = 'Ribbon Caption' DocumentName = 'Document Name' Tabs = < item Caption = 'RibbonPage1'

Page 4: Chapter 10

Перевод Loriem, 2010

Page = RibbonPage1 end item Caption = 'RibbonPage2' Page = RibbonPage2 end> StyleName = 'Ribbon – Luna' object RibbonPage1... object RibbonPage2... end После того как у нас появились одна или несколько вкладок Ribbon,

можно добавить на них группы Ribbon (или блоки). Опять же, вы можете

работать с контекстным меню компонентов через правый клик на форме или

в панели Structure. Вот как выглядит страница Ribbon с несколькими

пустыми группами:

На вкладке Ribbon вы можете добавить группу, удалить группу или

переупорядочить группы через особое диалоговое окно (использование

которого удобнее, чем перетаскивание групп внутри вкладки, в надежде, что

все они окажутся, в конце концов, на своих местах).

Что можно расположить в группе? Как правило, вы можете заполнять их

элементами различных типов: от команд до опций, связанных с действиями

компонента ActionManager. При желании сильно отклониться от

спецификации пользовательского интерфейса Ribbon можно добавить в

группу обыкновенные кнопки или специально предназначенный

RibbonSpinEdit, как сделано мною в этом демонстрационном примере. Но

помните, что это не рекомендуемый подход (хотя использование самого

элемента RibbonSpinEdit находится в пределах спецификации).

Вы можете видеть первые две заполненные страницы моего

демонстрационного приложения во время выполнения на следующем

скриншоте:

Page 5: Chapter 10

Перевод Loriem, 2010

Такая форма имеет мало общего со своим обычным видом, так как ее

заголовок и стандартные края заменены особой рамкой, нарисованной самим

элементом управления Ribbon. Это — стиль по умолчанию для интерфейса

Ribbon, в который мы позднее добавим другие графические элементы

(например, Меню приложения).

Вы можете отключить свойство UseCustomFrame. Я это сделал в

следующем коде, когда пользователь во время выполнения приложения

снимает флажок индикатора:

procedure TFormBareBoneRibbon.cbShowBorderClick( Sender: TObject); begin Ribbon1.UseCustomFrame := cbShowBorder.Checked; self.RecreateWnd; end;

При снятии флажка этот код будет работать — особая рамка Ribbon

исчезнет. Но если вы установите флажок заново, границы формы уже не

будут перерисованы правильно4. Я предполагаю, что в настоящее время

имеет смысл переключать это свойство во время проектирования

приложения или после создания формы.

Другое важное обстоятельство, которое необходимо иметь в виду, это то,

что когда элемент управления Ribbon уменьшится до размера 300 на 250

точек, он будет отображен в минимизированном состоянии (опять же, в

соответствии со спецификацией Ribbon). Если вы хотите избежать этого

(некоторые пользователи вашего приложения могут быть озадачены таким

поведением) — установите минимальные значения высоты и ширины для

формы:

object FormBareBoneRibbon: TFormBareBoneRibbon Caption = 'BareBoneRibbon' Constraints.MinHeight = 270

4 Это ошибка, нуждающаяся в исправлении. В качестве приема, искусственно обходящего эту неприятность,

можно использовать код, представленный в отчете об ошибках по адресу

http://qc.codegear.com/wc/qcmain.aspx?d=68955.

Page 6: Chapter 10

Перевод Loriem, 2010

Constraints.MinWidth = 320 ...

Этот размер получился добавлением некоторого значения к минимально

возможным размерам компонента Ribbon. Эти важные замечания вы должны

помнить, работая с формами, использующими элемент управления Ribbon.

Действия и Ribbon

Как уже было несколько раз упомянуто, элемент управления Ribbon

основан на архитектуре ActionManager. В следующем примере мы увидим,

как создать интерфейс пользователя с использованием этих компонентов. Но

прежде чем коснуться этой темы, хочу резюмировать ключевые особенности

этой архитектуры для тех, кто никогда ее не использовал5. Если ранее вы

использовали компонент ActionManager, то можете пропустить следующие

два раздела и перейти к параграфу «Действия и Ribbon на практике».

От событий к действиям

Дельфийская архитектура обработки событий весьма открыта: вы можете

написать единственный обработчик события и соединить с ним событие

OnClick кнопки на панели инструментов и в меню. Вы также можете

соединить этот обработчик с разными кнопками и пунктами меню, ведь

обработчик может использовать параметр Sender для обращения к объекту,

инициировавшему событие. Несколько сложнее синхронизировать состояние

кнопок панели инструментов и пунктов меню. Если у вас есть пункт меню и

кнопка панели инструментов, которые переключают одну и ту же опцию, то

всякий раз, когда опция переключается, вы должны добавить флажок к

пункту меню и изменить статус кнопки, чтобы отобразить ее нажатие.

Чтобы обойти эти и подобные проблемы, Delphi включает в себя

событийно-управляемую архитектуру, основанную на действиях. Действие

(или команда) в одно и то же время играет две различные роли:

Оно указывает операцию, которую необходимо выполнить, когда

активируются элементы пользовательского интерфейса (пункт меню,

кнопка), подключенные к действию с его обработчиком события

OnExecute;

Оно определяет статус элементов (или клиентов)

пользовательского интерфейса, соединенных с действием, включая их

текстовое описание, включение состояния, проверка состояния и т. п.;

5 Это введение в действия (Actions) и компоненты ActionList и ActionManager приводится из моей книги

«Mastering Delphi 7» (рус. издание – Кэнту М. Delphi 7. Для профессионалов. СПб: «Питер», 2005) с

некоторой правкой. Оригинальный текст имеет несколько характерных, поясняющих концепции, примеров,

опущенных в этом резюме.

Page 7: Chapter 10

Перевод Loriem, 2010

Соединение действия с интерфейсом элементов управления весьма важно

и не должно быть недооценено, поскольку это то, где вы можете получить

реальные преимущества этой архитектуры.

Практически, объект действие имеет свойства, которые будут применены

к связанным элементам управления (называющимся клиенты действия). Эти

свойства, наряду с другими, включают Caption, графическое изображение

(ImageIndex), состояние (Checked, Enabled и Visible) и обратную связь с

пользователем (Hint и HelpContext).

Основной класс для всех объектов действий – TBasicAction, который

вводит абстрактное основное поведение действия, без каких-либо связок или

соединений (даже с пунктами меню и элементами управления). Производный

класс TContainedAction вводит свойства и методы, которые дают

возможность действиям появиться в Списке действий (ActionList) и

Менеджере действий (ActionManager). Следующий производный класс

TCustomAction вводит поддержку свойств и методов пунктов меню и

элементов управления, связанных с объектами действий. В конечном итоге

существует производный, готовый к использованию, класс TAction.

Каждый объект действие соединяется с одним или более клиентскими

объектами через объект ActionLink. Сложные элементы управления,

возможно различных типов, могут совместно использовать один и тот же

объект действия, как показывает их свойство Action. Технически,

внутренние объекты ActionLink обслуживают двунаправленные соединения

между клиентским объектом и действием.

Элементами управления клиента обычно являются пункты меню и

кнопки различных типов (кнопки, индикаторы состояния, радиокнопки,

кнопки SpeedButton, кнопки панели инструментов, и т.п.). Запомните, что нет

необходимости устанавливать свойства клиентских элементов управления,

подключенных к действиям, так как действия будут перекрывать значения

свойств клиентских элементов управления. По этой причине, как правило,

сначала кодируйте действия, а затем создавайте пункты меню и кнопки, к

которым вы хотите их подключить. При использовании элемента управления

Ribbon это будет единственным и, понятно, наилучшим методом работы,

которому не существует альтернативы.

Компоненты ActionList и ActionManager

Объекты действий существуют в памяти, но не являются компонентами

VCL. В действительности, они управляются компонентами-контейнерами,

называющимися ActionList и ActionManager. Первый является старейшим и

простейшим контейнером для действий. Второй предоставил широкие

возможности в создании интерфейса и объединении его элементов.

Компонент ActionList имеет особый редактор, который вы можете

использовать для создания действий и управления ими. Действия

сгруппированы в именованных категориях:

Page 8: Chapter 10

Перевод Loriem, 2010

Компонент ActionManager, первоначально введенный в Delphi 6, имеет

дополнительные возможности, позволяющие вам создавать действия и

управлять интерфейсом для них. Кроме коллекции действий, у

ActionManager имеется коллекция панелей инструментов и связанных с

ними меню. Разработка этих панелей инструментов и меню является

полностью визуальной: вы перетаскиваете действия из особого редактора

ActionManager на инструментальные панели, чтобы получить необходимые

вам кнопки. Заметим, что работа с Ribbon в чем-то похожа: вы

перетаскиваете действия на группы Ribbon, чтобы получить готовые к

использованию командные кнопки Ribbon.

Кроме самого компонента ActionManager эта архитектура включает:

элемент управления ActionMainMenuBar, элемент управления

ActionToolBar, компонент PopupActionBarEx и компонент CustomizeDlg,

предоставляющий конечному пользователю возможность настраивать

пользовательский интерфейс. Эти визуальные компоненты не используются

при работе с Ribbon, поэтому я не буду говорить о них подробно. Вместо

этого давайте шаг за шагом создадим приложение, использующее

ActionManager и элемент управления Ribbon.

Действия и Ribbon на практике

После краткого введения в архитектуру дельфийского Менеджера

действий давайте создадим реальное демонстрационное приложение.

Первым шагом, конечно же, начнем новое VCL приложение и добавим

компонент ActionManager на его основную форму. Затем на форму

перетащим компонент Ribbon. Последний должен автоматически

подключиться к Менеджеру действий, если не использовано его свойство

ActionManager.

Перед выбором какого-либо действия в ActionManager добавьте

элементы ImageList и подключите их к нему. Добавление стандартных

действий автоматически заполнит списки изображений. Назначьте один

ImageList стандартному Images ActionManager’а (стандартные изображения

— используются командами Ribbon), а второй — свойству LargeImages

(используются в Меню приложения Ribbon и на больших кнопках в группе

Ribbon). Ваши настройки должны выглядеть так:

object RibbonEditorForm: TRibbonEditorForm Caption = 'RibbonEditor'

Page 9: Chapter 10

Перевод Loriem, 2010

Constraints.MinHeight = 300 Constraints.MinWidth = 400 object Ribbon1: TRibbon ActionManager = ActionManager1 Caption = 'RibbonEditor' StyleName = 'Ribbon - Luna' end object ActionManager1: TActionManager LargeImages = listLarge Images = listStandard StyleName = 'Ribbon - Luna' end object listStandard: TImageList... object listLarge: TImageList... end

Так как моей целью является создание простейшего редактора (не

полнофункционального текстового процессора — как вы знаете, я не могу

нарушать лицензионное соглашение Ribbon), мне, в основном, необходимы:

элемент управления RichEdit, заполненный по клиентской области формы;

стандартные действия по редактированию (6 стандартных действий в

категории Edit); поддержка RichEdit (8 стандартных действий в категории

Format); файловая поддержка (8 стандартных действий в категории File) и

еще кое-что — действие Download в категории Internet и действие Font в

категории Dialogs.

Группы и команды

Теперь, когда у меня есть все эти действия, я могу построить для них

пользовательский интерфейс Ribbon. После создания двух вкладок и

нескольких групп я перетаскиваю действия на группы. Вот так выглядят две

заполненные группы:

Эти группы содержат непосредственно команды, так что установка не

представляет никакой сложности. Третья группа имеет набор

неисключительных опций, таких как установка полужирного или курсивного

начертания шрифта текста. Для действий такой группы будет лучше

установить значение свойства CommandStyle в csCheckBox (чем

использовать предложенный по умолчанию стиль csButton). Эффектом

Page 10: Chapter 10

Перевод Loriem, 2010

будет появление набора индикаторов, которые вы можете переключать

кликом в квадратике индикатора или в области иконки и текста команды:

В перетаскивании действий в группы существует исключение,

представленное действием FontDialog, которое можно связать с одной из

групп в качестве диалогового окна группы, используя ее свойство

DialogAction. В результате добавится небольшой графический элемент в

правый нижний угол группы, как это видно на последней картинке.

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

радио-кнопками, — появляется установкой значения свойства

CommandStyle в csRadioButton с таким визуальным эффектом:

При выборе какого-либо элемента группы Ribbon можно наблюдать

различные свойства соответствующего ему объекта TActionClientItem.

Но как управляются такие объекты? Оказывается, компонент ActionManager

имеет панели инструментов для каждой группы Ribbon, как вы можете

увидеть в редакторе ActionManager.

Настоящая же внутренняя структура этих объектов представлена в панели

Structure (раскрывайте коллекцию компонента ActionManager, а не

Ribbon). Вот небольшая часть этой панели для нашего демонстрационного

приложения RibbonEditor (и визуальных элементов, о которых ранее

рассказывалось):

То есть можно перемещаться по элементам в разных группах Ribbon —

потеряв в наглядности, но при этом получить более детальный обзор

объектов, выбирая невидимые элементы, щелкая небольшие разделители и

даже добавляя новые объекты ActionClientItem. Далее эти новые объекты

конфигурируются и определяются в качестве текстовых элементов или

Page 11: Chapter 10

Перевод Loriem, 2010

разделителей, им назначаются действия, или они соединяются с визуальными

элементами управления.

Меню приложения

Для придания законченного вида нашему приложению, с которым мы

связали несколько действий и при этом не написали никакого кода, давайте

добавим два других важных элемента пользовательского интерфейса Ribbon.

Оба добавляются командами редактора компонента Ribbon (в контекстном

меню, появляющемся при клике на компоненте во время проектирования) и

только при установленном свойстве ActionManager.

Первым ключевым элементом является Меню приложения — круглый

элемент управления в левом верхнем углу Ribbon, заменяющий

традиционное меню приложения Windows. Вот как он выглядит во время

выполнения приложения:

Этот элемент реализует выпадающее меню (первоначально, конечно,

пустое). Естественным будет использовать его для файловых операций, так

что давайте добавим сюда различные стандартные действия, например, File

Open и Save. Конечно, действия можно просто перетащить на эту панель, но

это не всегда оказывается удобным из-за ее тенденции сворачиваться. Я

обнаружил, что проще будет выбрать ее в панели Structure, добавить

элементы, и связать каждый элемент с соответствующим действием.

Если левая половина Меню приложения представляет собой просто

список файловых операций с иконками большого размера, то правая должна

содержать список последних использованных файлов. Компонент Ribbon

имеет особую поддержку для управления таким списком «последних

использованных элементов» (most recently used — MRU)6. В этой простой

демонстрационной программке я решил поработать только с операциями

Load и Save As. Обе добавляют записи в список MRU использованием

специальной процедуры, вызывающей метод AddRecentItem элемента

управления Ribbon. В результате новая запись добавляется на вершину

списка последних документов, а существующая запись с таким же именем

файла будет удалена.

События OnAccept действий FileOpen1 и FileSaveAs1, а также

вызываемая ими процедура AddToMru, содержат следующий код:

6 В исходном коде демонстрационного приложения вы также найдете написанный мной «ручной» способ

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

Учитывая хорошее качество «родной» поддержки MRU, я не думаю, что ручной подход представляет

ценность для изучения. Я оставил его в коде потому, что он показывает, как можно динамически расширять

интерфейс Ribbon и использовать действия, общие для разных клиентских элементов.

Page 12: Chapter 10

Перевод Loriem, 2010

procedure TRibbonEditorForm.FileOpen1Accept( Sender: TObject); begin RichEdit1.Lines.Clear; RichEdit1.Lines.LoadFromFile(FileOpen1.Dialog.FileName); Ribbon1.DocumentName := FileOpen1.Dialog.FileName; AddToMru(FileOpen1.Dialog.FileName); end;

procedure TRibbonEditorForm.FileSaveAs1Accept( Sender: TObject); begin RichEdit1.Lines.SaveToFile(FileSaveAs1.Dialog.FileName); Ribbon1.DocumentName := FileSaveAs1.Dialog.FileName; AddToMru(FileSaveAs1.Dialog.FileName); end;

procedure TRibbonEditorForm.AddToMru( const strFilename: string); begin Ribbon1.AddRecentItem(strFilename); end;

При выборе какого-либо объекта списка MRU элемент управления Ribbon

вызывает обработчик события OnRecentItemClick, с простым кодом, без

проверки наличия в редакторе загружаемого файла. Кроме того, информация

списка MRU не сохраняется между сессиями. Я только хотел показать, как

можно вручную заполнять список MRU, получая, например, такой эффект:

Вот обработчик события выборки элемента из списка MRU:

procedure TRibbonEditorForm.Ribbon1RecentItemClick( Sender: TObject; FileName: string; Index: Integer); begin RichEdit1.Lines.Clear;

Page 13: Chapter 10

Перевод Loriem, 2010

RichEdit1.Lines.LoadFromFile(FileName); Ribbon1.DocumentName := FileName; end;

В правой половине меню приложения также можно отображать кнопки,

если установить в ctCommands свойство CommandType объекта

ApplicationMenu элемента управления Ribbon (по умолчанию там

находится ctRecent). В этом случае любой элемент, добавляемый в

коллекцию RecentItems, будет выглядеть как кнопка. Такая возможность

продемонстрирована поставляемым вместе с Delphi 2009 примером Меню

приложения — интересная демонстрация того, как это сделано в Office 2007.

Панель быстрого доступа

Вторым графическим элементом Ribbon является Панель быстрого

доступа – панель системных операций, управляемая операционной системой

автоматически. Она добавляется справа от круглой кнопки Меню

приложения, отображая в нашем случае два действия (Save As и Exit):

С кнопками действий соседствует также кнопка выпадающего меню,

которая позволяет пользователю добавить дополнительные команды в

Панель быстрого доступа — очень мощная возможность, но, вполне

вероятно, вы захотите ее отключить7.

Итак, очень простой, но вполне законченный редактор, основанный на

Ribbon, готов. Если посмотреть среди примеров, поставляемых с Delphi, то

вы найдете демонстрационную программку с дополнительными функциями и

более привлекательным интерфейсом, но без возможности управления

«последними использованными элементами».

Поддержка клавиатурных подсказок

Итак, у нас появилось вполне работающее приложение с приятным Fluent

UI. Пользователи могут работать с различными визуальными элементами

(вкладки страниц, элементы управления), щелкая на них. Но что же можно

сказать об использовании при этом клавиатуры? Конечно, никто не отменял

назначение «горячих клавиш» различным действиям (например,

классический Ctrl+C для Copy), но Ribbon имеет свой собственный

интерфейс подключения клавиатуры.

7 Я считаю сомнительной возможностью разрешение пользователям настраивать свои панели инструментов.

Опытный пользователь, конечно, найдет это весьма приятной функцией, но вы также можете получить

множество звонков в службу поддержки от «чайников», которые не могут понять, почему их любимые

кнопки пропали из панели. Так как 80% пользователей используют только 20% функций программного

обеспечения, реализация функции «сверх-настраиваемости» не всегда будет хорошей идеей.

Page 14: Chapter 10

Перевод Loriem, 2010

Все, что вам необходимо сделать — это назначить подходящее значение

свойству KeyTip каждого элемента пользовательского интерфейса Ribbon

(вкладки, группы, действия), и теперь вы можете активировать клавиатурные

подсказки клавишей Alt. Так, в приложении RibbonEditor я выбрал

следующие клавиатурные комбинации для вкладок, групп и действий:

RibbonPage1 'Editing' KeyTip = 'E' RibbonPage2 'Advanced' KeyTip = 'A' RibbonGroup1 'Copy&Paste' KeyTip = 'C' RibbonGroup2 'Edit' KeyTip = 'E' RibbonGroup3 'Style' KeyTip = 'S' RibbonGroup4 'Alignment' KeyTip = 'A' RibbonGroup5 'Paragraph' KeyTip = 'P' item Action = EditCopy1 KeyTip = 'C' item Action = EditPaste1 KeyTip = 'P' items Action = EditCut1 KeyTip = 'T' items Action = EditDelete1 KeyTip = 'D' items Action = EditUndo1 KeyTip = 'U' items Action = EditSelectAll1 KeyTip = 'S' Значением свойства KeyTip должна быть одна или более заглавных букв

8

или чисел, а эффектом является видимое отображение комбинаций клавиш

текущего уровня при нажатии пользователем клавиши Alt (не удерживайте

ее, просто нажмите один раз) и дальнейшем следовании своему выбору.

Благодаря отображению клавиатурной подсказки пользователь со временем

может обучиться правильным клавишным комбинациям. В нашем примере

RibbonEditor, когда вы нажмете (и отпустите) клавишу Alt, то увидите:

Отображены клавиатурные подсказки только первого уровня. Если вы

нажмете клавишу E, активируя вкладку Editing, Ribbon покажет подсказки

для каждого действия и группы (подсказки для группы отображаются, если с

последней связано диалоговое окно группы, как в нашем случае для группы

Style):

8 Не используйте строчные буквы, потому что они просто не будут работать. Теоретически, Ribbon должен

конвертировать их в заглавные автоматически, но пока это не работает (в Delphi 2009 Update 1).

Page 15: Chapter 10

Перевод Loriem, 2010

Не забывайте назначать клавиатурные подсказки всем элементам Ribbon,

иначе это ограничит пользователей, предпочитающих работать с

клавиатурой.

Компоненты Ribbon

Мы уже увидели на практическом примере роль различных компонентов

Ribbon: от самого элемента Ribbon до его вкладок и групп. Они

обеспечивают целостную организацию интерфейса пользователя, а также

широкий диапазон настроек, которые я, конечно, не могу здесь обсудить

подробно. Однако, могу предложить несколько рекомендаций.

Сначала обратим внимание на группу Ribbon. Одним из наиболее важных

визуальных свойств группы является свойство GroupAlign, отвечающее за

вертикальное или горизонтальное расположение элементов в ней.

Вертикальное расположение может быть предпочтительным для кнопок

большого размера, а горизонтальное — для выстроенных в ряд относительно

небольших кнопок. Также можно использовать свойства Columns и Rows

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

группы Ribbon имеют вертикальную схему с тремя строчками, так что три

кнопки будут подогнаны по вертикали под размер группы. Помните, что

Ribbon и его группы имеют фиксированную высоту (полосы прокрутки

никогда не отображаются в Ribbon).

Если вы сделаете кнопку большой, она заполнит всю группу. В этом

примере (взятом из поставляемого с Delphi RibbonDemo) кнопка большая и

используется стиль разделенной кнопки для отображения выпадающего

меню.

Этот эффект вы можете получить установкой подходящего стиля

ActionItem и назначив несколько подэлементов, связанных с другими

действиями:

item Action = EditPaste1 CommandProperties.ButtonSize = bsLarge CommandProperties.ButtonType = btSplit Items = < Item Action = EditPaste1 end item Action = EditPasteSpecial end

Page 16: Chapter 10

Перевод Loriem, 2010

item Action = EditPasteHyperlink end> end end Или вы захотите иметь маленькие кнопки без заголовков, использующие

горизонтальную схему. Вот пример группы с двумя строчками командных

кнопок:

Основные свойства этой группы:

object RibbonGroup8: TRibbonGroup Caption = 'Lines' GroupAlign = gaHorizontal Rows = 2 end Но как получить графический эффект таких сгруппированных кнопок?

Автоматически это не происходит — необходимо модифицировать объекты

TActionClientItem группы, убирая заголовки (свойство ShowCaption),

устанавливая особые значения для свойства

CommandProperties.GroupPosition и используя свойство NewRow по

мере надобности:

Items = < item Action = RichEditAlignCenter1 CommandProperties.GroupPosition = gpStart end item Action = RichEditAlignRight1 CommandProperties.GroupPosition = gpMiddle end item Action = RichEditAlignLeft1 CommandProperties.GroupPosition = gpEnd end item Action = RichEditUnderline1 NewRow = True CommandProperties.GroupPosition = gpStart

Page 17: Chapter 10

Перевод Loriem, 2010

end item Action = RichEditItalic1 CommandProperties.GroupPosition = gpMiddle end item Action = RichEditBold1 CommandProperties.GroupPosition = gpEnd end> ActionBar = RibbonGroup8 end Здесь придется немного поработать руками, зато вы получите немного

контроля над точным расположением элементов, и это будет лучше, чем

доверяться каким-то внутренним алгоритмам, которые могут работать не так,

как вам хочется.

Возвращаясь от позиционирования и графических элементов к действиям,

необходимо принять самое важное решение — назначить подходящий

элемент интерфейса и его поведение. Если вы выберете кнопку (большинство

элементов нашей демонстрационной программы именно кнопки), то можете

использовать структуру TButtonProperties, связанную со свойством

CommandProperties, о котором я ранее упоминал. Она позволяет назначить

размер кнопки, тип кнопки, позицию в группе кнопок и связь кнопки с ее

заголовком (Text Association), используя значения следующих перечислений

(определенных как вложенные типы):

type TButtonSize = (bsSmall, bsLarge); TButtonType = (btNone, btDropDown, btSplit, btGallery); TGroupPosition = (gpNone, gpStart, gpMiddle, gpEnd, gpSingle); TTextAssociation = (taImage, taDropdown);

Обратите внимание на то, что объекты, связанные со свойством

CommandProperties, зависят от типа команды. То есть если вы назначите,

например, текстовый элемент, то увидите такие свойства, как Alignment,

EllipsisPosition и Font, а не перечисленные ранее для кнопок. Какие же

типы команд доступны для Action Client Items, используемых компонентом

Ribbon9?

Вот список, взятый из исходного кода модуля ActMan: 9 Вообще говоря, эти типы команд могли бы быть использованы любым другим визуальным контейнером

активных ссылок, так как эта возможность определена как часть архитектуры Менеджера действий и не

особенно связанна с Ribbon. Но в настоящее время все остальные вьюверы и стили действий игнорируют

это свойство.

Page 18: Chapter 10

Перевод Loriem, 2010

csButton – команда является кнопкой;

csMenu – команда является меню;

csSeparator – команда является разделителем с заголовком;

csText – команда только отображает текст (клик на нем не имеет

никакого эффекта);

csGallery – команда отображает галерею (эта функция не

поддерживается в текущей версии Ribbon);

csComboBox – команда представляет выпадающий список в

стиле Office 2007 (этот тип устанавливается автоматически при

использовании RibbonComboBox);

csCheckBox – команда отображает индикатор в стиле Office

2007;

csRadioButton – команда отображает радиокнопки в стиле

Office 2007;

csControl – команда имеет ассоциированный с ней TControl;

csCustom – команда, позволяющая расширение сторонними

компонентами;

Интересной возможностью является использование типа csControl,

разрешающего расположить почти любой графический элемент управления

VCL на Ribbon. Например, в одну из групп примера RibbonEditor я включил

элемент управления ButtonedEdit с кнопкой справа и TextHint, и

небольшой элемент управления TreeView.

Через панель Structure я вручную добавил ActionToolbar в ActionManager,

связал новую панель с пустой группой Ribbon, выбрал элемент управления из

палитры компонентов, поместил его в группе Ribbon и затем связал с этим

элементом действие. Наконец, установил свойство ширины для всего

действия и его метки. Вот текстовое определение одного из элементов

ActionToolbar:

item Items = < item Caption = '&Search:' CommandStyle = csControl CommandProperties.Width = 150 CommandProperties.ContainedControl = ButtonedEdit1 CommandProperties.LabelWidth = 50 end

Page 19: Chapter 10

Перевод Loriem, 2010

item Caption = '&Pick:' CommandStyle = csControl NewRow = True CommandProperties.Width = 150 CommandProperties.ContainedControl = TreeView1 CommandProperties.LabelWidth = 50 end> ActionBar = RibbonGroup7 end> Вообще-то, архитектура ActionManager и новый элемент управления

Ribbon с его поддержкой классов имеют так много функций, что их

обсуждению можно посвятить много страниц. Но придется дать только

несколько наиболее интересных советов (не отраженных в нашей

демонстрационной программе):

Вы можете настраивать Меню приложения, например, изменяя

заголовок ―Recent Documents‖ или размеры его иконки — смотрите

свойство ApplicationMenu элемента управления Ribbon;

Точно также вы можете настраивать поведение Меню быстрого

доступа модификацией подсвойств свойства QuickAccessToolbar

элемента управления Ribbon;

Из особых требований Fluent UI Design Guidelines вытекает

причина, по которой вы не можете устанавливать размеры групп

Ribbon, но только адаптировать их к размеру содержащихся в них

элементов управления;

Через правый клик на элементах Ribbon пользователи могут

добавлять команды на Панель быстрого доступа, и выполнять другие

операции.

Ribbon в приложениях баз данных

Стремление увидеть применение Ribbon в документно-ориентированном

приложении является вполне естественным, но что же можно сказать о

программах, призванных выполнять совершенно другие операции, например,

работающих с базами данных? Приняв во внимание, что у нас есть набор

стандартных, относящихся к базам данных, действий, мы можем

подвергнуться соблазну использовать Ribbon для замены классического

DBNavigator, и это будет вполне возможным (и даже очень простым для

реализации). Правда, я не уверен, что такое новаторство выразит почтение к

правилам использования Fluent UI от Microsoft, но уж точно не нарушит

запрета на клонирование Office.

Для создания приложения DataRibbon на стороне базы данных я поместил

на форме ClientDataSet с DataSource и DBGrid, а на стороне

Page 20: Chapter 10

Перевод Loriem, 2010

пользовательского интерфейса — ImageList, ActionManager и Ribbon.

Выбрал почти все действия из категории DataSet (исключив Refresh),

добавил Undo, FileOpen и FileExit. Создал вкладку Ribbon с тремя группами

и на нее перетащил действия. До сих пор не было написано ни строчки кода.

И затем все, что я сделал — при запуске программы открыл ClientDataSet,

используя имя файла ClientDataSet в качестве имени документа Ribbon, и то

же самое сделал при выполнении операции FileOpen:

procedure TFormDataRibbon.FileOpen1Accept( Sender: TObject); begin ClientDataSet1.Close; ClientDataSet1.FileName := FileOpen1.Dialog.FileName; Ribbon1.DocumentName := ClientDataSet1.FileName; ClientDataSet1.Open; end;

Это почти весь код программы, исключая набор настроек, сделанных во

время проектирования и сохраненных в dfm-файле. Если хотите взглянуть на

эти настройки, то можно предложить небольшой участок из 788-ти строчного

dfm-кода, дающий полную картину взаимоотношений между Ribbon и

ActionManager:

object FormDataRibbon: TFormDataRibbon object Ribbon1: TRibbon ActionManager = ActionManager1 Caption = 'DataRibbon' Tabs = <item Page = RibbonPage1 end> StyleName = 'Ribbon - Luna' object RibbonPage1: TRibbonPage Caption = 'DBNavigation' Index = 0 object RibbonGroup1: TRibbonGroup Caption = 'Browse' GroupIndex = 1 end object RibbonGroup2: TRibbonGroup Caption = 'Edit' GroupIndex = 2 End object RibbonGroup3: TRibbonGroup Caption = 'File' GroupIndex = 0 end

Page 21: Chapter 10

Перевод Loriem, 2010

end end object ImageList1: TImageList... object ActionManager1: TActionManager ActionBars = < item Items = < item Action = DataSetFirst1 end item Action = DataSetPrior1 end item Action = DataSetNext1 end item Action = DataSetLast1 end> ActionBar = RibbonGroup1 end item Items = < item Action = DataSetDelete1 end item Action = DataSetEdit1 end item Action = DataSetInsert1 end item Action = DataSetPost1 end item Action = DataSetCancel1 end item Action = ClientDataSetUndo1 end> ActionBar = RibbonGroup2 end item Items = < item Action = FileOpen1 end item Action = FileExit1 end> ActionBar = RibbonGroup3 end> Images = ImageList1 StyleName = 'Ribbon - Luna' object DataSetFirst1: TDataSetFirst... object DataSetPrior1: TDataSetPrior... object DataSetNext1: TDataSetNext... object DataSetLast1: TDataSetLast... object DataSetInsert1: TDataSetInsert... object DataSetDelete1: TDataSetDelete... object DataSetEdit1: TDataSetEdit... object DataSetPost1: TDataSetPost... object DataSetCancel1: TDataSetCancel... object ClientDataSetUndo1: TClientDataSetUndo Caption = 'Undo' FollowChange = False end

Page 22: Chapter 10

Перевод Loriem, 2010

object FileOpen1: TFileOpen Caption = '&Open...' Dialog.Filter = 'CDS|*.cds|XML|*.xml' Dialog.InitialDir = '...\CodeGear Shared\Data' OnAccept = FileOpen1Accept end object FileExit1: TFileExit... end object DBGrid1: TDBGrid Align = alClient DataSource = DataSource1 end object DataSource1: TDataSource DataSet = ClientDataSet1 end object ClientDataSet1: TClientDataSet FileName = '...\CodeGear Shared\Data\customer.cds' end

Конечно, можно расширить данный пример, добавив основные операции

редактирования (например, Copy и Paste) и другие, но, думаю, я добился

своей цели продемонстрировать, что, хотя это и несколько необычное его

применение, Ribbon с успехом может использоваться в приложениях баз

данных. Программа в действии:

Использование экранных подсказок

Другим элементом пользовательского интерфейса Ribbon является

использование больших и детализированных советов, известных как

Page 23: Chapter 10

Перевод Loriem, 2010

экранные подсказки (screen tips). В общем случае, экранные подсказки

используются в Ribbon соединением с ними разных действий, но также могут

использоваться в приложениях, не использующих Ribbon и даже в

приложениях, не использующих действия. Delphi 2009 имеет особую

поддержку для экранных подсказок в виде двух разных компонентов:

Компонент ScreenTipsManager — всеобъемлющий обработчик

экранных подсказок. Он управляет нюансами отображения экранных

подсказок для каждого из действий компонентов ActionList или

ActionManager и имеет свой собственный редактор, позволяющий

генерировать экранную подсказку для каждого действия, как мы

увидим позже;

Компонент ScreenTipsPopup предоставляет особый

пользовательский интерфейс для экранных подсказок и может быть

закреплен за любым визуальным элементом управления, обеспечивая

подсказкой элементы интерфейса, не связанные с действиями. Однако

этот элемент управления необходимо связать с компонентом

ScreenTipsManager.

Я продемонстрирую вам два разных примера. Первый — простое и

самостоятельное использование экранных подсказок в приложении, не

имеющем Ribbon и действий. Второй покажет расширение функций Ribbon

экранными подсказками.

Экранные подсказки без Ribbonа

Как было упомянуто, вы можете использовать экранные подсказки в

приложениях без Ribbon. Чтобы показать, что эта техника легко добавляется

в любую программу, я взял классическое приложение типа ―hello, world‖ —

программу со списком, полем редактирования и кнопкой, добавляющей текст

из поля в список.

В эту программу я поместил компонент ScreenTipsManager (с

установками по умолчанию) и три компонента ScreenTipsPopup, по одному

на каждый визуальный элемент управления. Каждый ScreenTipsPopup

имеет свойство ScreenTip типа TScreenTipItem: вы можете настраивать

такие свойства, как Header, Image и Description и другие свойства. Также

ScreenTipsPopup имеет свойство Associate для установки связи с

визуальным элементом управления, которому назначена эта экранная

подсказка (связанный элемент управления должен иметь включенным

свойство ShowHint).

Если вы хотите скрыть небольшой глиф, представляющий

ScreenTipsPopup, то установите свойство Visible в False. Именно это

сделано мною для двух элементов. Итак, экранная подсказка появится, когда

пользователь передвигает курсор мыши над глифом, передвигает курсор

Page 24: Chapter 10

Перевод Loriem, 2010

мыши над связанным с ним элементом управления (при скрытом глифе), или

в обоих случаях.

Так выглядит экранная подсказка кнопки Add приложения PlainTips:

Обратите внимание на небольшой глиф слева от связанного с ним списка

(слева же от самой экранной подсказки). Вот наиболее значимые свойства

для экранных подсказок этого примера:

object FormPlainTips: TFormPlainTips object ScreenTipsPopup1: TScreenTipsPopup Associate = ListBox1 ScreenTip.Description.Strings = ( 'List of text elements that were added') ScreenTip.Header = 'List' ScreenTip.Image.Data = {...} ScreenTip.ShowImage = True ScreenTipManager = ScreenTipsManager1 end object ScreenTipsPopup2: TScreenTipsPopup Associate = btnAdd ScreenTip.Description.Strings = ( 'Add the text to the list box, avoiding...') ScreenTip.Header = 'Add Text' ScreenTip.Image.Data = {...} ScreenTip.ShowImage = True ScreenTipManager = ScreenTipsManager1 Visible = False end object ScreenTipsPopup3: TScreenTipsPopup Associate = edText ScreenTip.Description.Strings = ( 'Text to be added to the listbox') ScreenTip.Header = 'Text' ScreenTip.Image.Data = {...} ScreenTip.ShowFooter = False

Page 25: Chapter 10

Перевод Loriem, 2010

ScreenTip.ShowImage = True ScreenTipManager = ScreenTipsManager1 Visible = False end object ScreenTipsManager1: TScreenTipsManager FooterImage.Data = {...} end end

Обратите внимание, что только один из элементов управления

ScreenTipsPopup видим; другой отображает подсказку без колонтитула.

Колонтитул содержит текст, одинаковый для всех экранных подсказок, и

необязательную картинку, а предоставляется компонентом

ScreenTipsManager.

Менеджер экранных подсказок и действия

Хотя вы можете использовать экранные подсказки без Ribbon, без

ActionList и Action Manager, именно по этому, последнему, сценарию они

работают лучше и именно для него существует особая поддержка. Как только

компонент ScreenTipsManager будет помещен на форме, использующей

ActionList или ActionManager (с или без Ribbon), вам станут доступны

различные возможности его редактора через команды контекстного меню:

Команда Generate Screen Tips создаст базовую экранную

подсказку для каждого действия и свяжет их друг с другом. Если у вас

уже есть какие-то экранные подсказки для каких-либо действий, они

будут сохранены;

Команда Regenerate Screen Tips работает как предыдущая

команда, но удалит все существующие подсказки и даст вам

возможность начать с нуля;

Команда Edit Screen Tips… позволит вам смотреть и настраивать

все относящиеся к действиям экранные подсказки в удобном для

пользования редакторе;

Описанные возможности были применены к приложению RibbonEditor

при создании его версии RibbonEditorTips. Вот предпринятые после

написания первой версии шаги: добавление компонента ScreenTipsManager,

связывание его коллекции LinkedActionList с компонентом

ActionManager и вызов команды меню Generate Screen Tips — енерируется

базовая экранная подсказка для каждого действия.

Чтобы сделать экранные подсказки видимыми, необходимо для формы

включить свойство ShowHints (так как эта возможность недоступна на

уровне компонента Ribbon), а в элементе управления Ribbon назначить

компонент ScreenTipsManager свойству ScreenTips. Этого будет

Page 26: Chapter 10

Перевод Loriem, 2010

достаточно для появления подсказок при передвижении курсора мыши над

элементами пользовательского интерфейса Ribbon:

Это та самая базовая подсказка. Получить доступ к ней для

редактирования можно через коллекциию ScreenTips компонента

ScreenTipsManager:

Но для облегчения себе работы лучше будет использовать особый

редактор экранных подсказок, доступный через двойной клик на компоненте

ScreenTipsManager. Вот как выглядит редактор с редактируемой экранной

подсказкой:

Краткий обзор свойств для приложения RibbonEditorTips, показывающий

только отличия от предыдущей версии, приведен ниже:

object RibbonEditorForm: TRibbonEditorForm ShowHint = True

Page 27: Chapter 10

Перевод Loriem, 2010

object Ribbon1: TRibbon ScreenTips = ScreenTipsManager1 end object ScreenTipsManager1: TScreenTipsManager FooterImage.Data = {...} LinkedActionLists = < item ActionList = ActionManager1 Caption = 'ActionManager1' end> ScreenTips = < item Action = EditCut1 Description.Strings = ('Cuts the selection...') Header = 'Cut' end item Action = EditCopy1 Description.Strings = ('Copies the selection...') Header = 'Copy' ShowImage = True end ... end end