使用 Code::Blocks 在 ReactOS 上以编程方式创建的 Win32++ 菜单





5.00/5 (4投票s)
Win32++ 类库示例集合中没有包含一个通过 API 而不是通过资源创建框架菜单的示例。这里是缺失的部分。
引言
Win32++ 类库是一项伟大的作品。我目前正尝试用 Win32++ 类库替换我自己的类库,用于我的项目 一个在 ReactOS 上运行的基本图标编辑器。在这种情况下,我正在测试 Win32++ 提供有吸引力的菜单的可能性,这些菜单是根据 API 动态创建的(而不是资源生成的)。
但对于其他用例,使用 Win32++ 根据 API 动态创建有吸引力的菜单也可能很有趣。
背景
我对菜单有以下要求
- 在 ReactOS 上工作
- 支持透明位图或图标
- 可以根据 API 动态创建
这就是结果在 ReactOS 上的最终呈现效果
Using the Code
为了正确地分离所有内容,我将方法 OnCreateFrameMenu
添加到我的 CFrame
派生类 CMainFrame
中。
// OnCreate controls the way the frame is created.
int CMainFrame::OnCreate(CREATESTRUCT& cs)
{
// OnCreate controls the way the frame is created.
// Overriding CFrame::OnCreate is optional.
// A menu is added if the IDW_MAIN menu resource is defined.
// Frames have all options enabled by default.
// Use the following functions to disable options.
// UseIndicatorStatus(FALSE); // Don't show keyboard indicators in the StatusBar
// UseMenuStatus(FALSE); // Don't show menu descriptions in the StatusBar
// UseReBar(FALSE); // Don't use a ReBar
// UseStatusBar(FALSE); // Don't use a StatusBar
UseThemes(FALSE); // Don't use themes
UseToolBar(FALSE); // Don't use a ToolBar
// Create the complete frame-menu before layout calculation (done in CFrame::OnCreate()).
OnCreateFrameMenu();
// call the base class function
return CFrame::OnCreate(cs);
}
// OnCreateFrameMenu controls the main (frame) menu creation.
void CMainFrame::OnCreateFrameMenu()
{
m_frameMenu.CreateMenu();
m_filePopupMenu.CreateMenu();
if (m_frameMenu.AppendMenu(MF_POPUP, (UINT_PTR)m_filePopupMenu.GetHandle(),
_T("&File")) != FALSE)
{
HICON hIcon = NULL;
m_filePopupMenu.AppendMenu(MF_STRING, IDM_FILE_NEW, _T("&New"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\New2_16.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_FILE_NEW, hIcon, 16);
m_filePopupMenu.AppendMenu(MF_STRING, IDM_FILE_OPEN, _T("&Open"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Open2_16.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_FILE_OPEN, hIcon, 16);
m_filePopupMenu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)NULL);
m_filePopupMenu.AppendMenu(MF_STRING, IDM_FILE_SAVE, _T("&Save"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Save_16.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_FILE_SAVE, hIcon, 16);
m_filePopupMenu.AppendMenu(MF_STRING, IDM_FILE_SAVEAS, _T("Save &As"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\SaveAs_16.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_FILE_SAVEAS, hIcon, 16);
m_filePopupMenu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)NULL);
m_filePopupMenu.AppendMenu(MF_STRING, IDM_FILE_PRINT, _T("&Print"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Win32Print.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_FILE_PRINT, hIcon, 16);
m_filePopupMenu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)NULL);
m_filePopupMenu.AppendMenu(MF_STRING, IDM_FILE_CLOSE, _T("&Close"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Close_16.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_FILE_CLOSE, hIcon, 16);
m_filePopupMenu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)NULL);
m_filePopupMenu.AppendMenu(MF_STRING, IDM_FILE_EXIT, _T("&Exit"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Exit_16.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_FILE_EXIT, hIcon, 16);
}
m_editPopupMenu.CreateMenu();
if (m_frameMenu.AppendMenu(MF_POPUP, (UINT_PTR)m_editPopupMenu.GetHandle(),
_T("&Edit")) != FALSE)
{
HICON hIcon = NULL;
m_editPopupMenu.AppendMenu(MF_STRING, IDM_EDIT_UNDO, _T("&Undo"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Undo_16.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_EDIT_UNDO, hIcon, 16);
m_editPopupMenu.AppendMenu(MF_STRING, IDM_EDIT_REDO, _T("&Redo"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Redo_16.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_EDIT_REDO, hIcon, 16);
m_editPopupMenu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)NULL);
m_editPopupMenu.AppendMenu(MF_STRING, IDM_EDIT_CUT, _T("&Cut"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Win32Cut.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_EDIT_CUT, hIcon, 16);
m_editPopupMenu.AppendMenu(MF_STRING, IDM_EDIT_COPY, _T("Cop&y"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Win32Copy.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_EDIT_COPY, hIcon, 16);
m_editPopupMenu.AppendMenu(MF_STRING, IDM_EDIT_PASTE, _T("&Paste"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Win32Paste.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_EDIT_PASTE, hIcon, 16);
m_editPopupMenu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)NULL);
m_editPopupMenu.AppendMenu(MF_STRING, IDM_EDIT_DELETE, _T("&Delete"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Delete_16.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_EDIT_DELETE, hIcon, 16);
}
m_helpPopupMenu.CreateMenu();
if (m_frameMenu.AppendMenu(MF_POPUP, (UINT_PTR)m_helpPopupMenu.GetHandle(),
_T("&?")) != FALSE)
{
HICON hIcon = NULL;
m_helpPopupMenu.AppendMenu(MF_STRING, IDM_HELP_ABOUT, _T("&About"));
m_helpPopupMenu.AppendMenu(MF_STRING, IDM_HELP_HELP, _T("&Help"));
hIcon = (HICON)::LoadImage(NULL, _T("Images\\Help_16.ico"),
IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
AddMenuIcon(IDM_HELP_HELP, hIcon, 16);
}
SetFrameMenu(m_frameMenu.GetHandle());
}
除了方法 CreateMenu
、AppendMenu
和 SetFrameMenu
,我主要使用这两个方法 ::LoadImage
和 AddMenuIcon
来创建有吸引力的菜单,首先是因为我已有所需的图标,其次是因为图标支持透明度。(第三,因为 SetMenuItemBitmaps
方法似乎只是 Windows API 的一个瘦包装器,并且似乎没有以 Win32++ 菜单项可以处理的方式设置位图。)
正如您很容易看到的那样,菜单项在 Aero 主题中被绘制为 OWNERDRAW
,并且包含一个流畅的矩形,即使 ReactOS 系统没有安装 Aero 主题,并且当前视觉样式配置为“经典主题”。但我想我们可以接受这一点。
缺点
但是,ReactOS 偶尔会出现一个问题:OWNERDRAW
菜单项并不总是预先选择正确的字体。这就是它在我的系统上悬停后的样子
为了避免这种情况,我使用与我在提示中已经描述的相同技术,又一个完全功能的自定义绘制菜单:我显式选择菜单字体。为了实现这一点,我必须稍微修改 Win32++ 文件 wxx_frame.h,这没有问题,因为 Win32++ 是开源的。
//////////////////////////////////
// CFrameT is the base class for all frame in Win32++
// The template parameter T is typically either CWnd or CDocker
template <class T>
class CFrameT : public T
{
...
#ifdef OGWW
HFONT m_menuFontNormal; // The standard/default/normal menu font buffer.
#endif // OGWW
}; // class CFrameT
...
///////////////////////////////////
// Definitions for the CFrame class
//
template <class T>
inline CFrameT<T>::CFrameT() : m_aboutDialog(IDW_ABOUT), m_accel(0), m_pView(NULL),
m_maxMRU(0), m_oldFocus(0), m_drawArrowBkgrnd(FALSE),
m_kbdHook(0), m_useIndicatorStatus(TRUE), m_useMenuStatus(TRUE),
m_useStatusBar(TRUE), m_useThemes(TRUE), m_useToolBar(TRUE)
{
...
#ifdef OGWW
m_menuFontNormal = NULL;
#endif // OGWW
}
template <class T>
inline CFrameT<T>::~CFrameT()
{
if (m_kbdHook != 0) UnhookWindowsHookEx(m_kbdHook);
#ifdef OGWW
if (m_menuFontNormal != NULL)
::DeleteObject(m_menuFontNormal);
#endif // OGWW
}
...
// Called by DrawMenuItem to render the text for popup menus.
template <class T>
inline void CFrameT<T>::DrawMenuItemText(LPDRAWITEMSTRUCT pDIS)
{
...
#ifdef OGWW
// Obviously, at least ReactOS does not guarantee the selection of the proper menu font.
if (m_menuFontNormal == NULL)
{
NONCLIENTMETRICSW nm;
nm.cbSize = sizeof(NONCLIENTMETRICS);
assert(::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, nm.cbSize,&nm, 0) != FALSE);
m_menuFontNormal = ::CreateFontIndirect(&(nm.lfMenuFont));
}
assert(m_menuFontNormal != NULL);
HFONT hOldFont = NULL;
if (m_menuFontNormal != NULL)
hOldFont = (HFONT)::SelectObject(pDIS->hDC, m_menuFontNormal);
#endif // OGWW
SetTextColor(pDIS->hDC, colorText);
int mode = SetBkMode(pDIS->hDC, TRANSPARENT);
DrawText(pDIS->hDC, pItem, tab, textRect, DT_SINGLELINE | DT_LEFT | DT_VCENTER);
// Draw text after tab, right aligned
if (tab != -1)
DrawText(pDIS->hDC, &pItem[tab + 1], -1, textRect,
DT_SINGLELINE | DT_RIGHT | DT_VCENTER);
SetBkMode(pDIS->hDC, mode);
#ifdef OGWW
/* if (hOldFont == NULL)
::SelectObject(pDIS->hDC, hOldFont);*/
#endif // OGWW
}
}
我已将所有补充内容包含在 #ifdef OGWW
... #endif // OGWW
语句中。
最后几句话关于开发环境:我使用 Code::Blocks 17.12 版本,包含在 ReactOS 0.4.11 和当前 Win32++ 7.8 版本中的 MinGW。 我已将以下库链接到我的项目中
gdi32
user32
kernel32
comctl32
comdlg32
Ole32
Oleaut32
Ws2_32
Uuid
我已设置了这些通用的 #define
s
_UNICODE
UNICODE
__MSVCRT__
(是的,ReactOS 上的 MinGW 支持)OGWW
(用于标记特定的源代码部分)
历史
- 2020年1月24日:初始版本