(转)用MFC创建菜单按钮
来源:互联网 发布:网络的实用性 编辑:程序博客网 时间:2024/05/15 09:53
用MFC创建菜单按钮
现在有不少的软件都有这样的一种界面效果:当用户单击某一个按钮之后,并不是简单地执行某种功能或弹出一个对话框,而是在按钮旁边弹出一个菜单,让用户作更详细地选择,这在某种程度上就代替了简单的对话框,而且较对话框更为“用户友好”。这样的按钮基本上有两种类型:在按钮上显示文字的和在按钮上显示箭头的,显示箭头常见的有向右的和向下的两种,还有向上的和向左的。图示为常见的风格,即向下的箭头和在按钮左下角弹出菜单。那么,我们在编程时如何实现这一功能呢?
我们知道,MFC中的CButton类有一个虚函数名叫DrawItem(),若在对话框模板中为控件指定了BS_OWNERDRAW风格,则在运行时将调用这个函数来画按钮,而CMenu类的成员函数TrackPopupMenu()则可以在屏幕的任何位置弹出菜单。由上得到启发,只要我们合理地使用这两个函数,就能创建出“菜单按钮”来。
下面的CMenuButton类封装了全部的这些功能,让我们先来看一下它的制作原理。
在取得了按钮的矩形区域之后,取其一个角落的值传递给TrackPopupMenu()函数即可实现弹出菜单,在TrackPopupMenu内部使用TPM_RETURNCMD标志可以得到用户选择的菜单的命令ID,以供进一步的处理;在重载了DrawItem()函数之后,我们可以在函数的内部使用CDC::DrawFrameControl()函数来画出基本的按钮外观,再在中间部位画一个箭头即可。箭头可以用Marlett字体来画。也许有人会担心,若果其他人的机器没装Marlett字体怎么办?其实,任何一台安装Windows的机器离开了Marlett字体都无法正常工作,先请看下图,这是Windows“系统工具”中自带的“字符映射表”。
看到最上面一行中的那几个箭头了吗?就是要把它们画在按钮上。等一等,另外的几个符号怎么也那么熟悉?这不就是几乎每个窗口上都有的“最小化”、“还原”、“关闭”和“最大化”按钮吗?不错,Windows正是使用这几个字符在标题栏上绘图的。其实,Windows中的最“标准”的画箭头的方法就是使用Marlett字体,无论是工具栏上的箭头还是组合框中的箭头,都是这样画出的。有时,在乱删了字体之后,组合框或工具栏的下拉箭头会变成数字6或者9,为什么?看到状态栏上的“击键值”了吗?——“6”,往右数,那个小一点的下箭头正好是——“9”。
下面是具体的制作过程。
首先,生成一个MFC AppWizard EXE 工程,最好是基于对话框的工程,当然,利用现有的工程也可以。
生成一个以CButton为基类的新类,名为CMenuButton,然后用ClassWizard为其添加两个成员函数:DrawItem()和PreSubclassWidnow();手工为CMenuButton类添加BOOL类型m_bDrawFocusRect成员变量,用于决定是否在按钮上画焦点矩形,添加SetDrawFocusRect()函数用于设置这个标志,默认为画焦点矩形;添加两个枚举类型的变量m_ArrowType和m_PopupPos,用于决定所画的箭头的类型和菜单弹出的位置。箭头可为右箭头、下箭头、小右箭头、小下箭头、上箭头和左箭头(参见本文开始处的图);菜单的弹出位置可以为按钮的左上角、右上角、左下角和右下角。最后手工添加两个函数,SetArrowType()和SetMenuPopupPos(),用于设置以上各种风格,其默值分别为画右箭头和在左下角弹出。
如果只需要菜单而不需要画箭头,只需置空BS_OWNERDRAW标志位即可,添加一个SetStyle()函数,用于设置是画箭头还是显示文本,其默认值是画箭头。
为方便处理按钮的BN_CLICKED通知消息,为CMenuButton类创建一个公有的成员函数OnClick(),以便在BN_CLICKED的消息处理器中调用。它有两个参数,第一个是菜单资源的ID,第二个参数为子菜单的ID,默认为0。如果只有一组子菜单,则可使用其默认值0。OnClick()函数的返回值为所选的菜单项的命令ID,若未作任何有效选择,则返回0。
下面是程序代码。
头文件:
#if !defined(_EWAY_MEMUBUTTON_H__INCLUDED_)
#define _EWAY_MEMUBUTTON_H__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// MenuButton.h : header file
//
class CMenuButton : public CButton
{
public:
CMenuButton();
virtual ~CMenuButton();
DECLARE_DYNAMIC( CMenuButton )
enum ArrowType
{
arrowRight,//向右的箭头;
arrowDown, //向下的箭头;
arrowSmallRight, //向右的小箭头;
arrowSmallDown, //向下的小箭头;
arrowUp, //向上的箭头;
arrowLeft//向左的箭头;
}m_ArrowType;
enum PopupPos
{
//名称为TopLeft等等,遵守英文习惯;
posTopLeft, //左上角;
posBottomLeft, //左下角;
posTopRight, //右上角;
posBottomRight, //右下角;
}m_PopupPos;
virtual UINT OnClick(UINT nIDMenuResource,UINT nSubMenu=0);
void SetArrowType(CMenuButton::ArrowType type=CMenuButton::arrowRight);
void SetDrawFocusRect(BOOL bDrawFocusRect=TRUE);
void SetMenuPopupPos(CMenuButton::PopupPos pos=CMenuButton::posBottomLeft);
void SetStyle(BOOL bDrawArrow=TRUE);
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMenuButton)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL
protected:
BOOL m_bDrawFocusRect;
//{{AFX_MSG(CMenuButton)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif // !defined(_EWAY_MEMUBUTTON_H__INCLUDED_)
实现文件:
// MenuButton.cpp : implementation file
//
#include "stdafx.h"
#include "MenuButtonTest.h"
#include "MenuButton.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CMenuButton
IMPLEMENT_DYNAMIC( CMenuButton, CButton )
CMenuButton::CMenuButton()
{
SetArrowType();
SetDrawFocusRect();
SetMenuPopupPos();
}
CMenuButton::~CMenuButton()
{
}
BEGIN_MESSAGE_MAP(CMenuButton, CButton)
//{{AFX_MSG_MAP(CMenuButton)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMenuButton message handlers
void CMenuButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
//使用FromeHandle()创建临时的对象,若使用Attach()则必需在最后使用Detach()
CDC *pDC= CDC::FromHandle(lpDrawItemStruct->hDC);
//得到画笔的颜色;
CPen pen;
if( (lpDrawItemStruct->itemState&ODS_DISABLED) )
{
pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_GRAYTEXT) );
}
else
{
pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT) );
}
CPen * pOldPen = pDC->SelectObject(&pen);
CFont font;
font.CreateFont(12, 0, 0, 0, FW_NORMAL, 0, 0, 0,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH|FF_SWISS, "Marlett");
CFont * pOldFont=pDC->SelectObject(&font);
CSize size=pDC->GetTextExtent("4",1);
//决定箭头的样子,具体的值可在“字符映射表”中查得;
CString strArrow;
switch (m_ArrowType)
{
case CMenuButton::arrowRight:
strArrow="4";
break;
case CMenuButton::arrowDown:
strArrow="6";
break;
case CMenuButton::arrowSmallRight:
strArrow="8";
break;
case CMenuButton::arrowSmallDown:
strArrow="9";
break;
case CMenuButton::arrowUp:
strArrow="5";
break;
case CMenuButton::arrowLeft:
strArrow="3";
break;
default:
ASSERT(FALSE);
}
//计算座标值,用于绘制箭头;
int x=(lpDrawItemStruct->rcItem.right-lpDrawItemStruct->rcItem.left-size.cx)/2;
int y=(lpDrawItemStruct->rcItem.bottom-lpDrawItemStruct->rcItem.top-size.cy)/2;
//画按钮与箭头;
if( (lpDrawItemStruct->itemState&ODS_SELECTED) )
{
pDC->DrawFrameControl(&lpDrawItemStruct->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH|DFCS_PUSHED);
//在按钮被按下时,上面的字符要有一个向右和向下的偏移;
pDC->TextOut(++x,++y,strArrow);
}
else
{
pDC->DrawFrameControl(&lpDrawItemStruct->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH);
pDC->TextOut(x,y,strArrow);
}
//如果需要,画焦点矩形;
if( (lpDrawItemStruct->itemState&ODS_FOCUS) && m_bDrawFocusRect)
{
CRect rectFocus(lpDrawItemStruct->rcItem);
rectFocus.DeflateRect(3,3); //看起来比较接近的值;
pDC->DrawFocusRect(rectFocus);
}
//仅将对象选回即可,不必调用DeleteTempMap();
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldFont);
}
UINT CMenuButton::OnClick(UINT nIDMenuResource, UINT nSubMenu/*=0*/)
{
CMenu menu;
//装载菜单;
VERIFY(menu.LoadMenu(nIDMenuResource) );
//得到子菜单;
CMenu *pPopup = menu.GetSubMenu(nSubMenu);//默认为第一组子菜单;
ASSERT(pPopup != NULL);
CRect rect;
GetWindowRect(rect);
POINT point;
//决定弹出菜单的位置;
switch (m_PopupPos)
{
case CMenuButton::posTopLeft://左上角;
point.x=rect.left;
point.y=rect.top;
break;
case CMenuButton::posBottomLeft://左下角;
point.x=rect.left;
point.y=rect.bottom;
break;
case CMenuButton::posTopRight://右上角;
point.x=rect.right;
point.y=rect.top;
break;
case CMenuButton::posBottomRight://右下角;
point.x=rect.right;
point.y=rect.bottom;
break;
default:
ASSERT(FALSE);
}
//弹出菜单;
UINT nMenuSel = pPopup->TrackPopupMenu((TPM_LEFTALIGN|TPM_LEFTBUTTON |TPM_NONOTIFY |TPM_RETURNCMD),point.x, point.y, this);
pPopup->DestroyMenu();
//返回被选择的菜单的ID,如果无任何有效的选择,将返回0;
return nMenuSel;
}
void CMenuButton::PreSubclassWindow()
{
CButton::PreSubclassWindow();
//默认值:加入BS_OWNERDRAW风格;
ModifyStyle(0,BS_OWNERDRAW);
}
void CMenuButton::SetArrowType(CMenuButton::ArrowType type)
{
m_ArrowType=type;
}
void CMenuButton::SetDrawFocusRect(BOOL bDrawFocusRect)
{
m_bDrawFocusRect=bDrawFocusRect;
}
void CMenuButton::SetMenuPopupPos(CMenuButton::PopupPos pos)
{
m_PopupPos=pos;
}
void CMenuButton::SetStyle(BOOL bDrawArrow)
{
if(bDrawArrow)
{
ModifyStyle(0,BS_OWNERDRAW,SWP_NOMOVE|SWP_NOZORDER| SWP_NOSIZE);
}
else
{
ModifyStyle(BS_OWNERDRAW,0,SWP_NOMOVE|SWP_NOZORDER| SWP_NOSIZE);
}
}
要使用这个类,为对话框添加CMenuButton类型的按钮成员变量,若需改变默认风格,则可在OnInitDialog中调用CMenuButton类的公有成员函数SetArrowType()、SetDrawFocusRect()、SetMenuPopupPos()或SetStyle(),在ClassWizard中为对话框添加按钮的BN_CLICKED消息处理函数,然后在其中调用CMenuButton类的OnClick()成员函数,并指定一个菜单ID给它,最后处理OnClick()函数的返回值即可。下面是一个例子。
void CMenuButtonTestDlg::OnTest()
{
UINT nSel=m_btnTest.OnClick(IDR_POPUP);
switch(nSel)
{
case ID_APP_EXIT:
SendMessage(WM_CLOSE,0,0);
break;
case ID_POPUP_ITEM1:
AfxMessageBox("您选择了第一项!");
break;
case ID_POPUP_ITEM2:
AfxMessageBox("您选择了第二项!");
break;
case ID_POPUP_ITEM3:
AfxMessageBox("您选择了第三项!");
break;
default:
//Do nothing;
;
}
}
所用菜单的资源描述如下,外观可参见文首的图。
IDR_POPUP MENU DISCARDABLE
BEGIN
POPUP "_POPUP_"
BEGIN
MENUITEM "第一项", ID_POPUP_ITEM1
MENUITEM "第二项", ID_POPUP_ITEM2
MENUITEM "第三项", ID_POPUP_ITEM3
MENUITEM SEPARATOR
MENUITEM "退出(&X)", ID_APP_EXIT
END
END
BOOL CMenuButtonTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//因为这几个值都是默认值,所以注释掉,仅为了演示用法;
//m_btnTest.SetDrawFocusRect(TRUE);
//m_btnTest.ArrowType (CMenuButton::arrowRight);
//m_btnTest.SetStyle(TRUE);
//m_btnTest.SetMenuPopupPos(CMenuButton::posBottomLeft);
return TRUE;
}
值得补充说明的是,使用CMenuButton类的时候,并不一定需要在对话框模板中为按钮指定BS_OWNERDRAW风格,因为在缺省情况下,CMenuButton类的PreSubclassWindow()函数中已经自动加入了这一风格。
- (转)用MFC创建菜单按钮
- 用MFC创建菜单按钮
- MFC - 菜单按钮
- xla创建菜单按钮
- MFC批量创建按钮
- MFC 创建一个按钮
- MFC 单击按钮弹出菜单
- MFC点击按钮弹出菜单
- MFC动态创建菜单
- MFC创建菜单
- MFC动态创建菜单
- MFC创建菜单
- MFC动态创建菜单
- MFC创建窗口菜单
- vc mfc按钮的创建
- mfc中动态创建按钮
- 创建和编辑菜单 MFC
- MFC动态创建右键菜单
- java学习 暑假第四天 包。。。
- display标签的用法
- 使用ar命令查看静态库中目标文件
- 反编译 class 为 java 文件
- 【绪论】算法为什么重要?
- (转)用MFC创建菜单按钮
- gcc 内置宏配合调试
- VC位图按钮相关实现方法总结
- Javascript开发经验谈
- 使用 PHP 构建的 Web 应用如何避免 XSS 攻击
- 好好学习
- 只要死不了,总有出头的一天
- gcc -fPIC选项
- 判断little endian和big endian