Windows 功能区
Hands-On Lab
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 功能区文档
https://msdn.microsoft.com/en-us/library/dd371191.aspx
• MSDN上的Windows功能区准则
https://msdn.microsoft.com/en-us/library/cc872782.aspx
• Microsoft Office Fluent用户界面概述
https://office.microsoft.com/en-us/products/HA101679411033.aspx
• 关于Windows功能区开发的MSDN论坛
https://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="https://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>
<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>
<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功能区团队