[ATL/WTL]_[初级]_[自定义菜单项字体]

场景

  1. 在开发WTL程序时,菜单基本都是标配,比如菜单栏菜单,右键上下文菜单,按钮菜单等等。但是如何设置菜单的字体,大小,颜色,或者说自绘菜单?

说明

  1. 我们看WTL提供的CMenu类,并不是一个窗口类,它只是封装了一个HMENU菜单句柄,并不是我们所熟悉的窗口句柄HWND。所以它不会接收到WM_PAINT消息。
template <bool t_bManaged>
class CMenuT
{
public:
// Data members
	HMENU m_hMenu;

// Constructor/destructor/operators
	CMenuT(HMENU hMenu = NULL) : m_hMenu(hMenu)
	{ }
  1. 我们再看看CMenuAppendMenu方法,也只是调用了Win32的API ::AppendMenu 函数,关键这个方法参数nFlags的意义,我们看到有一个样式是MF_OWNERDRAW.
BOOL AppendMenu(UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL)
{
    ATLASSERT(::IsMenu(m_hMenu));
    return ::AppendMenu(m_hMenu, nFlags, nIDNewItem, lpszNewItem);
}
  1. MF_OWNERDRAW: 指定这个菜单项是自绘的菜单项。在菜单显示之前,拥有菜单的窗口接收到WM_MEASUREITEM消息查找菜单项的宽和高。而在菜单项更新时,WM_DRAWITEM消息会被发送大这个窗口的窗口处理函数里,可以通过这个消息绘制菜单项.

  2. 以下的样式不能用在一起, 因为MF_STRINGMF_OWNERDRAW不能用在一起,所以我们在创建菜单项时单独使用MF_OWNERDRAW.

MF_BITMAP, MF_STRING, and MF_OWNERDRAW
MF_CHECKED and MF_UNCHECKED
MF_DISABLED, MF_ENABLED, and MF_GRAYED
MF_MENUBARBREAK and MF_MENUBREAK
CMenu menu;
menu.CreatePopupMenu();

menu.AppendMenu(MF_OWNERDRAW,ID_EDIT_COPY,L"Copy-2");
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_OWNERDRAW,ID_EDIT_CUT,L"Cut-2");
menu.AppendMenu(MF_OWNERDRAW,ID_EDIT_PASTE,L"Paste-2");

menu.TrackPopupMenu(TPM_LEFTALIGN|TPM_TOPALIGN|TPM_LEFTBUTTON,pt.x,pt.y,m_hWnd,NULL);
  1. 简单来说在WM_MEASUREITEM消息里我们设置菜单项的宽和高。在WM_DRAWITEM消息里我们绘制菜单项,比如菜单项背景色,字体,字体颜色,选中颜色等。而这两个消息是属于OWNERDRAW类型的,在WTL了提供了一个类COwnerDraw映射了这两个消息,所以我们只需要继承这个类,并且重载以下两个方法即可:
void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);

例子

View.h

// View.h : interface of the CView class
//
/////////////////////////////////////////////////////////////////////////////

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 

enum
{
	kMyStaticId = WM_USER+1,
};

class CView : public CWindowImpl<CView>,public COwnerDraw<CView>
{
public:
	DECLARE_WND_CLASS(NULL)

	BOOL PreTranslateMessage(MSG* pMsg);

	BEGIN_MSG_MAP_EX(CView)
		MSG_WM_CREATE(OnCreate)
		MESSAGE_HANDLER(WM_PAINT, OnPaint)
		COMMAND_ID_HANDLER_EX(ID_EDIT_COPY, OnOperate)
		COMMAND_ID_HANDLER_EX(ID_EDIT_CUT, OnOperate)
		COMMAND_ID_HANDLER_EX(ID_EDIT_PASTE, OnOperate)
		MSG_WM_CONTEXTMENU(OnContextMenu)
		COMMAND_HANDLER_EX(kMyStaticId,STN_CLICKED,OnStaticClick)
		CHAIN_MSG_MAP_ALT(COwnerDraw<CView>, 0)
		REFLECT_NOTIFICATIONS()
	END_MSG_MAP()

// Handler prototypes (uncomment arguments if needed):
//	LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
//	LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
//	LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
	int OnCreate(LPCREATESTRUCT lpCreateStruct);
	LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
	void UpdateLayout();
	void OnContextMenu(HWND hwnd, CPoint point);
	void OnStaticClick(UINT uNotifyCode, int nID, HWND wndCtl);
	void OnOperate(UINT uNotifyCode, int nID, HWND wndCtl);

	void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
	void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);

private:
	std::wstring GetControlText(HWND hwnd,wchar_t* buf = NULL);

	CFont font_normal_;
	CStatic text_logo_;
	HICON icon_;

	CBrushHandle brush_white_;
	CBrushHandle brush_hollow_;
	CBrush brush_red_;

public:
};

View.cpp

// View.cpp : implementation of the CView class
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "resource.h"
#include 
#include 

#include "View.h"
#include 
#include 
#include 


BOOL CView::PreTranslateMessage(MSG* pMsg)
{
	return FALSE;
}

LRESULT CView::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	CPaintDC dc(m_hWnd);
	CMemoryDC mdc(dc,dc.m_ps.rcPaint);

	CRect rect_client;
	GetClientRect(&rect_client);
	mdc.FillSolidRect(rect_client,RGB(255,255,255));
	//TODO: Add your drawing code here

	return 0;
}

// https://docs.microsoft.com/en-us/windows/win32/menurc/using-menus#setting-fonts-for-menu-item-text-strings
void CView::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	if(lpDrawItemStruct->CtlType == ODT_MENU){

		COLORREF crSelText = GetSysColor(COLOR_HIGHLIGHTTEXT);
		COLORREF crSelBkgnd = GetSysColor(COLOR_HIGHLIGHT);
		COLORREF crText = -1;
		COLORREF crBkgnd = -1;
		BOOL fSelected = FALSE;
		CDCHandle cdc(lpDrawItemStruct->hDC);
		auto& rcItem = lpDrawItemStruct->rcItem;
		auto wCheckX = GetSystemMetrics(SM_CXMENUCHECK); 
		CRect rect(rcItem.left,rcItem.top,rcItem.right,rcItem.bottom);

		crBkgnd = cdc.GetBkColor();
		cdc.FillSolidRect(rect,crBkgnd);
		if(lpDrawItemStruct->itemState & ODS_SELECTED){ 
			crText = cdc.SetTextColor(crSelText); 
			crBkgnd = cdc.SetBkColor(crSelBkgnd);
			cdc.FillSolidRect(rect,crSelBkgnd);
			fSelected = TRUE; 
		} 

		auto text = (LPCTSTR)lpDrawItemStruct->itemData;
		cdc.SelectFont(font_normal_);
		CSize size;
		cdc.GetTextExtent(text,wcslen(text),&size);
		rect.left += wCheckX;
		rect.top += ((rect.Height()-size.cy)/2);
		cdc.DrawText(text,wcslen(text),rect,DT_LEFT);

		if (fSelected){ 
			cdc.SetTextColor(crText); 
			cdc.SetBkColor(crBkgnd);
		}
		SetMsgHandled(TRUE);
	}else{	
		SetMsgHandled(FALSE);
	}
	
}

void CView::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	if(lpMeasureItemStruct->CtlType == ODT_MENU){
		auto hdc = GetDC();
		CDCHandle cdc(hdc);
		cdc.SelectFont(font_normal_);
		auto text = (LPCTSTR)lpMeasureItemStruct->itemData;
		CSize size;
		cdc.GetTextExtent(text,wcslen(text),&size);

		lpMeasureItemStruct->itemWidth = size.cx+4;
		lpMeasureItemStruct->itemHeight = size.cy+4;
		SetMsgHandled(TRUE);
	}else{
		SetMsgHandled(FALSE);
	}
}

void CView::OnContextMenu(HWND hwnd, CPoint pt)
{
	CMenu menu;
	menu.CreatePopupMenu();
	
	menu.AppendMenu(MF_OWNERDRAW,ID_EDIT_COPY,L"Copy-2");
	menu.AppendMenu(MF_SEPARATOR);
	menu.AppendMenu(MF_OWNERDRAW,ID_EDIT_CUT,L"Cut-2");
	menu.AppendMenu(MF_OWNERDRAW,ID_EDIT_PASTE,L"Paste-2");
	
	menu.TrackPopupMenu(TPM_LEFTALIGN|TPM_TOPALIGN|TPM_LEFTBUTTON,pt.x,pt.y,m_hWnd,NULL);
}

static HFONT GetFont(int pixel,bool bold,const wchar_t* font_name)
{
	LOGFONT lf; 
	memset(&lf, 0, sizeof(LOGFONT)); // zero out structure 
	lf.lfHeight = pixel; // request a 8-pixel-height font
	if(bold)
	{
		lf.lfWeight = FW_BOLD;  
	}
	lstrcpy(lf.lfFaceName, font_name); // request a face name "Arial"
	
	HFONT font = ::CreateFontIndirect(&lf);
	return font;
}

void CView::OnOperate(UINT uNotifyCode, int nID, HWND wndCtl)
{
	switch(nID)
	{
	case ID_EDIT_COPY:
		{
			MessageBox(L"Copy-2");
		}
		break;
	case ID_EDIT_CUT:
		MessageBox(L"Cut-2");
		break;
	case ID_EDIT_PASTE:
		MessageBox(L"Paste-2");
		break;
	}
}

std::wstring CView::GetControlText(HWND hwnd,wchar_t* buf)
{
	auto length = ::GetWindowTextLength(hwnd);
	bool bufNull = false;
	if(!buf){
		buf = new wchar_t[length+1]();
		bufNull = true;
	}
	
	::GetWindowText(hwnd,buf,length+1);
	std::wstring str(buf);

	if(bufNull)
		delete []buf;

	return str;
}

static std::wstring GetProductBinDir()
{
	static wchar_t szbuf[MAX_PATH];  
	GetModuleFileName(NULL,szbuf,MAX_PATH);  
    PathRemoveFileSpec(szbuf);
	int length = lstrlen(szbuf);
	szbuf[length] = L'\\';
	szbuf[length+1] = 0;
	return std::wstring(szbuf);
}

int CView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	font_normal_ = ::GetFont(16,false,L"Arial");

	// 1.单击icon弹出菜单.
	icon_ = AtlLoadIcon(IDR_MAINFRAME);
	text_logo_.Create(m_hWnd,0,L"",WS_CHILD|WS_VISIBLE|SS_ICON|SS_NOTIFY,0,kMyStaticId);
	text_logo_.SetIcon(icon_);

	brush_hollow_ = AtlGetStockBrush(HOLLOW_BRUSH);
	brush_white_ = AtlGetStockBrush(WHITE_BRUSH);
	brush_red_.CreateSolidBrush(RGB(255,0,0));
	UpdateLayout();

	return 0;
}

void CView::OnStaticClick(UINT uNotifyCode, int nID, HWND wndCtl)
{
	CMenu menu;
	menu.CreatePopupMenu();

	menu.AppendMenu(MF_STRING,ID_EDIT_COPY,L"Copy");
	menu.AppendMenu(MF_STRING,ID_EDIT_CUT,L"Cut");
	menu.AppendMenu(MF_STRING,ID_EDIT_PASTE,L"Paste");

	CRect rect;
	text_logo_.GetWindowRect(&rect);
	auto ret = menu.TrackPopupMenu(TPM_LEFTALIGN|TPM_TOPALIGN|
		TPM_RETURNCMD|TPM_LEFTBUTTON,rect.left,rect.bottom,m_hWnd,NULL);
	switch(ret)
	{
	case ID_EDIT_COPY:
		MessageBox(L"Copy");
		break;
	case ID_EDIT_CUT:
		MessageBox(L"Cut");
		break;
	case ID_EDIT_PASTE:
		MessageBox(L"Paste");
		break;
	}
}

void CView::UpdateLayout()
{
	CRect rect;
	GetClientRect(&rect);

	CClientDC dc(m_hWnd);
	dc.SelectFont(font_normal_);
	
	CSize size_control;
	wchar_t* buf = new wchar_t[MAX_PATH]();

	CRect rect_control = CRect(CPoint(100,100),CSize(32,32));
	text_logo_.MoveWindow(rect_control);
}

输出

标准的菜单项

图1:
在这里插入图片描述

自定义的菜单项

图2:

[ATL/WTL]_[初级]_[自定义菜单项字体]_第1张图片

参考

  1. Setting Fonts for Menu-Item Text Strings

  2. Using Menus

  3. AppendMenu function

你可能感兴趣的:(ATL/WTL界面开发)