WINDOWS 功能區概述
本教程是針對開發桌面應用程序並希望利用 Windows Ribbon 框架的 C++程序員的。實驗步驟將幫助您實現為小應用程序添加 一個空功能區,在功能區中添加多個包含圖標、標簽和其他資源的控件,然後將 控件與應用程序中已經存在的命令結構相連接。您將會學習API是如何維護控件組 織(control organization)和事件處理的分離。最後,本教程將會說明如何定 制一個界面和調整大小的行為來展示功能區是如何在不同大小的情況下進行適應 並運行的。當您完成實驗的時候,您將學會所有關於在應用程序中添加並自定制 一個基礎的功能區的所有必要的步驟。
本教程將會涉及到代碼運行時編譯 和對文檔中已拷貝的內容進行標記。在事件中,拷貝錯誤(或其他的問題)會阻 止應用程序進行編譯,您可以在每個教程的完成源代碼中查找所有已完成的示例 。這些示例可以被用來幫助您免於編譯錯誤,或者可以被用來作為下一個練習的 開始。
要求
• Microsoft Visual Studio 2005(或 更新的版本)
本教程使用了Visual Studio並在幾個地方引用了配置 (configuring)。當然也不是必須使用這個環境來制作功能區,本教程會假定您 已經在您系統上安裝了Visual Studio 2005(或更新的版本)。
注意:當 然Visual Studio Express editions可以被用來創建功能區,這個版本缺少完成 本教程的一些必要功能。
• Windows 7 SDK (v7.0, RC Build)
這個教程第一次作為動手試驗被發布實在2008年10月的Professional Developers Conference (PDC2008)上。隨著Windows 7 SDK RC的發布,它也有了 一些更新。
注意: 我們建議SDK應該在Visual Studio安裝之後再進行安裝。這將幫助您預 防在編譯時潛在SDK版本沖突的問題。如果您在編譯您的代碼時遇到問題,請按照 第9頁上的“特別注意”步驟進行操作,可能會對您有所幫助。
學習目標
當進行本教程時,您將會學習如何:
• 配置一個Visual Studio項目來使用功能區
• 將功能區 和Win32窗口(HWND)進行集成
• 添加控件,例如:按鈕, 復選框,選項卡,區域
• 將功能區控件和應用程序業務代碼 相連
• 調整控件布局並定義功能區如何調整大小
解析Windows 功能區
本教程使用通用Windows 功能 區專門用語來描述UI的不同部分。下面的圖表應該可以幫助描述這些部分包括什 麼以及在哪兒可以找到它們:
從用戶角度考慮
在開始本教程之前 ,了解下面這些項目會對您有所幫助(雖然他們並不是必須的):
• Win32開發以及C++語言
• 對COM編程和概念有基礎性 的了解
• Visual Studio
更多信息
關於Windows功能區, API,Microsoft Office Fluent用戶界面,請參看下面的連接:
• MSDN上的Windows 功能區文檔
http://msdn.microsoft.com/en- us/library/dd371191.aspx
• MSDN上的Windows功能區准則
http://msdn.microsoft.com/en- us/library/cc872782.aspx
• Microsoft Office Fluent用 戶界面概述
http://office.microsoft.com/en- us/products/HA101679411033.aspx
• 關於Windows功能區開發的MSDN論壇
http://social.msdn.microsoft.com/Forums/en- US/windowsribbondevelopment/threads/
練習 1 - 創建一 個空的功能區並把它添加到一個應用程序
在這個練習中您將創建一個 Visual Studio項目並從零開始。然後,您將會編寫必要的代碼並初始化應用程序 的功能區。
背景信息
功能區API包含2部分:
• XML 標記(markup),用來定義功能區結構和控件組織
• C++ COM 接口,用來初始化和處理事件
標記必須被編譯成二進制格式以方 便在執行時被使用。功能區的編譯是通過叫做uicc.exe的工具來進行編譯的。在 本次實驗中我們將會向您展示如何配置Visual Studio來自動編譯標記。
下面步驟中所有引用到的文件都可以在教程包中的 “EX01_Starter\RibbonApp\”路徑下找到。
注意: 這個教程 中,已經存在的代碼和/或標記都會用灰色進行顯示(例如)。當告訴您要添加新 的代碼到這些文件的時候,這會給您以提示。
任務 1 - 在Visual Studio 中創建一個新項目並生成一個基礎應用程序
在這個任務中您將會進行基礎 設置以開始為應用程序添加一個功能區。
1.運行Visual Studio.
2.選擇File New Project,來創建一個新項 目
3.在“Visual C++”下選擇“Win32”。然後選 擇“Win32 Project”.
4.項目名稱輸入 “RibbonApp”
5.點擊“OK”,然後在下一個界面 上點擊“Finish”.
6.編譯您的新應用程序,並運行它(快捷鍵F5)。一個有簡單菜單的窗口將會 打開。
注意: 當生成應用程序的時候,如果彈出窗口顯示“This project is out of date”,則忽略它繼續生成進程就好。
任務 2 - 創建一個基礎功能區標記文件
您現在將為應用程序添加一個較小的功能 區XML標記文件。標記文件將定義暴露給功能區的命令,並且還將定義當暴露這些 命令到時候控件將如何被展現。
1.創建名為 “ribbonmarkup.xml”的文件並添加到項目中。
2.在解決方案 窗口中,右鍵點擊項目名稱 Add New Item。在 “Visual C++”下選擇 “Web”,然後選擇 “XML File (.xml)”,並在名稱字段中輸入 “ribbonmarkup.xml”.
3.復制粘貼下面的代碼到新創建的 ribbonmarkup.xml文件中,當.xml文件被創建的時候,重寫任何之前文本自動生 成的代碼。這將定義一個包含選項卡的功能區:
XML
<Application xmlns="http://schemas.microsoft.com/windows/2009/Ribbon">
<Application.Commands>
<Command Name="TabHome" Symbol="cmdTabHome" Id="30000" />
</Application.Commands>
<Application.Views>
<Ribbon>
<Ribbon.Tabs>
<Tab CommandName="TabHome">
</Tab>
</Ribbon.Tabs>
</Ribbon>
</Application.Views>
</Application>
注意: 練習 2將專注於讓您更加詳細的理解功能區XML格式。但是,希望您現 在能測試代碼並了解功能區選項卡和命令式如何被定義的。
任務 3 - 配 置Visual Studio 來自動編譯功能區標記
1.在Visual Studio中,右鍵點 擊新的XML文件並選擇“Properties”。在“Custom Build Step”下,在命令行中輸入下面的命令:
Command
uicc.exe ribbonmarkup.xml ribbonmarkup.bml /res:ribbonres.rc
2.uicc.exe是一個新的編譯工具,功能區可以用它來 生成在運行時使用的資源。"ribbonmarkup.bml”是輸出文件: “ribbonmarkup.xml”文件的二進制表示。
3.在相同的屬性頁 ,輸入下面的內容:
◦ ribbonmarkup.bml;ribbonres.rc
4.點擊“OK”關閉屬性頁面 。生成的ribbonres.rc文件定義了一個用來連接您的應用程序模塊的生成的二進 制標記資源。當您生成應用程序時,將會生成該二進制標記文件(.bml)。請查 看ribbonres.rc文件(要這樣做,你需要先生成您的項目,以便uicc.exe生成該 文件。)
5.最後,您應該在您的項目中包含該二進制標記文件。要這樣做 ,編輯RibbonApp.rc文件的代碼(在解決方案窗口中,右鍵點擊它選擇 “view code”)並在定義圖標的代碼前面添加下面的代碼(在叫做 “Icon”的注釋代碼段之前):
C++
#include "ribbonres.rc"
特別注意
如果您在進行練習的時候不能編譯您的 應用程序,盡管您預先安裝了Windows 7 RC SDK,那麼有可能您的系統沒有使用 SDK進行正確的配置。要檢查並/或修復這個問題,請按一下步驟進行:
點擊 Start 按鈕 All Programs Microsoft Windows SDK v7.0 Visual Studio Registration Windows SDK Configuration Tool.
如果出現了一個對話框上面顯示“Do you want to allow the following program to make changes to this computer? ”,請點擊“Yes”.
查看對話框中的組合框並確認 Windows SDK的v7.0當前正用於Visual Studio。如果被設置為了別的(比如v6.0 ),那麼請把它變成v7.0並點擊“Make Current”。甚至您發現它已 經被設置為v7.0,也請您點擊“Make Current”—如果您正運行 VS2005,這是非常重要的。
任務 4 - 為功能區創建一個宿主程序
宿主程序用來接收來自功能區的不同的通知。這首先需要功能區完成初始化。
1.使用ATL來配置您的項目。在解決方案窗口中,右鍵點擊項目文件名 Properties。展開 “Configuration Properties”,並選 擇 “General” 頁。在屬性頁中,根據您偏好來設置 “Use of ATL” 字段為下面的值:
a.Static Link to ATL
b.or- Dynamic Link to ATL
2.為項目添加一個新的文件並命名為 “Ribbon.cpp”。
a.在解決方案窗口中,右鍵點擊項目名稱 Add New Item。在 “Visual C++” 下選擇 “Code”,選擇 “C++ File (.cpp)”,在名稱字段中填 入 “Ribbon.cpp”。
3.編輯新的“Ribbon.cpp” 文件並包含下列頭文件:
C++
//Precompiled header for the project:
#include "stdafx.h"
//ATL/COM header files:
#include <atlbase.h>
#include <atlcom.h>
#include <initguid.h>
//And finally, the Ribbon header file which declares all the necessary Ribbon interfaces:
#include <uiribbon.h>
4.在 Ribbon.cpp文件中,實現uiribbon.h中定義的IUIApplication接口。在這個練習 中,我們不能真正的實現通知,所以您可以在3個方法中返回E_NOTIMPL。
5.下列代碼可以用在Ribbon.cpp文件中來實現IUIApplication接口:
C++
class CApplication
: public CComObjectRootEx<CComMultiThreadModel>
, public IUIApplication
{
public:
BEGIN_COM_MAP (CApplication)
COM_INTERFACE_ENTRY (IUIApplication)
END_COM_MAP()
STDMETHOD(OnViewChanged)(UINT32 nViewID, __in UI_VIEWTYPE typeID, __in IUnknown* pView, UI_VIEWVERB verb, INT32 uReasonCode)
{
return E_NOTIMPL;
}
STDMETHOD(OnCreateUICommand)(UINT32 nCmdID, __in UI_COMMANDTYPE typeID, __deref_out IUICommandHandler** ppCommandHandler)
{
return E_NOTIMPL;
}
STDMETHOD (OnDestroyUICommand)(UINT32 commandId, __in UI_COMMANDTYPE typeID, __in_opt IUICommandHandler* pCommandHandler)
{
return E_NOTIMPL;
}
};
任務 5 - 創建並初始化功能區
現在您實現了CApplication,在 Ribbon.cpp文件頂部聲明了IUIFramework*類型的全局變量g_pFramework (把它 放在CApplication類聲明前面,includes代碼的後面)。這個指針將會被用來實 例化功能區平台。
1.IUIFramework* g_pFramework = NULL;
然後 ,在Ribbon.cpp文件的最底下(CApplication定義的後面),定義一個功能區初 始化方法:
C++
HRESULT InitRibbon(HWND hWindowFrame)
2.hWindowFrame將會成為應用程序主窗口的一個處 理程序。
下面的第2-5步要按順序在新創建的InitRibbon方法中完成。
3.用CoCreateInstance來初始化一個CLSID_UIRibbonFramework類。您將 會獲得一個IUIFramework指針。IUIFramework是功能區平台暴露的主要接口
C++
HRESULT hr = ::CoCreateInstance (CLSID_UIRibbonFramework, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS (&g_pFramework));
if(FAILED(hr))
{
return hr;
}
4.初始化CApplication對象
C++
CComObject<CApplication> *pApplication = NULL;
hr = CComObject<CApplication>::CreateInstance (&pApplication);
if(FAILED(hr))
{
return hr;
}
5.調用初始化方法並返回一個IUIFramework指針,且 對宿主HWND和新創建的CApplication對象進行定義。
C++
hr = g_pFramework->Initialize(hWindowFrame, pApplication);
if (FAILED(hr))
{
return hr;
}
6.現在我們來加載標記,利用IUIFramework指針,調用 LoadUI.
C++
g_pFramework->LoadUI(GetModuleHandle (NULL), L"APPLICATION_RIBBON");
if(FAILED(hr))
{
return hr;
}
7.結束方法,這樣我們就完成任務了。
C++
return S_OK;
}
任務 6 -和應用 程序進行集成
我們現在需要從預先生成的應用程序代碼中調用InitRibbon 方法。
1.在應用程序中初始化COM。打開RibbonApp.cpp文件並在include 代碼後面,添加:
C++
#include <atlbase.h>
CComModule _Module;
extern HRESULT InitRibbon(HWND hWindowFrame);
2.在生成方法_tWinMain中,在方法的開頭添加:
C++
CoInitialize(NULL);
3.在最後,但在 “return”代碼前添加:
C++
CoUninitialize ();
4.在方法的初始化中,在調用ShowWindow之前,調用 InitRibbon方法:
C++
HRESULT hr = InitRibbon (hWnd);
if (FAILED(hr))
{
return FALSE;
}
5.編譯您的應用程序並運行它。它現在應該有一個空的功能區了 。
您應該注意到在這裡應用程序代碼中只改變了對InitRibbon方法的調用 。剩下添加的代碼是標准的COM初始化。
任務 7- 完成集成
1.在 Ribbon.cpp文件中,添加DestroyRibbon()方法。將下列定義粘貼到文件的最後:
C++
void DestroyRibbon()
{
if (g_pFramework)
{
g_pFramework- >Destroy();
g_pFramework->Release();
g_pFramework = NULL;
}
}
2.在RibbonApp.cpp文件中,在文件開始的附近對DestroyRibbon ()方法進行定義:
C++
#include <atlbase.h>
CComModule _Module;
extern HRESULT InitRibbon(HWND host);
extern void DestroyRibbon();
3.在RibbonApp.cpp文件的 WndProc方法中,在switch語句的WM_DESTROY進程中調用DestroyRibbon()。當您 完成上述步驟,代碼應該和下面的代碼類似:
C++
case WM_DESTROY:
DestroyRibbon();
PostQuitMessage (0);
break;
4.如果您運行應用程序,則您需要 注意在調整大小時功能區的閃爍。您可以通過向該方法實例化內的CreateWindow 調用中添加窗口樣式,來修復這個問題。
C++
hWnd = CreateWindow(szWindowClass, szTitle,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL,
hInstance, NULL);
注意: 閃爍是由於宿主窗口在功能區中進行繪制所引起的 。當繪制在父窗口發生的時候,WS_CLIPCHILDREN 會通知宿主窗口忽略子窗口所 占用的位置。
練習 2 - 向已存在的功能區添加簡單的控件
在這個練 習中,您將應用練習1中所完成的項目並寫代碼以向功能區中添加控件。您還將學 習如何從功能區獲得通知以及如何對這些控件獲取和設置屬性。
背景信息
下面步驟中所有引用到的文件都可以在教程包中的 “EX02_Starter\RibbonApp\”路徑下找到。
任務 1 - 在一個 選項卡中添加一個按鈕
在這個任務中,您將向功能區中添加一個按鈕控件 。
1.繼續您在練習1中使用的項目。或者,在Visual Studio中運行 EX01_Starter/RibbonApp.sln(在教程包中查找)。
2.在 RibbonMarkup.xml文件中,在<Application.Commands>節點中添加下面的 標記來為新控件定義命令。
XML
<Command Name="GroupMain" Symbol="cmdGroupMain" Id="30001"/>
<Command Name="MyButton" Symbol="cmdMyButton" Id="30002"/>
3.我們現在就在新組(Group)中添加一個新按 鈕控件。一個功能區包含選項卡(Tabs),在選項卡中的是組(groups)。這些 組包含了功能區中可交互的控件。向<Tab CommandName="TabHome">節點中 添加下面標記:
XML
<Group CommandName="GroupMain">
<Button CommandName="MyButton"/>
</Group>
4.生成並運 行應用程序。如果您把鼠標放在組上,請您注意新組中有一個空按鈕。(請注意 該按鈕是不可見,除非把鼠標放在上面,因為它的背景顏色和組的顏色是一樣的 。)
任務 2 -為按鈕設定標簽,工具提示和圖標
1.所有功能區使用到的所有資源都定義在了一個頭文件中。首先,您將會學習 到如何生成這個頭文件。然後,您將對所有不同的命令定義所有不同的屬性(標 簽,工具提示和圖標)。
2.使用uicc.exe設置項目屬性來生成頭文件。請 進行如下操作:
3.右鍵點擊RibbonMarkup.xml Properties Custom build step
4.在“Command Line”中, 在/res:ribbonres.rc之前添加下面代碼:
Command
/header:ribbonres.h
5.整個命令行,如下:
Command
uicc.exe ribbonmarkup.xml ribbonmarkup.bml /header:ribbonres.h /res:ribbonres.rc
6.在“Outputs”的 最後中添加下面代碼:
Command
;ribbonres.h
7.新的輸出 應該看起來和下面的類似:
Command
ribbonmarkup.bml;ribbonres.rc;ribbonres.h
8. 點擊“OK”關閉Properties對話框
9.右鍵點擊RibbonApp.rc View Code並在ribbonres.rc文件中的include代碼行上面添加下面的代 碼:(這些include代碼行會在文件較為靠後的地方,但在“Icon”注 釋代碼段的上面)
C++
#include "ribbonres.h"
10.從BITMAPS(包含在教程中)文件夾中將 Button_Image.bmp文件拷貝到項目目錄;這也是RibbonApp.rc文件所在的文件夾 。路徑應該和下面顯示的類似:
C:\Users\your_username\Documents\Visual Studio ####\Projects\RibbonApp\RibbonApp
11.在Visual Studio解決方案窗口 中,右鍵點擊RibbonApp project Add Existing Item Button_Image.bmp,把文件添加到項目中
12.在這個步驟中,我 們將會向cmdMyButton的Command定義中加入資源屬性。我們將會加入標簽,工具 提示標題,工具提示描述和引用這個命令的控件所使用的圖片。在 ribbonmarkup.xml文件中,在<Application.Commands>節點下修改 cmdMyButton的命令定義,如下:
XML
<Command Name="MyButton" Symbol="cmdMyButton" Id="30002" LabelTitle="My Button">
<Command.TooltipTitle>My Button</Command.TooltipTitle>
<Command.TooltipDescription>Click on My Button</Command.TooltipDescription>
<Command.LargeImages>
<Image Source="Button_Image.bmp"/>
</Command.LargeImages>
</Command>
13.重新生成(在菜單中選擇Build Rebuild solution)並運行該項。請注意標簽和圖片是否出現在了組中的功能區 按鈕上。
14.通過修改下面的屬性中的命令定義,來向其他控件(選項卡 ,組等)添加標簽:(注意允許定義XAML屬性的不同的語法。)
把下面代 碼:
XML
<Command Name="TabHome" Symbol="cmdTabHome" Id="30000" />
替換 為:
XML
<Command Name="TabHome" Symbol="cmdTabHome" Id="30000" LabelTitle="Home" />
然後,把下面代碼:
XML
<Command Name="GroupMain" Symbol="cmdGroupMain" Id="30001" />
替換 為:
XML
<Command Name="GroupMain" Symbol="cmdGroupMain" Id="30001" LabelTitle="Main" />
15.生成並運行該項目,並查看為功能 區的“Home”選項卡和“Main”組新添加的標簽。
任務 3 -當按鈕被點擊的時候顯示一個消息框
1.在Ribbon.cpp文件的 include代碼行後面添加下面的代碼:
C++
#include <uiribbon.h>
#include "ribbonres.h"
ribbonres.h是由uicc.exe生成的,並包含了在 標記中定義的命令,使它們可以通過代碼來訪問。
2.在Ribbon.cpp文件, 改變CApplication類來為項目中所有控件實現IUICommandHandler接口。
3.首先,通過添加下面最後一個行代碼,來讓CApplication類繼承 IUICommandHandler接口:
C++
class CApplication
: public CComObjectRootEx<CComMultiThreadModel>
, public IUIApplication
, public IUICommandHandler
4.然後,向已經存在的COM_MAP中添加 COM_INTERFACE_MAP入口:
C++
BEGIN_COM_MAP (CApplication)
COM_INTERFACE_ENTRY(IUIApplication)
COM_INTERFACE_ENTRY(IUICommandHandler)
END_COM_MAP()
5.向CApplication類中添加下面的代碼以實現 IUICommandHandler接口的2個方法:
C++
STDMETHODIMP Execute(UINT nCmdID,
UI_EXECUTIONVERB verb,
__in_opt const PROPERTYKEY* key,
__in_opt const PROPVARIANT* ppropvarValue,
__in_opt IUISimplePropertySet* pCommandExecutionProperties)
{
HRESULT hr = S_OK;
switch (verb)
{
case UI_EXECUTIONVERB_EXECUTE:
if (nCmdID == cmdMyButton)
{
MessageBox(NULL, L"Clicked on My Button!",
L"My Button Execute", MB_OK);
}
break;
}
return hr;
}
STDMETHODIMP UpdateProperty(UINT nCmdID,
__in REFPROPERTYKEY key,
__in_opt const PROPVARIANT* ppropvarCurrentValue,
__out PROPVARIANT* ppropvarNewValue)
{
return E_NOTIMPL;
}
6.在CApplication::OnCreateUICommand中的return語句之前添加 下面的if語句:
C++
STDMETHOD(OnCreateUICommand) (UINT32 nCmdID, __in UI_COMMAND_TYPE typeID, __deref_out IUICommandHandler** ppCommandHandler)
{
if (nCmdID == cmdMyButton)
{
return QueryInterface(IID_PPV_ARGS(ppCommandHandler));
}
return E_NOTIMPL;
}
7.生成並運行應用程序並點擊按鈕,看看是否有消息框彈出。
任務 4 - 添加一個復選框控件
進行下面的操作步驟,來創建復選框和與復選框命 令相關的命令處理程序。
1.在RibbonMarkup.xml文件中的 <Application.Commands>節點中,添加下面的命令定義(Commands Definition):
XML
<Command Name="MyChoice" Symbol="cmdMyChoice" Id="30003" LabelTitle="My Choice">
<Command.TooltipTitle>My Choice</Command.TooltipTitle>
<Command.TooltipDescription>Select My Choice! </Command.TooltipDescription>
<Command.LargeImages>
<Image Source="Button_Image.bmp"/>
</Command.LargeImages>
</Command>
2.通過添加下面的標記來在Group中添加一個 復選框:
XML
<Group CommandName="GroupMain">
<CheckBox CommandName="MyChoice"/>
<Button CommandName="MyButton"/>
</Group>
3.為新建的 復選框關聯一個命令處理程序。在OnCreateUICommand文件的Ribbon.cpp方法中, 修改if語句來給復選框控件關聯一個命令處理程序:
C++
if (nCmdID == cmdMyButton || nCmdID == cmdMyChoice )
{
return QueryInterface(IID_PPV_ARGS(ppCommandHandler));
}
4.在Ribbon.cpp文件的Execute方法中,添加下面的if語句(在 break;行之前),來實現當復選框被點中的時候,來進行通知:
C++
case UI_EXECUTIONVERB_EXECUTE:
if (nCmdID == cmdMyButton)
{
MessageBox(NULL, L"Clicked on My Button!", L"My Button Execute", MB_OK);
}
else if (nCmdID == cmdMyChoice)
{
MessageBox(NULL, L"Toggled My Choice!", L"My Choice Execute", MB_OK);
}
break;
5.生 成並運行項目並觀察新建的復選框。點擊復選框並注意復選框的狀態。注意彈出 的消息框。
任務 5 - 把復選框換成切換按鈕
在這部分中,我們將 會把功能區中復選框控件變成一個切換按鈕控件。不論是復選框還是切換按鈕控 件都是同樣的命令類型,並且應用程序代碼會以同樣的方式對他們進行處理。所 以當把復選框控件變成切換按鈕的時候,沒有必要改變C++代碼。
1.在 RibbonMarkup.xml文件中,將復選框控件替換為切換按鈕控件。只需要改變標簽 名稱即可。
把下面代碼:
XML
<CheckBox CommandName="MyChoice"/>
替換 為:
XML
<ToggleButton CommandName="MyChoice"/>
2.生成並運行該項目,注意復選框 控件已經變成一個切換按鈕控件。您將會注意到點擊切換按鈕會調用和之前一樣 的消息框。
任務 6 - 為組定制一個SizeDefinition 模板
在這個 步驟中,您使用SizeDefinition 的屬性為組(Group)定義一個SizeDefinition 模板。模板為組內的控件提供了布局信息。Windows功能區包含了一些預先定義的 模板,您也可以定義您自己的模板。由於組中現在有兩個按鈕,可以使用標准預 定義的“TwoButtons”模板。
1.為GroupMain添加一個“TwoButtons”的SizeDefinition模板。 組定義會變得和下面的代碼類似:
XML
<Group CommandName="GroupMain" SizeDefinition="TwoButtons">
2.生成並運行該應用程序,由 於使用了‘TwoButtons’模板,大按鈕會並排的出現在組中。
任務 7 - 現在我們將使用切換按鈕來啟用/ 禁用按鈕控件
1.通過在花括 號結束前添加下面代碼,以在CApplication類中添加一個私有的成員 _fEnabled :
C++
private:
BOOL _fEnabled;
2.在Ribbon.cpp文件的頂部,include代碼行的上面 (#include "ribbonres.h")添加下面的include代碼:
C++
#include <uiribbon.h>
#include <UIRibbonPropertyHelpers.h>
#include "ribbonres.h"
3.在linker屬性中連接propsys.lib。右鍵點擊 RibbonApp project properties Linker Input。 在“Additional Dependencies”字段中,添加propsys.lib文件,點 擊OK。
4.在Ribbon.cpp文件的Execute方法中,將切換按鈕的消息框代碼 替換為下面的代碼:
把下面代碼:
C++
MessageBox(NULL, L"Toggled My Choice!", L"My Choice Execute", MB_OK);
替換為:
C++
PROPVARIANT var, varNew;
hr = g_pFramework->GetUICommandProperty(cmdMyChoice, UI_PKEY_BooleanValue, &var);
if (FAILED(hr))
{
return hr;
}
hr = PropVariantToBoolean(var, &_fEnabled);
if (FAILED(hr))
{
return hr;
}
_fEnabled = !_fEnabled;
hr = UIInitPropertyFromBoolean(UI_PKEY_Enabled, _fEnabled, &varNew);
if (FAILED(hr))
{
return hr;
}
hr = g_pFramework->SetUICommandProperty (cmdMyButton, UI_PKEY_Enabled, varNew);
if (FAILED(hr))
{
return hr;
}
5.生成並運行該項目。點擊切換按鈕,注意“My Button”命令將 會在啟用和禁用間來回切換。
任務 8 - 在運行時更新ToggleButton 控件 的Label 屬性
1.在Ribbon.cpp文件的Execute方法中的else if (nCmdID == cmdMyChoice)的最後,添加下面代碼:
C++
hr = g_pFramework->InvalidateUICommand(cmdMyChoice, UI_INVALIDATIONS_PROPERTY, &UI_PKEY_Label);
if (FAILED(hr))
{
return hr;
}
2.將UpdateProperty 方法的實現替換為可以引起標簽更新的MyChoice方法的代碼。在UpdateProperty 方法中,將下面代碼:
C++
return E_NOTIMPL;
替換成:
C++
UNREFERENCED_PARAMETER(ppropvarCurrentValue);
HRESULT hr = E_FAIL;
if (key == UI_PKEY_Label)
{
// Update the Label of ToggleButton control
if (nCmdID == cmdMyChoice)
{
if (_fEnabled)
{
hr = UIInitPropertyFromString(UI_PKEY_Label,
L"Disable Button", ppropvarNewValue);
}
else
{
hr = UIInitPropertyFromString (UI_PKEY_Label,
L"Enable Button", ppropvarNewValue);
}
}
}
return hr;
3.生成並運行該應用程序。點擊切換按鈕並觀察按鈕的標 簽在每次點擊的時候都會改變。這說明命令資源可以在標記中或者運行時的代碼 中(或者同時在兩者中),被定義和改變。
練習 3 - 在已 存在的功能區中添加控件和組
在這個練習中,您將應用練習2中所完成的 項目並修改功能區標記來添加更多的控件。您將學習如何更好的利用組(Groups )來組織的控件。
背景信息
下面步驟中所有引用到的文件都可以 在教程包中的“EX03_Starter\RibbonApp\”路徑下找到。
任 務 1 - 在選項卡中添加附加的控件和組
在這個任務中,您將向功能區中 添加更多控件和區域。
1.繼續您在練習2中所使用的項目。或者用Visual Studio打開EX02_Starter/RibbonApp.sln。
2.在RibbonMarkup.xml文件的 <Application.Commands>節點中,為新組添加下面的命令定義標記。
XML
<Command Name="GroupDatabase" Symbol="cmdGroupDatabase" Id="30004">
<Command.LabelTitle>Database</Command.LabelTitle>
</Command>
<Command Name="GroupClipboard" Symbol="cmdGroupClipboard" Id="30005">
<Command.LabelTitle>Clipboard</Command.LabelTitle>
</Command>
3.向項目資源中添加按鈕圖標。
4.首 先,將BITMAPS文件夾中的圖片拷貝到您的項目目錄中。路徑應該和下面的路徑相 似: C:\Users\your_username\Documents\Visual Studio ####\Projects\RibbonApp\RibbonApp
5.然後把那些圖片添加到項目中,右鍵點擊RibbonApp project Add existing Item (選擇下面所有的圖片):
6.添加這些 :
◦ AddTableL.bmp
◦ AddTableS.bmp
◦ Copy.bmp
◦ Cut.bmp
◦ DeleteTableL.bmp
◦ DeleteTableS.bmp
◦ Paste.bmp
◦ PrintRelationshipsL.bmp
◦ PrintRelationshipsS.bmp
7.在<Application.Commands>節點中為 新按鈕添加更多的命令:
XML
<Command Name="AddTable" Symbol="cmdAddTable" Id="30006"
LabelTitle="Add Table">
<Command.TooltipTitle>Add Table</Command.TooltipTitle>
<Command.TooltipDescription>Add Table</Command.TooltipDescription>
<Command.LargeImages>
<Image Source="AddTableL.bmp"/>
</Command.LargeImages>
</Command>
<Command Name="DeleteTable" Symbol="cmdDeleteTable" Id="30007" LabelTitle="Delete Table">
<Command.TooltipTitle>Delete Table</Command.TooltipTitle>
<Command.TooltipDescription>Delete Table</Command.TooltipDescription>
<Command.LargeImages>
<Image Source="DeleteTableL.bmp"/>
</Command.LargeImages>
</Command>
<Command Name="PrintRelationships" Symbol="cmdPrintRelationships" Id="30008" LabelTitle="Print Relationships">
<Command.TooltipTitle>Print Relationships</Command.TooltipTitle>
<Command.TooltipDescription>Print Relationships</Command.TooltipDescription>
<Command.LargeImages>
<Image Source="PrintRelationshipsL.bmp"/>
</Command.LargeImages>
</Command>
<Command Name="Paste" Symbol="cmdPaste" Id="30009" LabelTitle="Paste">
<Command.TooltipTitle>Paste</Command.TooltipTitle>
<Command.TooltipDescription>Paste</Command.TooltipDescription& gt;
<Command.LargeImages>
<Image Source="Paste.bmp"/>
</Command.LargeImages>
</Command>
<Command Name="Cut" Symbol="cmdCut" Id="30010" LabelTitle="Cut">
<Command.TooltipTitle>Cut</Command.TooltipTitle>
<Command.TooltipDescription>Cut</Command.TooltipDescription> ;
<Command.SmallImages>
<Image Source="Cut.bmp"/>
</Command.SmallImages>
</Command>
<Command Name="Copy" Symbol="cmdCopy" Id="30011" LabelTitle="Copy">
<Command.TooltipTitle>Copy</Command.TooltipTitle>
<Command.TooltipDescription>Copy</Command.TooltipDescription&g t;
<Command.SmallImages>
<Image Source="Copy.bmp"/>
</Command.SmallImages>
</Command>
8.在<Tab CommandName="TabHome">節點中,在已經存在Group後面添加 下面的標記以添加組(命名為GroupMain)和按鈕:
XML
<Tab CommandName="TabHome">
<Group CommandName="GroupMain" Template="TwoButtons">
<ToggleButton CommandName="MyChoice"/>
<Button CommandName="MyButton"/>
</Group>
<Group CommandName="GroupDatabase" SizeDefinition="ThreeButtons">
<Button CommandName="AddTable"/>
<Button CommandName="DeleteTable"/>
<Button CommandName="PrintRelationships"/>
</Group>
<Group CommandName="GroupClipboard" SizeDefinition ="BigButtonsAndSmallButtonsOrInputs">
<ControlGroup>
<Button CommandName="Paste"/>
</ControlGroup>
<ControlGroup>
<Button CommandName="Cut"/>
<Button CommandName="Copy"/>
</ControlGroup>
</Group>
</Tab>
9.生成並運行應用程序。請 注意現在在功能區中有三個區域,每個區域中包含三個按鈕並且都是按照標記中 的SizeDefinition模板來顯示的。
10.現在拖動應用程序窗口的右邊界到 左側。一旦窗口的右邊界超出了最右側控件的位置,會彈出一個呼叫器控件 (pager control)提示有些控件超出了窗口范圍。如果您繼續調整窗口到足夠小 的話,功能區會最終消失,以最大化提供給應用程序工作區域的空間。
任務 2 - 定義適應的調整大小規則
現在我們將會定義功能區的調整大 小規則以便於提供一個適應於組的布局而不是固定大小的布局。
1.為 TabHome添加縮放規則。在<Tab CommandName="TabHome">節點下面添加下 面的標記:
XML
<Tab CommandName="TabHome">
<Tab.ScalingPolicy>
<ScalingPolicy>
<ScalingPolicy.IdealSizes>
<Scale Group="GroupMain" Size="Large"/>
<Scale Group ="GroupDatabase" Size="Large"/>
<Scale Group ="GroupClipboard" Size="Large"/>
</ScalingPolicy.IdealSizes>
<Scale Group ="GroupClipboard" Size="Medium"/>
<Scale Group ="GroupClipboard" Size="Popup"/>
<Scale Group ="GroupDatabase" Size="Medium"/>
</ScalingPolicy>
</Tab.ScalingPolicy>
2.現在重新生成該應用程序並運行 ,逐漸的調整窗口的大小。注意下面的行為:
3.如果沒有足夠的空間來顯 示它的控件的時候,Clipboard組將會調整它的大小。小按鈕的標簽會首先消失。
4.繼續縮小窗口。Clipboard組會變成一個下拉列別按鈕。當您點擊按鈕 的時候,所有的控件會在一個彈出框中顯示出來。
5.繼續縮小窗口。 Database組會重新排布它的控件。所有的控件會以小按鈕的方式顯示並且垂直排 列。
6.正如之前提到的,如果您繼續縮小功能區,它會最終消失以便給應用程序工 作區盡可能多的空間。
7.您可能會在調整窗口大小的時候發現一些問題。
8.當Clipboard組以下拉列表的方式進行顯示的時候,按鈕是沒有圖標的 。這是因為圖片是需要組來定義。
9.在Database組中,用於小按鈕的圖標 會看上去有點變形。這是由於功能區會去試著縮小較大的圖片。要修復這個問題 ,您需要提供一個較小的圖標(16x16)。
10.我們現在就為Clipboard組 添加一張圖片,以及為Database組中的命令添加一張小圖片。在 <Application.Commands>節點下面將GroupClipboard,AddTable, DeleteTable和 PrintRelationships命令替換為下面的代碼:
XML
<Command Name="GroupClipboard" Symbol="cmdGroupClipboard" Id="30005">
<Command.LabelTitle>Clipboard</Command.LabelTitle>
<Command.LargeImages>
<Image Source="Paste.bmp"/>
</Command.LargeImages>
</Command>
<Command Name="AddTable" Symbol="cmdAddTable" Id="30006"
LabelTitle="Add Table">
<Command.TooltipTitle>Add Table</Command.TooltipTitle>
<Command.TooltipDescription>Add Table</Command.TooltipDescription>
<Command.LargeImages>
<Image Source="AddTableL.bmp"/>
</Command.LargeImages>
<Command.SmallImages>
<Image Source="AddTableS.bmp"/>
</Command.SmallImages>
</Command>
<Command Name="DeleteTable" Symbol="cmdDeleteTable" Id="30007" LabelTitle="Delete Table">
<Command.TooltipTitle>Delete Table</Command.TooltipTitle>
<Command.TooltipDescription>Delete Table</Command.TooltipDescription>
<Command.LargeImages>
<Image Source="DeleteTableL.bmp"/>
</Command.LargeImages>
<Command.SmallImages>
<Image Source="DeleteTableS.bmp"/>
</Command.SmallImages>
</Command>
<Command Name="PrintRelationships" Symbol="cmdPrintRelationships" Id="30008" LabelTitle="Print Relationships">
<Command.TooltipTitle>Print Relationships</Command.TooltipTitle>
<Command.TooltipDescription>Print Relationships</Command.TooltipDescription>
<Command.LargeImages>
<Image Source="PrintRelationshipsL.bmp"/>
</Command.LargeImages>
<Command.SmallImages>
<Image Source="PrintRelationshipsS.bmp"/>
</Command.SmallImages>
</Command>
11.生成 並運行該項目。注意,之前的問題已經得到了修復,並且之前沒有出現的小的組 圖標也出現了。就像前面所講的,我們完成這項工作只用了標記並沒有修改任何 C++代碼。
總結
您已經成功的完成了Windows功能區的介紹教程! 您已經學會了該平台的標記/代碼分離的架構,並且您也看到了該結構幫助您處理 了很多傳統UI的創建任務,例如定位控件的位置,這為您節省了大量的開發時間 。
這個教程只是展示了Windows功能區的基本特性,還有很多的很有趣的 部分需要去探索。我們希望本次實驗對您能有所幫助。
快樂編程!
- Windows功能區團隊