在用VC6.0编写windows程序时,你是不是时常因为自己的界面不好看而自卑。从现在开始我就来尝试着使我们的程序旋起来。
按钮是我们编写windows程序时不可缺少的控件之一。按一般用户的理解,不管窗口是否为输入焦点,当鼠标(准确的说应该是光标cursor)划过按钮时,按钮应该变色,表示这个地方是可以按下的。可是用VC6.0原始给出的按钮像个傻瓜一样,没有丝毫反应。
其实要让他有反应还是很简单的,下面就是我们该做的。
1. 派生一个类,其基类为CButton。假如叫:CMyButton
2. 为该类添加WM_MOUSEMOVE消息,message handle里应该这样写:
void
CMyButton::OnMouseMove(UINT nFlags, CPoint point)
... {
if(!m_bOverControl)
...{
m_bOverControl = TRUE;
Invalidate();
SetTimer(m_nTimerID, 100, NULL);
}
CButton::OnMouseMove(nFlags, point);
}
... {
if(!m_bOverControl)
...{
m_bOverControl = TRUE;
Invalidate();
SetTimer(m_nTimerID, 100, NULL);
}
CButton::OnMouseMove(nFlags, point);
}
m_bOverControl:通过名字就可以猜到他是一个布尔变量,用来表示现在光标是否在按钮上放。
Invalidate():强制使按钮发生重绘。
SetTimer():设置一个定时器,定时检查光标是否还在按钮上方。
3. 添加WM_TIMER消息:message handle里应该这样写:
void
CMyButton::OnTimer(UINT nIDEvent)
... {
CPoint p(GetMessagePos());
CRect rc;
ScreenToClient(&p);
GetClientRect(&rc);
if(! rc.PtInRect(p))
...{
m_bOverControl = FALSE;
KillTimer(m_nTimerID);
Invalidate();
}
CButton::OnTimer(nIDEvent);
}
... {
CPoint p(GetMessagePos());
CRect rc;
ScreenToClient(&p);
GetClientRect(&rc);
if(! rc.PtInRect(p))
...{
m_bOverControl = FALSE;
KillTimer(m_nTimerID);
Invalidate();
}
CButton::OnTimer(nIDEvent);
}
GetMessagePos():获得上次消息发送时,光标的位置。如果要获得当前光标的位置就要用GetCursorPos()。
4. 重载基类的PreSubclassWindow。这个函数其实是在CWnd里定义的。当控件子类化是由framework知道调用。我们常常在这个函数里修改控件的style。在这里我们要将控件改为自绘的。
void
CMyButton::PreSubclassWindow()
... {
CButton::PreSubclassWindow();
ModifyStyle(0, BS_OWNERDRAW);
}
... {
CButton::PreSubclassWindow();
ModifyStyle(0, BS_OWNERDRAW);
}
5. 最后,也是最重要的,就是具体的绘制操作。在这里重载
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)就可以了。具体如下:
void
CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
... {
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rc = lpDrawItemStruct->rcItem;
UINT state = lpDrawItemStruct->itemState;
CString text;
GetWindowText(text);
if(state & ODS_SELECTED)
...{
pDC->DrawFrameControl(rc, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED);
}
else
...{
pDC->DrawFrameControl(rc , DFC_BUTTON, DFCS_BUTTONPUSH);
}
rc.DeflateRect( CSize(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)));
if (m_bOverControl)
pDC->FillSolidRect(rc, RGB(255, 255, 0)); // yellow
if(!text.IsEmpty())
...{
CSize size = pDC->GetTextExtent(text);
CPoint pt(rc.CenterPoint().x-size.cx/2, rc.CenterPoint().y - size.cy/2);
if(state & ODS_SELECTED)
pt.Offset(1, 1);
int mode = pDC->SetBkMode(TRANSPARENT);
if(state & ODS_DISABLED)
pDC->DrawState(pt, size, text, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL);
else
pDC->TextOut(pt.x, pt.y, text);
pDC->SetBkMode(mode);
}
}
... {
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rc = lpDrawItemStruct->rcItem;
UINT state = lpDrawItemStruct->itemState;
CString text;
GetWindowText(text);
if(state & ODS_SELECTED)
...{
pDC->DrawFrameControl(rc, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED);
}
else
...{
pDC->DrawFrameControl(rc , DFC_BUTTON, DFCS_BUTTONPUSH);
}
rc.DeflateRect( CSize(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)));
if (m_bOverControl)
pDC->FillSolidRect(rc, RGB(255, 255, 0)); // yellow
if(!text.IsEmpty())
...{
CSize size = pDC->GetTextExtent(text);
CPoint pt(rc.CenterPoint().x-size.cx/2, rc.CenterPoint().y - size.cy/2);
if(state & ODS_SELECTED)
pt.Offset(1, 1);
int mode = pDC->SetBkMode(TRANSPARENT);
if(state & ODS_DISABLED)
pDC->DrawState(pt, size, text, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL);
else
pDC->TextOut(pt.x, pt.y, text);
pDC->SetBkMode(mode);
}
}