关于Custom Control自定义控件的介绍,有一篇文章感觉不错,不过是英文的,英语不好的也没关系,不是有“有道”吗?再说了,我们只要看代码就行了,而且步骤也就那么几步,结合教程图片稍微猜一下也就知道了。
原文地址:http://www.codeproject.com/KB/static/CustomControl.aspx
接下来我把上面教程的操作步骤简单描述一下:
首先往对话框添加一个“自定义控件”,然后右击该控件选择“建立类向导”,在弹出的对话框点击“Add Class...”,选择“New...",
之后“Name"项填CMyCustomControl, “Base Class"选择generic CWnd,点确定。
给CMyCustomControl类添加如下函数:
BOOL CMyCustomControl::RegisterWndClass()
{
WNDCLASS windowclass;
HINSTANCE hInst=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();
return FALSE;
}
}
return TRUE;
}
在MyCustomControl类构造函数中调用RegisterWndClass函数,如下:
CMyCustomControl::CMyCustomControl()
{
RegisterWndClass();
}
接着右击对话框里“自定义控件”,选择属性,“种类”框里填上MyDrawPad(窗口类)。给对话框类添加一个CMyCustomControl类对象。如:
CMyCustomControl m_drawpad;(记得包含头文件)。对话框类的DoDataExchange函数里添加如下语句:
void CThirdDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CThirdDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}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:
CRect m_itemRect[10];//各项区域
UINT m_itemID[10];//各项ID号
CImageList *m_pImageList;//各项图标
int m_itemCount;//项数量
UINT m_BKID;//控件背景位图ID号
UINT m_itemMOverID;//项鼠标停留时的背景位图ID号
UINT m_itemMPushID;//项鼠标按下时的背景位图ID号
int m_itemWidth;//项宽
int m_itemHeight;//项高
int m_itemSpacing;//项区域间距,上下左右。
int m_CurMouseItem;//记录当前鼠标位置所在项
int m_TimerID;//计时器ID号
BOOL m_MousePush;//鼠标是否在按下中
响应自定义控件(CMyCustomControl) WM_ERASEBKGND、WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEMOVE、WM_PAINT、WM_TIMER消息,各消息处理函数如下:
BOOL CMyCustomControl::OnEraseBkgnd(CDC* pDC)
void CMyCustomControl::OnLButtonDown(UINT nFlags, CPoint point)
void CMyCustomControl::OnLButtonUp(UINT nFlags, CPoint point)
void CMyCustomControl::OnMouseMove(UINT nFlags, CPoint point)
void CMyCustomControl::OnPaint()
void CMyCustomControl::OnTimer(UINT nIDEvent)
添加如下成员函数:
BOOL CMyCustomControl::AddItem(UINT nID)//此函数添加项
//设置控件背景位图ID,项状态位图背景ID(鼠标停留,单击)
void CMyCustomControl::SetBitmapIDs(UINT BKID, UINT itemMOverID, UINT itemMPushID)
BOOL CMyCustomControl::SetImageList(CImageList *pImageList)//设置图像列表
BOOL CMyCustomControl::SetItemSize(int Width, int Height,int itemSpacing)//设置项宽高
构造函数中初始化部分成员变量:
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)派生出的类,则不要调用此语句
}
各函数代码如下:
BOOL CMyCustomControl::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
return TRUE;//直接返回真
//return CWnd::OnEraseBkgnd(pDC);//禁止父类擦除背景,重画。
}
void CMyCustomControl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_MousePush=TRUE;//鼠标左键处于按下状态
this->Invalidate();//刷新控件窗口
CWnd::OnLButtonDown(nFlags, point);
}
void CMyCustomControl::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
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);
}
void CMyCustomControl::OnMouseMove(UINT nFlags, CPoint point)
{
if(m_CurMouseItem==-1)
SetTimer(m_TimerID,50,NULL);//设置计时器
CWnd::OnMouseMove(nFlags, point);
}
void CMyCustomControl::OnPaint()
{
CPaintDC dc(this); // device context for painting
//先在内存DC里完成所有要进行的画图操作,然后再把内存DC里的画图(数据)一次性画上去。
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CRect rect;
GetClientRect(rect);
CBitmap bmp;
bmp.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());
memDC.SelectObject(&bmp);
if(m_BKID!=NULL) //画位图背景
{
CDC bmpDC;
bmpDC.CreateCompatibleDC(&memDC);
CBitmap mBmp;
mBmp.LoadBitmap(m_BKID);
bmpDC.SelectObject(&mBmp);
BITMAP bmpInfo;
mBmp.GetBitmap(&bmpInfo);
memDC.StretchBlt(0,0,rect.Width(),rect.Height(),&bmpDC,0,0,
bmpInfo.bmWidth,bmpInfo.bmHeight,SRCCOPY);
}
int spacing=0;
if(m_CurMouseItem!=-1)
{
CDC bmpDC;
bmpDC.CreateCompatibleDC(&memDC);
CBitmap mBmp;
if(m_MousePush)
{
spacing=2;
mBmp.LoadBitmap(m_itemMPushID);
}
else mBmp.LoadBitmap(m_itemMOverID);
bmpDC.SelectObject(&mBmp);
BITMAP bmpInfo;
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(int i=0;i
POINT pt;
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: Add your message handler code here
}
void CMyCustomControl::OnTimer(UINT nIDEvent)
{
int oldMouseItem=m_CurMouseItem;
// TODO: Add your message handler code here and/or call default
POINT pt;
::GetCursorPos(&pt);//获取鼠标当前位置
ScreenToClient(&pt);//转换成相对于客户区坐标
//判断当前鼠标是否在某一个项上
for(m_CurMouseItem=0;m_CurMouseItem
break;
if(m_CurMouseItem==m_itemCount)//不在所有项上
{
m_CurMouseItem=-1;
KillTimer(m_TimerID);
m_MousePush=FALSE;
this->Invalidate();
}
else if(m_CurMouseItem!=oldMouseItem)//鼠标位置所在项已改变
{
this->Invalidate();
}
CWnd::OnTimer(nIDEvent);
}
BOOL CMyCustomControl::AddItem(UINT nID)
{
if(m_itemCount==10) return FALSE;//增加项超过10个
m_itemID[m_itemCount]=nID;
//计算设置各项区域位置
if(m_itemCount==0)
{
m_itemRect[0]=CRect::CRect(m_itemSpacing,m_itemSpacing,
m_itemWidth+m_itemSpacing,m_itemHeight+m_itemSpacing);
}
else
{
m_itemRect[m_itemCount].left=m_itemRect[m_itemCount-1].right+m_itemSpacing;
m_itemRect[m_itemCount].top=m_itemSpacing;
m_itemRect[m_itemCount].right=m_itemRect[m_itemCount].left+m_itemWidth;
m_itemRect[m_itemCount].bottom=m_itemSpacing+m_itemHeight;
}
//根据当前项信息调整控件窗口大小,位置。
MoveWindow(0,0,m_itemRect[m_itemCount].right+m_itemSpacing,
m_itemRect[m_itemCount].bottom+m_itemSpacing);
m_itemCount++;//项数量增一
return TRUE;
}
//设置控件背景位图ID,项状态位图背景ID(鼠标停留,单击)
void CMyCustomControl::SetBitmapIDs(UINT BKID, UINT itemMOverID, UINT itemMPushID)
{
m_BKID=BKID;
m_itemMOverID=itemMOverID;
m_itemMPushID=itemMPushID;
}
BOOL CMyCustomControl::SetImageList(CImageList *pImageList)//设置图像列表
{
if(m_pImageList!=NULL) return FALSE;
m_pImageList=pImageList;
return TRUE;
}
BOOL CMyCustomControl::SetItemSize(int Width, int Height,int itemSpacing)//设置项宽高
{
if(m_itemCount!=0) return FALSE;//必须在调用AddItemText函数之前设置
m_itemWidth=Width;
m_itemHeight=Height;
m_itemSpacing=itemSpacing;
return TRUE;
}
对话框类(父窗口)初始化函数添加如下代码:
m_ImageList.Create(32,32,ILC_COLOR32|ILC_MASK,1,0);//m_ImageList是对话框类成员变量:CImageList m_ImageList;
for(int i=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);
运行效果如下图:
如果要处理项按钮单击消息的话,需手动添加消息映射,方法跟添加工具栏控件项单击消息映射一样。前面给第一项设置ID号为1001,那么添加这个项单击消息映射就是:
ON_COMMAND(1001,OnItem1)//OnItem1为消息处理函数
工程代码(VC 6.0)下载地址:http://ishare.iask.sina.com.cn/f/16967994.html
这个控件的局限性很大,在用法上也有诸多限制,如必须调用SetItemSize函数设置好项大小,才可以调用AddItem函数添加项,也就是说调用AddItem函数后,就不可以再调用SetItemSize函数,添加项也不可以超过10个。。。。不过咱这毕竟不是要搞出一个强大的控件来(貌似咱现在也搞不出来),只是仅仅作为一个实验性的例子,用于固定的模式下。足够了。