Introduction
Windows 7 introduces a new Ribbon control, a system update makes it available to Vista and Windows Server 2008 platforms.
This article concentrates on a C++ application programer's point of view in the process of adapting existing or new applications to give the user a reversible choice between a Ribbon or traditional UI, using atlribbon.h and WTL 8.
References
The main documentation about the Windows 7 Ribbon is the MSDN Windows Ribbon Framework section.
You can find in CodeProject Michael Chourdakis's article : Windows 7 Ribbon: The Time Has Come, Your Win32 Application Will Change, and in Arik Poznanski's Blog detailed descriptions of the Ribbon features, and Windows Ribbon for WinForms to operate it in C# applications.
Prerequisites
To follow this article you need to have correctly installed on your computer:
Visual Studio or VCExpress 2008 with all service packs or Visual Studio 2010 Beta2.
The Windows 7 platform SDK: you need the uicc tool which is not part of Visual Studio 2010 Beta2 distribution.
WTL 8.0.
With VCExpress you need ATL 3 that can be found in the Windows® Server 2003 SP1 Platform SDK or ATL 7.1 as indicated in this post.
Obviously you should test on an updated Vista or a Windows 7 platform, you can compile and run everything under Vista, Windows 7 or XP.
Download MTPad7Exe.zip and run MTPad.exe.
On an updated Vista or a Windows 7 platform MTPad opens with a Ribbon UI.
Open some text files, check the font selection preview feature, try the Print Preview mode, use the recent files in the Application Menu, open some new windows.
Uncheck the Ribbon checkbox to switch to traditional UI, use the View menu Ribbon entry to switch back to ribbon UI in Edit or Preview mode.
Change the layout settings using the built-in dropdown at right of the QAT (Quick Access Toolbar), then switch back and forth to traditional UI and notice that when you switch back to Ribbon UI the ribbon layout settings are persisted.
Close the last window in traditional UI and reopen MTPad. It reopens traditional.
Using atlribbon.h, in some minutes you can build a dual (ribbon and traditional) user selectable UI on top of a traditional UI WTL application.
This first experience performs the necessary steps, with many execution details, as they apply to any Ribbon UI application.
Step 0: Create a WTL Application with minimal functionality
If you use Visual Studio 2010 Beta2 or for some reason have not installed the WTL AppWizard, download FirstRibbon.zip , unzip it and open FirstRibbon.vcproj.
Otherwise with the WTL AppWizard, create a FirstRibbon WTL application with SDI and no cpp...
... default User Interface Features and Rich Edit view window.
To handle the Edit commands add CRichEditCommands
inheritance and message map chaining to CFirstRibbonView
...
#pragma once
class CFirstRibbonView :
public CWindowImpl<CFirstRibbonView, CRichEditCtrl>,
public CRichEditCommands<CFirstRibbonView>
{
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)
END_MSG_MAP()
};
… and chain the CMainFrame
commands to CFirstRibbonView
:
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)
CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
END_MSG_MAP()
Compile (any compilation error here means that your development environment is not correctly set up), run and check the edit functions with the toolbar buttons or the Edit menu.
Step 1: Adapt the C++ code to host a Ribbon UI
Change the constants in stdafx.h to access the SDK Windows 7 features.
#define WINVER 0x0601 // 1
#define _WIN32_WINNT 0x0601 // 1
#define _WIN32_IE 0x0700 // 1
#define _RICHEDIT_VER 0x0200
Download atlribbon.zip, extract atlribbon.h to your <FirstRibbon> directory, and include it in FirstRibbon.cpp.
#include "stdafx.h"
#include <atlframe.h>
#include <atlctrls.h>
#include <atldlgs.h>
#include <atlctrlw.h>
#include "atlribbon.h"
#include "resource.h"
Derive CMainFrame
from WTL::CRibbonFrameWindowImpl
; as CRibbonFrameWindowImpl
derives from WTL::CRibbonUpdateUI
, remove the CUpdateUI
parent in the class definition and the message map and chain only CRibbonFrameWindowImpl<CMainFrame>
in the message map.
class CMainFrame :
public CRibbonFrameWindowImpl<CMainFrame>,
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)
CHAIN_MSG_MAP(CRibbonFrameWindowImpl<CMainFrame>)
END_MSG_MAP()
To run on pre-Vista or non updated Vista platforms, in Project->FirstRibbon Properties->Linker->Input set propsys.dll and dwmapi.dll as Delay Loaded.
Compile to check that the step is completed.
Step 2: Create a ribbon ressource and embed it in the application
Add to your project a new xml file (for VCExpress a new C++ header file), name it FirstRibbonRibbon.xml and paste what follows into it:
<!-- 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>
In the VC Solution Explorer window right-click on FirstRibbonRibbon.xml and select Properties in the popup menu. Go to Custom Build Step->General and paste in the input fields:
CommandLine: uicc FirstRibbonRibbon.xml FirstRibbonRibbon.bml /header:FirstRibbonRibbon.h /res:FirstRibbonRibbon.rc
Description: Compiling FirstRibbonRibbon.xml
Outputs: FirstRibbonRibbon.bml;FirstRibbonRibbon.rc;FirstRibbonRibbon.h
Click OK to validate.
In the same VC Solution Explorer window right-click again on FirstRibbonRibbon.xml and select Compile: the output should be
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 ==========
Add the uicc generated files to the application FirstRibbon.rc file:
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "atlres.h"
#include "FirstRibbonRibbon.h"
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""atlres.h""\r\n"
"#include ""FirstRibbonRibbon.h""\r\0"
END
3 TEXTINCLUDE
BEGIN
"#include ""FirstRibbonRibbon.rc\0"
END
#endif // APSTUDIO_INVOKED
#ifndef APSTUDIO_INVOKED
#include "FirstRibbonRibbon.rc"
#endif // not APSTUDIO_INVOKED
#include "stdafx.h"
#include <atlframe.h>
#include <atlctrls.h>
#include <atldlgs.h>
#include <atlctrlw.h>
#include <atlmisc.h>
#include "atlribbon.h"
#include "FirstRibbonRibbon.h"
#include "resource.h"
Step 3: Operate the Ribbon
Create a Ribbon command in the application View menu:
With Visual Studio in the Resource View window edit the IDR_MAINFRAME menu and add a &Ribbon entry with ID_VIEW_RIBBON id on top of the View menu.
Edit the String Table to add an ID_VIEW_RIBBON string with Show or hide the ribbon\nToggle Ribbon as content.
With VCExpress in the VC Solution Explorer window right-click on FirstRibbon.rc and select View Code in the popup menu. Edit the POPUP "&View" block, and the STRINGTABLE block with the ID_VIEW_xxx strings.
IDR_MAINFRAME MENU
BEGIN
POPUP "&View"
BEGIN
MENUITEM "&Ribbon", ID_VIEW_RIBBON
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"
END
Add a command handler for ID_VIEW_RIBBON
in the CMainFrame
message map ...
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)
CHAIN_COMMANDS_MEMBER(m_view)
… and define it at the end of the class definition.
LRESULT OnViewRibbon(WORD , WORD ,
HWND , BOOL& )
{
ShowRibbonUI(!IsRibbonUI());
UISetCheck(ID_VIEW_RIBBON, IsRibbonUI());
return 0;
}
To remove the ID_VIEW_RIBBON
command when Ribbon UI is not supported, and otherwise set the Ribbon Command Labels with the menu texts, add to CMainFrame::OnCreate()
.
LRESULT OnCreate(UINT , WPARAM , LPARAM , BOOL& )
{
HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL,
ATL_SIMPLE_CMDBAR_PANE_STYLE);
m_CmdBar.AttachMenu(GetMenu());
m_CmdBar.LoadImages(IDR_MAINFRAME);
SetMenu(NULL);
bool bRibbonUI = RunTimeHelper::IsRibbonUIAvailable();
if (bRibbonUI)
UIAddMenu(m_CmdBar.GetMenu(), true);
else
CMenuHandle(m_CmdBar.GetMenu()).DeleteMenu(ID_VIEW_RIBBON, MF_BYCOMMAND);
HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE,
ATL_SIMPLE_TOOLBAR_PANE_STYLE);
Compile and run, see no changes, click Ribbon in the View menu:
Yes you can!
It's time to play around, change ribbon settings, switch UI modes, check commands, see ribbon tooltips, try running under XP, enjoy ...
Additional Step: contextual UI startup and enabling of edit commands
At the end of CMainFrame::OnCreate()
...
ShowRibbonUI(bRibbonUI);
UISetCheck(ID_VIEW_RIBBON, bRibbonUI);
return 0;
}
… and in CMainFrame::OnIdle()
.
virtual BOOL OnIdle()
{
UIEnable(ID_EDIT_UNDO, m_view.CanUndo());
UIEnable(ID_EDIT_PASTE, m_view.CanPaste(CF_TEXT));
UIEnable(ID_EDIT_CUT, m_view.CanCut());
UIEnable(ID_EDIT_COPY, m_view.CanCopy());
UIUpdateToolBar();
return FALSE;
}
Compile, run and check that FirstRibbon starts in Ribbon UI when supported and that in both UI the edit commands are disabled when irrelevant.
This guide describes with examples the implementation of a Ribbon UI in a CMainFrame
frame window deriving from CRibbonFrameWindowImplBase
such as CRibbonFrameWindowImpl
or CRibbonMDIFrameWindowImpl
.
Review of our First Experience
class CMainFrame :
public CRibbonFrameWindowImpl<CMainFrame>,
public CMessageFilter, public CIdleHandler
{
LRESULT OnCreate(UINT , WPARAM , LPARAM , BOOL& )
{
bool bRibbonUI = RunTimeHelper::IsRibbonUIAvailable();
if (bRibbonUI)
UIAddMenu(m_CmdBar.GetMenu(), true);
else
CMenuHandle(m_CmdBar.GetMenu()).DeleteMenu(ID_VIEW_RIBBON, MF_BYCOMMAND);
ShowRibbonUI(bRibbonUI);
UISetCheck(ID_VIEW_RIBBON, bRibbonUI);
LRESULT OnViewRibbon(WORD , WORD ,
HWND , BOOL& )
{
ShowRibbonUI(!IsRibbonUI());
UISetCheck(ID_VIEW_RIBBON, IsRibbonUI());
return 0;
}
CRibbonFrameWindowImpl<CMainFrame>
inherits from WTL::CFrameWindowImpl<CMainFrame>
which implements the traditional UI , and from WTL::RibbonUI::CRibbonImpl<CMainFrame>
which implements the Ribbon UI. So CMainFrame
is a dual UI window, in runtime environments which support the Ribbon UI.
bool WTL::RunTimeHelper::IsRibbonUIAvailable()
performs a runtime test and returns true if running under updated Vista or Win7. If false, the View->Ribbon menu entry is removed so that the Ribbon UI code is never called.
bool CRibbonImpl::IsRibbonUI()
returns true
if the Ribbon UI is currently shown, false otherwise
CRibbonImpl
inherits from WTL::CRibbonUpdateUI<CMainFrame>
which extends WTL::CAutoUpdateUI<CMainFrame>
described in this article and handles an additional UPDUI_RIBBON
type.
bool WTL::CAutoUpdateUI<CMainFrame>::UIAddMenu(HMENU hm, bool bSetText)
adds all (non popup) menu items of the hm menu to it's UPDATE_UI_MAP and calls UISetText(ID, text)
with each menu item ID and text.
bool WTL::CRibbonFrameWindowImplBase<CMainFrame>::ShowRibbonUI(bool bShow, INT32 imodes = UI_MAKEAPPMODE(0), LPCWSTR sResName = L"APPLICATION_RIBBON")
returns the Ribbon UI activity state after the call: bShow
is the requested state, imodes
is the requested Ribbon Modes and defaults to Mode 0, sResName
is the string ID of the UIFILE resource in the Ribbon resource file.
ShowRibbonUI(true) operation
ShowRibbonUI(true)
hides the Rebar containing the traditional UI CommandBar and Toolbar, and calls CRibbonImpl::CreateRibbon(sResName)
which performs the steps described here.
The newly created Ribbon calls CRibbonImpl::OnCreateUICommand()
for each ID in it's markup file. Each ID is added to the UPDATE_UI_MAP with UPDUI_RIBBON
type.
So the common UI elements have both UPDUI_RIBBON and -for instance- UPDUI_MENUPOPUP types in the UPDATE_UI_MAP. Ribbon elements label text, enabled and checked states can be managed through overrides of the usual UISetText()
, UISetCheck()
and UIEnable()
calls.
ShowRibbonUI(false) operation
ShowRibbonUI(false)
calls CRibbonImpl::DestroyRibbon()
which in turn calls IUIFramework::Destroy().
The Ribbon calls CRibbonImpl::OnDestroyUICommand()
for each ID in it's markup file. The UPDUI_RIBBON
type is removed from the UPDATE_UI_MAP for that ID.
The ID itself is removed from the UPDATE_UI_MAP if it has no other UPDUI_type, which can be prevented by a call to CRibbonUpdateUI::UIPersistElement(UINT nID, true)
.
ShowRibbonUI(false)
then restores the Rebar containing the traditional UI CommandBar and Toolbar.
Default Ribbon UI behavior
When the Ribbon is active it calls CRibbonImpl::UpdateProperty()
with an element ID and a Property Key reference.
This table summarizes the default CRibbonImpl
behavior.
Key
| Setting
| Overrideable Callback
| Default processing
|
---|
UI_PKEY_Label
| UISetText(ID, LPCWSTR)
| LPCWSTR OnRibbonQueryText(ID, key)
| returns UIGetText(ID)
|
UI_PKEY_Enabled
| UIEnable(ID, bool)
| bool OnRibbonQueryState(ID, key)
| returns UIGetState(ID) & ~UPDUI_DISABLED
|
UI_PKEY__BooleanValue
| UISetCheck(ID, bool)
| bool OnRibbonQueryState(ID, key)
| returns UIGetState(ID) & UPDUI_CHECKED
|
UI_PKEY_LabelDescription UI_PKEY_TooltipDescription
| | LPCWSTR OnRibbonQueryText(ID, key)
| part of the ressource string with ID preceding '\n' (or NULL)
|
UI_PKEY_TooltipTitle
| | LPCWSTR OnRibbonQueryText(ID, key)
| part of the ressource string with ID following '\n' (or NULL)
|
UI_PKEY_Keytip
| | LPCWSTR OnRibbonQueryText(ID, key)
| 1 character substring of the ressource string with ID following '&' (or NULL)
|
UI_PKEY_SmallImage
| | HBITMAP OnRibbonQueryImage(ID, key)
| HBITMAP of ID in m_CmdBar.m_arrVistaBitmap or HBITMAP of the ressource bitmap with ID (or NULL)
|
UI_PKEY_LargeImage UI_PKEY_SmallHighContrastImage UI_PKEY_LargeHighContrastImage
| | HBITMAP OnRibbonQueryImage(ID, key)
| HBITMAP of the ressource bitmap with ID (or NULL)
|
When the user acts on a Ribbon UI element CRibbonImpl::Execute
is called by the Ribbon with an element ID a UI_EXECUTIONVERB and a Property Key reference.
CRibbonImpl::Execute()
calls the overrideable OnRibbonCommandExecute(UINT32 uCmdID, ...)
.
If not overridden CRibbonImpl::OnRibbonCommandExecute()
posts a WM_COMMAND
message with WPARAM
set to uCmdID.
Practical summary
Any item of a Menu resource (inserted in the UI_MAP with UIAddMenu(IdMenu, true)
) with a same id two parts string resource as "Create a new document\nNew" gets the menu name, resource string tooltip title and tooltip body in the Ribbon.
For any Ribbon defined Command UI_PKEY_Label
, UI_PKEY_Enabled
and UI_PKEY_BooleanValue
are handled through UISetText()
, UIEnable()
and UISetCheck()
.
WM_COMMAND
messages with the Ribbon element ID are posted when the user clicks or checks/unchecks a Ribbon element.
That's how our FirstRibbon Ribbon UI could work at once.
Example : add an Undo Ribbon Button with default processing
ID_EDIT_UNDO
is present in the IDR_MAINFRAME
menu and has a string resource in FirstRibbon.rc, add it to the Commands declaration and the GroupClipboard Group in FirstRibbonRibbon.xml
.
<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>
Download FirstRibbonBitmaps.zip extract Undo.bmp to FirstRibbon\res and add it to FirstRibbon.rc with ID_EDIT_UNDO
id.
IDR_MAINFRAME BITMAP "res\\Toolbar.bmp"
ID_EDIT_UNDO BITMAP "res\\Undo.bmp"
CRibbonImpl control classes and RIBBON_CONTROL_MAP() macros
The CRibbonImpl::CRibbonXXXCtrl
control classes handle the Windows Ribbon Framework Control Library supported controls.
CRibbonImpl
and all CRibbonXXXCtrl classes implement the WTL::RibbonUI::ICtrl
abstract interface. The BEGIN_RIBBON_CONTROL_MAP()
, RIBBON_CONTROL()
and END_RIBBON_CONTROL_MAP()
macros build a ICtrl& GetRibbonCtrl(UINT ID)
function which returns a reference to a control's ICtrl
if it's ID is in the map or to CRibbonImpl::ICtrl
otherwise.
Implementation of a control requires 3 steps:
Identify and insert the control in a Group with adequate properties in the Ribbon markup.
<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>
Instanciate a CRibbonImpl::CRibbonXXXCtrl with matching ID CMainFrame member.
CRibbonColorCtrl<ID_TEST_COLOR> m_color;
Insert the member in the RIBBON_CONTROL_MAP.
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_color)
...
END_RIBBON_CONTROL_MAP()
CRibbonImpl control classes common API
All CMainFrame::CRibbonImpl
control classes derive from WTL::RibbonUI::CtrlImpl<ID, CMainFrame>
and expose:
HRESULT Invalidate()
and HRESULT Invalidate(key, UI_INVALIDATIONS flags = UI_INVALIDATIONS_PROPERTY)
which Invalidates all (or one) Property Key for this ID.
HRESULT SetText(key&, LPCWSTR sText, bool bUpdate = false)
: copies sText
to an internal string associated to key
and if bUpdate
is true
calls Invalidate(key)
.
At startup or when a key is uninitialized or invalidated the Ribbon calls CRibbonImpl::UpdateProperty()
which uses the RIBBON_CONTROL_MAP to select the affected control class instance and call it's relevant member function (ie OnGetText()
for text keys).
If the Condition for callback is met:
value for this key is considered invalid,
the relevant CRibbonImpl overrideable callback is called,
If not overridden Default processing is performed ,
the return value is stored.
- The stored value is returned to the Ribbon.
CRibbonImpl control classes common Property Keys
Key
| Setting
| CRibbonImpl overrideable callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_LabelDescription UI_PKEY_TooltipDescription
| SetText(key, LPCWSTR, bool bUpdate = false)
| LPCWSTR OnRibbonQueryText(nCmdID, key)
| Current string value is empty
| part of the ressource string with ID preceding a '\n' (or the whole string)
|
UI_PKEY_TooltipTitle
| SetText(key, LPCWSTR, bool bUpdate = false)
| LPCWSTR OnRibbonQueryText(nCmdID, key)
| Current string value is empty
| part of the ressource string with ID following a '\n' (or NULL)
|
UI_PKEY_Keytip
| SetText(key, LPCWSTR, bool bUpdate = false)
| LPCWSTR OnRibbonQueryText(nCmdID, key)
| Current string value is empty
| 1 character of the ressource string with ID following a '&' if found or a '\n' (or NULL)
|
The default processing is the same as CRibbonImpl
, but values can be set and are persistent when the user switches UI.
Preliminary step for the examples
Some of the examples use the status bar to output user actions on the Ribbon. To enable their usage of UISetText(ID_DEFAULT_PANE, someText)
, define _ATL_USE_CSTRING_FLOAT
in stdafx.h ...
#define _RICHEDIT_VER 0x0200
#define _ATL_USE_CSTRING_FLOAT
... and add to CMainFrame
calls to UIUpdateStatusBar()
in OnIdle()
and to UIAddStatusBar()
in OnCreate().
virtual BOOL OnIdle()
{
UIUpdateToolBar();
UIUpdateStatusBar();
return FALSE;
}
LRESULT OnCreate(UINT , WPARAM , LPARAM , BOOL& )
{
CreateSimpleStatusBar();
UIAddStatusBar(m_hWndStatusBar);
Ribbon Single Control classes
The CRibbonImpl
single control classes handle all Ribbon Controls except Galleries, Combos and Recent Items.
CRibbonCommandCtrl
Declaration in CRibbonImpl
derived class: CRibbonCommandCtrl<ID> m_Ctrl;
User action message map macro: COMMAND_ID_HANDLER(wID, OnCommand)
Handler prototype: LRESULT OnCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled)
CRibbonImpl::CRibbonCommandCtrl
operates on Ribbon Buttons, Drop-Down Buttons, Toggle Buttons, Groups, Menu Groups, Tabs and Tab Groups to implement a specific handling of the CRibbonImpl
control classes common Property Keys and if relevant:
Key
| Setting
| CRibbonImpl overrideable callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_SmallImage
| SetImage(key, HBITMAP, bool bUpdate = false)
| HBITMAP OnRibbonQueryImage(nCmdID, key)
| Current HBITMAP is NULL
| HBITMAP of ID in m_CmdBar.m_arrVistaBitmap or HBITMAP of the ressource bitmap with ID (or NULL)
|
UI_PKEY_LargeImage UI_PKEY_SmallHighContrastImage UI_PKEY_LargeHighContrastImage
| SetImage(key, HBITMAP, bool bUpdate = false)
| HBITMAP OnRibbonQueryImage(nCmdID, key)
| Current HBITMAP is NULL
| HBITMAP of the ressource bitmap with ID (or NULL)
|
Example : handle a Ribbon Group Control
In CMainFrame
declare a CRibbonCommandCtrl
with the Ribbon GroupClipboard Control ID and insert it in the RIBBON_CONTROL_MAP,
CRibbonCommandCtrl<ID_GROUP_CLIPBOARD> m_clipboard;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_clipboard)
Set some values in CMainFrame::OnCreate()
LRESULT OnCreate(UINT , WPARAM , LPARAM , BOOL& )
{
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");
When the Ribbon is more populated if the GroupClipboard collapses to Button:
CRibbonColorCtrl
- Declaration in
CRibbonImpl
derived class: CRibbonColorCtrl<wID> m_Ctrl;
User action message map macro: RIBBON_COLOR_CONTROL_HANDLER(wID, OnRibbonColorCtrl)
Handler prototype: LRESULT OnRibbonColorCtrl(UI_EXECUTIONVERB verb, WORD wID, COLORREF color, BOOL& bHandled);
CRibbonImpl::CRibbonColorCtrl
operates on Ribbon DropDownColorPicker Controls and handles:
Key
| Setting
| Overrideable Callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_Color
| m_Ctrl.SetColor(COLORREF color, bool bUpdate = false)
| COLORREF OnRibbonQueryColor(ID)
| Current color is MAGENTA
| returns 0x800080 MAGENTA (no change)
|
UI_PKEY_ColorType
| m_Ctrl.SetColorType( UI_SWATCHCOLORTYPE type, bool bUpdate = false)
| | | UI_SWATCHCOLORTYPE_NOCOLOR if not set by SetColorType() or 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, bool bUpdate = false)
| LPCWSTR OnRibbonQueryColorLabel(ID, key)
| Current string value is empty
| Returns NULL (no change)
|
UI_PKEY_ThemeColors UI_PKEY_StandardColors
| m_Ctrl.SetColorArray(key, COLORREF* pColor, bool bUpdate = false)
| COLORREF* OnRibbonQueryColorArray(ID, key)
| Current color array is empty
| Returns NULL (no change)
|
UI_PKEY_ThemeColorsTooltips UI_PKEY_StandardColorsTooltips
| m_Ctrl.SetColorTooltips(key, LPCWSTR*, bool bUpdate = false)
| LPCWSTR* OnRibbonQueryColorTooltips(ID, key)
| Current string array is empty
| Returns NULL (no change)
|
The COLORREF*
returned by OnRibbonQueryColorArray()
or passed to SetColorArray()
is the address of element 0 of an array of COLORREF
ending with 0x800080
MAGENTA.
The LPCWSTR*
returned by OnRibbonQueryColorTooltips()
or passed to SetColorTooltips()
is the address of element 0 of an array of LPCWSTR
ending with NULL.
Example : add and handle a Color Control
In the markup file declare group and command IDs and declare a DropDownColorPicker in a Tab
<!--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>
In CMainFrame
declare a CRibbonColorCtrl
with the same ID and insert it in the RIBBON_CONTROL_MAP.
CRibbonColorCtrl<ID_TEST_COLOR> m_color;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_color)
END_RIBBON_CONTROL_MAP()
CRibbonImpl
calls OnRibbonQueryColor()
when the Ribbon needs to know which color it should display as selected. CRibbonImpl::OnRibbonQueryColor()
returns 0x800080
(MAGENTA) so override it to return the current background color.
COLORREF OnRibbonQueryColor(UINT nID)
{
COLORREF color = m_view.SetBackgroundColor();
m_view.SetBackgroundColor(color);
return color;
}
When the user selects or hovers a color in the control CRibbonImpl
posts a WM_COMMAND
message with HIWORD(wParam)
set to the UI_EXECUTIONVERB
, LOWORD(wParam)
set to the control ID, and lParam
set to the selected COLORREF
.
Handle this message in CFirstRibbonView
with the RIBBON_COLOR_CONTROL_HANDLER
macro.
BEGIN_MSG_MAP(CFirstRibbonView)
CHAIN_MSG_MAP_ALT(CRichEditCommands<CFirstRibbonView>, 1)
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;
}
No image is associated to the Color Control button. Extract DDCP.bmp from FirstRibbonBitmaps.zip to FirstRibbon\res and add it to FirstRibbon.rc with ID_TEST_COLOR
id.
ID_TEST_COLOR BITMAP "res\\DDCP.bmp"
CRibbonFontCtrl
Declaration in CRibbonImpl
derived class: CRibbonFontCtrl<wID> m_Ctrl;
User action message map macro: RIBBON_FONT_CONTROL_HANDLER(wID, OnRibbonFontCtrl)
Handler prototype: LRESULT OnRibbonFontCtrl(UI_EXECUTIONVERB verb, WORD wID, CHARFORMAT2* pcf, BOOL& bHandled);
CRibbonImpl::CRibbonFontCtrl
operates on Ribbon Font Controls and handles:
Key
| Setting
| Overrideable Callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_FontProperties
| Invalidate(UI_PKEY_FontProperties)
| bool OnRibbonQueryFont(ID, CHARFORMAT2&)
| always
| returns false (no change)
|
Example : add and handle a Font Control
In the markup file declare group and command IDs and declare a FontControl in a Tab
<!--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>
In CMainFrame
declare a CRibbonFontCtrl
with the same ID and insert it in the RIBBON_CONTROL_MAP.
CRibbonFontCtrl<ID_TEST_FONT> m_font;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_font)
CRibbonImpl
calls OnRibbonQueryFont()
when the Ribbon needs to know which font features it should display as selected. CRibbonImpl::OnRibbonQueryFont()
returns false
(no change) so override it to return the current font's CHARFORMAT2
.
bool OnRibbonQueryFont(UINT , CHARFORMAT2& cf)
{
m_view.GetDefaultCharFormat(cf);
return true;
}
When the user selects a feature in the control CRibbonImpl
posts a WM_COMMAND
message with HIWORD(wParam)
set to the UI_EXECUTIONVERB
, LOWORD(wParam)
set to the control ID, and lParam set to a CHARFORMAT2*
.
Handle this message in CFirstRibbonView
with the RIBBON_FONT_CONTROL_HANDLER
macro.
BEGIN_MSG_MAP(CFirstRibbonView)
CHAIN_MSG_MAP_ALT(CRichEditCommands<CFirstRibbonView>, 1)
RIBBON_FONT_CONTROL_HANDLER(ID_TEST_FONT, OnRibbonFont)
END_MSG_MAP()
LRESULT OnRibbonFont(UI_EXECUTIONVERB verb, WORD , CHARFORMAT2* pcf, BOOL& )
{
SetDefaultCharFormat(*pcf);
return 0;
}
CRibbonSpinnerCtrl, CRibbonFloatSpinnerCtrl
Declarations in CRibbonImpl
derived class:
CRibbonSpinnerCtrl<wID> m_Ctrl;
CRibbonFloatSpinnerCtrl<wID> m_Ctrl;
User action message map macros:
RIBBON_SPINNER_CONTROL_HANDLER(wID, OnRibbonSpinnerCtrl)
RIBBON_FLOATSPINNER_CONTROL_HANDLER(wID, OnRibbonFloatSpinnerCtrl)
Handler prototypes:
LRESULT OnRibbonSpinnerCtrl(WORD wID, LONG lVal, BOOL& bHandled);
LRESULT OnRibbonFloatSpinnerCtrl(WORD wID, DOUBLE* pdVal, BOOL& bHandled);
CRibbonImpl:: CRibbonSpinnerCtrl
and CRibbonImpl:: CRibbonFloatSpinnerCtrl
both operate on Ribbon Spinner Controls and handle V beeing a LONG
(CRibbonSpinnerCtrl
) or a DOUBLE
(CRibbonFloatSpinnerCtrl
):
Key
| Setting
| Overrideable Callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_DecimalPlaces
| SetDecimalPlaces(V vPlaces, bool bUpdate = false)
| bool OnRibbonQuerySpinnerValue(ID, key, LONG*) orOnRibbonQueryFloatSpinnerValue(ID, key, DOUBLE*)
| always
| returns false (no change)
|
UI_PKEY_DecimalValue
| SetVal(V vVal, bool bUpdate = false)
| bool OnRibbonQuerySpinnerValue(ID, key, LONG*) orOnRibbonQueryFloatSpinnerValue(ID, key, DOUBLE*)
| always
| returns false (no change)
|
UI_PKEY_MinValue
| SetMin(V vMin, bool bUpdate = false)
| bool OnRibbonQuerySpinnerValue(ID, key, LONG*) orOnRibbonQueryFloatSpinnerValue(ID, key, DOUBLE*)
| always
| returns false (no change)
|
UI_PKEY_MaxValue
| SetMax(V vMax, bool bUpdate = false)
| bool OnRibbonQuerySpinnerValue(ID, key, LONG*) orOnRibbonQueryFloatSpinnerValue(ID, key, DOUBLE*)
| always
| returns false (no change)
|
UI_PKEY_Increment
| SetIncrement(V vIncrement, bool bUpdate = false)
| bool OnRibbonQuerySpinnerValue(ID, key, LONG*) orOnRibbonQueryFloatSpinnerValue(ID, key, DOUBLE*)
| always
| returns false (no change)
|
UI_PKEY_FormatString
| SetFormatString(LPCWSTR sFormat, bool bUpdate = false)
| LPCWSTR OnRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key)
| Current string is empty
| Returns NULL (no change)
|
UI_PKEY_RepresentativeString
| SetRepresentativeString(LPCWSTR sRepresentative, bool bUpdate = false)
| LPCWSTR OnRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key)
| Current string is empty
| Returns NULL (no change)
|
Example : add and handle Spinner Controls
In the markup file declare group and command IDs and declare the Spinners in a Tab
<!--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>
In CMainFrame
declare a CRibbonSpinnerCtrl
and a CRibbonFloatSpinnerCtrl
with the matching Ids and insert them in the RIBBON_CONTROL_MAP.
CRibbonSpinnerCtrl<ID_TEST_SPINNER> m_spinner;
CRibbonFloatSpinnerCtrl<ID_TEST_FLOATSPINNER> m_spinnerFloat;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_spinner)
RIBBON_CONTROL(m_spinnerFloat)
When the user sets a value in a Spinner:
For a CRibbonSpinnerCtrl
, CRibbonImpl
posts a WM_COMMAND
message with HIWORD(wParam)
set to 0, LOWORD(wParam)
set to the control ID, and lParam
set to the LONG value.
For a CRibbonFloatSpinnerCtrl, CRibbonImpl posts a WM_COMMAND message with the same wParam
, and lParam
set to a DOUBLE*
pointing to the value.
… Handle these messages in CMainFrame
with the RIBBON_SPINNER_CONTROL_HANDLER
and RIBBON_FLOATSPINNER_CONTROL_HANDLER
macros.
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;
}
Example : Three ways to initialize Ribbon Controls
The default initial values for CRibbonImpl::CRibbonSpinnerCtrl
and CRibbonImpl::CRibbonSpinnerFloatCtrl
are a range of 0 to 100, an increment of 1, a start value of 1, 0 decimal places (1 for CRibbonSpinnerFloatCtrl).
Initialize the controls to:
ID_TEST_FLOATSPINNER a range of 10 to 20, an increment of .25, a start value of 14.75, 2 decimal places, and adjust the width for 4 digits and 1 decimal point.
ID_TEST_SPINNER the same width and a start value of 51.
using one of the following methods:
- The easiest, initialize in
CMainFrame::OnCreate()
:
LRESULT OnCreate(UINT , WPARAM , LPARAM , BOOL& )
{
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);
}
- If the values may change at runtime, override the relevant
CRibbonImpl::OnRibbonQueryxxx()
members:
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);
}
- The most object oriented, specialize the constructors (not available for simple and toolbar gallery and combo controls):
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);
}
- Any mix of 1 to 3 which meets your liking or needs.
Ribbon Collection Controls
The CRibbonImpl
collection control classes handle Ribbon Galleries, Combos and Recent Items Ribbon Controls.
The full classes, CRibbonItemGalleryCtrl
, CRibbonCommandGalleryCtrl
and CRibbonComboCtrl
handle categories, dynamic resizing and item configuration.
The simple classes CRibbonSimpleGalleryCtrl
, CRibbonSimpleComboCtrl
and CRibbonToolBarGalleryCtrl
don't handle categories and are configured once at Ribbon Creation.
Ribbon Full Collection classes
Common Property Keys for Full Collection classes:
Key
| Setting
| Overrideable Callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_ItemsSource
| InvalidateItems() or Resize(size_t size, bool bUpdate = false)
| | | Item keys are queried for all items.
|
UI_PKEY_Categories
| InvalidateCategories()
| | | UI_PKEY_Label is queried for each category and UI_PKEY_CategoryId for each item.
|
Category Key
| Setting
| Overrideable Callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_Label
| SetCategoryText(UINT uCat, LPCWSTR sText, bool bUpdate = false)
| LPCWSTR OnRibbonQueryCategoryText(ID, UINT32 uCat)
| Current string is empty
| Returns « Category »
|
Item Key
| Setting
| Overrideable Callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_CategoryId
| SetItemCategory(UINT uItem, UINT uCat, bool bUpdate = false)
| UINT32 OnRibbonQueryItemCategory(ID, UINT32 uItem)
| Current category index is UI_COLLECTION_INVALIDINDEX
| returns 0
|
Unlike the single controls the collection controls have a uItems
, and an optionnal uCategories
template parameter.
CRibbonItemGalleryCtrl
Declaration in CRibbonImpl
derived class: CRibbonItemGalleryCtrl<wID, uItems, uCategories = 0>
m_Ctrl: uItems
is the maximum number of items in the gallery, uCategory
the number of categories.
User action message map macro: RIBBON_GALLERY_CONTROL_HANDLER(wID, OnRibbonGalleryCtrl)
Handler prototype: LRESULT OnRibbonGalleryCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled)
. If uSel
is UI_COLLECTION_INVALIDINDEX
the Button part of a SplitButton Gallery has been clicked.
CRibbonImpl::CRibbonItemGalleryCtrl
operates on Ribbon Drop-Down Gallery, In-Ribbon Gallery, Split Button Gallery which do not have the Type = "Commands" property.
Key
| Setting
| Overrideable Callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_SelectedItem
| Select(UINT uItem, bool bUpdate = false)
| bool OnRibbonQuerySelectedItem(ID, UINT32& uSel)
| Current selection is UI_COLLECTION_INVALIDINDEX
| Returns false (no change)
|
Item Key
| Setting
| Overrideable Callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_Label
| SetItemText(UINT uItem, LPCWSTR sText, bool bUpdate = false)
| LPCWSTR OnRibbonQueryItemText(ID, UINT32 uItem)
| Current string is empty
| Returns resource string with ID + 1 + uItem (or NULL)
|
UI_PKEY_ItemImage
| SetItemImage(UINT uIndex, HBITMAP hbm, bool bUpdate = false)
| HBITMAP OnRibbonQueryItemImage(ID, UINT32 uItem)
| Current HBITMAP is NULL
| HBITMAP of the ressource bitmap with ID + 1 + uItem (or NULL)
|
Example : add and handle a Item InRibbonGallery Control
In the markup file declare new Tab, Group and ID_TEST_GALLERY
Command ID and declare a InRibbonGallery in a Group:
<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>
Create the IDs for the Gallery items following ID_TEST_GALLERY in resource.h to enable default loading of the item bitmaps.
#define ID_DIAMOND 201 // == ID_TEST_GALLERY + 1
#define ID_RECT 202
#define ID_ROUNDRECT 203
#define ID_ELLIPSE 204
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 205
Extract the 4 shape bitmaps from FirstRibbonBitmaps.zip to FirstRibbon\res, add the bitmaps and their title strings to FirstRibbon.rc with the proper IDs.
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
In CMainFrame
declare a CRibbonItemGalleryCtrl
with ID_TEST_GALLERY
, 4 items, 2 categories and insert it in the RIBBON_CONTROL_MAP.
CRibbonItemGalleryCtrl<ID_TEST_GALLERY, 4, 2> m_shapes;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_shapes)
The default initial values for CRibbonImpl:: CRibbonItemGalleryCtrl
are 'Category' for category names, all items belong to Category 0, no selection.
Initialize m_shapes
in CMainFrame::OnCreate()
member:
LRESULT OnCreate(UINT , WPARAM , LPARAM ,
BOOL& )
{
SetMenu(NULL);
bool bRibbonUI = RunTimeHelper::IsRibbonUIAvailable();
if (bRibbonUI)
{
UIAddMenu(m_CmdBar.GetMenu(), true);
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);
Or alternatively in a specialized constructor:
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);
}
When the user selects or hovers an item in the Gallery CRibbonImpl
posts a WM_COMMAND
message with HIWORD(wParam)
set to the UI_EXECUTIONVERB
, LOWORD(wParam)
set to the Gallery ID, and lParam
set to the selected item.
Handle this message in CMainFrame with the RIBBON_GALLERY_CONTROL_HANDLER
macro.
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;
}
Example : handle a DropDownGallery Control
In the markup file change the InRibbonGallery to a DropDownGallery with a change in the Group SizeDefinition:
<Tab CommandName="TabGalleries">
<Group CommandName="GroupGallery" SizeDefinition="OneButton">
<DropDownGallery CommandName="Gallery"
ItemHeight="32" ItemWidth="32"
TextPosition="Hide" />
</Group>
</Tab>
In the initialization code, set the Button Image to the initially selected shape:
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);
}
To update the Button Image to the currently selected shape:
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;
}
Example : handle a SplitButtonGallery Control
In the markup file change the DropDownGallery to a SplitButtonGallery:
<Tab CommandName="TabGalleries">
<Group CommandName="GroupGallery" SizeDefinition="OneButton">
<SplitButtonGallery CommandName="Gallery"
ItemHeight="32" ItemWidth="32"
TextPosition="Hide" />
</Group>
When a CRibbonItemGalleryCtrl
is attached to a SplitButtonGallery, if the user clicks the main Button part, CRibbonImpl
posts a WM_COMMAND
message with HIWORD(wParam)
set to UI_EXECUTIONVERB_EXECUTE
, LOWORD(wParam)
set to the Gallery ID, and lParam
set to UI_COLLECTION_INVALIDINDEX
.
CMainFrame::OnGalleryShape()
must handle this case:
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
Declaration in CRibbonImpl
derived class: CRibbonCommandGalleryCtrl<wID, uItems, uCategories = 0> m_Ctrl
: uItems
is the maximum number of items in the gallery, uCategories
the number of categories.
User action message map macro: COMMAND_ID_HANDLER(nCmdID, OnCommand)
for the Gallery items IDs.
Handler prototype: LRESULT OnCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled)
;
CRibbonImpl::CRibbonItemGalleryCtrl
operates on Ribbon Drop-Down Gallery, In-Ribbon Gallery, Split Button Gallery which have the Type = "Commands" property.
Item Key
| Setting
| Overrideable Callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_CommandId
| SetItemCommand(uItem, ID, bool bUpdate = false)
| UINT32 OnRibbonQueryItemCommand(ID, uItem)
| Current CommandId is NULL
| returns ID + 1 + uItem
|
UI_PKEY_CommandType
| SetItemCommandType(uItem, UI_COMMANDTYPE, bool bUpdate = false)
| UI_COMMANDTYPE OnRibbonQueryItemCommandType(ID, uItem)
| Current CommandType is UI_COMMANDTYPE_UNKNOWN
| returns UI_COMMANDTYPE_ACTION
|
Example : Implement a Command InRibbonGallery Control
In the markup file declare the SplitButtonGallery as a 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
default initialization sets the command IDs to GalleryID + 1 + index which are already inserted in FirstRibbon.h.
In FirstRibbon.rc insert a Menu ressource with ID_TEST_GALLERY
, and change the shapes string ressource:
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
In CMainFrame
declare a CRibbonCommandGalleryCtrl
and insert it in the RIBBON_CONTROL_MAP.
CRibbonCommandGalleryCtrl<ID_TEST_GALLERY, 4, 2> m_cmdshapes;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_cmdshapes)
m_cmdShapes
requires the same category initialization that m_shapes
(here in CMainFrame::OnCreate()
, could be in a specialized constructor), SetItemCommandType(1, UI_COMMANDTYPE_BOOLEAN)
sets the item at index 1 (ID_RECT) as a Ribbon ToggleButton:
if (bRibbonUI)
{
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);
A CRibbonCommandGalleryCtrl
Posts WM_COMMAND
messages with the ID of the clicked item: handle them with a COMMAND_RANGE_HANDLER
macro:
BEGIN_MSG_MAP(CMainFrame)
COMMAND_RANGE_HANDLER(ID_DIAMOND, ID_ELLIPSE, OnCommandShape)
LRESULT OnCommandShape(WORD , WORD wID, HWND ,
BOOL& )
{
UISetText(ID_DEFAULT_PANE, CString(MAKEINTRESOURCE(wID)));
return 0;
}
Simple Galleries classes
CRibbonToolbarGalleryCtrl
CRibbonToolBarGalleryCtrl
is a Ribbon Command Gallery built from a Toolbar Resource. There is no category handling and no callback to the application.
Declaration in CRibbonImpl derived class: CRibbonToolBarGalleryCtrl<ID, IDToolBar, uItems>
m_Ctrl: uItems
is the number of items in the gallery.
User action message map macro: COMMAND_ID_HANDLER(nCmdID, OnCommand)
for the toolbar Resource IDs.
Handler prototype: LRESULT OnCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled);
CRibbonImpl::CRibbonItemGalleryCtrl
operates on Ribbon Drop-Down Gallery, In-Ribbon Gallery, Split Button Gallery which have the Type = "Commands" property.
Item Key
| Processing
|
---|
UI_PKEY_CategoryId
| returns UI_COLLECTION_INVALIDINDEX
|
UI_PKEY_CommandId
| Returns IDToolBar resource command ID at uItem position
|
UI_PKEY_CommandType
| Returns UI_COMMANDTYPE_ACTION
|
Example : Implement a InRibbonGallery CRibbonToolbarGalleryCtrl.
In the markup file:
Declare a Ribbon Command named ToolbarGallery with ID_RIBBON_TOOLBAR
Id,
Add it to the TabGalleries Tab as a InRibbonGallery with Commands Type.
<!--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>
In CMainFrame declare a CRibbonToolbarGalleryCtrl
using the IDR_MAINFRAME
toolbar ressource, with 7 elements (the Ribbon won't insert ID_APP_ABOUT
in a Command Gallery as it conflicts with the Ribbon.HelpButton setting), and insert it in the RIBBON_CONTROL_MAP.
CRibbonToolbarGalleryCtrl<ID_RIBBON_TOOLBAR, IDR_MAINFRAME, 7> m_toolbar;
CRibbonCommandGalleryCtrl<ID_TEST_GALLERY, 4, 2> m_cmdshapes;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_toolbar)
RIBBON_CONTROL(m_cmdshapes)
m_toolbar
requires no initialization and the included commands are already handled so compile and run.
Example : Implement a DropDownGallery CRibbonToolbarGalleryCtrl.
In the markup file change the TabGalleries Tab SizeDefinition to accomodate both galleries:
<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>
Extract Tools.bmp from FirstRibbonBitmaps.zip to FirstRibbon\res and add it to FirstRibbon.rc
with ID_RIBBON_TOOLBAR
Id.
ID_RIBBON_TOOLBAR BITMAP "res\\Tools.bmp"
CRibbonSimpleGalleryCtrl
CRibbonSimpleGalleryCtrl
is a Ribbon (Command or Item) Gallery built from a set of resources. There is no category handling and no callback to the application.
Declaration in CRibbonImpl derived class: CRibbonSimpleGalleryCtrl<ID, uItems, UI_COMMANDTYPE type = UI_COMMANDTYPE_ACTION>
m_Ctrl. uItems
is the number of items in the gallery.
User action message map macros:
RIBBON_GALLERY_CONTROL_HANDLER(ID, OnRibbonGalleryCtrl)
for Item Galleries
COMMAND_ID_HANDLER(nCmdID, OnCommand)
for Command Galleries.
Handler prototypes:
LRESULT OnRibbonGalleryCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled);
LRESULT OnCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled);
CRibbonImpl::CSimpleGalleryCtrl
operates on Ribbon Drop-Down Gallery, In-Ribbon Gallery, Split Button Gallery.
Key
| Setting
| Processing
|
---|
UI_PKEY_SelectedItem
| Select(UINT uItem, bool bUpdate = false)
| Returns 0 or last selected item
|
Item Key
| Processing
|
---|
UI_PKEY_CategoryId
| returns UI_COLLECTION_INVALIDINDEX
|
UI_PKEY_Label
| Returns string ressource with ID + 1 + uItem (or NULL)
|
UI_PKEY_ItemImage
| HBITMAP of the ressource bitmap with ID + 1 + uItem (or NULL)
|
UI_PKEY_CommandId
| Returns ID + 1 + uItem
|
UI_PKEY_CommandType
| Returns t_CommandType
|
Example : Implement a Command CRibbonSimpleGalleryCtrl.
In CMainFrame
declare a CRibbonSimpleGalleryCtrl
with ID_TEST_GALLERY
Id, and 4 items and insert it in the RIBBON_CONTROL_MAP (remove m_cmdshapes
from the map):
CRibbonSimpleGalleryCtrl<ID_TEST_GALLERY, 4> m_simpleshapes;
CRibbonToolbarGalleryCtrl<ID_RIBBON_TOOLBAR, IDR_MAINFRAME, 7> m_toolbar;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_simpleshapes)
RIBBON_CONTROL(m_toolbar)
m_simpleshapes
is already declared in the markup file as a Command Gallery, just compile and run.
Example : Implement a Item CRibbonSimpleGalleryCtrl.
In the markup file remove the Type = "Commands" line in the Gallery declaration.
<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>
In FirstRibbon.rc revert the strings to what they were for a shapes Item Gallery:
STRINGTABLE
BEGIN
ID_DIAMOND "Diamond"
ID_RECT "Rectangle"
ID_ROUNDRECT "Round Rectangle"
ID_ELLIPSE "Ellipse"
END
CRibbonComboCtrl
Declaration in CRibbonImpl derived class: CRibbonComboCtrl<wID, uItems, uCategories = 0>
m_Ctrl: uItems
is the maximum number of items in the gallery, uCategories
the number of categories.
User action message map macro: RIBBON_COMBO_CONTROL_HANDLER(wID, OnRibbonComboCtrl)
Handler prototype: LRESULT OnRibbonComboCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled)
CRibbonImpl::CRibbonComboCtrl
operates on Ribbon Combo Box Controls, handles the keys of CRibbonItemGalleryCtrl and:
Key
| Getting
| Setting
|
---|
UI_PKEY_StringValue
| LPCWSTR GetComboText()
| SetComboText(LPCWSTR sText)
|
Example : add and handle a Combo Control
In the markup file declare Tab, Group and Command IDs and declare a ComboBox in a Group:
<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>
In CMainFrame
declare a CRibbonComboCtrl
with ID_TEST_COMBO ID, 6 items, 2 categories and insert it in the RIBBON_CONTROL_MAP.
CRibbonComboCtrl<ID_TEST_COMBO, 6, 2> m_combo;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_combo)
The default initial values for CRibbonImpl::CRibbonComboCtrl
are Category' for category names, all items belong to Category 0, no selection.
To initialize m_combo
override the relevant CRibbonImpl::OnRibbonQueryxxx()
members:
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;
}
You can as well initialize m_combo
in CMainFrame::OnCreate()
as shown earlier.
When the user selects or hovers an item in the Combo CRibbonImpl
posts a WM_COMMAND
message with HIWORD(wParam)
set to the UI_EXECUTIONVERB
, LOWORD(wParam)
set to the Combo ID, and lParam
set to the selected item.
Handle this message in CMainFrame
with the RIBBON_COMBO_CONTROL_HANDLER
macro.
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
is a Ribbon Combo Box built from a set of resources. There is no category handling and no callback to the application.
Declaration in CRibbonImpl derived class: CRibbonSimpleComboCtrl<ID, uItems>
m_Ctrl. uItems
is the number of items in the combo.
User action message map macro: RIBBON_COMBO_CONTROL_HANDLER(ID, OnRibbonComboCtrl)
.
Handler prototype: LRESULT OnRibbonComboCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled);
CRibbonImpl::CRibbonSimpleComboCtrl
operates on Ribbon Combo Box Controls.
Example : handle a CRibbonSimpleComboCtrl Combo Control
In the Markup file declare a Command with ID_RIBBON_SIMPLECOMBO
, and insert it in the Galleries Tab:
<Command Name="SimpleCombo" Symbol="ID_RIBBON_SIMPLECOMBO"
Id="300"
LabelTitle ="Simple Combo" />
<Group CommandName="GroupCombo">
<ComboBox CommandName="SimpleCombo" />
</Group>
In FirstRibbon.h, declare some identifiers following ID_TEST_COMBO
.
#define ID_COMBO1 301
#define ID_COMBO2 302
#define ID_COMBO3 303
#define ID_COMBO4 304
#define ID_COMBO5 305
In FirstRibbon.rc add matching string ressources.
STRINGTABLE
BEGIN
ID_COMBO1 "Simple 1"
ID_COMBO2 "Simple 2"
ID_COMBO3 "Simple 3"
ID_COMBO4 "Simple 4"
ID_COMBO5 "Simple 5"
END
In CMainFrame
declare a CRibbonSimpleComboCtrl
with 5 Items and same Id, and insert it in the RIBBON_CONTROL_MAP, reuse the OnGalleryShape()
handler to handle the selection messages with the RIBBON_COMBO_CONTROL_HANDLER
macro.
CRibbonSimpleComboCtrl<ID_RIBBON_SIMPLECOMBO, 5> m_simplecombo;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_simplecombo)
BEGIN_MSG_MAP(CMainFrame)
RIBBON_COMBO_CONTROL_HANDLER(ID_RIBBON_SIMPLECOMBO, OnGalleryShape)
CRibbonRecentItemsCtrl
CRibbonImpl:: CRibbonRecentItemsCtrl
operates on the Ribbon Recent Items Control.
Item Key
| Setting
| Overrideable Callback
| Condition for callback
| Default processing
|
---|
UI_PKEY_LabelDescription
| TDocList members
| | | returns TDocList::m_arrDocs[uItem].szDocName
|
UI_PKEY_Label
| | LPCWSTR OnRibbonQueryRecentItemName(LPCWSTR sPath)
| always
| returns ::PathFindFileName(sPath)
|
Example : Add and handle a Ribbon Recent Items
In the Markup file declare a RecentFiles Command and insert a RecentItems using it in the Application.Menu section.
<!--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>
In CMainFrame
add
CRibbonRecentItemsCtrl<ID_RIBBON_RECENT_FILES> m_recent;
BEGIN_RIBBON_CONTROL_MAP(CMainFrame)
RIBBON_CONTROL(m_recent)
The RecentItems Ribbon Control is now operated through the WTL
::CRecentDocumentList m_recent
member.
Add a message map entry for ID_FILE_OPEN
and a matching CMainFrame::OnFileOpen()
member.
BEGIN_MSG_MAP(CMainFrame)
COMMAND_ID_HANDLER(ID_FILE_OPEN, OnFileOpen)
...
LRESULT OnFileOpen(WORD , WORD , HWND ,
BOOL& )
{
CFileDialog dlg(TRUE);
if (dlg.DoModal() == IDOK)
m_recent.AddToList(dlg.m_ofn.lpstrFile);
return 0;
}
Compile, run and 'open' some files.
Other Ribbon features handling
Ribbon Application modes
CRibbonImpl member: HRESULT SetRibbonModes(INT32 iModes)
;
Ribbon context menu
CRibbonImpl members:
bool HasRibbonMenu(UINT32 uID)
returns true
if uID
is a Ribbon Context Popup ID.
HRESULT TrackRibbonMenu(UINT32 uID, INT32 x, INT32 y)
pops the Context Menu at (x, y) screen coordinates.
HRESULT TrackRibbonMenu(UINT32 uID, LPARAM lParam)
extracts the (x, y) coordinates from a WM_CONTEXTMENU
lParam and calls TrackRibbonMenu(uID, x, y)
.
Example: Implement a Ribbon Context Menu
In the Markup file declare ContextMap, ContextMenu and MiniToolbar Commands, and insert a ContextPopup description in the Application.Views section.
<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>
In CMainFrame add a WM_CONTEXTMENU
handler, and
BEGIN_MSG_MAP(CMainFrame)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
LRESULT OnContextMenu(UINT , WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (IsRibbonUI())
TrackRibbonMenu(ID_CONTEXTMAP, lParam);
return 0;
}
Ribbon contextual Tabs
Key
| Getting
| Setting
|
---|
UI_PKEY_ContextAvailable
| UI_CONTEXTAVAILABILITY GetRibbonContextAvail(ID)
| SetRibbonContextAvail(ID, UI_CONTEXTAVAILABILITY cav)
|
Example : Add and handle a Ribbon contextual Tab
<!-- 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>
In CMainFrame::OnIdle()
virtual BOOL OnIdle()
{
SetRibbonContextAvail(ID_GROUP_CONTEXT, m_view.HasSelection() ?
UI_CONTEXTAVAILABILITY_ACTIVE :
UI_CONTEXTAVAILABILITY_NOTAVAILABLE);
}
Ribbon colors
Key
| Getting
| Setting
|
---|
UI_PKEY_GlobalBackgroundColor UI_PKEY_GlobalHighlightColor UI_PKEY_GlobalTextColor
| UI_HSBCOLOR GetRibbonColor(key)
| bool SetRibbonColor(key, UI_HSBCOLOR color)
|
Example : Handle Ribbon colors
In CMainFrame::OnFileNew()
LRESULT OnFileNew(WORD , WORD , HWND , BOOL& )
{
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 (QuickAccessToolbar) docking
Key
| Getting
| Setting
|
---|
UI_PKEY_QuickAccessToolbarDock
| UI_CONTROLDOCK GetQATDock()
| bool SetQATDock(UI_CONTROLDOCK dockState)
|
Example : Handle QuickAccessToolbar docking
In CMainFrame::OnFileNew()
LRESULT OnFileNew(WORD , WORD , HWND , BOOL& )
{
if (IsRibbonUI())
{
if (GetQATDock() == UI_CONTROLDOCK_TOP)
SetQATDock(UI_CONTROLDOCK_BOTTOM);
else
SetQATDock(UI_CONTROLDOCK_TOP);
}
return 0;
}
Ribbon visibility and size
Key
| Getting
| Setting
|
---|
UI_PKEY_Viewable
| bool IsRibbonHidden()
| HideRibbon(bool bHide = true)
|
UI_PKEY_Minimized
| bool IsRibbonMinimized()
| MinimizeRibbon(bool bMinimize = true)
|
Example : Handle Ribbon size
In CMainFrame::OnFileNew()
LRESULT OnFileNew(WORD , WORD , HWND , BOOL& )
{
if (IsRibbonUI())
{
MinimizeRibbon(!IsRibbonMinimized());
}
return 0;
}
Ribbon persistency
When switching from Ribbon to traditional UI:
All CRibbonImpl
controls are not affected. The UpdateUI 'ribbon only' IDs are destroyed. To keep them, their text, enabled state and checked state call UIPersistElement(ID)
before Ribbon destruction.
HRESULT <code>CRibbonImpl::
SaveRibbonSettings() saves the Ribbon Settings (Ribbon size and colors, QAT position and content) in a <code><code>HGLOBAL
m_hgRibbonSettings memory zone.
When switching from traditional to Ribbon UI:
If m_hgRibbonSettings
is not NULL
, the Ribbon Settings were persisted and they are restored by HRESULT CRibbonImpl::RestoreRibbonSettings()
.
The newly created Ribbon calls CRibbonImpl::UpdateProperty()
on each CommandId with the relevant Property Keys. CRibbonImpl
returns the stored values and the Ribbon is restored as it was.
For Ribbon Settings persistency between sessions, the helper CRibbonPersist
class exposes members to save and restore the Ribbon Settings to a Registry key under HKCU:
CRibbonPersist(LPCWSTR sAppKey)
constructs a CRibbonPersist
object from a registry key name
LONG CRibbonPersist::Save(bool bRibbonUI, HGLOBAL hgSettings = NULL)
saves a bool
and a HGLOBAL
to the registry.
LONG CRibbonPersist::Restore(bool& bRibbonUI, HGLOBAL& hgSettings)
restores the saved bool
and HGLOBAL
.
MTPad7 uses them in CMainFrame::OnCreate()
to reopen in the last UI with the last Ribbon Settings ...
CRibbonPersist(lpcstrMTPadRegKey).Restore(bRibbonUI, m_hgRibbonSettings);
... which were saved by CMainFrame::OnClose()
.
bool bRibbonUI = IsRibbonUI();
if (bRibbonUI)
SaveRibbonSettings();
CRibbonPersist(lpcstrMTPadRegKey).Save(bRibbonUI, m_hgRibbonSettings);
MTPad is one of the first WTL samples, it is extensively described in this article. The relooking changes no existing functionality. The Ribbon Spinner adds a direct page selection in Preview mode.
The project settings are changed as in First Experience step 1 and 2.
7 bitmaps are added to MTPad.rc.
CMainFrame changes
Derive from CRibbonFrameWindowImpl.
class CMainFrame :
public CRibbonFrameWindowImpl<CMainFrame>
#ifndef _WIN32_WCE
, public CPrintJobInfo
#endif // _WIN32_WCE
{
Add the ribbon controls declaration and control 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()
Handle the Ribbon queries.
DWORD OnRibbonQueryFont(UINT , CHARFORMAT2& cf)
{
return m_view.GetDefaultCharFormat(cf);
}
bool OnRibbonQuerySpinnerValue(UINT , REFPROPERTYKEY key, LONG* pVal)
{
if (key == UI_PKEY_DecimalValue)
{
*pVal = prev.m_nCurPage + 1;
return true;
}
return false;
}
Handle the Ribbon commands.
BEGIN_MSG_MAP(CMainFrame)
RIBBON_SPINNER_CONTROL_HANDLER(ID_PAGE_SPINNER, OnSpinnerPage)
COMMAND_ID_HANDLER(ID_VIEW_RIBBON, OnViewRibbon)
Initialize the UI settings, Ribbon Controls and restore the Ribbon Settings in OnCreate().
LRESULT OnCreate(UINT , WPARAM , LPARAM , BOOL& )
{
#ifndef _WIN32_WCE
HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
m_CmdBar.AttachMenu(GetMenu());
m_CmdBar.LoadImages(IDR_MAINFRAME);
SetMenu(NULL);
bool bRibbonUI = RunTimeHelper::IsRibbonUIAvailable();
if (bRibbonUI)
{
UIAddMenu(m_CmdBar.GetMenu(), true);
UIRemoveUpdateElement(ID_FILE_MRU_FIRST);
UIPersistElement(ID_GROUP_PAGE);
m_groupFont.SetImage(UI_PKEY_SmallImage, GetCommandBarBitmap(ID_FORMAT_FONT));
m_spinner.SetValue(UI_PKEY_MinValue, 1);
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;
}
Save the Ribbon Settings in OnClose().
LRESULT OnClose(UINT , WPARAM , LPARAM , BOOL& bHandled)
{
if (RunTimeHelper::IsRibbonUIAvailable())
{
bool bRibbonUI = IsRibbonUI();
if (bRibbonUI)
SaveRibbonSettings();
CRibbonPersist(lpcstrMTPadRegKey).Save(bRibbonUI, m_hgRibbonSettings);
}
bHandled = !m_view.QueryClose();
return 0;
}
Implement the UI switching in OnViewRibbon().
LRESULT OnViewRibbon(WORD , WORD , HWND , BOOL& )
{
ShowRibbonUI(!IsRibbonUI(), UI_MAKEAPPMODE(prev.IsWindow()));
UISetCheck(ID_VIEW_RIBBON, IsRibbonUI());
return 0;
}
Implement the Ribbon Context Popup in OnContextMenu().
LRESULT OnContextMenu(UINT , 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
}
Set the Ribbon Application Mode 1 and the Page Group Label in OnFilePrintPreview().
LRESULT OnFilePrintPreview(WORD , WORD , HWND , BOOL& )
{
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;
}
Set the Ribbon Application Mode 0 in OnPrintPreviewClose().
LRESULT OnPrintPreviewClose(WORD , WORD , HWND , BOOL& )
{
if (IsRibbonUI())
SetRibbonModes(UI_MAKEAPPMODE(0));
}
Update the Ribbon Spinner value in OnPrintPreviewForward()
and OnPrintPreviewBack().
LRESULT OnPrintPreviewForward(WORD , WORD , HWND , BOOL& )
{
prev.NextPage();
m_spinner.SetVal(prev.m_nCurPage + 1, true);
return 0;
}
LRESULT OnPrintPreviewBack(WORD , WORD , HWND , BOOL& )
{
prev.PrevPage();
m_spinner.SetVal(prev.m_nCurPage + 1, true);
return 0;
}
Handle direct preview page selection in a new OnSpinnerPage()
member.
LRESULT OnSpinnerPage(WORD , LONG lVal, BOOL& )
{
prev.SetPage(lVal - 1);
prev.Invalidate();
return 0;
}
CEditView changes
Handle the Ribbon Font Control messages.
BEGIN_MSG_MAP(CEditView)
#ifndef _WIN32_WCE
RIBBON_FONT_CONTROL_HANDLER(ID_RIBBON_FONT, OnRibbonFont)
Preview a hovered font, reverse to the internal m_font
member when Preview is cancelled, update it when the font is changed through the Ribbon, .
LRESULT OnRibbonFont(UI_EXECUTIONVERB verb, WORD , CHARFORMAT2* pcf, BOOL& )
{
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);
}
Conclusion
As the Ribbon UI is somehow controversial, dual UI applications may satisfy both ribbon-lovers and ribbon-haters. With the WTL library, the best of both worlds is at your hands.
Cheers!
Revision History
- 26 Jan 2010: Released
- 28 Jan 2010: Update
- atlribbon.h: Fixed multiple instances issues.
- MTPad7: Correct handling of Font Control UI_EXECUTIONVERB_CANCELPREVIEW.
- Article: Some typos and bad links
- 19 Feb 2010: Update
- 13 Apr 2010: atlribbon.h and MTPad7 update
- Better handling of UI switching,
- Fixed Vista Basic sizing issue when switched to traditional UI,
- Fixed IUIImage leak,
- Context tab default initialized to UI_CONTEXTAVAILABILITY_NOTAVAILABLE.