Поделиться через


Переход на Платформу ленты Windows

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

Введение

Как правило, перенос существующего приложения на платформу ленты включает в себя следующее:

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

Примечание.

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

 

Проектирование макета ленты

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

  1. Инвентаризация всех существующих функциональных возможностей.
  2. Преобразование этой функции в команды ленты.
  3. Упорядочение команд в логические группы.

Взять инвентаризацию

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

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

Символ Идентификатор 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

Замена устаревших меню и панелей инструментов

Для замены стандартных меню и панелей инструментов на ленту в устаревшем приложении требуется следующее:

  1. Удалите ссылки на ресурсы панели инструментов и меню из файла ресурсов приложения.
  2. Удалите весь код инициализации панели инструментов и строки меню.
  3. Удалите любой код, используемый для присоединения панели инструментов или строки меню к окну верхнего уровня приложения.
  4. Создайте экземпляр платформы ленты.
  5. Подключите ленту к окну верхнего уровня приложения.
  6. Загрузите скомпилированную разметку.

Важно!

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

 

В следующем примере показано, как инициализировать платформу с помощью 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>

Указание ресурсов образа ленты