现在的位置: 首页 > 综合 > 正文

VC MFC(Custom Control)自定义控件

2014年02月18日 ⁄ 综合 ⁄ 共 7927字 ⁄ 字号 评论关闭

关于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<m_itemCount;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<m_itemCount;m_CurMouseItem++)
  if(m_itemRect[m_CurMouseItem].PtInRect(pt))
   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个。。。。不过咱这毕竟不是要搞出一个强大的控件来(貌似咱现在也搞不出来),只是仅仅作为一个实验性的例子,用于固定的模式下。足够了。

抱歉!评论已关闭.