MFC自定义控件.docx
《MFC自定义控件.docx》由会员分享,可在线阅读,更多相关《MFC自定义控件.docx(17页珍藏版)》请在冰点文库上搜索。
MFC自定义控件
关于CustomControl自定义控件的介绍,有一篇文章感觉不错,不过是英文的,英语不好的也没关系,不是有“有道”吗?
再说了,我们只要看代码就行了,而且步骤也就那么几步,结合教程图片稍微猜一下也就知道了。
原文地址:
接下来我把上面教程的操作步骤简单描述一下:
1、首先往对话框添加一个“自定义控件”,然后右击该控件选择“建立类向导”,在弹出的对话框点击“AddClass...”,选择“New...",
之后“Name"项填CMyCustomControl,“BaseClass"选择genericCWnd,点确定。
在MyCustomControl.h
添加函数声明BOOLRegisterWndClass();
给CMyCustomControl类添加如下函数:
BOOLCMyCustomControl:
:
RegisterWndClass()
{
WNDCLASSwindowclass;
HINSTANCEhInst=AfxGetInstanceHandle();
if(!
:
:
GetClassInfo(hInst,"MyDrawPad",&windowclass))
{
windowclass.style=CS_DBLCLKS;
windowclass.lpfnWndProc=:
:
DefWindowProc;
windowclass.cbClsExtra=windowclass.cbWndExtra=0;
windowclass.hInstance=hInst;
windowclass.hIcon=NULL;
windowclass.hCursor=AfxGetApp()->LoadStandardCursor(IDC_ARROW);
windowclass.hbrBackground=:
:
GetSysColorBrush(COLOR_WINDOW);
windowclass.lpszMenuName=NULL;
windowclass.lpszClassName="MyDrawPad";
if(!
AfxRegisterClass(&windowclass))
{
AfxThrowResourceException();
returnFALSE;
}
}
returnTRUE;
}
在MyCustomControl类构造函数中调用RegisterWndClass函数,如下:
CMyCustomControl:
:
CMyCustomControl()
{
RegisterWndClass();
}
接着右击对话框里“自定义控件”,选择属性,“种类”框里填上MyDrawPad(窗口类)。
给对话框类添加一个CMyCustomControl类对象(是在*Dlg.h)。
如:
CMyCustomControlm_drawpad;(记得包含头文件)。
#include"MyCustomControl.h"
对话框类的DoDataExchange函数里添加如下语句:
voidCThirdDlg:
:
DoDataExchange(CDataExchange*pDX)
{
CDialog:
:
DoDataExchange(pDX);
//{{AFX_DATA_MAP(CThirdDlg)
//NOTE:
theClassWizardwilladdDDXandDDVcallshere
//}}AFX_DATA_MAP
DDX_Control(pDX,IDC_CUSTOM1,m_drawpad);//IDC_CUSTOM1为自定义控件的ID号
}
到了这里,自定义控件的基本问题算是解决了。
可自行给CMyCustomControl类添加消息响应函数,运行一下吧。
自定义控件仿工具栏
标题名是仿工具栏控件,其实也只是仿下工具栏那个模式而已,想一下,工具栏类里有那么多个函数,能仿得过来吗?
如果有谁觉得用自定义控件模仿这个太麻烦的话,也可直接从按钮控件(CButton)派生出类来代替“自定义控件”类。
我这里就直接以上面工程的CMyCustomControl类为例。
1、首先往工程里引三张图片如下:
2、再引入六个图标,用于项显示图标,ID号保持默认不变,
3、CMyCustomControl添加如下成员变量(私有变量)
4、响应自定义控件(CMyCustomControl)WM_ERASEBKGND、WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEMOVE、WM_PAINT、WM_TIMER消息
5、添加如下成员函数
6、构造函数中初始化部分成员变量(私有变量初始化)
7、添加各函数代码
8、成员函数要在.h中的public中声明
voidSetBitmapIDs(UINTBKID,UINTitemMOverID,UINTitemPushID);
BOOLSetItemSize(intWidth,intHeight,intitemSpacing=5);
BOOLSetImageList(CImageList*pImageList);
BOOLAddItem(UINTnID);
9、在*Dlg.h中定义一个需要用到的变量
CImageListm_ImageList;
10、在*.cpp中添加初始化响应函数
m_ImageList.Create(32,32,ILC_COLOR32|ILC_MASK,1,0);
for(inti=0;i<6;i++)
m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON1+i));
m_drawpad.SetImageList(&m_ImageList);
m_drawpad.SetBitmapIDs(IDB_BK,IDB_ITEMOVER,IDB_ITEMPUSH);
m_drawpad.SetItemSize(38,40,3);
for(i=0;i<6;i++)
m_drawpad.AddItem(1001+i);
11、如果要处理项按钮单击消息的话,需手动添加消息映射,方法跟添加工具栏控件项单击消息映射一样。
前面给第一项设置ID号为1001,那么添加这个项单击消息映射就是:
在*Dlg.cpp中的BEGIN_MESSAGE_MAP(CThirdDlg,CDialog)函数下添加
ON_COMMAND(1001,OnItem1)//OnItem1为消息处理函数
BEGIN_MESSAGE_MAP(CThirdDlg,CDialog)
END_MESSAGE_MAP()为消息映射区域
如何获知ID号:
查看Resource.h
成员变量的初始化可以再构造函数中进行。
存在分配相同ID的可能。
详情见下:
ID的分配是通过头文件控制的。
你看你的resource.h的最后几行(注释是我加的,源文件里没有):
#ifndefAPSTUDIO_READONLY_SYMBOLS
#define_APS_NEXT_RESOURCE_VALUE2000//下一个资源的ID,比如字符串资源,位图资源等
#define_APS_NEXT_COMMAND_VALUE40001//下一个command的ID,这个一般是作为WM_COMMAND消息的参数用的。
#define_APS_NEXT_CONTROL_VALUE1001//下一个控件的ID
#define_APS_NEXT_SYMED_VALUE101//下一个符号的ID
#endif
比如,如果这个时候你用VC的对话框插入一个字符串,那么这个字符串的ID就会自动被分配成2000,同时_APS_NEXT_RESOURCE_VALUE会被改成2001。
其他几个值也是以类似方式工作的。
所以其实VC自动分配的ID号,跟你自己定义的是有可能重复的。
你必须自己保证没有重复,而不是依赖于VC帮你检查。
一个办法是,比如你要新加一个控件,你可以用_APS_NEXT_CONTROL_VALUE的值作为这个控件的ID,然后自己手动把_APS_NEXT_CONTROL_VALUE的值加1,这样就能保持跟VC的ID分配机制一致。
全文复制如下:
关于CustomControl自定义控件的介绍,有一篇文章感觉不错,不过是英文的,英语不好的也没关系,不是有“有道”吗?
再说了,我们只要看代码就行了,而且步骤也就那么几步,结合教程图片稍微猜一下也就知道了。
原文地址:
接下来我把上面教程的操作步骤简单描述一下:
首先往对话框添加一个“自定义控件”,然后右击该控件选择“建立类向导”,在弹出的对话框点击“AddClass...”,选择“New...",
之后“Name"项填CMyCustomControl,“BaseClass"选择genericCWnd,点确定。
给CMyCustomControl类添加如下函数:
BOOLCMyCustomControl:
:
RegisterWndClass()
{
WNDCLASSwindowclass;
HINSTANCEhInst=AfxGetInstanceHandle();
if(!
:
:
GetClassInfo(hInst,"MyDrawPad",&windowclass))
{
windowclass.style=CS_DBLCLKS;
windowclass.lpfnWndProc=:
:
DefWindowProc;
windowclass.cbClsExtra=windowclass.cbWndExtra=0;
windowclass.hInstance=hInst;
windowclass.hIcon=NULL;
windowclass.hCursor=AfxGetApp()->LoadStandardCursor(IDC_ARROW);
windowclass.hbrBackground=:
:
GetSysColorBrush(COLOR_WINDOW);
windowclass.lpszMenuName=NULL;
windowclass.lpszClassName="MyDrawPad";
if(!
AfxRegisterClass(&windowclass))
{
AfxThrowResourceException();
returnFALSE;
}
}
returnTRUE;
}
在MyCustomControl类构造函数中调用RegisterWndClass函数,如下:
CMyCustomControl:
:
CMyCustomControl()
{
RegisterWndClass();
}
接着右击对话框里“自定义控件”,选择属性,“种类”框里填上MyDrawPad(窗口类)。
给对话框类添加一个CMyCustomControl类对象。
如:
CMyCustomControlm_drawpad;(记得包含头文件)。
对话框类的DoDataExchange函数里添加如下语句:
voidCThirdDlg:
:
DoDataExchange(CDataExchange*pDX)
{
CDialog:
:
DoDataExchange(pDX);
//{{AFX_DATA_MAP(CThirdDlg)
//NOTE:
theClassWizardwilladdDDXandDDVcallshere
//}}AFX_DATA_MAP
DDX_Control(pDX,IDC_CUSTOM1,m_drawpad);//IDC_CUSTOM1为自定义控件的ID号
}
到了这里,自定义控件的基本问题算是解决了。
可自行给CMyCustomControl类添加消息响应函数,运行一下吧。
自定义控件仿工具栏
标题名是仿工具栏控件,其实也只是仿下工具栏那个模式而已,想一下,工具栏类里有那么多个函数,能仿得过来吗?
如果有谁觉得用自定义控件模仿这个太麻烦的话,也可直接从按钮控件(CButton)派生出类来代替“自定义控件”类。
我这里就直接以上面工程的CMyCustomControl类为例。
首先往工程里引三张图片如下:
自定义控件背景,ID号:
IDB_BK
鼠标停留时,项背景,ID号:
IDB_ITMEOVER
鼠标单击时,项背景,ID号:
IDB_ITEMPUSH
再引入六个图标,用于项显示图标,ID号保持默认不变,IDI_ICON1、IDI_ICON2。
。
。
IDI_ICON6
CMyCustomControl添加如下成员变量:
private:
CRectm_itemRect[10];//各项区域
UINTm_itemID[10];//各项ID号
CImageList*m_pImageList;//各项图标
intm_itemCount;//项数量
UINTm_BKID;//控件背景位图ID号
UINTm_itemMOverID;//项鼠标停留时的背景位图ID号
UINTm_itemMPushID;//项鼠标按下时的背景位图ID号
intm_itemWidth;//项宽
intm_itemHeight;//项高
intm_itemSpacing;//项区域间距,上下左右。
intm_CurMouseItem;//记录当前鼠标位置所在项
intm_TimerID;//计时器ID号
BOOLm_MousePush;//鼠标是否在按下中
响应自定义控件(CMyCustomControl)WM_ERASEBKGND、WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEMOVE、WM_PAINT、WM_TIMER消息,各消息处理函数如下:
BOOLCMyCustomControl:
:
OnEraseBkgnd(CDC*pDC)
voidCMyCustomControl:
:
OnLButtonDown(UINTnFlags,CPointpoint)
voidCMyCustomControl:
:
OnLButtonUp(UINTnFlags,CPointpoint)
voidCMyCustomControl:
:
OnMouseMove(UINTnFlags,CPointpoint)
voidCMyCustomControl:
:
OnPaint()
voidCMyCustomControl:
:
OnTimer(UINTnIDEvent)
添加如下成员函数:
BOOLCMyCustomControl:
:
AddItem(UINTnID)//此函数添加项
//设置控件背景位图ID,项状态位图背景ID(鼠标停留,单击)
voidCMyCustomControl:
:
SetBitmapIDs(UINTBKID,UINTitemMOverID,UINTitemMPushID)
BOOLCMyCustomControl:
:
SetImageList(CImageList*pImageList)//设置图像列表
BOOLCMyCustomControl:
:
SetItemSize(intWidth,intHeight,intitemSpacing)//设置项宽高
构造函数中初始化部分成员变量:
CMyCustomControl:
:
CMyCustomControl()
{
m_pImageList=NULL;
m_itemCount=0;
m_itemWidth=26;
m_itemHeight=30;
m_BKID=NULL;
m_itemSpacing=5;
m_CurMouseItem=-1;
m_TimerID=1500;
m_MousePush=FALSE;
RegisterWndClass();//如果是从按钮控件(CButton)派生出的类,则不要调用此语句
}
各函数代码如下:
BOOLCMyCustomControl:
:
OnEraseBkgnd(CDC*pDC)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
returnTRUE;//直接返回真
//returnCWnd:
:
OnEraseBkgnd(pDC);//禁止父类擦除背景,重画。
}
voidCMyCustomControl:
:
OnLButtonDown(UINTnFlags,CPointpoint)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
m_MousePush=TRUE;//鼠标左键处于按下状态
this->Invalidate();//刷新控件窗口
CWnd:
:
OnLButtonDown(nFlags,point);
}
voidCMyCustomControl:
:
OnLButtonUp(UINTnFlags,CPointpoint)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
m_MousePush=FALSE;
this->Invalidate();//刷新(控件)窗口
if(m_CurMouseItem!
=-1)//给父窗口发送项被单击消息
GetParent()->SendMessage(WM_COMMAND,
MAKEWPARAM(m_itemID[m_CurMouseItem],0),LPARAM(GetSafeHwnd()));
CWnd:
:
OnLButtonUp(nFlags,point);
}
voidCMyCustomControl:
:
OnMouseMove(UINTnFlags,CPointpoint)
{
if(m_CurMouseItem==-1)
SetTimer(m_TimerID,50,NULL);//设置计时器
CWnd:
:
OnMouseMove(nFlags,point);
}
voidCMyCustomControl:
:
OnPaint()
{
CPaintDCdc(this);//devicecontextforpainting
//先在内存DC里完成所有要进行的画图操作,然后再把内存DC里的画图(数据)一次性画上去。
CDCmemDC;
memDC.CreateCompatibleDC(&dc);
CRectrect;
GetClientRect(rect);
CBitmapbmp;
bmp.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());
memDC.SelectObject(&bmp);
if(m_BKID!
=NULL)//画位图背景
{
CDCbmpDC;
bmpDC.CreateCompatibleDC(&memDC);
CBitmapmBmp;
mBmp.LoadBitmap(m_BKID);
bmpDC.SelectObject(&mBmp);
BITMAPbmpInfo;
mBmp.GetBitmap(&bmpInfo);
memDC.StretchBlt(0,0,rect.Width(),rect.Height(),&bmpDC,0,0,
bmpInfo.bmWidth,bmpInfo.bmHeight,SRCCOPY);
}
intspacing=0;
if(m_CurMouseItem!
=-1)
{
CDCbmpDC;
bmpDC.CreateCompatibleDC(&memDC);
CBitmapmBmp;
if(m_MousePush)
{
spacing=2;
mBmp.LoadBitmap(m_itemMPushID);
}
elsemBmp.LoadBitmap(m_itemMOverID);
bmpDC.SelectObject(&mBmp);
BITMAPbmpInfo;
mBmp.GetBitmap(&bmpInfo);
memDC.StretchBlt(m_itemRect[m_CurMouseItem].left,m_itemRect[m_CurMouseItem].top,
m_itemRect[m_CurMouseItem].Width(),m_itemRect[m_CurMouseItem].Height(),
&bmpDC,0,0,bmpInfo.bmWidth,bmpInfo.bmHeight,SRCCOPY);
}
for(inti=0;i {
POINTpt;
pt.x=m_itemRect[i].left+2;
pt.y=m_itemRect[i].top+2;
if(i==m_CurMouseItem)
{pt.x+=spacing;pt.y+=spacing;}
m_pImageList->Draw(&memDC,i,pt,ILD_NORMAL);
}
dc.BitBlt(0,0,rect.Width(),rect.Height(),&memDC,0,0,SRCCOPY);
//TODO:
Addyourmessagehandlercodehere
}
voidCMyCustomControl:
:
OnTimer(UINTnIDEvent)
{
intoldMouseItem=m_CurMouseItem;
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
POINTpt;
:
:
GetCursorPos(&pt);//获取鼠标当前位置
ScreenToClient(&pt);//转换成相对于客户区坐标
//判断当前鼠标是否在某一个项上
for(m_CurMouseItem=0;m_CurMouseItem if(m_itemRect[m_CurMouseItem].PtInRect(pt))
break;
if(m_CurMouseItem==m_itemCount)//不在所有项上
{
m_CurMouseItem=-1;
KillTimer(m_TimerID);
m_MousePush=