Переход на Платформу ленты Windows
Приложение, использующее традиционные меню, панели инструментов и диалоговые окна, можно перенести в богатый, динамический и контекстный пользовательский интерфейс командной системы платформы Windows Ribbon Framework. Это простой и эффективный способ модернизации и возрождения приложения, а также улучшения доступности, удобства использования и возможности обнаружения его функциональных возможностей.
Введение
Как правило, перенос существующего приложения на платформу ленты включает в себя следующее:
- Проектирование макета ленты и управления организацией, которая предоставляет функциональные возможности существующего приложения и является достаточно гибким для поддержки новых функций.
- Адаптация кода существующего приложения.
- Перенос существующих ресурсов приложения (строк и изображений) в платформу ленты.
Примечание.
Рекомендации по пользовательскому интерфейсу ленты должны быть рассмотрены, чтобы определить, является ли приложение подходящим кандидатом для пользовательского интерфейса ленты.
Проектирование макета ленты
Потенциальные макеты пользовательского интерфейса ленты и макеты элементов управления можно определить, выполнив следующие действия:
- Инвентаризация всех существующих функциональных возможностей.
- Преобразование этой функции в команды ленты.
- Упорядочение команд в логические группы.
Взять инвентаризацию
В платформе ленты функциональные возможности, предоставляемые приложением, которое управляет состоянием или представлением рабочей области или документа, считается командой. То, что представляет собой команду, может отличаться и зависит от характера и домена существующего приложения.
В следующей таблице перечислены набор базовых команд для гипотетического приложения редактирования текста:
Символ | Идентификатор | Description |
---|---|---|
ID_FILE_NEW | 0xE100 | Новый документ |
ID_FILE_SAVE | 0xE103 | Сохранить документ |
ID_FILE_SAVEAS | 0xE104 | Сохранить как... (диалоговое окно) |
ID_FILE_OPEN | 0xE101 | Открыть... (диалоговое окно) |
ID_FILE_EXIT | 0xE102 | Выход из приложения |
ID_EDIT_UNDO | 0xE12B | Отмена |
ID_EDIT_CUT | 0xE123 | Вырезать выделенный текст |
ID_EDIT_COPY | 0xE122 | Копирование выделенного текста |
ID_EDIT_PASTE | 0xE125 | Вставка текста из буфера обмена |
ID_EDIT_CLEAR | 0xE120 | Удаление выделенного текста |
ID_VIEW_ZOOM | 1242 | Зум... (диалоговое окно) |
Ознакомьтесь с существующими меню и панелями инструментов при создании инвентаризации команд. Рассмотрим все способы взаимодействия пользователя с рабочей областью. Несмотря на то, что не каждая команда подходит для включения в ленту, это упражнение может очень хорошо предоставлять команды, которые ранее были скрыты слоями пользовательского интерфейса.
Перевести
Не каждая команда должна быть представлена в пользовательском интерфейсе ленты. Например, ввод текста, изменение выделения, прокрутки или перемещение точки вставки с мышью все квалифицируются как команды в гипотетическом текстовом редакторе, однако они не подходят для предоставления в панели команд, так как каждая из них включает прямое взаимодействие с пользовательским интерфейсом приложения.
И наоборот, некоторые функции могут не рассматриваться как команда в традиционном смысле. Например, вместо того, чтобы быть похороненным в диалоговом окне, корректировки полей страниц принтера можно представить на ленте как группу элементов управления Spinner на контекстной вкладке или в режиме приложения.
Примечание.
Запишите числовые идентификаторы, которые могут быть назначены каждой команде. Некоторые платформы пользовательского интерфейса, такие как классы Microsoft Foundation (MFC), определяют идентификаторы для таких команд, как файл и меню редактирования (0xE100 для 0xE200).
Организация
Прежде чем пытаться упорядочить инвентаризацию команд, рекомендации по пользовательскому интерфейсу ленты должны быть проверены для рекомендаций при реализации пользовательского интерфейса ленты.
Как правило, к организации "Команда ленты" можно применять следующие правила:
- Команды, работающие с файлом или общим приложением, скорее всего, принадлежат в меню приложения.
- Часто используемые команды, такие как вырезать, копировать и вставлять (как в примере текстового редактора), обычно размещаются на домашней вкладке по умолчанию. В более сложных приложениях они могут дублироваться в другом месте пользовательского интерфейса ленты.
- Важные или часто используемые команды могут гарантировать включение по умолчанию на панели быстрого доступа.
Платформа ленты также предоставляет элементы управления ContextMenu и MiniToolbar через представление ContextPopup. Эти функции не являются обязательными, но если в приложении есть одно или несколько существующих контекстных меню, перенос команд, которые они содержат, могут разрешить повторное использование существующей базы кода с автоматической привязкой к существующим ресурсам.
Так как каждое приложение отличается, ознакомьтесь с рекомендациями и попробуйте применить их к максимально возможной степени. Одна из целей платформы ленты — обеспечить знакомый, согласованный пользовательский интерфейс, и эта цель будет более достижимой, если проекты новых приложений зеркало существующих приложений ленты максимально возможно.
Адаптация кода
После определения и упорядочения команд ленты в логические группировки количество шагов, связанных с включением платформы ленты в существующий код приложения, зависит от сложности исходного приложения. Как правило, существует три основных шага:
- Создайте разметку ленты на основе спецификации командной организации и макета.
- Замените устаревшие меню и функции панели инструментов функциональными возможностями ленты.
- Реализуйте адаптер IUICommandHandler.
Создание разметки
Список команд, а также их организация и макет объявляются с помощью файла разметки ленты, который используется компилятором разметки ленты.
Примечание.
Многие шаги, необходимые для адаптации существующего приложения, аналогичны тем, которые необходимы для запуска нового приложения ленты. Дополнительные сведения см. в руководстве по созданию приложения ленты для нового приложения ленты.
В разметке ленты есть два основных раздела. Первый раздел — это манифест команд и связанных с ними ресурсов (строк и изображений). Второй раздел определяет структуру и размещение элементов управления на ленте.
Разметка для простого текстового редактора может выглядеть примерно так:
Примечание.
Сведения о ресурсах изображений и строк см. далее в этой статье.
<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://schemas.microsoft.com/windows/2009/Ribbon">
<Application.Commands>
<Command Name="cmdNew" Id="0xE100" Symbol="ID_CMD_NEW" LabelTitle="New document" />
<Command Name="cmdSave" Id="0xE103" Symbol="ID_CMD_SAVE" LabelTitle="Save" />
<Command Name="cmdSaveAs" Id="0xE104" Symbol="ID_CMD_SAVEAS" LabelTitle="Save as" />
<Command Name="cmdOpen" Id="0xE101" Symbol="ID_CMD_OPEN" LabelTitle="Open" />
<Command Name="cmdExit" Id="0xE102" Symbol="ID_CMD_EXIT" LabelTitle="Exit" />
<Command Name="cmdUndo" Id="0xE12B" Symbol="ID_CMD_UNDO" LabelTitle="Undo" />
<Command Name="cmdCut" Id="0xE123" Symbol="ID_CMD_CUT" LabelTitle="Cut" />
<Command Name="cmdCopy" Id="0xE122" Symbol="ID_CMD_COPY" LabelTitle="Copy" />
<Command Name="cmdPaste" Id="0xE125" Symbol="ID_CMD_PASTE" LabelTitle="Paste" />
<Command Name="cmdDelete" Id="0xE120" Symbol="ID_CMD_DELETE" LabelTitle="Delete" />
<Command Name="cmdZoom" Id="1242" Symbol="ID_CMD_ZOOM" LabelTitle="Zoom" />
</Application.Commands>
<Application.Views>
<Ribbon>
<Ribbon.ApplicationMenu>
<ApplicationMenu>
<MenuGroup>
<Button CommandName="cmdNew" />
<Button CommandName="cmdOpen" />
<Button CommandName="cmdSave" />
<Button CommandName="cmdSaveAs" />
</MenuGroup>
<MenuGroup>
<Button CommandName="cmdExit" />
</MenuGroup>
</ApplicationMenu>
</Ribbon.ApplicationMenu>
<Ribbon.QuickAccessToolbar>
<QuickAccessToolbar>
<QuickAccessToolbar.ApplicationDefaults>
<Button CommandName="cmdSave" />
<Button CommandName="cmdUndo" />
</QuickAccessToolbar.ApplicationDefaults>
</QuickAccessToolbar>
</Ribbon.QuickAccessToolbar>
<Ribbon.Tabs>
<Tab>
<Group CommandName="grpClipboard" SizeDefinition="FourButtons">
<Button CommandName="cmdPaste" />
<Button CommandName="cmdCut" />
<Button CommandName="cmdCopy" />
<Button CommandName="cmdDelete" />
</Group>
</Tab>
<Tab>
<Group CommandName="grpView" SizeDefinition="OneButton">
<Button CommandName="cmdZoom" />
</Group>
</Tab>
</Ribbon.Tabs>
</Ribbon>
</Application.Views>
</Application>
Чтобы избежать переопределения символов, определенных платформой пользовательского интерфейса, например MFC, в предыдущем примере используются несколько разные имена символов для каждой команды (ID_FILE_NEW и ID_CMD_NEW). Однако числовые идентификаторы, назначенные каждой команде, должны оставаться одинаковыми.
Чтобы интегрировать файл разметки в приложение, настройте пользовательский шаг сборки для запуска компилятора разметки ленты UICC.exe. Затем результирующий заголовок и файлы ресурсов включаются в существующий проект. Если пример приложения ленты текстового редактора называется "RibbonPad", требуется настраиваемая командная строка сборки, аналогичная следующему:
UICC.exe res\RibbonPad_ribbon.xml res\RibbonPad_ribbon.bin
/header:res\RibbonPad_ribbon.h /res:res\RibbonPad_ribbon.rc2
Компилятор разметки создает двоичный файл, файл заголовка (H) и файл ресурса (RC). Так как существующее приложение, скорее всего, имеет существующий RC-файл, включите созданные H и RC-файлы в этот RC-файл, как показано в следующем примере:
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#define _AFX_NO_SPLITTER_RESOURCES
#define _AFX_NO_OLE_RESOURCES
#define _AFX_NO_TRACKER_RESOURCES
#define _AFX_NO_PROPERTY_RESOURCES
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE 9, 1
#pragma code_page(1252)
#include "res\RibbonPad_ribbon.h" // Ribbon resources
#include "res\RibbonPad_ribbon.rc2" // Ribbon resources
#include "res\RibbonPad.rc2" // non-Microsoft Visual C++ edited resources
#include "afxres.rc" // Standard components
#include "afxprint.rc" // printing/print preview resources
#endif
#endif // not APSTUDIO_INVOKED
Замена устаревших меню и панелей инструментов
Для замены стандартных меню и панелей инструментов на ленту в устаревшем приложении требуется следующее:
- Удалите ссылки на ресурсы панели инструментов и меню из файла ресурсов приложения.
- Удалите весь код инициализации панели инструментов и строки меню.
- Удалите любой код, используемый для присоединения панели инструментов или строки меню к окну верхнего уровня приложения.
- Создайте экземпляр платформы ленты.
- Подключите ленту к окну верхнего уровня приложения.
- Загрузите скомпилированную разметку.
Важно!
Существующие таблицы состояния и сочетания клавиш должны сохраняться, так как платформа ленты не заменяет эти функции.
В следующем примере показано, как инициализировать платформу с помощью IUIFramework::Initialize:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_RibbonBar.Create(this, WS_CHILD|WS_DISABLED|WS_VISIBLE|CBRS_TOP|CBRS_HIDE_INPLACE,0))
return -1; // fail to create
if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT)))
return -1; // fail to create
// Ribbon initialization
InitRibbon(this, &m_spUIFramework);
return 0;
}
В следующем примере показано, как использовать IUIFramework::LoadUI для загрузки скомпилированной разметки:
HRESULT InitRibbon(CMainFrame* pMainFrame, IUnknown** ppFramework)
{
// Create the IUIFramework instance.
CComPtr<IUIFramework> spFramework;
HRESULT hr = ::CoCreateInstance(CLSID_UIRibbonFramework, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spFramework));
if (FAILED(hr))
return hr;
// Instantiate the CApplication object.
CComObject<CApplication>* pApplication;
hr = CComObject<CApplication>::CreateInstance(&pApplication); // Refcount is 0
// Call AddRef on the CApplication object. The smart pointer will release the refcount when it is out of scope.
CComPtr< CComObject<CApplication> > spApplication(pApplication);
if (FAILED(hr))
return hr;
// Initialize and load the Ribbon.
spApplication->Initialize(pMainFrame);
hr = spFramework->Initialize(*pMainFrame, spApplication);
if (FAILED(hr))
return hr;
hr = spFramework->LoadUI(GetModuleHandle(NULL), L"APPLICATION_RIBBON");
if (FAILED(hr))
return hr;
// Return IUIFramework interface to the caller.
hr = spFramework->QueryInterface(ppFramework);
return hr;
}
Класс CApplication, упомянутый выше, должен реализовать пару интерфейсов объектной модели компонента (COM), определенных платформой ленты: IUIApplication и IUICommandHandler.
IUIApplication предоставляет основной интерфейс обратного вызова между платформой и приложением (например, высота ленты передается через IUIApplication::OnViewChanged), а обратные вызовы для отдельных команд предоставляются в ответ на IUIApplication::OnCreateUICommand.
Совет. Для некоторых платформ приложений, таких как MFC, необходимо учитывать высоту панели ленты при отрисовке пространства документа приложения. В этих случаях добавление скрытого окна для наложения панели ленты и принудительного пространства документа необходимой высоты. Пример этого подхода, в котором вызывается функция макета на основе высоты ленты, возвращаемой методом IUIRibbon::GetHeight, см. пример HTMLEditRibbon.
В следующем примере кода демонстрируется реализация IUIApplication::OnViewChanged :
// This is the Ribbon implementation that is done by an application.
// Applications have to implement IUIApplication and IUICommandHandler to set up communication with the Windows Ribbon.
class CApplication
: public CComObjectRootEx<CComSingleThreadModel>
, public IUIApplication
, public IUICommandHandler
{
public:
BEGIN_COM_MAP(CApplication)
COM_INTERFACE_ENTRY(IUIApplication)
COM_INTERFACE_ENTRY(IUICommandHandler)
END_COM_MAP()
CApplication() : _pMainFrame(NULL)
{
}
void Initialize(CMainFrame* pFrame)
{
// Hold a reference to the main frame.
_pMainFrame = pFrame;
}
void FinalRelease()
{
// Release the reference.
_pMainFrame = NULL;
__super::FinalRelease();
}
STDMETHOD(OnViewChanged)(UINT32 nViewID, __in UI_VIEWTYPE typeID, __in IUnknown* pView, UI_VIEWVERB verb, INT32 uReasonCode)
{
HRESULT hr;
// The Ribbon size has changed.
if (verb == UI_VIEWVERB_SIZE)
{
CComQIPtr<IUIRibbon> pRibbon = pView;
if (!pRibbon)
return E_FAIL;
UINT ulRibbonHeight;
// Get the Ribbon height.
hr = pRibbon->GetHeight(&ulRibbonHeight);
if (FAILED(hr))
return hr;
// Update the Ribbon bar so that the main frame can recalculate the child layout.
_pMainFrame->m_RibbonBar.SetRibbonHeight(ulRibbonHeight);
_pMainFrame->RecalcLayout();
}
return S_OK;
}
STDMETHOD(OnCreateUICommand)(UINT32 nCmdID,
__in UI_COMMANDTYPE typeID,
__deref_out IUICommandHandler** ppCommandHandler)
{
// This application uses one command handler for all ribbon commands.
return QueryInterface(IID_PPV_ARGS(ppCommandHandler));
}
STDMETHOD(OnDestroyUICommand)(UINT32 commandId, __in UI_COMMANDTYPE typeID, __in_opt IUICommandHandler *commandHandler)
{
return E_NOTIMPL;
}
private:
CMainFrame* _pMainFrame;
};
Реализация адаптера IUICommandHandler
В зависимости от структуры исходного приложения может быть проще использовать несколько реализаций обработчика команд или один обработчик команд, вызывающий существующую логику команды. Многие приложения используют WM_COMMAND сообщения для этой цели, где достаточно предоставить единый обработчик команд всех целей, который просто пересылает сообщения WM_COMMAND в окно верхнего уровня.
Однако для этого подхода требуется специальная обработка таких команд, как выход или закрытие. Так как лента не может быть уничтожена при обработке сообщения окна, WM_CLOSE сообщение должно быть размещено в потоке пользовательского интерфейса приложения и не должно обрабатываться синхронно, как показано в следующем примере:
// User action callback, with transient execution parameters.
STDMETHODIMP Execute(UINT nCmdID,
UI_EXECUTIONVERB verb,
__in_opt const PROPERTYKEY* key,
__in_opt const PROPVARIANT* ppropvarValue,
__in_opt IUISimplePropertySet* pCommandExecutionProperties)
{
switch(nCmdID)
{
case cmdExit:
::PostMessage(*_pMainFrame, WM_CLOSE, nCmdID, 0);
break;
default:
::SendMessage(*_pMainFrame, WM_COMMAND, nCmdID, 0);
}
return S_OK;
}
STDMETHODIMP UpdateProperty(UINT32 nCmdID,
__in REFPROPERTYKEY key,
__in_opt const PROPVARIANT *currentValue,
__out PROPVARIANT *newValue)
{
return S_OK;
}
Перенос ресурсов
Когда был определен манифест команд, объявлена структура ленты и код приложения, адаптированный для размещения платформы ленты, последний шаг — спецификация строковых ресурсов и ресурсов изображений для каждой команды.
Примечание.
Ресурсы строк и изображений обычно предоставляются в файле разметки. Однако их можно создать или заменить программным способом, реализуя метод обратного вызова IUICommandHandler::UpdateProperty .
Строковые ресурсы
Command.LabelTitle — это наиболее распространенное строковое свойство, определенное для команды. Они отображаются в виде текстовых меток для вкладок, групп и отдельных элементов управления. Строка метки из устаревшего элемента меню обычно может использоваться повторно для Command.LabelTitle без большого изменения.
Однако следующие соглашения изменились с появлением ленты:
- Суффикс многоточия (...), используемый для указания команды запуска диалога, больше не требуется.
- Амперсанд (&) по-прежнему можно использовать для указания сочетания клавиш для команды, которая отображается в меню, но свойство Command.Keytip , поддерживаемое платформой, выполняет аналогичную цель.
При обратном обращении к примеру текстового редактора можно указать следующие строки для LabelTitle и Keytip:
Символ | Исходная строка | Строка LabelTitle | Строка подсказки |
---|---|---|---|
ID_FILE_NEW | &New | &New | N |
ID_FILE_SAVE | &Сохранить | &Сохранить | S |
ID_FILE_SAVEAS | Сохранить как... | Сохранить как | а |
ID_FILE_OPEN | &Открыть... | &Открыть | O |
ID_FILE_EXIT | Вы&ход | Вы&ход | X |
ID_EDIT_UNDO | &Undo | Отмена | Z |
ID_EDIT_CUT | Cu&t | Cu&t | X |
ID_EDIT_COPY | &Копировать | &Копировать | О |
ID_EDIT_PASTE | & Вставка | & Вставка | V |
ID_EDIT_CLEAR | &Удалить | &Удалить | D |
ID_VIEW_ZOOM | &Zoom... | Масштабирование | Z |
Ниже приведен список других строковых свойств, которые должны быть заданы в большинстве команд:
Теперь вкладки, группы и другие функции пользовательского интерфейса ленты можно объявить со всеми ресурсами строки и изображения.
В следующем примере разметки ленты показаны различные строковые ресурсы:
<Application.Commands>
<!-- Tabs -->
<Command Name="tabHome" LabelTitle="Home" Keytip="H" />
<Command Name="tabView" LabelTitle="View" Keytip="V" />
<!-- Groups -->
<Command Name="grpClipboard" LabelTitle="Clipboard" />
<Command Name="grpZoom" LabelTitle="Zoom" />
<!-- App menu commands -->
<Command Name="cmdNew" Id="0xE100" Symbol="ID_CMD_NEW" LabelTitle="New document" Keytip="N" >
<Command.TooltipTitle>New (Ctrl+N)</Command.TooltipTitle>
<Command.TooltipDescription>Create a new document.</Command.TooltipDescription>
</Command>
<Command Name="cmdSave" Id="0xE103" Symbol="ID_CMD_SAVE" LabelTitle="Save" Keytip="S">
<Command.TooltipTitle>Save (Ctrl+S)</Command.TooltipTitle>
<Command.TooltipDescription>Save the current document.</Command.TooltipDescription>
</Command>
<Command Name="cmdSaveAs" Id="0xE104" Symbol="ID_CMD_SAVEAS" LabelTitle="Save as" Keytip="A">
<Command.TooltipDescription>Save the current document with a new name.</Command.TooltipDescription>
</Command>
<Command Name="cmdOpen" Id="0xE101" Symbol="ID_CMD_OPEN" LabelTitle="Open" Keytip="O">
<Command.TooltipTitle>Open (Ctrl+O)</Command.TooltipTitle>
<Command.TooltipDescription>Open a document.</Command.TooltipDescription>
</Command>
<Command Name="cmdExit" Id="0xE102" Symbol="ID_CMD_EXIT" LabelTitle="Exit" Keytip="X">
<Command.TooltipDescription>Exit the application.</Command.TooltipDescription>
</Command>
<!-- ...other commands -->
</Application.Commands>
Ресурсы изображений
Платформа ленты поддерживает форматы изображений, которые обеспечивают гораздо более широкий внешний вид и ощущение, чем форматы изображений, поддерживаемые предыдущими компонентами меню и панели инструментов.
Для Windows 8 и более поздних версий платформа ленты поддерживает следующие графические форматы: 32-разрядные файлы растрового изображения ARGB (BMP) и файлы переносимой сетевой графики (PNG) с прозрачностью.
Для Windows 7 и более ранних версий ресурсы изображений должны соответствовать стандартному формату графики BMP, используемому в Windows.
Примечание.
Существующие файлы изображений можно преобразовать в любой формат. Однако результаты могут быть менее удовлетворительными, если файлы изображений не поддерживают защиту и прозрачность.
Невозможно указать один размер по умолчанию для ресурсов изображений в платформе ленты. Однако для поддержки адаптивного макета элементов управления изображения можно указать в двух размерах (больших и небольших). Все изображения в платформе ленты масштабируются в соответствии с разрешением точек на дюйм (dpi) дисплея с точным размером отрисовки, зависящим от этого параметра dpi. Дополнительные сведения см. в разделе "Указание ресурсов изображений ленты".
В следующем примере показано, как на разметку ссылается набор образов, относящихся к dpi:
<Command Name="cmdNew" Id="0xE100" Symbol="ID_CMD_NEW" LabelTitle="New document" Keytip="N" >
<Command.TooltipTitle>New (Ctrl+N)</Command.TooltipTitle>
<Command.TooltipDescription>Create a new document.</Command.TooltipDescription>
<Command.LargeImages>
<Image Source="cmdNew-32px.png" MinDPI="96" />
<Image Source="cmdNew-40px.png" MinDPI="120" />
<Image Source="cmdNew-48px.png" MinDPI="144" />
<Image Source="cmdNew-64px.png" MinDPI="192" />
</Command.LargeImages>
<Command.SmallImages>
<Image Source="cmdNew-16px.png" MinDPI="96" />
<Image Source="cmdNew-20px.png" MinDPI="120" />
<Image Source="cmdNew-24px.png" MinDPI="144" />
<Image Source="cmdNew-32px.png" MinDPI="192" />
</Command.SmallImages>
</Command>
См. также