使用 WTL 为 Vista 或 Windows 7 上的原生应用程序重新设计外观,增加 Ribbon UI






4.97/5 (71投票s)
包含 Ribbon UI 实现指南,附带示例和双 UI 启用的遗留应用程序
引言
Windows 7 引入了新的 Ribbon 控件,系统更新使其可用于 Vista 和 Windows Server 2008 平台。
本文从 C++ 应用程序开发者的角度出发,重点介绍如何使用 atlribbon.h 和 WTL 8 来改编现有或新应用程序,使用户能够在 Ribbon 或传统 UI 之间进行可逆的选择。
-
预览 展示了支持双 UI 的 Ribbon MTPad WTL 示例。
-
初次体验 在不向 AppWizard 应用程序添加任何资源的情况下,从 WTL AppWizard 代码创建了一个简单的双 UI 应用程序。
-
Ribbon UI 实现指南 通过示例描述了 Ribbon UI 的实现。
-
使用 Ribbon UI 重新审视 MTPad 展示了现有应用程序的改编。
参考文献
关于 Windows 7 Ribbon 的主要文档是 MSDN 的 Windows Ribbon Framework 部分。
您可以在 CodeProject 上找到 Michael Chourdakis 的文章:Windows 7 Ribbon: The Time Has Come, Your Win32 Application Will Change, and i,以及 Arik Poznanski 的博客 中有关 Ribbon 功能的详细描述,以及 Windows Ribbon for WinForms,用于在 C# 应用程序中使用它。
必备组件
要跟进本文,您需要在计算机上正确安装:
-
Visual Studio 或 VCExpress 2008(所有服务包)或 Visual Studio 2010 Beta2。
-
Windows 7 平台 SDK:您需要 uicc 工具,该工具不包含在 Visual Studio 2010 Beta2 分发版中。
-
对于 VCExpress,您需要 ATL 3,可以在 Windows® Server 2003 SP1 Platform SDK 或 ATL 7.1 中找到,如此帖子所示。
显然,您应该在已更新的 Vista 或 Windows 7 平台上进行测试,您可以在 Vista、Windows 7 或 XP 下编译和运行所有内容。
预览
下载 MTPad7Exe.zip 并运行 MTPad.exe。
在已更新的 Vista 或 Windows 7 平台上,MTPad 会以 Ribbon UI 打开。
打开一些文本文件,检查字体选择预览功能,尝试打印预览模式,使用应用程序菜单中的最近使用的文件,打开一些新窗口。
取消选中 Ribbon 复选框以切换到传统 UI,使用 View 菜单中的 Ribbon 条目在编辑或预览模式下切换回 Ribbon UI。
使用QAT(快速访问工具栏)右侧的内置下拉菜单更改布局设置,然后切换回传统 UI 并观察,当切换回 Ribbon UI 时,Ribbon 布局设置会被保留。
在传统 UI 中关闭最后一个窗口并重新打开 MTPad。它会以传统模式重新打开。
初次体验
使用 atlribbon.h ,您可以在几分钟内为基于传统 UI 的 WTL 应用程序构建一个用户可选择的双(Ribbon 和传统)UI。
初次体验执行了必要步骤,并提供了许多执行细节,这些步骤适用于任何 Ribbon UI 应用程序。
步骤 0:创建一个功能最少的 WTL 应用程序
如果您使用 Visual Studio 2010 Beta2 或由于某种原因未安装 WTL AppWizard,请下载 FirstRibbon.zip ,解压缩并打开 FirstRibbon.vcproj 。
否则,使用 WTL AppWizard,创建一个带有 SDI 且不包含 cpp 文件的 FirstRibbon WTL 应用程序……
……默认的用户界面功能 和富文本编辑器视图窗口。
要处理编辑命令,请将 CRichEditCommands
继承和消息映射链接添加到 CFirstRibbonView
……
// FirstRibbonView.h : interface of the CFirstRibbonView class // ///////////////////////////////////////////////////////////////////////////// #pragma once class CFirstRibbonView : public CWindowImpl<CFirstRibbonView, CRichEditCtrl>, // 0 public CRichEditCommands<CFirstRibbonView> // 0 { public: DECLARE_WND_SUPERCLASS(NULL, CRichEditCtrl::GetWndClassName()) BOOL PreTranslateMessage(MSG* pMsg) { pMsg; return FALSE; } BEGIN_MSG_MAP(CFirstRibbonView) CHAIN_MSG_MAP_ALT(CRichEditCommands<CFirstRibbonView>, 1) // 0 END_MSG_MAP() };
……并将 CMainFrame
命令链接到 CFirstRibbonView
// MainFrm.h : interface of the CMainFrame class // ... BEGIN_MSG_MAP(CMainFrame) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit) COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew) COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar) COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar) COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout) CHAIN_COMMANDS_MEMBER(m_view) // 0 CHAIN_MSG_MAP(CUpdateUI<CMainFrame>) CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>) END_MSG_MAP()
编译(此处任何编译错误意味着您的开发环境未正确设置),运行并使用工具栏按钮或“编辑”菜单检查编辑功能。
步骤 1:改编 C++ 代码以托管 Ribbon UI
更改 stdafx.h 中的常量以访问 SDK Windows 7 功能。
// stdafx.h : include file for standard system include files, //... // Change these values to use different versions #define WINVER 0x0601 // 1 #define _WIN32_WINNT 0x0601 // 1 #define _WIN32_IE 0x0700 // 1 #define _RICHEDIT_VER 0x0200
下载 atlribbon.zip ,将 atlribbon.h 解压到您的 <FirstRibbon> 目录,并将其包含在 FirstRibbon.cpp 中。
// FirstRibbon.cpp : main source file for FirstRibbon.exe // #include "stdafx.h" #include <atlframe.h> #include <atlctrls.h> #include <atldlgs.h> #include <atlctrlw.h> #include "atlribbon.h" // 1 #include "resource.h"
将 CMainFrame
派生自 WTL::CRibbonFrameWindowImpl
;由于 CRibbonFrameWindowImpl
派生自 WTL::CRibbonUpdateUI
,请删除类定义中的 CUpdateUI
父类以及消息映射,只在消息映射中链接 CRibbonFrameWindowImpl<CMainFrame>
。
// MainFrm.h : interface of the CMainFrame class // ... class CMainFrame : public CRibbonFrameWindowImpl<CMainFrame>, // 1 public CMessageFilter, public CIdleHandler // ... BEGIN_MSG_MAP(CMainFrame) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit) COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew) COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar) COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar) COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout) CHAIN_COMMANDS_MEMBER(m_view) // 0 CHAIN_MSG_MAP(CRibbonFrameWindowImpl<CMainFrame>) // 1 END_MSG_MAP()
要在预 Vista 或未更新的 Vista 平台下运行,请在 Project->FirstRibbon Properties->Linker->Input 中将 propsys.dll 和 dwmapi.dll 设置为延迟加载。
编译以检查步骤是否完成。
步骤 2:创建 Ribbon 资源并将其嵌入应用程序
向您的项目中添加一个新的 xml 文件(对于 VCExpress,是一个新的 C++ 头文件),命名为 FirstRibbonRibbon.xml ,并将以下内容粘贴进去
<!-- FirstRibbonRibbon.xml --> <Application xmlns="http://schemas.microsoft.com/windows/2009/Ribbon"> <Application.Commands> <!-- FirstTab.rc Menu Commands--> <Command Name="wtl_FILE_NEW" Symbol="ID_FILE_NEW" Id="0xE100"/> <Command Name="wtl_FILE_OPEN" Symbol="ID_FILE_OPEN" Id="0xE101"/> <Command Name="wtl_FILE_SAVE" Symbol="ID_FILE_SAVE" Id="0xE103"/> <Command Name="wtl_FILE_SAVE_AS" Symbol="ID_FILE_SAVE_AS" Id="0xE104"/> <Command Name="wtl_FILE_PRINT" Symbol="ID_FILE_PRINT" Id="0xE107"/> <Command Name="wtl_FILE_PRINT_PREVIEW" Symbol="ID_FILE_PRINT_PREVIEW" Id="0xE109"/> <Command Name="wtl_FILE_PRINT_SETUP" Symbol="ID_FILE_PRINT_SETUP" Id="0xE106"/> <Command Name="wtl_APP_EXIT" Symbol="ID_APP_EXIT" Id="0xE141"/> <Command Name="wtl_EDIT_CUT" Symbol="ID_EDIT_CUT" Id="0xE123"/> <Command Name="wtl_EDIT_COPY" Symbol="ID_EDIT_COPY" Id="0xE122"/> <Command Name="wtl_EDIT_PASTE" Symbol="ID_EDIT_PASTE" Id="0xE125"/> <Command Name="wtl_VIEW_TOOLBAR" Symbol="ID_VIEW_TOOLBAR" Id="0xE800"/> <Command Name="wtl_VIEW_STATUS_BAR" Symbol="ID_VIEW_STATUS_BAR" Id="0xE801"/> <Command Name="wtl_VIEW_RIBBON" Symbol="ID_VIEW_RIBBON" Id="0xE804"/> <Command Name="wtl_APP_ABOUT" Symbol="ID_APP_ABOUT" Id="0xE140"/> <!-- Tabs --> <Command Name="TabHome" Symbol="ID_TAB_HOME" LabelTitle="Home" /> <!-- Groups --> <Command Name="GroupClipboard" Symbol="ID_GROUP_CLIPBOARD" LabelTitle="Clipboard" /> <Command Name="GroupView" Symbol="ID_GROUP_VIEW" LabelTitle="View" /> <!-- App Menu, MRU list, Help button and Quick Access Toolbar --> <Command Name="AppMenu" Symbol="ID_RIBBON_APP_MENU"/> <Command Name="SaveMore" Symbol="ID_SAVEMORE"/> <Command Name="PrintMore" Symbol="ID_PRINTMORE"/> <Command Name="QAT" Symbol="ID_RIBBON_QAT"/> </Application.Commands> <Application.Views> <Ribbon> <!-- Application Menu --> <Ribbon.ApplicationMenu > <ApplicationMenu CommandName="AppMenu" > <MenuGroup Class="StandardItems" > <Button CommandName="wtl_FILE_NEW"/> <Button CommandName="wtl_FILE_OPEN"/> <!-- Saving SplitButton --> <SplitButton CommandName="SaveMore"> <SplitButton.ButtonItem> <Button CommandName="wtl_FILE_SAVE" /> </SplitButton.ButtonItem> <SplitButton.MenuGroups> <MenuGroup Class="StandardItems"> <Button CommandName="wtl_FILE_SAVE" /> <Button CommandName="wtl_FILE_SAVE_AS" /> </MenuGroup> </SplitButton.MenuGroups> </SplitButton> <!-- Printing SplitButton --> <SplitButton CommandName="PrintMore"> <SplitButton.ButtonItem> <Button CommandName="wtl_FILE_PRINT"/> </SplitButton.ButtonItem> <SplitButton.MenuGroups> <MenuGroup Class="StandardItems"> <Button CommandName="wtl_FILE_PRINT" /> <Button CommandName="wtl_FILE_PRINT_PREVIEW"/> <Button CommandName="wtl_FILE_PRINT_SETUP"/> </MenuGroup> </SplitButton.MenuGroups> </SplitButton> </MenuGroup> <MenuGroup > <Button CommandName="wtl_APP_EXIT"/> </MenuGroup> </ApplicationMenu> </Ribbon.ApplicationMenu > <!-- Help button --> <Ribbon.HelpButton> <HelpButton CommandName="wtl_APP_ABOUT" /> </Ribbon.HelpButton> <!-- QAT (Quick Access Toolbar) --> <Ribbon.QuickAccessToolbar> <QuickAccessToolbar CommandName="QAT"> <QuickAccessToolbar.ApplicationDefaults> <Button CommandName="wtl_FILE_NEW"/> <Button CommandName="wtl_FILE_OPEN"/> <Button CommandName="wtl_FILE_SAVE"/> <Button CommandName="wtl_FILE_PRINT"/> </QuickAccessToolbar.ApplicationDefaults> </QuickAccessToolbar> </Ribbon.QuickAccessToolbar> <Ribbon.Tabs> <Tab CommandName="TabHome"> <Tab.ScalingPolicy> <ScalingPolicy> <ScalingPolicy.IdealSizes> <Scale Group="GroupClipboard" Size="Medium"/> <Scale Group="GroupView" Size="Large"/> </ScalingPolicy.IdealSizes> </ScalingPolicy> </Tab.ScalingPolicy> <Group CommandName="GroupClipboard" SizeDefinition="ThreeButtons"> <Button CommandName="wtl_EDIT_PASTE"/> <Button CommandName="wtl_EDIT_CUT"/> <Button CommandName="wtl_EDIT_COPY"/> </Group> <Group CommandName="GroupView"> <SizeDefinition> <ControlNameMap> <ControlNameDefinition Name="wtl_VIEW_RIBBON"/> <ControlNameDefinition Name="wtl_VIEW_STATUS_BAR"/> </ControlNameMap> <GroupSizeDefinition Size="Large"> <ControlSizeDefinition ControlName="wtl_VIEW_RIBBON" ImageSize="Small" IsLabelVisible="true" /> <ControlSizeDefinition ControlName="wtl_VIEW_STATUS_BAR" ImageSize="Small" IsLabelVisible="true" /> </GroupSizeDefinition> <GroupSizeDefinition Size="Medium"> <ControlSizeDefinition ControlName="wtl_VIEW_RIBBON" ImageSize="Small" IsLabelVisible="true" /> <ControlSizeDefinition ControlName="wtl_VIEW_STATUS_BAR" ImageSize="Small" IsLabelVisible="true" /> </GroupSizeDefinition> <GroupSizeDefinition Size="Small"> <ControlSizeDefinition ControlName="wtl_VIEW_RIBBON" ImageSize="Small" IsLabelVisible="false" /> <ControlSizeDefinition ControlName="wtl_VIEW_STATUS_BAR" ImageSize="Small" IsLabelVisible="false" /> </GroupSizeDefinition> </SizeDefinition> <CheckBox CommandName="wtl_VIEW_RIBBON"/> <CheckBox CommandName="wtl_VIEW_STATUS_BAR"/> </Group> </Tab> </Ribbon.Tabs> </Ribbon> </Application.Views> </Application>
在 VC 解决方案资源管理器窗口 中,右键单击 FirstRibbonRibbon.xml 并选择弹出菜单中的属性 。转到自定义生成步骤->常规 ,并将以下内容粘贴到输入字段中
-
命令行:uicc FirstRibbonRibbon.xml FirstRibbonRibbon.bml /header:FirstRibbonRibbon.h /res:FirstRibbonRibbon.rc
-
描述:正在编译 FirstRibbonRibbon.xml
-
输出:FirstRibbonRibbon.bml;FirstRibbonRibbon.rc;FirstRibbonRibbon.h
-
单击确定 进行验证。
在同一个 VC 解决方案资源管理器窗口 中,再次右键单击 FirstRibbonRibbon.xml 并选择编译 :输出应为
1>------ Build started: Project: FirstRibbon, Configuration: Debug Win32 ------ 1>Compiling FirstRibbonRibbon.xml 1>Header file generation successful: 'FirstRibbonRibbon.h'. 1>Ribbon markup file validation successful: 'FirstRibbonRibbon.xml'. 1>Ribbon resource file generation successful: 'FirstRibbonRibbon.rc'. 1>Build log was saved at "file://<...>\FirstRibbon\Debug\BuildLog.htm" 1>FirstRibbon - 0 error(s), 0 warning(s) ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
将 uicc 生成的文件添加到应用程序的 FirstRibbon.rc 文件中
-
使用 Visual Studio 在资源视图窗口 中,右键单击 FirstRibbon.rc ,并在只读符号指令 框中添加
#include "FirstRibbonRibbon.h"
,在编译时指令 框中添加#include "FirstRibbonRibbon.rc"
。
-
使用 VCExpress 在 VC 解决方案资源管理器窗口 中,右键单击 FirstRibbon.rc 并选择弹出菜单中的查看代码 。编辑 FirstRibbon.rc 中的三个地方:
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "atlres.h" #include "FirstRibbonRibbon.h" // 2 // … ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""atlres.h""\r\n" "#include ""FirstRibbonRibbon.h""\r\0" // 2 END 3 TEXTINCLUDE BEGIN "#include ""FirstRibbonRibbon.rc\0" // 2 END #endif // APSTUDIO_INVOKED // ... // at the end of the file #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // #include "FirstRibbonRibbon.rc" // 2 ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED // nothing more in the file
-
在 FirstRibbon.cpp 中
#include
uicc 生成的 FirstRibbonRibbon.h
// FirstRibbon.cpp : main source file for FirstRibbon.exe // #include "stdafx.h" #include <atlframe.h> #include <atlctrls.h> #include <atldlgs.h> #include <atlctrlw.h> #include <atlmisc.h> // 1 #include "atlribbon.h" // 1 #include "FirstRibbonRibbon.h" // 2 #include "resource.h"
-
生成项目,FirstRibbon.rc 应能编译,并且您应该不会收到任何错误。运行它,没有任何区别。
步骤 3:操作 Ribbon
在应用程序的 View 菜单中创建一个 Ribbon 命令
-
使用 Visual Studio 在资源视图窗口 中,编辑 IDR_MAINFRAME 菜单,并在 View 菜单顶部添加一个 ID_VIEW_RIBBON id 的 &Ribbon 条目。
编辑字符串表以添加一个 ID_VIEW_RIBBON 字符串,其内容为 显示或隐藏 ribbon\n切换 Ribbon。 -
使用 VCExpress 在 VC 解决方案资源管理器窗口 中,右键单击 FirstRibbon.rc 并选择弹出菜单中的查看代码 。编辑 POPUP "&View" 块,以及带有 ID_VIEW_xxx 字符串的 STRINGTABLE 块。
// Menu // IDR_MAINFRAME MENU BEGIN // ... POPUP "&View" BEGIN MENUITEM "&Ribbon", ID_VIEW_RIBBON // 3 MENUITEM "&Toolbar", ID_VIEW_TOOLBAR MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR ENDPOPUP "&View" // ... STRINGTABLE BEGIN ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar" ID_VIEW_RIBBON "Show or hide the ribbon\nToggle Ribbon" // 3 END
在 CMainFrame
消息映射中为 ID_VIEW_RIBBON
添加一个命令处理程序……
// MainFrm.h : interface of the CMainFrame class // ... BEGIN_MSG_MAP(CMainFrame) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit) COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew) COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar) COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar) COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout) COMMAND_ID_HANDLER(ID_VIEW_RIBBON, OnViewRibbon) // 3 CHAIN_COMMANDS_MEMBER(m_view) // 0
……并在类定义的末尾定义它。
// MainFrm.h : interface of the CMainFrame class // ... LRESULT OnViewRibbon(WORD /*wNotifyCode*/, WORD /*wID*/, // 3 HWND /*hWndCtl*/, BOOL& /*bHandled*/) // 3 { // 3 ShowRibbonUI(!IsRibbonUI()); // 3 UISetCheck(ID_VIEW_RIBBON, IsRibbonUI()); // 3 return 0; // 3 } // 3
要删除不支持 Ribbon UI 时的 ID_VIEW_RIBBON
命令,并设置 Ribbon 命令标签为菜单文本,请将以下内容添加到 CMainFrame::OnCreate()
。
// MainFrm.h : interface of the CMainFrame class // ... LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // create command bar window HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE); // attach menu m_CmdBar.AttachMenu(GetMenu()); // load command bar images m_CmdBar.LoadImages(IDR_MAINFRAME); // remove old menu SetMenu(NULL); bool bRibbonUI = RunTimeHelper::IsRibbonUIAvailable(); // 3 if (bRibbonUI) // 3 UIAddMenu(m_CmdBar.GetMenu(), true); // 3 else // 3 CMenuHandle(m_CmdBar.GetMenu()).DeleteMenu(ID_VIEW_RIBBON, MF_BYCOMMAND); // 3 HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE); // ...
编译并运行,看不到任何变化,点击 View 菜单中的Ribbon
是的,你可以!
是时候玩玩了,更改 Ribbon 设置,切换 UI 模式,检查命令,查看 Ribbon 工具提示,尝试在 XP 下运行,享受……
附加步骤:上下文 UI 启动和启用编辑命令
在 CMainFrame::OnCreate()
的末尾……
// ... ShowRibbonUI(bRibbonUI); // 4 UISetCheck(ID_VIEW_RIBBON, bRibbonUI); // 4 return 0; }
……以及在 CMainFrame::OnIdle()
中。
virtual BOOL OnIdle() { UIEnable(ID_EDIT_UNDO, m_view.CanUndo()); // 4 UIEnable(ID_EDIT_PASTE, m_view.CanPaste(CF_TEXT)); // 4 UIEnable(ID_EDIT_CUT, m_view.CanCut()); // 4 UIEnable(ID_EDIT_COPY, m_view.CanCopy()); // 4 UIUpdateToolBar(); return FALSE; }
编译,运行并检查 FirstRibbon 是否在支持的情况下以 Ribbon UI 启动,以及在两种 UI 中,当编辑命令不相关时是否被禁用。
Ribbon UI 实现指南
本指南通过示例描述了在派生自 CRibbonFrameWindowImplBase
(如 CRibbonFrameWindowImpl
或 CRibbonMDIFrameWindowImpl
)的 CMainFrame
框架窗口中实现 Ribbon UI。
回顾我们的初次体验
class CMainFrame : public CRibbonFrameWindowImpl<CMainFrame>, // 1 public CMessageFilter, public CIdleHandler { // ... LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // ... bool bRibbonUI = RunTimeHelper::IsRibbonUIAvailable(); // 2 if (bRibbonUI) UIAddMenu(m_CmdBar.GetMenu(), true); // 4 else CMenuHandle(m_CmdBar.GetMenu()).DeleteMenu(ID_VIEW_RIBBON, MF_BYCOMMAND); // ... ShowRibbonUI(bRibbonUI); // 5 UISetCheck(ID_VIEW_RIBBON, bRibbonUI); // ... LRESULT OnViewRibbon(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ShowRibbonUI(!IsRibbonUI()); // 5 & 3 UISetCheck(ID_VIEW_RIBBON, IsRibbonUI()); return 0; }
-
CRibbonFrameWindowImpl<CMainFrame>
继承自WTL::CFrameWindowImpl<CMainFrame>
(实现传统 UI),并继承自WTL::RibbonUI::CRibbonImpl<CMainFrame>
(实现 Ribbon UI)。因此,CMainFrame
是一个双 UI 窗口,在支持 Ribbon UI 的运行时环境中。 -
bool
WTL::RunTimeHelper::IsRibbonUIAvailable()
执行运行时测试,并在运行于已更新的 Vista 或 Win7 时返回 true。如果返回 false,则会移除 View->Ribbon 菜单项,以避免调用 Ribbon UI 代码。 -
bool
CRibbonImpl::IsRibbonUI()
返回true
表示当前显示 Ribbon UI,否则返回 false。 -
CRibbonImpl
继承自WTL::CRibbonUpdateUI<CMainFrame>
,它扩展了 本文 中描述的WTL::CAutoUpdateUI<CMainFrame>
,并处理额外的UPDUI_RIBBON
类型。
boolWTL::CAutoUpdateUI<CMainFrame>::UIAddMenu(HMENU hm, bool bSetText)
将 hm 菜单的所有(非弹出)菜单项添加到其 UPDATE_UI_MAP 中,并为每个菜单项 ID 和文本调用UISetText(ID, text)
。 -
bool
WTL::CRibbonFrameWindowImplBase<CMainFrame>::ShowRibbonUI(bool bShow, INT32 imodes = UI_MAKEAPPMODE(0), LPCWSTR sResName = L"APPLICATION_RIBBON")
在调用后返回 Ribbon UI 活动状态:bShow
是请求的状态,imodes
是请求的 Ribbon 模式,默认为模式 0,sResName
是 Ribbon 资源文件中的 UIFILE 资源的字符串 ID。
ShowRibbonUI(true) 操作
ShowRibbonUI(true)
隐藏包含传统 UI 命令栏和工具栏的 Rebar,并调用 CRibbonImpl::CreateRibbon(sResName)
,该函数执行 此处 描述的步骤。
新创建的 Ribbon 会为标记文件中的每个 ID 调用 CRibbonImpl::OnCreateUICommand()
。每个 ID 都以 UPDUI_RIBBON
类型添加到 UPDATE_UI_MAP 中。
因此,常见的 UI 元素在 UPDATE_UI_MAP 中同时具有 UPDUI_RIBBON 和(例如)UPDUI_MENUPOPUP 类型。可以通过重写常用的 UISetText()
、UISetCheck()
和 UIEnable()
调用来管理 Ribbon 元素的标签文本、启用状态和选中状态。
ShowRibbonUI(false) 操作
ShowRibbonUI(false)
调用 CRibbonImpl::DestroyRibbon()
,然后该函数调用 IUIFramework::Destroy()。
Ribbon 会为标记文件中的每个 ID 调用 CRibbonImpl::OnDestroyUICommand()
。UPDUI_RIBBON
类型将从该 ID 的 UPDATE_UI_MAP 中移除。
如果 ID 没有其他 UPDUI_type,则该 ID 本身将从 UPDATE_UI_MAP 中移除,可以通过调用 CRibbonUpdateUI::UIPersistElement(UINT nID, true)
来阻止这种情况。
ShowRibbonUI(false)
然后恢复包含传统 UI 命令栏和工具栏的 Rebar。
默认 Ribbon UI 行为
当 Ribbon 激活时,它会调用 CRibbonImpl::UpdateProperty()
,传入一个元素 ID 和一个 Property Key 引用。
此表总结了默认的 CRibbonImpl
行为。
键 |
设置 |
可覆盖的回调 |
默认处理 |
---|---|---|---|
UI_PKEY_Label |
UISetText(ID, LPCWSTR) |
LPCWSTR OnRibbonQueryText(ID, key) |
返回 UIGetText(ID) |
UI_PKEY_Enabled |
UIEnable(ID, bool) |
bool OnRibbonQueryState(ID, key) |
返回 UIGetState(ID) & ~UPDUI_DISABLED |
UI_PKEY__BooleanValue |
UISetCheck(ID, bool) |
bool OnRibbonQueryState(ID, key) |
返回 UIGetState(ID) & UPDUI_CHECKED |
UI_PKEY_LabelDescription UI_PKEY_TooltipDescription |
LPCWSTR OnRibbonQueryText(ID, key) |
资源字符串中 ID 前面的部分('\n' 之前,或 NULL) |
|
UI_PKEY_TooltipTitle |
LPCWSTR OnRibbonQueryText(ID, key) |
资源字符串中 ID 后面的部分('\n' 之后,或 NULL) |
|
UI_PKEY_Keytip |
LPCWSTR OnRibbonQueryText(ID, key) |
资源字符串中 '&' 之后(或 NULL)的 1 个字符子串 |
|
UI_PKEY_SmallImage |
HBITMAP OnRibbonQueryImage(ID, key) |
m_CmdBar.m_arrVistaBitmap 中的 HBITMAP,或具有 ID 的资源位图的 HBITMAP(或 NULL) |
|
UI_PKEY_LargeImage UI_PKEY_SmallHighContrastImage UI_PKEY_LargeHighContrastImage |
HBITMAP OnRibbonQueryImage(ID, key) |
具有 ID 的资源位图的 HBITMAP(或 NULL) |
当用户操作 Ribbon UI 元素时,Ribbon 会使用元素 ID、一个 UI_EXECUTIONVERB 和一个 Property Key 引用调用 CRibbonImpl::Execute
。
-
CRibbonImpl::Execute()
调用可覆盖的OnRibbonCommandExecute(UINT32 uCmdID, ...)
。 -
如果未被覆盖,
CRibbonImpl::OnRibbonCommandExecute()
会发布一个WM_COMMAND
消息,其中WPARAM
设置为 uCmdID。
实用总结
-
菜单资源中的任何项(通过
UIAddMenu(IdMenu, true)
插入 UI_MAP)如果具有相同的 ID 和两个部分的字符串资源,如“创建新文档\n新建”,则会在 Ribbon 中获得菜单名称、资源字符串工具提示标题和工具提示正文。
-
如果一个项是添加到
m_CmdBar
的工具栏的一部分(通过m_CmdBar.LoadImages(IdToolBar)
),那么它在 Ribbon 中的UI_PKEY_SmallImage
与 CommandBar 菜单中的相同,并且具有相同 ID 的位图资源(如果存在)将用于所有其他 Image 属性(如果不存在 CCommandBarCtrl 位图,则用于所有)。
-
对于任何 Ribbon 定义的 Command,
UI_PKEY_Label
、UI_PKEY_Enabled
和UI_PKEY_BooleanValue
都通过UISetText()
、UIEnable()
和UISetCheck()
处理。 -
当用户单击或选中/取消选中 Ribbon 元素时,将发布
WM_COMMAND
消息,其 ID 为 Ribbon 元素 ID。
这就是我们的 FirstRibbon Ribbon UI 能够立即工作的原因。
示例:添加一个具有默认处理的撤销 Ribbon 按钮
ID_EDIT_UNDO
存在于 IDR_MAINFRAME
菜单中,并在 FirstRibbon.rc 中具有字符串资源,将其添加到 Commands 声明以及 FirstRibbonRibbon.xml
中的GroupClipboard Group 。
<Application.Commands> <!-- FirstTab.rc Menu Commands--> <Command Name="wtl_EDIT_UNDO" Symbol="ID_EDIT_UNDO" Id="0xE12B"/> ... <Ribbon.Tabs> ... <Tab CommandName="TabHome"> <Group CommandName="GroupClipboard" SizeDefinition="FourButtons"> <Button CommandName="wtl_EDIT_UNDO"/> <Button CommandName="wtl_EDIT_PASTE"/> <Button CommandName="wtl_EDIT_CUT"/> <Button CommandName="wtl_EDIT_COPY"/> </Group>
下载 FirstRibbonBitmaps.zip ,将 Undo.bmp 解压到 FirstRibbon\res ,并以 ID_EDIT_UNDO
ID 添加到 FirstRibbon.rc 。
// Bitmap // IDR_MAINFRAME BITMAP "res\\Toolbar.bmp" ID_EDIT_UNDO BITMAP "res\\Undo.bmp"
CRibbonImpl 控制类和 RIBBON_CONTROL_MAP() 宏
CRibbonImpl::CRibbonXXXCtrl
控制类处理 Windows Ribbon Framework Control Library 支持的控件。
CRibbonImpl
和所有 CRibbonXXXCtrl 类都实现了 WTL::RibbonUI::ICtrl
抽象接口。BEGIN_RIBBON_CONTROL_MAP()
、RIBBON_CONTROL()
和 END_RIBBON_CONTROL_MAP()
宏构建一个 ICtrl& GetRibbonCtrl(UINT ID)
函数,该函数如果 ID 在映射中,则返回对控件 ICtrl
的引用,否则返回 CRibbonImpl::ICtrl
。
控件的实现需要 3 个步骤
-
在 Ribbon 标记中,识别控件并将其插入具有适当属性的组中。
<Command Name="TestColor" Symbol="ID_TEST_COLOR" LabelTitle="Color" /> ... <Command Name="GroupColor" Symbol="ID_GROUP_COLOR" LabelTitle="Background" /> ... <Group CommandName="GroupColor" SizeDefinition="OneButton"> <DropDownColorPicker CommandName="TestColor" ColorTemplate="StandardColors"/> </Group>
-
实例化一个具有匹配 ID 的
CMainFrame
成员的 CRibbonImpl::CRibbonXXXCtrl。
CRibbonColorCtrl<ID_TEST_COLOR> m_color;
-
将成员插入 RIBBON_CONTROL_MAP。
BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_color) ... END_RIBBON_CONTROL_MAP()
CRibbonImpl 控制类公共 API
所有 CMainFrame::CRibbonImpl
控制类都派生自 WTL::RibbonUI::CtrlImpl<ID, CMainFrame>
并公开
-
HRESULT Invalidate()
和HRESULT Invalidate(key, UI_INVALIDATIONS flags = UI_INVALIDATIONS_PROPERTY)
,它们使此 ID 的所有(或一个)Property Key 无效。 -
HRESULT SetText(key&, LPCWSTR sText, bool bUpdate = false)
:将sText
复制到与key
关联的内部字符串,如果bUpdate
为true
,则调用Invalidate(key)
。
启动时或当一个 key 未初始化或无效时,Ribbon 会调用 CRibbonImpl::UpdateProperty()
,该函数使用 RIBBON_CONTROL_MAP 来选择受影响的控件实例并调用其相关成员函数(例如,文本 key 调用 OnGetText()
)。
如果满足回调条件
-
此 key 的值被视为无效,
-
调用相关的CRibbonImpl 可覆盖回调,
-
如果未被覆盖,则执行默认处理,
-
返回存储的值。
- 将存储的值返回给 Ribbon。
CRibbonImpl 控制类公共 Property Keys
键 |
设置 |
CRibbonImpl 可覆盖回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_LabelDescription UI_PKEY_TooltipDescription |
SetText(key, LPCWSTR, bool bUpdate = false) |
LPCWSTR OnRibbonQueryText(nCmdID, key) |
当前字符串值为空 |
资源字符串中 ID 前面的部分(或整个字符串) |
UI_PKEY_TooltipTitle |
SetText(key, LPCWSTR, bool bUpdate = false) |
LPCWSTR OnRibbonQueryText(nCmdID, key) |
当前字符串值为空 |
资源字符串中 ID 后面的部分(或 NULL) |
UI_PKEY_Keytip |
SetText(key, LPCWSTR, bool bUpdate = false) |
LPCWSTR OnRibbonQueryText(nCmdID, key) |
当前字符串值为空 |
资源字符串中 '&' 之后(如果找到)或 '\n' 之后的 1 个字符(或 NULL) |
默认处理与 CRibbonImpl
相同,但值可以设置,并且在用户切换 UI 时是持久的。
示例的预备步骤
一些示例使用状态栏来输出用户在 Ribbon 上的操作。要启用它们使用 UISetText(ID_DEFAULT_PANE, someText)
,请在 stdafx.h 中定义 _ATL_USE_CSTRING_FLOAT
……
// stdafx.h : include file for standard system include files, // ... #define _RICHEDIT_VER 0x0200 #define _ATL_USE_CSTRING_FLOAT
……并在 OnIdle()
中向 CMainFrame
添加 UIUpdateStatusBar()
调用,在 OnCreate().
中添加 UIAddStatusBar()
调用。
//... virtual BOOL OnIdle() { //... UIUpdateToolBar(); UIUpdateStatusBar(); return FALSE; } // ... LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { //... CreateSimpleStatusBar(); UIAddStatusBar(m_hWndStatusBar); //...
Ribbon 单个控件类
CRibbonImpl
单个控件类处理除 Galleries、Combos 和 Recent Items 之外的所有 Ribbon 控件。
CRibbonCommandCtrl
-
在
CRibbonImpl
派生类中的声明:CRibbonCommandCtrl<ID> m_Ctrl;
-
用户操作消息映射宏:
COMMAND_ID_HANDLER(wID, OnCommand)
-
处理程序原型:
LRESULT OnCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled)
CRibbonImpl::CRibbonCommandCtrl
操作 Ribbon Buttons、Drop-Down Buttons、Toggle Buttons、Groups、Menu Groups、Tabs 和 Tab Groups 以实现 CRibbonImpl
控件类公共 Property Keys 的特定处理,并在适当时
键 |
设置 |
CRibbonImpl 可覆盖回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_SmallImage |
SetImage(key, HBITMAP, bool bUpdate = false) |
HBITMAP OnRibbonQueryImage(nCmdID, key) |
当前 HBITMAP 为 NULL |
m_CmdBar.m_arrVistaBitmap 中的 HBITMAP,或具有 ID 的资源位图的 HBITMAP(或 NULL) |
UI_PKEY_LargeImage UI_PKEY_SmallHighContrastImage UI_PKEY_LargeHighContrastImage |
SetImage(key, HBITMAP, bool bUpdate = false) |
HBITMAP OnRibbonQueryImage(nCmdID, key) |
当前 HBITMAP 为 NULL |
具有 ID 的资源位图的 HBITMAP(或 NULL) |
示例:处理一个 Ribbon Group 控件
在 CMainFrame
中声明一个 CRibbonCommandCtrl
,其 ID 为 Ribbon GroupClipboard 控件 ID,并将其插入 RIBBON_CONTROL_MAP 中,
// Ribbon controls CRibbonCommandCtrl<ID_GROUP_CLIPBOARD> m_clipboard; // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_clipboard)
在 CMainFrame::OnCreate()
中设置一些值
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // ... bool bRibbonUI = RunTimeHelper::IsRibbonUIAvailable(); if (bRibbonUI) { // ... m_clipboard.SetImage(UI_PKEY_SmallImage, GetCommandBarBitmap(ID_EDIT_PASTE)); m_clipboard.SetText(UI_PKEY_TooltipTitle, L"Clipboard Commands"); m_clipboard.SetText(UI_PKEY_TooltipDescription, L"Transfer selection to and from the clipboard");
当 Ribbon 更充实时,如果 GroupClipboard 折叠为 Button
CRibbonColorCtrl
- 在
CRibbonImpl
派生类中的声明:CRibbonColorCtrl<wID> m_Ctrl;
-
用户操作消息映射宏:
RIBBON_COLOR_CONTROL_HANDLER(wID, OnRibbonColorCtrl)
-
处理程序原型:
LRESULT OnRibbonColorCtrl(UI_EXECUTIONVERB verb, WORD wID, COLORREF color, BOOL& bHandled);
CRibbonImpl::CRibbonColorCtrl
操作 Ribbon DropDownColorPicker 控件并处理
键 |
设置 |
可覆盖的回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_Color |
m_Ctrl.SetColor(COLORREF color, |
COLORREF OnRibbonQueryColor(ID) |
当前颜色为 MAGENTA |
返回 0x800080 MAGENTA(无变化) |
UI_PKEY_ColorType |
m_Ctrl.SetColorType( |
UI_SWATCHCOLORTYPE_NOCOLOR,如果未通过 SetColorType() 或 SetColor() 设置 |
||
UI_PKEY_ThemeColorsCategoryLabel UI_PKEY_StandardColorsCategoryLabel UI_PKEY_RecentColorsCategoryLabel UI_PKEY_AutomaticColorLabel UI_PKEY_NoColorLabel UI_PKEY_MoreColorsLabel |
m_Ctrl.SetColorLabel(key, LPCWSTR sLabel, |
LPCWSTR OnRibbonQueryColorLabel(ID, key) |
当前字符串值为空 |
返回 NULL(无变化) |
UI_PKEY_ThemeColors UI_PKEY_StandardColors |
m_Ctrl.SetColorArray(key, COLORREF* pColor, |
COLORREF* OnRibbonQueryColorArray(ID, key) |
当前颜色数组为空 |
返回 NULL(无变化) |
UI_PKEY_ThemeColorsTooltips UI_PKEY_StandardColorsTooltips |
m_Ctrl.SetColorTooltips(key, LPCWSTR*, |
LPCWSTR* OnRibbonQueryColorTooltips(ID, key) |
当前字符串数组为空 |
返回 NULL(无变化) |
OnRibbonQueryColorArray()
返回的 COLORREF*
或传递给 SetColorArray()
的是 COLORREF
数组元素 0 的地址,该数组以 0x800080
MAGENTA 结尾。
OnRibbonQueryColorTooltips()
返回的 LPCWSTR*
或传递给 SetColorTooltips()
的是 LPCWSTR
数组元素 0 的地址,该数组以 NULL 结尾。
示例:添加并处理一个颜色控件
在标记文件中,声明组和命令 ID,并在 Tab 中声明一个 DropDownColorPicker
<!--Controls--> <Command Name="TestColor" Symbol="ID_TEST_COLOR" LabelTitle="Color" /> <Command Name="GroupColor" Symbol="ID_GROUP_COLOR" LabelTitle="Background" /> <Ribbon.Tabs> ... <Tab CommandName="TabHome"> ... <Group CommandName="GroupColor" SizeDefinition="OneButton"> <DropDownColorPicker CommandName="TestColor" ColorTemplate="StandardColors"/> </Group> </Tab>
在 CMainFrame
中,声明一个 CRibbonColorCtrl
,其 ID 相同,并将其插入 RIBBON_CONTROL_MAP。
// Ribbon controls CRibbonColorCtrl<ID_TEST_COLOR> m_color; // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_color) END_RIBBON_CONTROL_MAP()
当 Ribbon 需要知道显示哪个颜色作为选中状态时,CRibbonImpl
会调用 OnRibbonQueryColor()
。CRibbonImpl::OnRibbonQueryColor()
返回 0x800080
(MAGENTA),因此重写它以返回当前背景色。
// Ribbon controls queries handling COLORREF OnRibbonQueryColor(UINT nID) { COLORREF color = m_view.SetBackgroundColor(); m_view.SetBackgroundColor(color); return color; }
当用户在控件中选择或将鼠标悬停在颜色上时,CRibbonImpl
会发布一个 WM_COMMAND
消息,其中 HIWORD(wParam)
设置为 UI_EXECUTIONVERB
,LOWORD(wParam)
设置为控件 ID,lParam
设置为选定的 COLORREF
。
使用 RIBBON_COLOR_CONTROL_HANDLER
宏在 CFirstRibbonView
中处理此消息。
// FirstRibbonView.h : interface of the CFirstRibbonView class // BEGIN_MSG_MAP(CFirstRibbonView) CHAIN_MSG_MAP_ALT(CRichEditCommands<CFirstRibbonView>, 1) // 0 RIBBON_COLOR_CONTROL_HANDLER(ID_TEST_COLOR, OnRibbonColor) END_MSG_MAP() LRESULT OnRibbonColor(UI_EXECUTIONVERB verb, WORD wID, COLORREF color, BOOL& bHandled) { SetBackgroundColor(color); return 0; }
颜色控件按钮没有关联的图像。从 FirstRibbonBitmaps.zip 中提取 DDCP.bmp 到 FirstRibbon\res ,并以 ID_TEST_COLOR
ID 添加到 FirstRibbon.rc 。
// Bitmap // ID_TEST_COLOR BITMAP "res\\DDCP.bmp"
CRibbonFontCtrl
-
在
CRibbonImpl
派生类中的声明:CRibbonFontCtrl<wID> m_Ctrl;
-
用户操作消息映射宏:
RIBBON_FONT_CONTROL_HANDLER(wID, OnRibbonFontCtrl)
-
处理程序原型:
LRESULT OnRibbonFontCtrl(UI_EXECUTIONVERB verb, WORD wID, CHARFORMAT2* pcf, BOOL& bHandled);
CRibbonImpl::CRibbonFontCtrl
操作 Ribbon Font Controls 并处理
键 |
设置 |
可覆盖的回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_FontProperties |
Invalidate(UI_PKEY_FontProperties) |
bool OnRibbonQueryFont(ID, CHARFORMAT2&) |
总是 |
返回 false(无变化) |
示例:添加并处理一个字体控件
在标记文件中,声明组和命令 ID,并在 Tab 中声明一个 FontControl
<!--Controls--> <Command Name="TestFont" Symbol="ID_TEST_FONT" LabelTitle="Font" /> <!-- Groups --> <Command Name="GroupFont" Symbol="ID_GROUP_FONT" LabelTitle="Font" /> <Ribbon.Tabs> ... <Tab CommandName="TabHome"> ... <Group CommandName="GroupFont" SizeDefinition="OneFontControl"> <FontControl CommandName="TestFont" FontType="FontWithColor" ShowTrueTypeOnly="false" ShowVerticalFonts="false"/> </Group> </Tab>
在 CMainFrame
中,声明一个 CRibbonFontCtrl
,其 ID 相同,并将其插入 RIBBON_CONTROL_MAP。
// Ribbon controls CRibbonFontCtrl<ID_TEST_FONT> m_font; // ... // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_font)
当 Ribbon 需要知道显示哪个字体特征作为选中状态时,CRibbonImpl
会调用 OnRibbonQueryFont()
。CRibbonImpl::OnRibbonQueryFont()
返回 false
(无变化),因此重写它以返回当前字体的 CHARFORMAT2
。
bool OnRibbonQueryFont(UINT /*nId*/, CHARFORMAT2& cf) { m_view.GetDefaultCharFormat(cf); return true; }
当用户在控件中选择一个特征时,CRibbonImpl
会发布一个 WM_COMMAND
消息,其中 HIWORD(wParam)
设置为 UI_EXECUTIONVERB
,LOWORD(wParam)
设置为控件 ID,lParam 设置为 CHARFORMAT2*
。
使用 RIBBON_FONT_CONTROL_HANDLER
宏在 CFirstRibbonView
中处理此消息。
// FirstRibbonView.h : interface of the CFirstRibbonView class // BEGIN_MSG_MAP(CFirstRibbonView) CHAIN_MSG_MAP_ALT(CRichEditCommands<CFirstRibbonView>, 1) // 0 RIBBON_FONT_CONTROL_HANDLER(ID_TEST_FONT, OnRibbonFont) END_MSG_MAP() LRESULT OnRibbonFont(UI_EXECUTIONVERB verb, WORD /*wID*/, CHARFORMAT2* pcf, BOOL& /*bHandled*/) { SetDefaultCharFormat(*pcf); return 0; }
CRibbonSpinnerCtrl, CRibbonFloatSpinnerCtrl
-
在
CRibbonImpl
派生类中的声明 -
CRibbonSpinnerCtrl<wID> m_Ctrl;
-
CRibbonFloatSpinnerCtrl<wID> m_Ctrl;
-
用户操作消息映射宏
-
RIBBON_SPINNER_CONTROL_HANDLER(wID, OnRibbonSpinnerCtrl)
-
RIBBON_FLOATSPINNER_CONTROL_HANDLER(wID, OnRibbonFloatSpinnerCtrl)
-
处理程序原型
-
LRESULT OnRibbonSpinnerCtrl(WORD wID, LONG lVal, BOOL& bHandled);
-
LRESULT OnRibbonFloatSpinnerCtrl(WORD wID, DOUBLE* pdVal, BOOL& bHandled);
CRibbonImpl:: CRibbonSpinnerCtrl
和CRibbonImpl:: CRibbonFloatSpinnerCtrl
都操作 Ribbon Spinner 控件并处理 V 为LONG
(CRibbonSpinnerCtrl
)或DOUBLE
(CRibbonFloatSpinnerCtrl
)
键 |
设置 |
可覆盖的回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_DecimalPlaces |
SetDecimalPlaces(V vPlaces, bool bUpdate = false) |
bool OnRibbonQuerySpinnerValue(ID, key, LONG*) |
总是 |
返回 false(无变化) |
UI_PKEY_DecimalValue |
SetVal(V vVal, bool bUpdate = false) |
bool OnRibbonQuerySpinnerValue(ID, key, LONG*) |
总是 |
返回 false(无变化) |
UI_PKEY_MinValue |
SetMin(V vMin, bool bUpdate = false) |
bool OnRibbonQuerySpinnerValue(ID, key, LONG*) |
总是 |
返回 false(无变化) |
UI_PKEY_MaxValue |
SetMax(V vMax, bool bUpdate = false) |
bool OnRibbonQuerySpinnerValue(ID, key, LONG*) |
总是 |
返回 false(无变化) |
UI_PKEY_Increment |
SetIncrement(V vIncrement, bool bUpdate = false) |
bool OnRibbonQuerySpinnerValue(ID, key, LONG*) |
总是 |
返回 false(无变化) |
UI_PKEY_FormatString |
SetFormatString(LPCWSTR sFormat, bool bUpdate = false) |
LPCWSTR OnRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key) |
当前字符串为空 |
返回 NULL(无变化) |
UI_PKEY_RepresentativeString |
SetRepresentativeString(LPCWSTR sRepresentative, bool bUpdate = false) |
LPCWSTR OnRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key) |
当前字符串为空 |
返回 NULL(无变化) |
示例:添加并处理 Spinner 控件
在标记文件中,声明组和命令 ID,并在 Tab 中声明 Spinners
<!--Controls--> <Command Name="TestSpinner" Symbol="ID_TEST_SPINNER" LabelTitle="Spinner" /> <Command Name="TestFloatSpinner" Symbol="ID_TEST_FLOATSPINNER" LabelTitle="Float Spinner" /> <!-- Groups --> <Command Name="GroupSpinner" Symbol="ID_GROUP_SPINNER" LabelTitle="Spinners" /> <Ribbon.Tabs> ... <Tab CommandName="TabHome"> ... <Group CommandName="GroupSpinner"> <Spinner CommandName="TestSpinner" /> <Spinner CommandName="TestFloatSpinner" /> </Group> </Tab>
在 CMainFrame
中,声明一个 CRibbonSpinnerCtrl
和一个 CRibbonFloatSpinnerCtrl
,其 ID 匹配,并将它们插入 RIBBON_CONTROL_MAP。
// Ribbon controls CRibbonSpinnerCtrl<ID_TEST_SPINNER> m_spinner; CRibbonFloatSpinnerCtrl<ID_TEST_FLOATSPINNER> m_spinnerFloat; // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_spinner) RIBBON_CONTROL(m_spinnerFloat)
当用户在 Spinner 中设置值时
-
对于
CRibbonSpinnerCtrl
,CRibbonImpl
发布一个WM_COMMAND
消息,其中HIWORD(wParam)
设置为 0,LOWORD(wParam)
设置为控件 ID,lParam
设置为 LONG 值。 -
对于 CRibbonFloatSpinnerCtrl,CRibbonImpl 发布一个具有相同
wParam
的 WM_COMMAND 消息,lParam
设置为指向值的DOUBLE*
。
……在 CMainFrame
中使用 RIBBON_SPINNER_CONTROL_HANDLER
和 RIBBON_FLOATSPINNER_CONTROL_HANDLER
宏处理这些消息。
BEGIN_MSG_MAP(CMainFrame) // ... RIBBON_SPINNER_CONTROL_HANDLER(ID_TEST_SPINNER, OnSpinner) RIBBON_FLOATSPINNER_CONTROL_HANDLER(ID_TEST_FLOATSPINNER, OnFloatSpinner) // … LRESULT OnSpinner(WORD wID, LONG lVal, BOOL& bHandled) { CString sVal; sVal.Format(L"%d", lVal); UISetText(ID_DEFAULT_PANE, sVal); return 0; } LRESULT OnFloatSpinner(WORD wID, DOUBLE* pdVal, BOOL& bHandled) { CString sVal; sVal.Format(L"%.2f", *pdVal); UISetText(ID_DEFAULT_PANE, sVal); return 0; }
示例:初始化 Ribbon 控件的三种方法
CRibbonImpl::CRibbonSpinnerCtrl
和 CRibbonImpl::CRibbonSpinnerFloatCtrl
的默认初始值是 0 到 100 的范围,增量为 1,起始值为 1,小数位数为 0(CRibbonSpinnerFloatCtrl 为 1)。
将控件初始化为
-
ID_TEST_FLOATSPINNER 范围为 10 到 20,增量为 .25,起始值为 14.75,2 位小数,并调整宽度以容纳 4 位数字和 1 位小数。
-
ID_TEST_SPINNER 宽度相同,起始值为 51。
使用以下方法之一
- 最简单的方法,在
CMainFrame::OnCreate()
中初始化LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // ... bool bRibbonUI = RunTimeHelper::IsRibbonUIAvailable(); if (bRibbonUI) { // ... m_spinnerFloat.SetRepresentativeString(L"99.99"); m_spinnerFloat.SetDecimalPlaces(2); m_spinnerFloat.SetIncrement(.25); m_spinnerFloat.SetMin(10); m_spinnerFloat.SetMax(20); m_spinnerFloat.SetVal(14.75); m_spinner.SetRepresentativeString(L"99.99"); m_spinner.SetVal(51); }
- 如果值可能在运行时更改,请重写相关的
CRibbonImpl::OnRibbonQueryxxx()
成员// Ribbon controls queries handling bool OnRibbonQuerySpinnerValue(UINT nCmdID, REFPROPERTYKEY key, LONG* pVal) { if (key == UI_PKEY_DecimalValue) { *pVal = 50; return true; } return false; } bool OnRibbonQueryFloatSpinnerValue(UINT nCmdID, REFPROPERTYKEY key, DOUBLE* pdVal) { if (key == UI_PKEY_DecimalPlaces) *pdVal = 2; else if (key == UI_PKEY_MinValue) *pdVal = 10; else if (key == UI_PKEY_MaxValue) *pdVal = 20; else if (key == UI_PKEY_DecimalValue) *pdVal = 14.75; else if (key == UI_PKEY_Increment) *pdVal = .25; return true; } LPCWSTR OnRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key) { if ((nCmdID == ID_TEST_SPINNER) || (nCmdID == ID_TEST_FLOATSPINNER)) return key == UI_PKEY_RepresentativeString ? L"99.99" : NULL; else return DefRibbonQueryText(nCmdID, key); }
- 最面向对象的方法,特化构造函数(对于简单和工具栏画廊及组合框控件不可用)
// MainFrm.h : interface of the CMainFrame class // ... class CMainFrame { // ... }; CMainFrame::CRibbonSpinnerCtrl<ID_TEST_SPINNER>::CRibbonSpinnerCtrl() { SetRepresentativeString(L"99.99"); SetVal(51); } CMainFrame::CRibbonFloatSpinnerCtrl<ID_TEST_FLOATSPINNER>::CRibbonFloatSpinnerCtrl() { SetRepresentativeString(L"99.99"); SetDecimalPlaces(2); SetIncrement(.25); SetMin(10); SetMax(20); SetVal(14.75); }
- 任何 1 到 3 的组合,以满足您的喜好或需求。
Ribbon Collection 控件
CRibbonImpl
集合控件类处理 Ribbon Galleries、Combos 和 Recent Items Ribbon 控件。
完整的类 CRibbonItemGalleryCtrl
、CRibbonCommandGalleryCtrl
和 CRibbonComboCtrl
处理类别、动态调整大小和项配置。
简单的类 CRibbonSimpleGalleryCtrl
、CRibbonSimpleComboCtrl
和 CRibbonToolBarGalleryCtrl
不处理类别,并且在 Ribbon 创建时配置一次。
Ribbon 完整集合类
完整集合类的通用 Property Keys
键 |
设置 |
可覆盖的回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_ItemsSource |
InvalidateItems() 或 |
Item keys 会为所有项进行查询。 |
||
UI_PKEY_Categories |
InvalidateCategories() |
UI_PKEY_Label 会为每个类别查询,UI_PKEY_CategoryId 会为每个项查询。 |
Category Key |
设置 |
可覆盖的回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_Label |
SetCategoryText(UINT uCat, LPCWSTR sText, bool bUpdate = false) |
LPCWSTR OnRibbonQueryCategoryText(ID, UINT32 uCat) |
当前字符串为空 |
返回 « Category » |
Item Key |
设置 |
可覆盖的回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_CategoryId |
SetItemCategory(UINT uItem, UINT uCat, bool bUpdate = false) |
UINT32 OnRibbonQueryItemCategory(ID, UINT32 uItem) |
当前类别索引为 UI_COLLECTION_INVALIDINDEX |
返回 0 |
与单个控件不同,集合控件有一个 uItems
模板参数,以及一个可选的 uCategories
模板参数。
CRibbonItemGalleryCtrl
-
在
CRibbonImpl
派生类中的声明:CRibbonItemGalleryCtrl<wID, uItems, uCategories = 0>
m_Ctrl:uItems
是画廊中的最大项数,uCategory
是类别数。 -
用户操作消息映射宏:
RIBBON_GALLERY_CONTROL_HANDLER(wID, OnRibbonGalleryCtrl)
-
处理程序原型:
LRESULT OnRibbonGalleryCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled)
。如果uSel
为UI_COLLECTION_INVALIDINDEX
,则表示点击了 SplitButton Gallery 的 Button 部分。
CRibbonImpl::CRibbonItemGalleryCtrl
操作 Ribbon Drop-Down Gallery、In-Ribbon Gallery、Split Button Gallery,这些控件不具有 Type = "Commands" 属性。
键 |
设置 |
可覆盖的回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_SelectedItem |
Select(UINT uItem, bool bUpdate = false) |
bool OnRibbonQuerySelectedItem(ID, UINT32& uSel) |
当前选择为 UI_COLLECTION_INVALIDINDEX |
返回 false(无变化) |
Item Key |
设置 |
可覆盖的回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_Label |
SetItemText(UINT uItem, LPCWSTR sText, bool bUpdate = false) |
LPCWSTR OnRibbonQueryItemText(ID, UINT32 uItem) |
当前字符串为空 |
返回资源字符串,其 ID 为 ID + 1 + uItem(或 NULL) |
UI_PKEY_ItemImage |
SetItemImage(UINT uIndex, HBITMAP hbm, bool bUpdate = false) |
HBITMAP OnRibbonQueryItemImage(ID, UINT32 uItem) |
当前 HBITMAP 为 NULL |
资源位图的 HBITMAP,其 ID 为 ID + 1 + uItem(或 NULL) |
示例:添加并处理一个 InRibbonGallery 控件
在标记文件中,声明新的 Tab、Group 和 ID_TEST_GALLERY
命令 ID,并在 Group 中声明一个 InRibbonGallery。
<Command Name="Gallery" Symbol="ID_TEST_GALLERY" Id="200" LabelTitle ="Shapes" /> <!-- Tabs --> <Command Name="TabGalleries" Symbol="ID_TAB_GALLERIES" LabelTitle="Galleries" /> <!-- Groups --> <Command Name="GroupGallery" Symbol="ID_GROUP_GALLERY" LabelTitle="Galleries" /> </Ribbon.Tabs> ... <Tab CommandName="TabGalleries"> <Group CommandName="GroupGallery" SizeDefinition="OneInRibbonGallery"> <InRibbonGallery CommandName="Gallery" ItemHeight="32" ItemWidth="32" MinColumnsLarge="2" TextPosition="Hide" /> </Group> </Tab> </Ribbon.Tabs>
在 resource.h 中为画廊项创建紧跟 ID_TEST_GALLERY 的 ID,以启用默认加载项位图。
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by FirstRibbon.rc // ... #define ID_DIAMOND 201 // == ID_TEST_GALLERY + 1 #define ID_RECT 202 #define ID_ROUNDRECT 203 #define ID_ELLIPSE 204 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 205
从 FirstRibbonBitmaps.zip 中提取 4 个形状位图到 FirstRibbon\res ,并使用适当的 ID 将位图及其标题字符串添加到 FirstRibbon.rc 。
// Bitmap // ID_DIAMOND BITMAP "res\\DIAMOND.BMP" ID_RECT BITMAP "res\\RECTANGLE.BMP" ID_ROUNDRECT BITMAP "res\\ROUNDEDRECTANGLE.BMP" ID_ELLIPSE BITMAP "res\\ELLIPSE.BMP" STRINGTABLE BEGIN ID_DIAMOND "Diamond" ID_RECT "Rectangle" ID_ROUNDRECT "Round Rectangle" ID_ELLIPSE "Ellipse" END
在 CMainFrame
中,声明一个 CRibbonItemGalleryCtrl
,其 ID 为 ID_TEST_GALLERY
,包含 4 个项,2 个类别,并将其插入 RIBBON_CONTROL_MAP。
// Ribbon controls CRibbonItemGalleryCtrl<ID_TEST_GALLERY, 4, 2> m_shapes; // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_shapes)
CRibbonImpl:: CRibbonItemGalleryCtrl
的默认初始值为类别名称为 'Category',所有项属于 Category 0,未选择任何项。
在 CMainFrame::OnCreate()
成员中初始化 m_shapes
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // ... // remove old menu SetMenu(NULL); bool bRibbonUI = RunTimeHelper::IsRibbonUIAvailable(); // 3 if (bRibbonUI) // 3 { UIAddMenu(m_CmdBar.GetMenu(), true); // 3 m_shapes.SetCategoryText(0, L"Angles"); m_shapes.SetCategoryText(1, L"Rounded"); m_shapes.Select(1); for (UINT uItem = 0; uItem < 4; uItem++) m_shapes.SetItemCategory(uItem, uItem > 1); // ...
或者,在特化的构造函数中
CMainFrame::CRibbonItemGalleryCtrl<ID_TEST_GALLERY, 4, 2>::CRibbonItemGalleryCtrl() { SetCategoryText(0, L"Angles"); SetCategoryText(1, L"Curves"); Select(1); for (UINT uItem = 0; uItem < 4; uItem++) SetItemCategory(uItem, uItem > 1); }
当用户在画廊中选择或将鼠标悬停在某个项上时,CRibbonImpl
会发布一个 WM_COMMAND
消息,其中 HIWORD(wParam)
设置为 UI_EXECUTIONVERB
,LOWORD(wParam)
设置为画廊 ID,lParam
设置为选定的项。
在 CMainFrame
中使用 RIBBON_GALLERY_CONTROL_HANDLER
宏处理此消息。
BEGIN_MSG_MAP(CMainFrame) RIBBON_GALLERY_CONTROL_HANDLER(ID_TEST_GALLERY, OnGalleryShape) // ... LRESULT OnGalleryShape(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled) { UISetText(ID_DEFAULT_PANE, CString(MAKEINTRESOURCE(wID + uSel + 1))); return 0; }
示例:处理一个 DropDownGallery 控件
在标记文件中,将 InRibbonGallery 更改为 DropDownGallery ,并更改 Group 的 SizeDefinition。
<Tab CommandName="TabGalleries"> <Group CommandName="GroupGallery" SizeDefinition="OneButton"> <DropDownGallery CommandName="Gallery" ItemHeight="32" ItemWidth="32" TextPosition="Hide" /> </Group> </Tab>
在初始化代码中,设置 Button Image 为最初选择的形状
CMainFrame::CRibbonItemGalleryCtrl<ID_TEST_GALLERY, 4, 2>::CRibbonItemGalleryCtrl() { SetCategoryText(0, L"Angles"); SetCategoryText(1, L"Curves"); Select(1); SetImage(UI_PKEY_LargeImage, AtlLoadBitmapImage(ID_RECT, LR_CREATEDIBSECTION)); for (UINT uItem = 0; uItem < 4; uItem++) SetItemCategory(uItem, uItem > 1); }
要将 Button Image 更新为当前选定的形状
LRESULT OnGalleryShape(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled) { UISetText(ID_DEFAULT_PANE, OnRibbonQueryItemText(wID, uSel)); if (verb == UI_EXECUTIONVERB_EXECUTE) m_shapes.SetImage(UI_PKEY_LargeImage, OnRibbonQueryItemImage(wID, uSel), true); return 0; }
示例:处理一个 SplitButtonGallery 控件
在标记文件中,将 DropDownGallery 更改为 SplitButtonGallery。
<Tab CommandName="TabGalleries"> <Group CommandName="GroupGallery" SizeDefinition="OneButton"> <SplitButtonGallery CommandName="Gallery" ItemHeight="32" ItemWidth="32" TextPosition="Hide" /> </Group>
当 CRibbonItemGalleryCtrl
附加到 SplitButtonGallery 时,如果用户单击主 Button 部分,CRibbonImpl
会发布一个 WM_COMMAND
消息,其中 HIWORD(wParam)
设置为 UI_EXECUTIONVERB_EXECUTE
,LOWORD(wParam)
设置为画廊 ID,lParam
设置为 UI_COLLECTION_INVALIDINDEX
。
CMainFrame::OnGalleryShape()
必须处理这种情况
LRESULT OnGalleryShape(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled) { if (uSel == UI_COLLECTION_INVALIDINDEX) uSel = m_shapes.GetSelected(); ATLASSERT(uSel != UI_COLLECTION_INVALIDINDEX); UISetText(ID_DEFAULT_PANE, OnRibbonQueryItemText(wID, uSel)); if (verb == UI_EXECUTIONVERB_EXECUTE) m_shapes.SetImage(UI_PKEY_LargeImage, OnRibbonQueryItemImage(wID, uSel), true); return 0; }
CRibbonCommandGalleryCtrl
-
在
CRibbonImpl
派生类中的声明:CRibbonCommandGalleryCtrl<wID, uItems, uCategories = 0>
m_Ctrl:uItems
是画廊中的最大项数,uCategories
是类别数。 -
用户操作消息映射宏:对于画廊项 ID,使用
COMMAND_ID_HANDLER(nCmdID, OnCommand)
。 -
处理程序原型:
LRESULT OnCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled)
;
CRibbonImpl::CRibbonItemGalleryCtrl
操作 Ribbon Drop-Down Gallery、In-Ribbon Gallery、Split Button Gallery,这些控件具有 Type = "Commands" 属性。
Item Key |
设置 |
可覆盖的回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_CommandId |
SetItemCommand(uItem, ID, bool bUpdate = false) |
UINT32 OnRibbonQueryItemCommand(ID, uItem) |
当前 CommandId 为 NULL |
返回 ID + 1 + uItem |
UI_PKEY_CommandType |
SetItemCommandType(uItem, UI_COMMANDTYPE, bool bUpdate = false) |
UI_COMMANDTYPE OnRibbonQueryItemCommandType(ID, uItem) |
当前 CommandType 为 UI_COMMANDTYPE_UNKNOWN |
返回 UI_COMMANDTYPE_ACTION |
示例:实现一个 Command InRibbonGallery 控件
在标记文件中,将 SplitButtonGallery 声明为 Command Gallery
</Tab> <Tab CommandName="TabGalleries"> <Group CommandName="GroupGallery" SizeDefinition="OneInRibbonGallery"> <InRibbonGallery CommandName="Gallery" Type = "Commands" MinColumnsLarge="4" ItemHeight="32" ItemWidth="32" TextPosition="Hide" /> </Group> </Tab>
CRibbonImpl::CRibbonCommandGalleryCtrl
的默认初始化将命令 ID 设置为 GalleryID + 1 + index,这些 ID 已在 FirstRibbon.h 中插入。
在 FirstRibbon.rc 中,插入一个具有 ID_TEST_GALLERY
的 Menu 资源,并更改形状的字符串资源
ID_TEST_GALLERY MENU BEGIN MENUITEM "Diamond", ID_DIAMOND MENUITEM "Rectangle", ID_RECT MENUITEM "Round Rectangle", ID_ROUNDRECT MENUITEM "Ellipse", ID_ELLIPSE END STRINGTABLE BEGIN ID_DIAMOND "Angled shape\nDiamond" ID_RECT "Angled shape\nRectangle" ID_ROUNDRECT "Curved shape\nRound Rectangle" ID_ELLIPSE "Curved shape\nEllipse" END
在 CMainFrame
中,声明一个 CRibbonCommandGalleryCtrl
并将其插入 RIBBON_CONTROL_MAP。
// Ribbon controls CRibbonCommandGalleryCtrl<ID_TEST_GALLERY, 4, 2> m_cmdshapes; // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_cmdshapes)
m_cmdShapes
需要与 m_shapes
相同的类别初始化(此处为 CMainFrame::OnCreate()
,也可以在特化的构造函数中),SetItemCommandType(1, UI_COMMANDTYPE_BOOLEAN)
将索引为 1(ID_RECT)的项设置为 Ribbon ToggleButton。
if (bRibbonUI) // 3 { // ... UIAddMenu(ID_TEST_GALLERY); m_cmdshapes.SetItemCommandType(1, UI_COMMANDTYPE_BOOLEAN); m_cmdshapes.SetCategoryText(0, L"Angles"); m_cmdshapes.SetCategoryText(1, L"Curves"); for (UINT uItem = 0; uItem < 4; uItem++) m_cmdshapes.SetItemCategory(uItem, uItem > 1);
CRibbonCommandGalleryCtrl
发布点击项 ID 的 WM_COMMAND
消息:使用 COMMAND_RANGE_HANDLER
宏处理它们。
BEGIN_MSG_MAP(CMainFrame) COMMAND_RANGE_HANDLER(ID_DIAMOND, ID_ELLIPSE, OnCommandShape) // ... LRESULT OnCommandShape(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { UISetText(ID_DEFAULT_PANE, CString(MAKEINTRESOURCE(wID))); return 0; }
Simple Galleries 类
CRibbonToolbarGalleryCtrl
CRibbonToolBarGalleryCtrl
是一个从 Toolbar 资源构建的 Ribbon Command Gallery。没有类别处理,也没有回调到应用程序。
-
在 CRibbonImpl 派生类中的声明:
CRibbonToolBarGalleryCtrl<ID, IDToolBar, uItems>
m_Ctrl:uItems
是画廊中的项数。 -
用户操作消息映射宏:对于工具栏资源 ID,使用
COMMAND_ID_HANDLER(nCmdID, OnCommand)
。 -
处理程序原型:
LRESULT OnCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled);
CRibbonImpl::CRibbonItemGalleryCtrl
操作 Ribbon Drop-Down Gallery、In-Ribbon Gallery、Split Button Gallery,这些控件具有 Type = "Commands" 属性。
Item Key |
处理 |
---|---|
UI_PKEY_CategoryId |
返回 UI_COLLECTION_INVALIDINDEX |
UI_PKEY_CommandId |
返回 IDToolBar 资源命令 ID 在 uItem 位置。 |
UI_PKEY_CommandType |
返回 UI_COMMANDTYPE_ACTION |
示例:实现一个 InRibbonGallery CRibbonToolbarGalleryCtrl。
在标记文件中
-
声明一个名为 ToolbarGallery 的 Ribbon Command,ID 为
ID_RIBBON_TOOLBAR
, -
将其作为 InRibbonGallery 添加到 TabGalleries Tab 中,类型为 Commands。
<!--Controls--> <Command Name="ToolbarGallery" Symbol="ID_RIBBON_TOOLBAR" LabelTitle ="Toolbar Gallery" /> <Tab CommandName="TabGalleries"> <Group CommandName="GroupGallery" SizeDefinition="OneInRibbonGallery"> <InRibbonGallery CommandName="ToolbarGallery" Type = "Commands" MaxRows = "3" MinColumnsLarge="3" ItemHeight="15" ItemWidth="16" HasLargeItems = "false" TextPosition="Hide" > <InRibbonGallery.MenuLayout> <FlowMenuLayout Columns = "4" Rows = "2"/> </InRibbonGallery.MenuLayout> </InRibbonGallery> </Group> </Tab>
在 CMainFrame 中,声明一个 CRibbonToolbarGalleryCtrl
,使用 IDR_MAINFRAME
工具栏资源,包含 **7** 个元素(Ribbon 不会将 ID_APP_ABOUT
插入 Command Gallery,因为它与 Ribbon.HelpButton 设置冲突),并将其插入 RIBBON_CONTROL_MAP。
// Ribbon controls CRibbonToolbarGalleryCtrl<ID_RIBBON_TOOLBAR, IDR_MAINFRAME, 7> m_toolbar; CRibbonCommandGalleryCtrl<ID_TEST_GALLERY, 4, 2> m_cmdshapes; // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_toolbar) RIBBON_CONTROL(m_cmdshapes)
m_toolbar
不需要初始化,并且包含的命令已得到处理,因此编译并运行。
示例:实现一个 DropDownGallery CRibbonToolbarGalleryCtrl。
在标记文件中,将 TabGalleries Tab 的 SizeDefinition 更改为容纳两个画廊
-
前面的 InRibbonGallery Gallery
-
ToolbarGallery 作为 DropDownGallery。
<Tab CommandName="TabGalleries"> <Group CommandName="GroupGallery" SizeDefinition="InRibbonGalleryAndBigButton"> <InRibbonGallery CommandName="Gallery" Type = "Commands" MinColumnsLarge="4" ItemHeight="32" ItemWidth="32" TextPosition="Hide" /> <DropDownGallery CommandName="ToolbarGallery" Type = "Commands" HasLargeItems = "false" TextPosition="Hide" > <DropDownGallery.MenuLayout> <FlowMenuLayout Columns = "4"/> </DropDownGallery.MenuLayout> </DropDownGallery> </Group> </Tab>
从 FirstRibbonBitmaps.zip 中提取 Tools.bmp 到 FirstRibbon\res ,并以 ID_RIBBON_TOOLBAR
ID 添加到 FirstRibbon.rc
。
ID_RIBBON_TOOLBAR BITMAP "res\\Tools.bmp"
CRibbonSimpleGalleryCtrl
CRibbonSimpleGalleryCtrl
是一个从资源集构建的 Ribbon(Command 或 Item)Gallery。没有类别处理,也没有回调到应用程序。
-
在 CRibbonImpl 派生类中的声明:
CRibbonSimpleGalleryCtrl<ID, uItems, UI_COMMANDTYPE type = UI_COMMANDTYPE_ACTION>
m_Ctrl。uItems
是画廊中的项数。 -
用户操作消息映射宏
-
Item Galleries 使用
RIBBON_GALLERY_CONTROL_HANDLER(ID, OnRibbonGalleryCtrl)
-
Command Galleries 使用
COMMAND_ID_HANDLER(nCmdID, OnCommand)
。 -
处理程序原型
-
LRESULT OnRibbonGalleryCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled);
-
LRESULT OnCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled);
CRibbonImpl::CSimpleGalleryCtrl
操作 Ribbon Drop-Down Gallery、In-Ribbon Gallery、Split Button Gallery。
键 |
设置 |
处理 |
---|---|---|
UI_PKEY_SelectedItem |
Select(UINT uItem, bool bUpdate = false) |
返回 0 或上次选择的项 |
Item Key |
处理 |
---|---|
UI_PKEY_CategoryId |
返回 UI_COLLECTION_INVALIDINDEX |
UI_PKEY_Label |
返回字符串资源,ID 为 ID + 1 + uItem(或 NULL) |
UI_PKEY_ItemImage |
资源位图的 HBITMAP,ID 为 ID + 1 + uItem(或 NULL) |
UI_PKEY_CommandId |
返回 ID + 1 + uItem |
UI_PKEY_CommandType |
返回 t_CommandType |
示例:实现一个 Command CRibbonSimpleGalleryCtrl。
在 CMainFrame
中,声明一个 CRibbonSimpleGalleryCtrl
,ID 为 ID_TEST_GALLERY
,包含 4 个项,并将其插入 RIBBON_CONTROL_MAP(从映射中移除 m_cmdshapes
)。
// Ribbon controls CRibbonSimpleGalleryCtrl<ID_TEST_GALLERY, 4> m_simpleshapes; CRibbonToolbarGalleryCtrl<ID_RIBBON_TOOLBAR, IDR_MAINFRAME, 7> m_toolbar; // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_simpleshapes) RIBBON_CONTROL(m_toolbar) //RIBBON_CONTROL(m_cmdshapes)
m_simpleshapes
已在标记文件中声明为 Command Gallery,只需编译并运行。
示例:实现一个 Item CRibbonSimpleGalleryCtrl。
在标记文件中,移除 Gallery 声明中的 Type = "Commands" 行。
<Tab CommandName="TabGalleries"> <Group CommandName="GroupGallery" SizeDefinition="InRibbonGalleryAndBigButton"> <InRibbonGallery CommandName="Gallery" MinColumnsLarge="4" ItemHeight="32" ItemWidth="32" TextPosition="Hide" /> <DropDownGallery CommandName="ToolbarGallery" Type = "Commands" HasLargeItems = "false" TextPosition="Hide" > <DropDownGallery.MenuLayout> <FlowMenuLayout Columns = "4"/> </DropDownGallery.MenuLayout> </DropDownGallery> </Group> </Tab>
在 FirstRibbon.rc 中,将字符串还原为用于形状 Item Gallery 的状态。
STRINGTABLE BEGIN ID_DIAMOND "Diamond" ID_RECT "Rectangle" ID_ROUNDRECT "Round Rectangle" ID_ELLIPSE "Ellipse" END
CRibbonComboCtrl
-
在 CRibbonImpl 派生类中的声明:
CRibbonComboCtrl<wID, uItems, uCategories = 0>
m_Ctrl。uItems
是组合框中的最大项数,uCategories
是类别数。 -
用户操作消息映射宏:
RIBBON_COMBO_CONTROL_HANDLER(wID, OnRibbonComboCtrl)
-
处理程序原型:
LRESULT OnRibbonComboCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled)
CRibbonImpl::CRibbonComboCtrl
操作 Ribbon Combo Box 控件,处理 CRibbonItemGalleryCtrl 的 key 并
键 |
获取 |
设置 |
---|---|---|
UI_PKEY_StringValue |
LPCWSTR GetComboText() |
SetComboText(LPCWSTR sText) |
示例:添加并处理一个组合框控件
在标记文件中,声明 Tab、Group 和 Command ID,并在 Group 中声明一个 ComboBox。
<Command Name="Combo" Symbol="ID_TEST_COMBO" LabelTitle ="Combo" /> <!-- Tabs --> <Command Name="TabGalleries" Symbol="ID_TAB_GALLERIES" LabelTitle="Galleries" /> <!-- Groups --> <Command Name="GroupCombo" Symbol="ID_GROUP_COMBO" LabelTitle="Combos" /> <Tab CommandName="TabGalleries"> ... <Group CommandName="GroupCombo"> <ComboBox CommandName="Combo" /> </Group> </Tab> </Ribbon.Tabs>
在 CMainFrame
中,声明一个 CRibbonComboCtrl
,ID 为 ID_TEST_COMBO,包含 6 个项,2 个类别,并将其插入 RIBBON_CONTROL_MAP。
// Ribbon controls CRibbonComboCtrl<ID_TEST_COMBO, 6, 2> m_combo; // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_combo)
CRibbonImpl::CRibbonComboCtrl
的默认初始值为类别名称为 'Category',所有项属于 Category 0,未选择任何项。
要初始化 m_combo
,请重写相关的 CRibbonImpl::OnRibbonQueryxxx()
成员。
// Ribbon controls queries handling LPCWSTR OnRibbonQueryCategoryText(UINT32 uCtrlID, UINT32 uCat) { if (uCtrlID == ID_TEST_COMBO) return uCat ? L"End" : L"Begin"; return L"Category"; } UINT32 OnRibbonQueryItemCategory(UINT32 uCtrlID, UINT32 uItem) { if (uCtrlID == ID_TEST_COMBO) return uItem > 3; return 0; } LPCWSTR OnRibbonQueryItemText(UINT32 uCtrlID, UINT32 uItem) { if (uCtrlID == ID_TEST_COMBO) { static LPCWSTR text[] = { L"First", L"Second", L"Third", L"Fourth", L"Fifth", L"Last" }; return text[uItem]; } return L"Item"; } bool OnRibbonQuerySelectedItem(UINT32 uCtrlID, UINT32& uSel) { if (uCtrlID == ID_TEST_COMBO) { uSel = 1; return true; } return false; }
您也可以像前面所示,在 CMainFrame::OnCreate()
中初始化 m_combo
。
当用户在组合框中选择或将鼠标悬停在某个项上时,CRibbonImpl
会发布一个 WM_COMMAND
消息,其中 HIWORD(wParam)
设置为 UI_EXECUTIONVERB
,LOWORD(wParam)
设置为组合框 ID,lParam
设置为选定的项。
在 CMainFrame
中使用 RIBBON_COMBO_CONTROL_HANDLER
宏处理此消息。
BEGIN_MSG_MAP(CMainFrame) RIBBON_COMBO_CONTROL_HANDLER(ID_TEST_COMBO, OnCombo) //... LRESULT OnCombo(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled) { static LPCWSTR text[] = { L"First", L"Second", L"Third", L"Fourth", L"Fifth", L"Last" }; UISetText(ID_DEFAULT_PANE, text[uSel]); return 0; }
CRibbonSimpleComboCtrl
CRibbonSimpleComboCtrl
是一个从资源集构建的 Ribbon 组合框。没有类别处理,也没有回调到应用程序。
-
在 CRibbonImpl 派生类中的声明:
CRibbonSimpleComboCtrl<ID, uItems>
m_Ctrl。uItems
是组合框中的项数。 -
用户操作消息映射宏:
RIBBON_COMBO_CONTROL_HANDLER(ID, OnRibbonComboCtrl)
。 -
处理程序原型:
LRESULT OnRibbonComboCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled);
-
CRibbonImpl::CRibbonSimpleComboCtrl
操作 Ribbon Combo Box 控件。
示例:处理一个 CRibbonSimpleComboCtrl 组合框控件
在 Markup 文件中,声明一个 ID 为 ID_RIBBON_SIMPLECOMBO
的 Command,并将其插入 Galleries Tab。
<Command Name="SimpleCombo" Symbol="ID_RIBBON_SIMPLECOMBO" Id="300" LabelTitle ="Simple Combo" /> <Group CommandName="GroupCombo"> <ComboBox CommandName="SimpleCombo" /> </Group>
在 FirstRibbon.h 中,声明一些紧跟 ID_TEST_COMBO
的标识符。
#define ID_COMBO1 301 #define ID_COMBO2 302 #define ID_COMBO3 303 #define ID_COMBO4 304 #define ID_COMBO5 305
在 FirstRibbon.rc 中,添加匹配的字符串资源。
STRINGTABLE BEGIN ID_COMBO1 "Simple 1" ID_COMBO2 "Simple 2" ID_COMBO3 "Simple 3" ID_COMBO4 "Simple 4" ID_COMBO5 "Simple 5" END
在 CMainFrame
中,声明一个 CRibbonSimpleComboCtrl
,包含 5 个项且 ID 相同,并将其插入 RIBBON_CONTROL_MAP,重用 OnGalleryShape()
处理程序以使用 RIBBON_COMBO_CONTROL_HANDLER
宏处理选择消息。
// Ribbon controls CRibbonSimpleComboCtrl<ID_RIBBON_SIMPLECOMBO, 5> m_simplecombo; // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_simplecombo) BEGIN_MSG_MAP(CMainFrame) RIBBON_COMBO_CONTROL_HANDLER(ID_RIBBON_SIMPLECOMBO, OnGalleryShape)
CRibbonRecentItemsCtrl
-
在 CRibbonImpl 派生类中的声明:
CRibbonRecentItemsCtrl<ID, class TDocList = CRecentDocumentList>
m_Ctrl。TDocList
是任何派生自WTL::CRecentDocumentListBase
的类。
CRibbonImpl:: CRibbonRecentItemsCtrl
操作 Ribbon Recent Items 控件。
Item Key |
设置 |
可覆盖的回调 |
回调条件 |
默认处理 |
---|---|---|---|---|
UI_PKEY_LabelDescription |
TDocList 成员 |
返回 TDocList::m_arrDocs[uItem].szDocName |
||
UI_PKEY_Label |
LPCWSTR OnRibbonQueryRecentItemName(LPCWSTR sPath) |
总是 |
返回 ::PathFindFileName(sPath) |
示例:添加并处理一个 Ribbon Recent Items
在 Markup 文件中,声明一个 RecentFiles Command,并在 Application.Menu 部分插入一个使用它的 RecentItems。
<!--Controls--> <Command Name="RecentFiles" Symbol="ID_RIBBON_RECENT_FILES" LabelTitle="Recent Files" /> ... <!-- Application Menu --> <Ribbon.ApplicationMenu > <ApplicationMenu CommandName="AppMenu" > <ApplicationMenu.RecentItems> <RecentItems CommandName="RecentFiles" MaxCount="16" EnablePinning="false"/> </ApplicationMenu.RecentItems>
在 CMainFrame
中添加
// Ribbon controls CRibbonRecentItemsCtrl<ID_RIBBON_RECENT_FILES> m_recent; // Ribbon control map BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_recent)
RecentItems Ribbon 控件现在通过 WTL
::CRecentDocumentList m_recent
成员进行操作。
为 ID_FILE_OPEN
添加消息映射条目和一个匹配的 CMainFrame::OnFileOpen()
成员。
BEGIN_MSG_MAP(CMainFrame) COMMAND_ID_HANDLER(ID_FILE_OPEN, OnFileOpen) ... LRESULT OnFileOpen(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { CFileDialog dlg(TRUE); if (dlg.DoModal() == IDOK) m_recent.AddToList(dlg.m_ofn.lpstrFile); return 0; }
编译,运行并“打开”一些文件。
其他 Ribbon 功能处理
Ribbon 应用程序模式
CRibbonImpl 成员:HRESULT SetRibbonModes(INT32 iModes)
;
Ribbon 上下文菜单
CRibbonImpl 成员
-
bool
HasRibbonMenu(UINT32 uID)
返回true
如果uID
是 Ribbon Context Popup ID。 -
HRESULT TrackRibbonMenu(UINT32 uID, INT32 x, INT32 y)
在屏幕坐标 (x, y) 处弹出上下文菜单。 -
HRESULT TrackRibbonMenu(UINT32 uID, LPARAM lParam)
从WM_CONTEXTMENU
lParam 中提取 (x, y) 坐标并调用TrackRibbonMenu(uID, x, y)
。
示例:实现一个 Ribbon 上下文菜单
在 Markup 文件中,声明 ContextMap、ContextMenu 和 MiniToolbar Commands,并在 Application.Views 部分插入一个 ContextPopup 描述。
<Command Name="MiniToolbar" Symbol="ID_MINITOOLBAR"/> <Command Name="ContextMenu" Symbol="ID_CONTEXTMENU"/> <Command Name="ContextMap" Symbol="ID_CONTEXTMAP" /> ... </Ribbon.Tabs> </Ribbon> <ContextPopup> <ContextPopup.MiniToolbars> <MiniToolbar Name="MiniToolbar"> <MenuGroup> <Button CommandName="wtl_FILE_OPEN" /> <Button CommandName="wtl_FILE_NEW" /> <Button CommandName="wtl_FILE_SAVE" /> </MenuGroup> </MiniToolbar> </ContextPopup.MiniToolbars> <ContextPopup.ContextMenus> <ContextMenu Name="ContextMenu"> <MenuGroup> <Button CommandName="wtl_EDIT_UNDO" /> </MenuGroup> <MenuGroup> <Button CommandName="wtl_EDIT_CUT" /> <Button CommandName="wtl_EDIT_COPY" /> <Button CommandName="wtl_EDIT_PASTE" /> </MenuGroup> </ContextMenu> </ContextPopup.ContextMenus> <ContextPopup.ContextMaps> <ContextMap CommandName="ContextMap" ContextMenu="ContextMenu" MiniToolbar="MiniToolbar"/> </ContextPopup.ContextMaps> </ContextPopup> </Application.Views>
在 CMainFrame 中添加一个 WM_CONTEXTMENU
处理程序,以及
BEGIN_MSG_MAP(CMainFrame) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu) // ... LRESULT OnContextMenu(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if (IsRibbonUI()) TrackRibbonMenu(ID_CONTEXTMAP, lParam); return 0; }
Ribbon 上下文选项卡
键 |
获取 |
设置 |
---|---|---|
UI_PKEY_ContextAvailable |
UI_CONTEXTAVAILABILITY GetRibbonContextAvail(ID) |
SetRibbonContextAvail(ID, UI_CONTEXTAVAILABILITY cav) |
示例:添加并处理一个 Ribbon 上下文选项卡
<!-- Tabs --> <Command Name="TabContext" Symbol="ID_TAB_CONTEXT" LabelTitle="Context" /> ... <!-- Groups --> <Command Name="GroupTabContext" Symbol="ID_GROUP_CONTEXT" LabelTitle='Context Tabs'/> ... <Ribbon.ContextualTabs> <TabGroup CommandName="GroupTabContext"> <Tab CommandName="TabContext"> <Group CommandName="GroupFont" SizeDefinition="OneFontControl"> <FontControl CommandName="TestFont" FontType="FontWithColor" ShowTrueTypeOnly="false" ShowVerticalFonts="false"/> </Group> </Tab> </TabGroup> </Ribbon.ContextualTabs>
在 CMainFrame::OnIdle()
中
virtual BOOL OnIdle() { // ... SetRibbonContextAvail(ID_GROUP_CONTEXT, m_view.HasSelection() ? UI_CONTEXTAVAILABILITY_ACTIVE : UI_CONTEXTAVAILABILITY_NOTAVAILABLE); }
Ribbon 颜色
键 |
获取 |
设置 |
---|---|---|
UI_PKEY_GlobalBackgroundColor UI_PKEY_GlobalHighlightColor UI_PKEY_GlobalTextColor |
UI_HSBCOLOR GetRibbonColor(key) |
bool SetRibbonColor(key, UI_HSBCOLOR color) |
示例:处理 Ribbon 颜色
在 CMainFrame::OnFileNew()
中
LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { if (IsRibbonUI() && (GetRibbonColor(UI_PKEY_GlobalBackgroundColor) != UI_HSB(0x14, 0x38, 0x54))) { SetRibbonColor(UI_PKEY_GlobalBackgroundColor, UI_HSB(0x14, 0x38, 0x54)); SetRibbonColor(UI_PKEY_GlobalHighlightColor, UI_HSB(0x00, 0x36, 0x87)); SetRibbonColor(UI_PKEY_GlobalTextColor, UI_HSB(0x2B, 0xD6, 0x00)); } return 0; }
QAT(快速访问工具栏)停靠
键 |
获取 |
设置 |
---|---|---|
UI_PKEY_QuickAccessToolbarDock |
UI_CONTROLDOCK GetQATDock() |
bool SetQATDock(UI_CONTROLDOCK dockState) |
示例:处理快速访问工具栏停靠
在 CMainFrame::OnFileNew()
中
LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { if (IsRibbonUI()) { if (GetQATDock() == UI_CONTROLDOCK_TOP) SetQATDock(UI_CONTROLDOCK_BOTTOM); else SetQATDock(UI_CONTROLDOCK_TOP); } return 0; }
Ribbon 可视性和大小
键 |
获取 |
设置 |
---|---|---|
UI_PKEY_Viewable |
bool IsRibbonHidden() |
HideRibbon(bool bHide = true) |
UI_PKEY_Minimized |
bool IsRibbonMinimized() |
MinimizeRibbon(bool bMinimize = true) |
示例:处理 Ribbon 大小
在 CMainFrame::OnFileNew()
中
LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { if (IsRibbonUI()) { MinimizeRibbon(!IsRibbonMinimized()); } return 0; }
Ribbon 持久性
从 Ribbon 切换到传统 UI 时
-
所有
CRibbonImpl
控件不受影响。仅限 Ribbon 的 UpdateUI ID 被销毁。要保留它们,请在 Ribbon 销毁前调用UIPersistElement(ID)
来保存它们的文本、启用状态和选中状态。 -
HRESULT
将 Ribbon 设置(Ribbon 大小和颜色、QAT 位置和内容)保存在CRibbonImpl::
SaveRibbonSettings()
内存区域中。HGLOBAL m_hgRibbonSettings
从传统 UI 切换到 Ribbon UI 时
-
如果
m_hgRibbonSettings
不为NULL
,则 Ribbon 设置已持久化,并通过HRESULT CRibbonImpl::RestoreRibbonSettings()
恢复。 -
新创建的 Ribbon 会为每个 CommandId 和相应的 Property Keys 调用
CRibbonImpl::UpdateProperty()
。CRibbonImpl
返回存储的值,Ribbon 将恢复到之前的状态。
对于会话之间的 Ribbon 设置持久性,助手类 CRibbonPersist
公开了将 Ribbon 设置保存和恢复到 HKCU 下的注册表项的成员。
-
CRibbonPersist(LPCWSTR sAppKey)
使用注册表项名称构造CRibbonPersist
对象。 -
LONG CRibbonPersist::Save(bool bRibbonUI, HGLOBAL hgSettings = NULL)
将一个bool
和一个HGLOBAL
保存到注册表中。 -
LONG CRibbonPersist::Restore(bool& bRibbonUI, HGLOBAL& hgSettings)
恢复保存的bool
和HGLOBAL
。
MTPad7 在 CMainFrame::OnCreate()
中使用它们来以最后一个 UI 和最后一个 Ribbon 设置重新打开……
// Ribbon UI state and settings restoration CRibbonPersist(lpcstrMTPadRegKey).Restore(bRibbonUI, m_hgRibbonSettings);
……这些设置已由 CMainFrame::OnClose()
保存。
bool bRibbonUI = IsRibbonUI(); if (bRibbonUI) SaveRibbonSettings(); CRibbonPersist(lpcstrMTPadRegKey).Save(bRibbonUI, m_hgRibbonSettings);
使用 Ribbon UI 重新审视 MTPad
MTPad 是最早的 WTL 示例之一,在本文中有详细描述。重新审视并没有改变任何现有功能。Ribbon Spinner 在预览模式下增加了直接页面选择。
项目设置已按初次体验的步骤 1 和 2 进行了更改。
7 个位图已添加到 MTPad.rc。
CMainFrame 更改
从 CRibbonFrameWindowImpl
派生。
class CMainFrame : public CRibbonFrameWindowImpl<CMainFrame> #ifndef _WIN32_WCE , public CPrintJobInfo #endif // _WIN32_WCE {
添加 ribbon 控件声明和控件映射。
// Ribbon Controls and map CRibbonRecentItemsCtrl<ID_RIBBON_RECENT_FILES> m_mru; CRibbonFontCtrl<ID_RIBBON_FONT> m_font; CRibbonSpinnerCtrl<ID_PAGE_SPINNER> m_spinner; CRibbonCommandCtrl<ID_GROUP_FONT> m_groupFont; BEGIN_RIBBON_CONTROL_MAP(CMainFrame) RIBBON_CONTROL(m_font) RIBBON_CONTROL(m_spinner) RIBBON_CONTROL(m_groupFont) RIBBON_CONTROL(m_mru) END_RIBBON_CONTROL_MAP()
处理 Ribbon 查询。
// Ribbon queries DWORD OnRibbonQueryFont(UINT /*nId*/, CHARFORMAT2& cf) { return m_view.GetDefaultCharFormat(cf); } bool OnRibbonQuerySpinnerValue(UINT /*nCmdID*/, REFPROPERTYKEY key, LONG* pVal) { if (key == UI_PKEY_DecimalValue) { *pVal = prev.m_nCurPage + 1; return true; } return false; }
处理 Ribbon 命令。
BEGIN_MSG_MAP(CMainFrame) // ... RIBBON_SPINNER_CONTROL_HANDLER(ID_PAGE_SPINNER, OnSpinnerPage) COMMAND_ID_HANDLER(ID_VIEW_RIBBON, OnViewRibbon)
在 OnCreate().
中初始化 UI 设置、Ribbon 控件并恢复 Ribbon 设置。
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { #ifndef _WIN32_WCE // create command bar window HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE); // atach menu m_CmdBar.AttachMenu(GetMenu()); // load command bar images m_CmdBar.LoadImages(IDR_MAINFRAME); // remove old menu SetMenu(NULL); bool bRibbonUI = RunTimeHelper::IsRibbonUIAvailable(); if (bRibbonUI) { // UI Setup and adjustments UIAddMenu(m_CmdBar.GetMenu(), true); UIRemoveUpdateElement(ID_FILE_MRU_FIRST); UIPersistElement(ID_GROUP_PAGE); // Ribbon Controls initialization m_groupFont.SetImage(UI_PKEY_SmallImage, GetCommandBarBitmap(ID_FORMAT_FONT)); m_spinner.SetValue(UI_PKEY_MinValue, 1); // Ribbon UI state and settings restoration CRibbonPersist(lpcstrMTPadRegKey).Restore(bRibbonUI, m_hgRibbonSettings); } else CMenuHandle(m_CmdBar.GetMenu()).DeleteMenu(ID_VIEW_RIBBON, MF_BYCOMMAND); // ... ShowRibbonUI(bRibbonUI); UISetCheck(ID_VIEW_RIBBON, bRibbonUI); return 0; }
在 OnClose().
中保存 Ribbon 设置。
LRESULT OnClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if (RunTimeHelper::IsRibbonUIAvailable()) { bool bRibbonUI = IsRibbonUI(); if (bRibbonUI) SaveRibbonSettings(); CRibbonPersist(lpcstrMTPadRegKey).Save(bRibbonUI, m_hgRibbonSettings); } bHandled = !m_view.QueryClose(); return 0; }
在 OnViewRibbon().
中实现 UI 切换。
LRESULT OnViewRibbon(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { ShowRibbonUI(!IsRibbonUI(), UI_MAKEAPPMODE(prev.IsWindow())); UISetCheck(ID_VIEW_RIBBON, IsRibbonUI()); return 0; }
在 OnContextMenu().
中实现 Ribbon 上下文弹出菜单。
LRESULT OnContextMenu(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if((HWND)wParam == m_view.m_hWnd) { CMenu menuContext; menuContext.LoadMenu(IDR_CONTEXTMENU); CMenuHandle menuPopup(menuContext.GetSubMenu(0)); #ifndef _WIN32_WCE if (IsRibbonUI()) TrackRibbonMenu(ID_CONTEXTMAP, lParam); else m_CmdBar.TrackPopupMenu(menuPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, LOWORD(lParam), HIWORD(lParam)); #else TrackPopupMenuEx(menuPopup, TPM_LEFTALIGN, LOWORD(lParam), HIWORD(lParam), m_hWndCECommandBar, NULL); #endif // _WIN32_WCE }
在 OnFilePrintPreview().
中设置 Ribbon 应用程序模式 1 和 Page Group 标签。
LRESULT OnFilePrintPreview(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { // ... m_spinner.SetValue(UI_PKEY_MaxValue, m_arrPages.GetSize(), true); CString sPageGroup = L"1 Page"; INT nPage = m_arrPages.GetSize(); if (nPage > 1) sPageGroup.Format(L"%d Pages", nPage); UISetText(ID_GROUP_PAGE, sPageGroup); if (IsRibbonUI()) { SetRibbonModes(UI_MAKEAPPMODE(1)); } return 0; }
在 OnPrintPreviewClose().
中设置 Ribbon 应用程序模式 0。
LRESULT OnPrintPreviewClose(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { if (IsRibbonUI()) SetRibbonModes(UI_MAKEAPPMODE(0)); }
在 OnPrintPreviewForward()
和 OnPrintPreviewBack().
中更新 Ribbon Spinner 值。
LRESULT OnPrintPreviewForward(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { prev.NextPage(); m_spinner.SetVal(prev.m_nCurPage + 1, true); return 0; } LRESULT OnPrintPreviewBack(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { prev.PrevPage(); m_spinner.SetVal(prev.m_nCurPage + 1, true); return 0; }
在新的 OnSpinnerPage()
成员中处理直接预览页面选择。
LRESULT OnSpinnerPage(WORD /*wID*/, LONG lVal, BOOL& /*bHandled*/) { prev.SetPage(lVal - 1); prev.Invalidate(); return 0; }
CEditView 更改
处理 Ribbon Font Control 消息。
BEGIN_MSG_MAP(CEditView) // ... #ifndef _WIN32_WCE // ... RIBBON_FONT_CONTROL_HANDLER(ID_RIBBON_FONT, OnRibbonFont)
预览悬停的字体,在预览取消时恢复到内部 m_font
成员,当字体通过 Ribbon 更改时更新它。
LRESULT OnRibbonFont(UI_EXECUTIONVERB verb, WORD /*wID*/, CHARFORMAT2* pcf, BOOL& /*bHandled*/) { if (verb == UI_EXECUTIONVERB_CANCELPREVIEW) SetFont(m_font); else SetDefaultCharFormat(*pcf); if (verb == UI_EXECUTIONVERB_EXECUTE) UpdateFont(*pcf); return 0; } void UpdateFont(CHARFORMAT2& cf) { CLogFont lf(m_font); if (cf.dwMask & CFM_SIZE) { lf.lfHeight = -MulDiv(cf.yHeight, CWindowDC(NULL).GetDeviceCaps(LOGPIXELSY), 1440); lf.lfWidth = 0; } if (cf.dwMask & CFM_BOLD) lf.lfWeight = cf.dwEffects & CFE_BOLD ? FW_BOLD : 0; if (cf.dwMask & CFM_WEIGHT) lf.lfWeight = cf.wWeight; if (cf.dwMask & CFM_ITALIC) lf.lfItalic = cf.dwEffects & CFE_ITALIC ? TRUE : FALSE; if (cf.dwMask & CFM_UNDERLINE) lf.lfUnderline = cf.dwEffects & CFE_UNDERLINE ? TRUE : FALSE; if (cf.dwMask & CFM_STRIKEOUT) lf.lfStrikeOut = cf.dwEffects & CFE_STRIKEOUT ? TRUE : FALSE; if (cf.dwMask & CFM_CHARSET) lf.lfCharSet = cf.bCharSet; if (cf.dwMask & CFM_FACE) { lf.lfPitchAndFamily = cf.bPitchAndFamily; SecureHelper::strcpyW_x(lf.lfFaceName, LF_FACESIZE, cf.szFaceName); } m_font.DeleteObject(); m_font.CreateFontIndirect(&lf); }
结论
鉴于 Ribbon UI 有些争议,双 UI 应用程序可能既能满足喜爱 Ribbon 的用户,也能满足不喜欢 Ribbon 的用户。使用 WTL 库,您可以获得两全其美。
祝好!
修订历史
- 2010 年 1 月 26 日:发布
- 2010 年 1 月 28 日:更新
- atlribbon.h:修复了多个实例问题。
- MTPad7:正确处理 Font Control UI_EXECUTIONVERB_CANCELPREVIEW。
- 文章:一些拼写错误和错误的链接
- 2010 年 2 月 19 日:更新
- 修复了 Win7 非 Aero 调整大小错误:RibbonUI.aspx?msg=3372329#xx3372329xx 并更新了下载。
- 2010 年 4 月 13 日:atlribbon.h 和 MTPad7 更新
- 更好地处理 UI 切换,
- 修复了 Vista Basic 切换到传统 UI 时的尺寸问题,
- 修复了 IUIImage 泄露,
- 上下文选项卡默认初始化为 UI_CONTEXTAVAILABILITY_NOTAVAILABLE。