共用方式為


移轉至 Windows 功能區架構

依賴傳統功能表、工具列和對話框的應用程式可以移轉至 Windows 功能區架構命令系統的豐富、動態和內容導向 UI。 這是一種簡單且有效的方法來現代化和振興應用程式,同時改善其功能的輔助功能、可用性和可探索性。

簡介

一般而言,將現有的應用程式移轉至功能區架構牽涉到下列各項:

  • 設計功能區配置和控制組織,以公開現有應用程式的功能,且具有足夠的彈性來支援新功能。
  • 調整現有應用程式的程序代碼。
  • 將現有的應用程式資源(字串和映像)移轉至功能區架構。

注意

應該檢閱功能區用戶體驗指導方針,以判斷應用程式是否適合功能區 UI 的候選專案。

 

設計功能區配置

您可以執行下列步驟來識別潛在的功能區 UI 設計和控制項設定:

  1. 清查所有現有的功能。
  2. 將此功能轉譯為功能區命令。
  3. 將命令組織成邏輯群組。

清查

在功能區架構中,操作工作區或文件之狀態或檢視的應用程式所公開的功能會被視為命令。 構成命令的內容可能會有所不同,並取決於現有應用程式的本質和網域。

下表列出一組假設文字編輯應用程式的基本命令:

符號 識別碼 描述
ID_FILE_NEW 0xE100 新檔
ID_FILE_SAVE 0xE103 儲存文件
ID_FILE_SAVEAS 0xE104 另存新檔...(dialog)
ID_FILE_OPEN 0xE101 打開。。。(dialog)
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 縮放。。。(dialog)

 

建置命令清查時,請超越現有的功能表和工具列。 請考慮使用者與工作區互動的所有方式。 雖然並非所有命令都適合包含在功能區中,但此練習可能會非常公開先前被UI層遮蔽的命令。

翻譯

並非所有命令都必須在功能區 UI 中表示。 例如,輸入文字、變更選取範圍、捲動或移動具有滑鼠的插入點,都符合假設文本編輯器中的命令資格,不過,這些都不適合在命令行中公開,因為每個都牽涉到與應用程式 UI 的直接互動。

相反地,某些功能可能不是傳統意義上的命令。 例如,印表機頁面邊界調整可以在功能區中以內容索引卷標或 應用程式模式的Spinner控件群組來表示,而不是隱藏在對話框中。

注意

記下可能指派給每個命令的數值標識碼。 某些UI架構,例如 Microsoft Foundation Classes (MFC),會定義命令的標識碼,例如檔案和編輯功能表(0xE100至0xE200)。

 

組織

嘗試組織命令清查之前, 應該先檢閱功能區用戶體驗指導方針 ,以取得實作功能區 UI 時的最佳做法。

一般而言,下列規則可以套用至功能區命令組織:

  • 在檔案或整體應用程式上運作的命令最有可能屬於 應用程式功能表
  • 常用命令,例如剪下、複製和貼上(如文本編輯器範例中),通常會放在預設的主索引標籤上。在更複雜的應用程式中,它們可能會複製到功能區 UI 的其他地方。
  • 重要或常用命令可能需要在快速存取工具列中包含預設。

功能區架構也會透過 ContextPopup 檢視提供 ContextMenu 和 MiniToolbar 控件。 這些功能並非必要功能,但如果您的應用程式有一或多個現有的操作功能表,則移轉所包含的命令可能會允許重複使用現有的程式代碼基底,並自動系結至現有資源。

因為每個應用程式都不同,請閱讀指導方針,並嘗試盡可能盡可能套用這些指導方針。 功能區架構的其中一個目標是提供熟悉且一致的用戶體驗,如果新應用程式的設計能盡可能鏡像現有的功能區應用程式,則此目標將會更容易達成。

調整程序代碼

在識別功能區命令並組織成邏輯群組之後,將功能區架構納入現有應用程式程式代碼時所涉及的步驟數目取決於原始應用程式的複雜度。 一般而言,有三個基本步驟:

  • 根據命令組織和配置規格建立功能區標記。
  • 將舊版功能表和工具列功能取代為功能區功能。
  • 實作 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 等 UI 架構定義的符號,上一個範例會針對每個命令使用稍微不同的符號名稱(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 檔案,因此請在該 RC 檔案中包含產生的 H 和 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) 介面:IUIApplicationIUICommandHandler。

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 配接器

根據原始應用程式的設計,可能更容易執行多個 Command 處理程式實作,或叫用現有應用程式命令邏輯的單一橋接命令處理程式。 許多應用程式會針對此目的使用WM_COMMAND訊息,只要提供單一、所有用途的命令處理程式,只要將WM_COMMAND訊息轉送至最上層視窗即可。

不過,此方法需要特殊處理命令,例如 ExitClose。 因為功能區處理視窗訊息時無法終結,所以WM_CLOSE訊息應該張貼至應用程式的 UI 線程,而且不應該同步處理,如下列範例所示:

// 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 定義的最常見字串屬性。 這些會轉譯為索引標籤、群組和個別控件的文字標籤。 舊版功能表項中的標籤字串通常可用於 Command.LabelTitle ,而不需要太多編輯。

不過,下列慣例隨著功能區的出現而有所變更:

  • 用來表示對話框啟動命令的省略號 (...) 後綴已不再需要。
  • 連字元號 (&) 仍可用來指出出現在功能表中之 Command 的鍵盤快捷方式,但 架構支援的 Command.Keytip 屬性也符合類似的目的。

回到文本編輯器範例時,可以指定 LabelTitle 和 Keytip 的下列字串:

符號 原始字串 LabelTitle 字串 Keytip 字串
ID_FILE_NEW &New &New
ID_FILE_SAVE &Save &Save S
ID_FILE_SAVEAS 另存新檔... 另存新檔 A
ID_FILE_OPEN &Open... &Open O
ID_FILE_EXIT 結束(&X) 結束(&X) X
ID_EDIT_UNDO &復原 復原 Z
ID_EDIT_CUT Cu&t Cu&t X
ID_EDIT_COPY &Copy &Copy C
ID_EDIT_PASTE &Paste &Paste V
ID_EDIT_CLEAR &Delete &Delete D
ID_VIEW_ZOOM &Zoom... Zoom Z

 

以下是應該在大部分命令上設定的其他字串屬性清單:

索引標籤、群組和其他功能區UI功能現在可以使用指定的所有字串和影像資源來宣告。

下列功能區標記範例示範各種字串資源:

<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 和更早版本,影像資源必須符合 Windows 中使用的標準 BMP 圖形格式。

注意

現有的圖像檔可以轉換成任一格式。 不過,如果圖像檔不支援反鋸齒和透明度,結果可能會不盡如人意。

 

無法在功能區架構中指定影像資源的單一預設大小。 不過,若要支援 控件的調適型配置 ,可以在兩個大小(大而小)中指定影像。 功能區架構中的所有影像都會根據顯示器的每英吋 (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>

指定功能區映像資源