在对话框重绘设计与实现过程中,一般需要绘制的对话框区域主要有标题部分、边框部分和客户区部分。具体的区域划分如下图所示。
既然要对多个区域进行位图显示输出,所以我们先封装一个bmp位图显示输出函数如下:
void CCTestDlg::ShowBmp(int x,int y,int w,int h,int nID)
{//nID 表示位图资源的ID
CRect winRC;
CDC* pDC=GetWindowDC();
CDC memDC;
memDC.CreateCompatibleDC(pDC);
BITMAPINFO bmpInfo;
CBitmap bmp;
GetWindowRect(&winRC);
bmp.LoadBitmap(nID);
bmp.GetObject(sizeof(BITMAPINFO),&bmpInfo);
int nBmpCX = bmpInfo.bmiHeader.biWidth;
int nBmpCY = bmpInfo.bmiHeader.biHeight;
memDC.SelectObject(bmp);
pDC->StretchBlt(x,y,w,h,
&memDC,0,0,nBmpCX,nBmpCY,SRCCOPY);//在窗口中绘制位图
bmp.DeleteObject();
ReleaseDC(pDC);
}
(1)对各个区域进行位图输出重绘。由于标题栏以及边框主要都是非客户区域绘制,因此应该在WM_NCPAINT 消息中绘制。当然得先通过添加资源的方式将所用到的bmp位图资源导入到项目中。
首先定义一些常量值,表示对话框各个组成区域部分。代码如下:
#define LEFTTITLE 1//左标题
#define MIDTITLE 1//中间标题
#define RIGHTTITLE 1//右标题
#define MINBUTTON 1//右标题
#define MAXBUTTON 1//右标题
#define CLOSEBUTTON 1//右标题
#define APPICON 1 //程序icon图标
#define LEFTBAR 1 //左边框
#define RIGHTBAR 1 //右边边框
#define BOTTOMBAR 1 //底边框
在 WM_NCPAINT消息对于的 方法OnNcPaint()中调用对话框绘制方法SetFace()。该方法的功能就是绘制对话框各个区域的位图。主要代码如下:
void CCTestDlg::SetFact()
{
// TODO: 在此添加控件通知处理程序代码
int nFrameCY = GetSystemMetrics(SM_CYFIXEDFRAME);//获取对话框边框的高度
int nFrameCX = GetSystemMetrics(SM_CXDLGFRAME);//获取对话边框的宽度
int m_nBorderCY;
int m_nBorderCX;
int m_nTitleBarCY ;
int m_nTitleBarCX;
if(GetStyle()&WS_BORDER)//获取对话框是否有边框
{
m_nBorderCY = GetSystemMetrics(SM_CYBORDER) + nFrameCY;
m_nBorderCX = GetSystemMetrics(SM_CXBORDER) +nFrameCX;
}
else
{
m_nBorderCX = nFrameCX;
m_nBorderCY = nFrameCY;
}
m_nTitleBarCY = GetSystemMetrics(SM_CYCAPTION) + m_nBorderCY;//计算标题栏高度
m_nTitleBarCX =m_nBorderCX;
CRect winRect,factRect;
GetWindowRect(&winRect); //获取窗口区域
factRect.CopyRect(CRect(0,0,winRect.Width(),winRect.Height()));
CWindowDC windowsDC(this);//获取窗口设备上下文
//获取整个MFC窗口的高度和宽度
int winCX = winRect.Width();
int winCY = winRect.Height();
if(LEFTTITLE)
{//绘制对话框左标题栏位图
ShowBmp(0,0,100,m_nTitleBarCY,IDB_RIGHTTITLE);
}
if(RIGHTTITLE)
{//绘制对话框右标题栏位图
ShowBmp(winCX-100,0,100,m_nTitleBarCY,IDB_RIGHTTITLE);
}
if(MIDTITLE)
{//绘制对话框中标题栏位图
ShowBmp(100,0,winCX-200,m_nTitleBarCY,IDB_MIDTITLE);
}
if(LEFTBAR)
{//绘制对话框左边框位图 ShowBmp(0,m_nTitleBarCY,m_nBorderCX,factRect.Height()-m_nBorderCY,IDB_LEFTBAR);
}
if(BOTTOMBAR)
{//绘制对话框底边框位图 ShowBmp(m_nBorderCX,winCY-m_nBorderCX,winCX-2*m_nBorderCX,m_nBorderCX,IDB_BOTTOMBAR);
}
if(RIGHTBAR)
{//绘制对话框左边框位图 ShowBmp(winCX-m_nBorderCX,m_nTitleBarCY,m_nBorderCX,factRect.Height()-m_nBorderCY,IDB_RIGHTBAR);
}
if(MINBUTTON)
{//给对话框绘制最小化按钮
ShowBmp(winCX-3-24-3-24-3-24,1,24,24,IDB_MINBUTTON);
}
if(MAXBUTTON)
{//给对话框绘制最大化按钮
ShowBmp(winCX-3-24-3-24,1,24,24,IDB_MAXBUTTON);
}
if(CLOSEBUTTON)
{//给对话框绘制关闭按钮
ShowBmp(winCX-3-24,1,24,24,IDB_CLOSEBUTTON);
}
ReleaseDC(&windowsDC);
DrawTitleBarText();//输出标题栏文本
}
上面代码中最后的绘制对话框标题文本的方法DrawTitleBarText()的主要代码如下:
CString strTitle ="自绘窗口标题栏和边框";
CDC* pDC= GetWindowDC();//获取窗口设备上下文
pDC->SetBkMode(TRANSPARENT);//设置透明的背景模式
pDC->SetTextColor(RGB(255,255,255));//设置文本颜色
pDC->SetTextAlign(TA_CENTER);//设置文本对齐方式
CRect rect;
GetClientRect(&rect);//获取窗口客户区域
CSize szText = pDC->GetTextExtent(strTitle);//获取文本高度
pDC->TextOut(rect.Width()/2,3,strTitle,20);//在窗口中输出文本
ReleaseDC(pDC);//释放窗口设备上下文
绘制后的效果图如下图所示。
在完成对话框相应区域的位图后,并没有完成任务,还需要处理标题栏按钮的热点效果,以及按钮的单击事件。首先得处理鼠标在非客户区域移动时的事件,即WM_NCMOUSEMOVE消息,在其消息处理函数中判断当前的鼠标点是否位于标题栏的按钮区域,如果是则设置按钮的热点效果,并且记录当前的按钮状态,及鼠标点在哪个按钮上。同样的,处理对话框非客户区域的单击事件,即WM_NCLBUTTONDOWN消息,在其消息处理函数中完成单击事件操作。主要代码如下:
void CCTestDlg::OnNcMouseMove(UINT nHitTest, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CRect minRC,maxRC,closeRC,winRC;
GetWindowRect(&winRC);
closeRC.CopyRect(CRect(winRC.Width()-27,1,winRC.Width()-27+24,1+24));
maxRC.CopyRect(CRect(winRC.Width()-27*2,1,winRC.Width()-27*2+24,1+24));
minRC.CopyRect(CRect(winRC.Width()-27*3,1,winRC.Width()-27*3+24,1+24));
point.Offset(-winRC.left,-winRC.top);//由于point为屏幕坐标,这里将其转换为窗口坐标
if(closeRC.PtInRect(point)) //判断鼠标是否在关闭按钮区域上
{
ShowBmp(winRC.Width()-3-24,1,24,24,IDB_CLOSEBUTTON2);
}
else if(maxRC.PtInRect(point)) //判断鼠标是否在最大化按钮区域上
{
ShowBmp(winRC.Width()-27*2,1,24,24,IDB_MAXBUTTON2);
}
else if(minRC.PtInRect(point)) //判断鼠标是否在最小化按钮区域上
{
ShowBmp(winRC.Width()-27*3,1,24,24,IDB_MINBUTTON2);
}
else//鼠标没有在标题栏的按钮区域上
{
ShowBmp(winRC.Width()-3-24-54,1,24,24,IDB_MINBUTTON);
ShowBmp(winRC.Width()-3-24-27,1,24,24,IDB_MAXBUTTON);
ShowBmp(winRC.Width()-3-24,1,24,24,IDB_CLOSEBUTTON);
}
}
添加热点效果后的效果如下图所示。