MFC窗口消息的处理过程包含两个部分,父窗口和子窗口的处理。父窗口作为主窗口肯定先于子窗口收到消息,这样的就需要一个子窗口和父窗口的互动——父窗口收到消息将消息传递给子窗口进行处理,如果子窗口处理了消息,那么返回一个标志告诉父窗口消息一经处理,不再需要父窗口代劳,否则直接传递给父窗口。由父窗口进行默认的处理。
BOOL CListBox::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam,
LRESULT* pResult)
{
switch (message)
{
case WM_DRAWITEM:
DrawItem((LPDRAWITEMSTRUCT)lParam);
break;
case WM_MEASUREITEM:
MeasureItem((LPMEASUREITEMSTRUCT)lParam);
break;
case WM_COMPAREITEM:
*pResult = CompareItem((LPCOMPAREITEMSTRUCT)lParam);
break;
case WM_DELETEITEM:
DeleteItem((LPDELETEITEMSTRUCT)lParam);
break;
case WM_VKEYTOITEM:
*pResult = VKeyToItem(LOWORD(wParam), HIWORD(wParam));
break;
case WM_CHARTOITEM:
*pResult = CharToItem(LOWORD(wParam), HIWORD(wParam));
break;
default:
return CWnd::OnChildNotify(message, wParam, lParam, pResult);
}
return TRUE;
}
上面是列表框对父窗口传递过来的消息的默认分发,其中涉及到重画的包含四个消息即:WM_DRAWITEM,WM_MEASURITEM,WM_COMPAREITEM,WM_DELETEITEM。从这四个消息的名称也可以分别看出着四个函数的含义。至于后面两个主要是将快捷键和字符消息转化为item索引。消息分发部分的XxxItem函数在列表框函数中都有默认实现,不过这些默认实现正好是什么也不做,完全交给父窗口处理。所以当需要自定义的效果时就需要程序员自己来设计上诉函数的实现。
在讲具体的函数之前,先分析一下函数的参数结构体。根据函数所需要的操作基本可以知道参数结构体的一些成员变量的含义。下面以WM_DRAWITEM消息的结构体参数来解释下。首席那因为整个对WM_DRAWITEM的处理都是针对列表框里面的item所做的处理,所以需要知道空间的类型和空间的ID号码,在空间ID号码下面还需要知道item的ID号码以及item当前的状态和对item的操作;由于控件的ID不具备唯一性,除了上面的一些信息之外,其他的必须信息还包括重绘的矩形区域以及DC,当然作为惯例还可以有自定义的一些数据。
typedef struct tagDRAWITEMSTRUCT {
UINT CtlType;
UINT CtlID;
UINT itemID;
UINT itemAction;
UINT itemState;
HWND hwndItem;
HDC hDC;
RECT rcItem;
DWORD itemData;
} DRAWITEMSTRUCT, NEAR *PDRAWITEMSTRUCT, FAR *LPDRAWITEMSTRUCT;
上面是结构体的定义,对照上面的解释很容易就知道每个结构体成员变量的含义。下面是一段控件自主画的实现过程。代码整体分为三个部分,并且三部分不是互斥的而是相互补充。首先第一步将所有的需要重画的部分都进行重画,因为重画可能根据滚动条改变重画区域。第二步,根据当前鼠标的选择查看是否选中某一个目标而做出判断是否进行重绘矩形框的操作,如果选中则需要进行重画。第三部取消原来被选中的单元边缘的矩形框;因为此时选用的画刷颜色和内部的颜色是一样的所以实际上是对矩形框做出清除的操作。
CDC* pDC = CDC::FromHandle(lpDrSt->hDC);
COLORREF cr = (COLORREF)lpDrSt->itemData;
int i=0;
CString str;
if (lpDrSt->itemAction & ODA_DRAWENTIRE)
{
CBrush br(cr);
pDC->FillRect(&lpDrSt->rcItem, &br);
GetText(lpDrSt->itemID,str);
pDC->SetBkColor(cr);
pDC->SetTextColor(RGB(255,255,255));
pDC->DrawText(str,&lpDrSt->rcItem,DT_VCENTER | DT_SINGLELINE);
}
if ((lpDrSt->itemState & ODS_SELECTED) && (lpDrSt->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
{
COLORREF crHilite = RGB(255-GetRValue(cr),255-GetGValue(cr), 255-GetBValue(cr));CBrush br(crHilite);
pDC->FrameRect(&lpDrSt->rcItem, &br);
}
if (!(lpDrSt->itemState & ODS_SELECTED) && (lpDrSt->itemAction & ODA_SELECT))
{
CBrush br(cr);
pDC->FrameRect(&lpDrSt->rcItem, &br);
}
上面是对列表框中的item进行自主重画,下面的代码段是针对按钮进行的自主重画。其中m_rcDrawClient矩形包含需要重画的全部矩形区域,而m_rcFocus包含选中的按钮中的虚线框区域。首先不论按钮是否被选中都利用FillSolidRect进行更新,然后根据是否被选中判断是否进行三维颜色的重画。然后根据是否包含ICON来判断是否需要对相应的ICON进行处理。首先得到ICON重画的矩形区域,然后判断是否选中,如果选中则要进行ICON位置的特殊处理以区别于其他的位置,然后根据是否当前角点在当前的按钮上,如果按钮在当前按钮上则需要重新画上虚线方框。
m_rcDrawClient.CopyRect(&lpDrSt->rcItem);
m_rcFocus.CopyRect(&m_rcDrawClient);
m_rcFocus.DeflateRect(4,4,4,4);
CDC* pDC = CDC::FromHandle(lpDrSt->hDC);
pDC->FillSolidRect(&m_rcDrawClient,::GetSysColor(COLOR_BTNFACE));
if ((lpDrSt->itemState& ODS_SELECTED))
{
pDC->Draw3dRect (&m_rcDrawClient, ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHIGHLIGHT));
}
else {
pDC->Draw3dRect (&m_rcDrawClient,::GetSysColor(COLOR_BTNFACE),::GetSysColor(COLOR_BTNFACE));
}
if(GetIcon())
{
CRect rcIcon;
rcIcon.CopyRect(m_rcDrawClient);
rcIcon.DeflateRect(2,2,2,2);
if((lpDrSt->itemState& ODS_SELECTED))
{
rcIcon.OffsetRect(1,1);
}
DrawIconEx (pDC->GetSafeHdc(), rcIcon.left, rcIcon.top,GetIcon(),rcIcon.Width(), rcIcon.Height(), NULL, (HBRUSH)NULL, DI_NORMAL);
}
if((lpDrSt->itemState& ODS_FOCUS))
{
pDC->DrawFocusRect(m_rcFocus);
m_bFocus=true;
}
else
m_bFocus=false;
ReleaseDC (pDC);
}
下面是ComboBox的重画,整体流程和上面的很相似。就是根据是否被选中进行操作。
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CString DrawText;
HICON DrawIcon=NULL;
TEXTMETRIC TextMetr;
pDC->GetTextMetrics(&TextMetr);
if(lpDIS->itemID !=-1)
{
GetLBText(lpDIS->itemID, DrawText);
DrawIcon=(HICON)lpDIS->itemData;
}
else
DrawText="Please select";
if ((lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
{
pDC->FillSolidRect(&lpDIS->rcItem,::GetSysColor(COLOR_HIGHLIGHT));
pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
pDC->TextOut(lpDIS->rcItem.left+SIZE_ICON+2, lpDIS->rcItem.top+(SIZE_ICON+2-TextMetr.tmHeight)/2,DrawText);
if(DrawIcon!=NULL)
{
DrawIconEx(pDC->GetSafeHdc(),lpDIS->rcItem.left,lpDIS->rcItem.top+2, DrawIcon,SIZE_ICON,SIZE_ICON,NULL,(HBRUSH)NULL, DI_NORMAL);
}
}
else if (lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE))
{
pDC->FillSolidRect(&lpDIS->rcItem,::GetSysColor(COLOR_WINDOW));
pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
pDC->TextOut(lpDIS->rcItem.left+SIZE_ICON+2, lpDIS->rcItem.top+(SIZE_ICON+2-TextMetr.tmHeight)/2,DrawText);
if(DrawIcon!=NULL)
{
DrawIconEx(pDC->GetSafeHdc(),lpDIS->rcItem.left,lpDIS->rcItem.top+2, DrawIcon,SIZE_ICON,SIZE_ICON,NULL,(HBRUSH)NULL, DI_NORMAL);
}
}
if (lpDIS->itemAction & ODA_FOCUS)
pDC->DrawFocusRect(&lpDIS->rcItem);
综合上面的分析,整个重画主要是根据是否选中以及是否具备鼠标焦点进行处理。使得选中与没被选中、焦点所在和普通的控件区分开来对待。