第2章控件应用
2.1 按钮控件典型实例
个性化的按钮可以美化界面,从而吸引更多的用户,本节将介绍创建各种不同风格的按钮的方法。
实例040 AVI动画按钮
本实例是一个人性化的实例
实例位置:光盘\mingrisoft\02\040
实例说明
在开发程序的时候,经常会用到个性化按钮来美化程序界面,其中,能播放AVI动画的按钮会吸引更多年轻人的目光。运行程序,当鼠标光标在控件上方移动时,按钮将产生动画效果,效果如图2.1所示。
图2.1 AVI动画按钮
技术要点
本实例主要是通过使用动画控件和设置鼠标的消息响应来实现的。
首先通过CAnimateCtrl类来创建和使用动画控件,CAnimateCtrl类的方法如下所示。
(1)Open方法。从一个文件或资源打开一个AVI文件,并显示第一帧。语法如下:
BOOL Open( LPCTSTR lpszFileName ); BOOL Open( UINT nID );
参数说明:
● lpszFileName:AVI文件名。
● nID:资源ID。
(2)Play方法。播放AVI文件。语法如下:
BOOL Play( UINT nFrom, UINT nTo, UINT nRep );
参数说明:
● nFrom:起始帧索引。
● nTo:结束帧索引。
● nRep:是否循环播放。
(3)Seek方法。显示AVI文件中的指定帧,语法如下:
BOOL Seek( UINT nTo );
参数说明:
● nTo:指定帧索引。
(4)Stop方法。停止播放AVI文件,语法如下:
BOOL Stop( );
(5)Close方法。关闭已打开的AVI文件,语法如下:
BOOL Close( );
设置鼠标的WM_MOUSEMOVE消息,消息响应函数原型如下:
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
参数说明:
● nFlags:指示是否按下了各种虚键。
● point:指出光标的横坐标和纵坐标。
实现过程
(1)新建一个基于对话框的应用程序。
(2)向窗体中添加一个按钮控件,用鼠标右键单击按钮控件,在弹出的菜单中选择Properties选项,选择Owner Draw属性。
(3)单击Insert/Resource菜单项,在打开的Insert Resource对话框中单击Import按钮,添加一个AVI文件,在弹出的Custom Resource Type对话框中定制新的资源类型。
(4)以CButton类为基类派生一个CButtonAvi类。
(5)重载CButtonAvi类的DrawItem虚方法,在该方法中创建播放AVI的动画控件,代码如下:
void CButtonAvi::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CRect rect; GetClientRect(rect); //获得按钮的客户区域 if (!::IsWindow(m_Animate)) { m_Animate.Create(WS_CHILD|WS_VISIBLE,rect,this,0); //创建动画控件 m_Animate.Open(m_id); //打开AVI文件资源 m_Animate.GetClientRect(rect); //获得动画控件的客户区域 VERIFY(SetWindowPos(NULL,0,0, rect.Width()+2, rect.Height()+2,SWP_NOMOVE));//设置按钮控件显示位置 m_Animate.MoveWindow(rect); //移动动画控件位置 } CDC*pDC=CDC::FromHandle(lpDrawItemStruct->hDC); //获得按钮控件设备上下文 UINT State=lpDrawItemStruct->itemState; //获得按钮控件状态 DrawButton(pDC,State,rect); //调用自定义函数绘制按钮 }
(6)添加自定义DrawButton,在该函数中绘制按钮控件,代码如下:
void CButtonAvi::DrawButton(CDC *pDC,UINT nState,CRect rect) { COLORREF UpCol,DownCol; if((nState&ODS_SELECTED)==ODS_SELECTED) //选中状态 { UpCol=RGB(0,0,0); //设置上边颜色为黑色 DownCol=RGB(0,0,0); //设置下边颜色为黑色 m_play = false; } else if((nState&ODS_DISABLED)!=ODS_DISABLED) //如果不可用 { UpCol=RGB(255,255,255); //设置上边颜色为白色 DownCol=RGB(128,128,128); //设置下边颜色为灰色 } //画按钮的左边和上边 CPen pen1,pen2; pen1.CreatePen(PS_SOLID,3,UpCol); //创建画笔 pDC->SelectObject(&pen1); //选入画笔 pDC->MoveTo(0,rect.Height()-1); pDC->LineTo(0,0); pDC->LineTo(rect.Width()-1,0); //画按钮的右边和下边 pen2.CreatePen(PS_SOLID,2,DownCol); //创建画笔 pDC->SelectObject(&pen2); //选入画笔 pDC->MoveTo(rect.Width()-1,0); pDC->LineTo(rect.Width()-1,rect.Height()-1); pDC->LineTo(0,rect.Height()-1); pen1.DeleteObject(); pen2.DeleteObject(); }
(7)设置鼠标的WM_MOUSEMOVE消息,在该消息的响应函数中捕获鼠标光标位置,判断是否播放AVI文件,代码如下:
void CButtonAvi::OnMouseMove(UINT nFlags, CPoint point) { ClientToScreen(&point); //将鼠标光标位置转换为屏幕坐标 CRect rc; GetWindowRect(rc); //获得按钮窗口的区域 if(rc.PtInRect(point)) //判断鼠标光标是否在按钮区域内 { if (::IsWindow(m_Animate) && !m_play) { m_Animate.Play(0,-1,1); //播放AVI动画 m_play = true; SetCapture(); //捕获鼠标 } } else { m_play = false; ReleaseCapture(); //释放鼠标 } CButton::OnMouseMove(nFlags, point); }
举一反三
根据本实例,读者可以:
开发AVI文件播放器。
实例041 GIF动画按钮
本实例是一个人性化的实例
实例位置:光盘\mingrisoft\02\041
实例说明
通常情况下,动画按钮会比其他形式的按钮更加吸引人,可是Visual C++中的动画控件只能显示简单的AVI动画,显然这并不能满足用户的要求。本实例将创建一个可以显示GIF动画的按钮,运行程序,效果如图2.2所示。
图2.2 GIF动画按钮
技术要点
本实例使用API函数FindResource、SizeofResource和LoadResource来装载GIF资源。
(1)FindResource函数:该函数确定指定模块中指定类型和名称的资源所在的位置。函数原型如下:
HRSRC FindResource(HMODULE hModule,LPCTSTR lpName,LPCTSTR lpType);
参数说明:
● hModule:处理包含资源的可执行文件的模块。NULL值指定模块句柄指向操作系统,通常情况下创建最近过程的相关位图文件。
● lpName:指定资源名称。
● lpType:指定资源类型。
(2)SizeofResource函数:该函数返回指定资源字节数大小。函数原型如下:
DWORD SizeofResource(HMODULE hModule,HRSRC hReslnfo);
参数说明:
● hModule:包含资源的可执行文件模块的句柄。
● hReslnfo:资源句柄,此句柄必须由函数FindResource或FindResourceEx来创建。
(3)LoadResource函数:该函数装载指定资源到全局存储器。函数原型如下:
HGLOSAL LoadResouare(HMODULE hModule,HRSRC hReslnfo);
参数说明:
● hModule:处理包含资源的可执行文件的模块句柄。若hModule为NULL,系统从当前过程的模块中装载资源。
● hReslnfo:将被装载资源的句柄,它必须由函数FirtdResource或FindResourceEx创建。
实现过程
(1)新建一个基于对话框的应用程序。
(2)向窗体中添加一个按钮控件,选择Owner Draw属性。
(3)以CButton类为基类创建一个按钮类CButtonGif。
(4)主要程序代码如下:
//显示GIF动画 static UINT GifThread(LPVOID GifDC) { CDC* pDC = (CDC*)GifDC; HINSTANCE hAndle = ::AfxGetResourceHandle(); HRSRC hRsrc=::FindResource(hAndle,MAKEINTRESOURCE(IDR_GIF1),"GIF"); //查找资源位置 DWORD word=::SizeofResource(hAndle,hRsrc); //获得资源大小 BYTE*lpBy=(BYTE*)LoadResource(hAndle,hRsrc); //加载资源 BYTE* pByte[200]; DWORD nu[200]; int num = 0; DWORD firstLocation = 0; for(DWORD i=0;i<word;i++) { if(lpBy[i]==0x2c) //如果是图像分隔符 { if(num==0) { firstLocation = i; } pGif nImage = (pGif)&lpBy[i+1]; DWORD number = 1+sizeof(Gif); while(lpBy[i+number]!=0) { number = number+(DWORD)lpBy[i+number]+1; } number++; pByte[num] = new BYTE[number]; for(DWORD n=0;n<number;n++) { *(BYTE*)(pByte[num]+n) = lpBy[i+n]; } nu[num] = number; i = i+number-1; num++; } } while(1) { for(int m=0;m<num;m++) { DWORD dWord; VirtualProtect(lpBy,word,PAGE_READWRITE,&dWord); for(DWORD n=0;n<nu[m];n++) { lpBy[firstLocation+n] = *(BYTE*)(pByte[m]+n); } VirtualProtect(lpBy,word,dWord,NULL); CMemFile mfile(lpBy,word); //构造驻留文件类对象 CArchive aRc(&mfile,CArchive::load|CArchive::bNoFlushOnDelete); //构造CArchive对象 CArchiveStream aRcstream(&aRc); CComQIPtr<IPicture> m_picture; OleLoadPicture((LPSTREAM)&aRcstream,0,false,IID_IPicture,(void**)&m_picture); long x,y; m_picture->get_Width(&x); m_picture->get_Height(&y); CSize size(x,y); pDC->HIMETRICtoDP(&size); CRect rect; m_picture->Render(*pDC,0,0,size.cx,size.cy,0,y,x,-y,&rect); Sleep(30); } } return 1; } //播放GIF动画 void CButtonGif::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC*pDC=this->GetDC(); //获得设备上下文指针 if(!m_play) { AfxBeginThread(GifThread,(LPVOID)pDC); //开启线程 m_play = true; } CRect rect(-1,-1,180,59); UINT State=lpDrawItemStruct->itemState; //设置按钮状态 DrawButton(pDC,State,rect); //绘制按钮 }
举一反三
根据本实例,读者可以:
开发GIF播放器。
实例042 图文按钮
本实例是一个提高效率、人性化的程序
实例位置:光盘\mingrisoft\01\042
实例说明
通常情况下,MFC提供的按钮CButton并不能显示图标。如果在应用程序的按钮中显示一个图标,将使程序更加美观。运行程序,图文按钮效果如图2.3所示。
图2.3 图文按钮
技术要点
在Visual C++中,可以通过改写CButton的DrawItem方法实现自定义按钮的绘制。在按钮中绘制图标主要使用了DrawItem方法中lpDrawItemStruct参数的hDC成员来实现。DrawItem方法是一个虚拟方法,用于绘制控件的外观。当按钮控件包含BS_OWNERDRAW风格时,应用程序将自动调用DrawItem方法绘制按钮。语法如下:
virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
参数说明:
● lpDrawItemStruct:是一个DRAWITEMSTRUCT结构指针。其结构成员如下。
● CtlType:表示控件的类型。可选值如下。
■ ODT_BUTTON:按钮。
■ ODT_COMBOBOX:组合框。
■ ODT_LISTBOX :列表框。
■ ODT_MENU :菜单。
■ ODT_LISTVIEW:列表视图。
■ ODT_STATIC:静态控件。
■ ODT_TAB:标签控件。
● CtlID:表示控件ID。
● ItemID:表示菜单项ID或列表框、组合框中的项目索引。
● ItemAction:表示绘画的动作。可选值如下。
■ ODA_DRAWENTIRE:整个控件需要被绘制时设置该标识。
■ ODA_FOCUS:控件获得或失去焦点时设置该标识。
■ ODA_SELECT:表示控件处于选中状态时设置该标识。
● ItemState:表示需要绘画的状态。可选值如下。
■ ODS_CHECKED:菜单项被选中。
■ ODS_DISABLED:控件不可用。
■ ODS_FOCUS:控件获得焦点。
■ ODS_GRAYED:控件处于灰色状态,只用于菜单控件。
■ ODS_SELECTED:控件被选中。
■ ODS_COMBOBOXEDIT:组合框中编辑控件的文本被选中。
■ ODS_DEFAULT:默认状态。
■ HwndItem:表示控件的句柄。
■ HDC:控件的画布句柄。
■ RcItem:控件的矩形区域。
■ ItemData:控件的附加信息。
实现过程
(1)新建一个基于对话框的应用程序。
(2)向窗体中添加4个按钮控件,全都选择Owner Draw属性,向工程中添加4个ICO图标资源。
(3)以CButton类为基类创建一个按钮类CImageButton。
(4)主要程序代码如下:
void ImageButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC dc ; dc.Attach(lpDrawItemStruct->hDC); //获得设备上下文 if (m_pImagelist) { UINT state=lpDrawItemStruct->itemState; //获取状态 UINT action = lpDrawItemStruct ->itemAction; //获取图像列中图像的大小 IMAGEINFO imageinfo; m_pImagelist->GetImageInfo(m_ImageIndex,&imageinfo); CSize imagesize; imagesize.cx = imageinfo.rcImage.right-imageinfo.rcImage.left; imagesize.cy = imageinfo.rcImage.bottom - imageinfo.rcImage.top; //在按钮垂直方向居中显示图标 CRect rect; GetClientRect(rect); CPoint point; point.x = 5; point.y = (rect.Height() - imagesize.cy)/2; m_pImagelist->Draw(&dc,m_ImageIndex,point,ILD_NORMAL|ILD_TRANSPARENT);//绘制图标 //按钮被选中或者获得焦点时 if ((state&ODS_SELECTED)||(state&ODS_FOCUS)) { CRect focusRect(rect); //焦点矩形 focusRect.DeflateRect(4,4,4,4); //设置区域 CPen pen(PS_DASHDOTDOT,1,RGB(0,0,0)); //创建画笔 CBrush brush; brush.CreateStockObject(NULL_BRUSH); //创建画刷 dc.SelectObject(&brush); //选入画刷 dc.SelectObject(&pen); //选入画笔 //绘制焦点矩形 dc.DrawFocusRect(focusRect); //绘制立体效果 dc.DrawEdge(rect,BDR_RAISEDINNER|BDR_RAISEDOUTER,BF_BOTTOMLEFT|BF_TOPRIGHT); //获得焦点时绘制黑色边框 dc.Draw3dRect(rect,RGB(51,51,51),RGB(0,0,0)); } else //默认情况下 { CRect focusRect(rect); //焦点矩形 focusRect.DeflateRect(4,4,4,4); CPen pen(PS_DOT,1,RGB(192,192,192)); //创建画笔 CBrush brush; brush.CreateStockObject(NULL_BRUSH); //创建画刷 dc.SelectObject(&brush); dc.SelectObject(&pen); dc.Rectangle(focusRect); //绘制矩形 //绘制立体效果 dc.DrawEdge(rect,BDR_RAISEDINNER|BDR_RAISEDOUTER,BF_BOTTOMLEFT|BF_TOPRIGHT); } if (IsPressed) //在按钮被按下时绘制按下效果 { CRect focusRect1(rect); focusRect1.DeflateRect(4,4,4,4); dc.DrawFocusRect(focusRect1); //绘制焦点矩形 dc.DrawEdge(rect,BDR_SUNKENINNER |BDR_SUNKENOUTER ,BF_BOTTOMLEFT|BF_TOPRIGHT); dc.Draw3dRect(rect,RGB(51,51,51),RGB(0,0,0)); //绘制3D边框 } CString text; GetWindowText(text); //获得按钮文本 rect.DeflateRect(point.x+imagesize.cx+2,0,0,0); //设置文本显示区域 dc.SetBkMode(TRANSPARENT); //设置背景透明 dc.DrawText(text,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER); //绘制按钮文本 } }
举一反三
根据本实例,读者可以:
实现位图按钮。
实例043 热点按钮
本实例是一个提高效率、人性化的程序
实例位置:光盘\mingrisoft\02\043
实例说明
通常情况下,人们的眼睛会追随动态的东西,所以当鼠标滑过热点按钮时,按钮发生变化,自然就可以引起用户的注意了,热点按钮就可以起到这样的效果。运行程序,热点按钮效果如图2.4所示。
图2.4 热点按钮
技术要点
要实现具有热点效果的按钮控件,主要用到GetCursorPos方法和PtInRect方法。
(1)GetCursorPos方法。GetCursorPos方法用于获得鼠标的当前坐标,语法如下:
BOOL GetCursorPos( LPPOINT lpPoint );
参数说明:
● lpPoint:指向鼠标当前位置的POINT结构指针。
(2)PtInRect方法。PtInRect方法用于判断指定点是否在指定矩形区域内,语法如下:
BOOL PtInRect( POINT point ) const;
参数说明:
● point:包含一个POINT结构或CPoint对象。
实现过程
(1)新建一个基于对话框的应用程序。
(2)以CButton类为基类派生一个CButtonHot类。
(3)向工程中导入4个BMP位图资源,并向对话框中添加2个按钮控件,为按钮控件选择Owner Draw属性。
(4)在CButtonHot类的头文件中声明变量,代码如下:
UINT m_DownPic; //鼠标按下时显示的图片 UINT m_NomalPic; //正常情况下显示的图片 UINT m_EnablePic; //按钮失效时显示的图片 UINT m_MovePic; //鼠标经过按钮时显示的图片 BOOLm_IsInRect; //是否在按钮区域内
(5)在CButtonHot类的构造函数中初始化变量,代码如下:
CButtonHot::CButtonHot() { m_DownPic =IDB_BUTTONDOWN; //鼠标按下时显示的图片 m_NomalPic =IDB_BUTTONUP; //正常情况下显示的图片 m_EnablePic =IDB_BUTTONENABLE; //按钮失效时显示的图片 m_MovePic =IDB_BUTTONMOVE; //鼠标经过按钮时显示的图片 m_IsInRect =FALSE; }
(6)在CButtonHot类中,添加自定义函数DrawBK,该函数用于绘制按钮控件的背景位图,代码如下:
void CButtonHot::DrawBK(CDC *pDC, UINT ResID) { CDC memDC; memDC.CreateCompatibleDC(pDC); //创建兼容的设备上下文 CRect rect; //声明区域对象 GetClientRect(rect); //获得编辑框客户区域 CBitmap bitmap; BITMAP bitStruct; bitmap.LoadBitmap(ResID); //加载位图资源 bitmap.GetBitmap(&bitStruct); //获得位图资源信息 memDC.SelectObject(&bitmap); //选入位图对象 pDC->StretchBlt(0,0,rect.Width(),rect.Height(),&memDC,0,0,bitStruct.bmWidth ,bitStruct.bmHeight,SRCCOPY); //绘制背景 memDC.DeleteDC(); //删除设备上下文 bitmap.DeleteObject(); //删除位图对象 }
(7)在CButtonHot类中,重载DrawItem虚函数,在该虚函数中根据按钮状态绘制按钮的背景图片,代码如下:
void CButtonHot::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC dc; //声明设备上下文 dc.Attach(lpDrawItemStruct->hDC); //获得绘制按钮设备上下文 UINT state=lpDrawItemStruct->itemState; //获取状态 CRect rect; //声明区域对象 GetClientRect(rect); //获得编辑框客户区域 CString text; //声明字符串变量 GetWindowText(text); //获得控件显示文本 if(state&ODS_DISABLED) //如果不可用 { DrawBK(&dc,m_EnablePic); //绘制不可用背景 dc.SetTextColor(RGB(0,0,0)); //设置文本颜色 } else if(state&ODS_SELECTED) //如果选择按钮 { DrawBK(&dc,m_DownPic); //绘制选择状态背景 dc.SetTextColor(RGB(0,0,255)); //设置文本颜色 } else if(m_IsInRect==TRUE) //如果是热点 { DrawBK(&dc,m_MovePic); //绘制热点状态背景 dc.SetTextColor(RGB(255,0,0)); //绘制文本颜色 } else //默认情况下 { DrawBK(&dc,m_NomalPic); //绘制默认按钮状态背景 dc.SetTextColor(RGB(0,0,0)); //绘制文本颜色 } if(state&ODS_FOCUS) //如果获得焦点 { CRect FocTect(rect); //构造焦点区域 FocTect.DeflateRect(2,2,2,2); //设置焦点区域大小 dc.DrawFocusRect(&FocTect); //绘制焦点框 lpDrawItemStruct->itemAction = ODA_FOCUS ; } dc.SetBkMode(TRANSPARENT); //设置背景透明 dc.DrawText(text,&rect,DT_CENTER|DT_VCENTER|DT_SINGLELINE); //绘制按钮文本 }
(8)在CButtonHot类中,重载PreSubclassWindow虚函数,在该虚函数中设置定时器,代码如下:
void CButtonHot::PreSubclassWindow() { SetTimer(1,10,NULL); //设置定时器 CButton::PreSubclassWindow(); }
(9)在CButtonHot类中,处理按钮的WM_TIMER事件,在该事件的处理函数中判断按钮是否为热点效果,代码如下:
void CButtonHot::OnTimer(UINT nIDEvent) { CPoint point; //声明Cpoint变量 GetCursorPos(&point); //获得鼠标光标位置 CRect rcWnd; //声明区域对象 GetWindowRect(&rcWnd); //获得按钮区域 if(rcWnd.PtInRect(point)) //判断鼠标光标是否在按钮上 { if(m_IsInRect==TRUE) //判断鼠标光标是否一直在按钮上 goto END; //跳转到标记 else //鼠标光标移动到按钮上 { m_IsInRect=TRUE; //设置m_IsInRect变量值 Invalidate(); //重绘按钮 } } else //不在按钮区域内 { if(m_IsInRect==FALSE) //判断鼠标光标一直在按钮外 goto END; //跳转到标记 else //鼠标光标移动到按钮外 { Invalidate(); //重绘按钮 m_IsInRect=FALSE; //设置m_IsInRect变量值 } } END:CButton::OnTimer(nIDEvent); //设置标记,调用基类方法 }
(10)在CButtonHot类中,处理按钮的WM_ERASEBKGND事件,在该事件的处理函数中禁止调用基类方法重绘按钮控件,代码如下:
BOOL CButtonHot::OnEraseBkgnd(CDC* pDC) { return true;//CButton::OnEraseBkgnd(pDC); //不调用基类方法 }
(11)在CButtonHot类中,重载PreTranslateMessage虚函数,在该函数中截获回车键按下和释放事件,并修改为鼠标左键的按下和释放事件,代码如下:
BOOL CButtonHot::PreTranslateMessage(MSG* pMsg) { //截获回车键的按下事件 if(pMsg->hwnd==this->GetSafeHwnd()&&pMsg->message==WM_KEYDOWN && pMsg->wParam==13) { pMsg->lParam =0; pMsg->message=WM_LBUTTONDOWN; //改为鼠标左键的按下事件 } //截获回车键的释放事件 if(pMsg->hwnd==this->GetSafeHwnd()&&pMsg->message==WM_KEYUP && pMsg->wParam==13) { pMsg->lParam =0; pMsg->message=WM_LBUTTONUP; //改为鼠标左键的释放事件 } return CButton::PreTranslateMessage(pMsg); }
举一反三
根据本实例,读者可以:
根据按钮状态显示不同的位图。