MFC 에서 CMenu
의 색상을 원하는 대로 바꿀 수 있다.
https://msdn.microsoft.com/en-us/library/z25as7e5(v=vs.120).aspx
에서 보듯이 CMenu::DrawItem
함수를 오버라이딩 해서 사용한다.
https://www.codeproject.com/Articles/7073/How-to-create-owner-drawn-menus-Step-by-Step
or
ownmenu_src.zip (GoogleDrive)
위 사이트에서 간략한 예제가 나와 있다.
그리고 아래의 사이트에서는 CMenu
의 border 를 제거하는 방법을 소개한다.
위 방식에서 소개된 방법은 CMenu
를 내부적으로 동적으로 생성하기 때문에 외부 환경 값을 저장 할땐 static 멤버로 저장한다.
아래는 배경색,글자색,Hover색,경계색,폰트 의 5가지를 수정할 수 있는 간단한 예시 코드이다.
BMDLMenu.h
#pragma once
#include<afxwin.h>
#include<vector>
class BMDLMenu : public CMenu {
public:
//constructor,destructor
BMDLMenu(CWnd* wnd);
virtual ~BMDLMenu();
private:
//Inner class
struct MenuObject {
HICON m_hIcon;
CString m_strCaption;
BOOL bFirstMenu;
};
std::vector<DWORD> deleteItem;
std::vector<DWORD> deleteMenu;
public:
CWnd* m_parent_wnd;
void MakeItemsOwnDraw();
void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)override;
void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)override;
//static member
static CString m_font_string;
static COLORREF m_color_bk;
static COLORREF m_color_text;
static COLORREF m_color_hover;
static COLORREF m_color_border;
static void SetStyle(CString font_string, COLORREF color_bk, COLORREF color_text, COLORREF color_hover, COLORREF color_border);
};
BMDLMenu.cpp
#include"stdafx.h"
#include"BMDLMenu.h"
#include"stdmfc.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///static member
CString BMDLMenu::m_font_string = TEXT("Arial");
COLORREF BMDLMenu::m_color_bk = RGB(255, 255, 255);
COLORREF BMDLMenu::m_color_text = RGB(0, 0, 0);
COLORREF BMDLMenu::m_color_hover = RGB(199, 199, 199);
COLORREF BMDLMenu::m_color_border = RGB(103, 153, 255);
///constructor
BMDLMenu::BMDLMenu(CWnd* wnd) :CMenu() {
m_parent_wnd = wnd;
}
///destructor
BMDLMenu::~BMDLMenu() {
for (int i = 0; i < deleteItem.size(); i++) {
delete ((MenuObject*)deleteItem[i]);
}
for (int i = 0; i < deleteMenu.size(); i++) {
delete ((BMDLMenu*)deleteMenu[i]);
}
}
///static metnod
void BMDLMenu::SetStyle(CString font_string, COLORREF color_bk, COLORREF color_text, COLORREF color_hover, COLORREF color_border) {
BMDLMenu::m_font_string = font_string;
BMDLMenu::m_color_bk = color_bk;
BMDLMenu::m_color_text = color_text;
BMDLMenu::m_color_hover = color_hover;
BMDLMenu::m_color_border = color_border;
}
///method
void BMDLMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) {
CRect rect(lpDrawItemStruct->rcItem);
COLORREF color_bk = BMDLMenu::m_color_bk;
if (((MenuObject*)lpDrawItemStruct->itemData)->bFirstMenu) {
//일반 상태
color_bk = BMDLMenu::m_color_bk;
}
if ((lpDrawItemStruct->itemState & ODS_SELECTED) &&
(lpDrawItemStruct->itemAction & (ODA_SELECT | ODA_DRAWENTIRE))) {
//Hover 상태
color_bk = BMDLMenu::m_color_hover;
}
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
::FillRect(pDC->GetSafeHdc(), &rect, CreateSolidBrush(color_bk));
CFont font;
int h = stdmfc::MeasureFontHeight(m_font_string, 20, pDC);
font.CreatePointFont(h, m_font_string);
CFont* old_font = pDC->SelectObject(&font);
pDC->SetBkColor(color_bk);
pDC->TextOut(rect.left, rect.top, ((MenuObject*)lpDrawItemStruct->itemData)->m_strCaption);
}
void BMDLMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) {
static CWindowDC dc(m_parent_wnd);
lpMeasureItemStruct->itemHeight = 20;
CFont font;
int h = stdmfc::MeasureFontHeight(m_font_string, lpMeasureItemStruct->itemHeight, &dc);
font.CreatePointFont(h, m_font_string);
CFont* old_font = dc.SelectObject(&font);
CString str = ((MenuObject*)lpMeasureItemStruct->itemData)->m_strCaption;
CSize sz;
::GetTextExtentPoint32W(dc.GetSafeHdc(), str, str.GetLength(), &sz);
lpMeasureItemStruct->itemWidth = sz.cx < 100 ? 100 : sz.cx;
}
void BMDLMenu::MakeItemsOwnDraw() {
//https://stackoverflow.com/questions/30353644/cmenu-border-color-on-mfc-solved
MENUINFO MenuInfo = { 0 };
MenuInfo.cbSize = sizeof(MENUINFO);
GetMenuInfo(&MenuInfo);
MenuInfo.hbrBack = ::CreateSolidBrush(BMDLMenu::m_color_border);
MenuInfo.fMask = MIM_BACKGROUND | MIM_STYLE;
MenuInfo.dwStyle = MIM_APPLYTOSUBMENUS;
SetMenuInfo(&MenuInfo);
int iMaxItems = GetMenuItemCount();
for (int i = 0; i < iMaxItems; i++) {
CString nameHolder;
MenuObject* pObject = new MenuObject;
deleteItem.push_back((DWORD)pObject);
pObject->m_hIcon = NULL;
pObject->bFirstMenu = TRUE;
GetMenuString(i, pObject->m_strCaption, MF_BYPOSITION);
MENUITEMINFO mInfo;
ZeroMemory(&mInfo, sizeof(MENUITEMINFO));
//I dont use GetMenuItemID because it doesn't return 0/-1 when it's a Popup (so the MSDN is wrong)
UINT uID = mInfo.wID;
ModifyMenu(i, MF_BYPOSITION | MF_OWNERDRAW,
uID, (TCHAR*)pObject);
if (GetSubMenu(i)) {
BMDLMenu* pSubMenu = new BMDLMenu(m_parent_wnd);
deleteMenu.push_back((DWORD)pSubMenu);
pSubMenu->Attach(GetSubMenu(i)->GetSafeHmenu());
pSubMenu->MakeItemsOwnDraw();
}
}
}
stdmfc.h
namespace stdmfc {
inline int MeasureFontHeight(CString font_str, int h, CDC* pdc) {
int L = 0;
int R = 1000;
while (L <= R) {
int M = (L + R) / 2;
CFont font;
font.CreatePointFont(M, font_str);
CFont* old_font = pdc->SelectObject(&font);
CSize sz;
::GetTextExtentPoint32W(pdc->GetSafeHdc(), TEXT("A"), 1, &sz);
if (sz.cy == h) {
break;
} else if (sz.cy > h) {
R = M - 1;
} else {
L = M + 1;
}
pdc->SelectObject(old_font);
font.DeleteObject();
}
return (L + R) / 2;
}
}