在我们使用MFC中的控件的时候,如果不满足于系统提供的控件样式也不想自己自绘控件的时候我们就可以对控件进行二次开发。对已经存在的控件的样式进行绘制。
开发过程:
1 首先在窗口设计界面增加需要二次开发的控件。选择控件的Owner Draw的属性。
2 增加控件的控件型变量
3 创建一个派生于自绘控件的控件类,重载DrawItem函数。
4 在派生类的重载DrawItem消息映射函数中增加需要二次开发的功能。
以二次开发按钮控件为例:
1 创建一个CButtonEx类继承CButton类
2 向窗口拖一个想要自绘控件,将控件的Owner Draw的属性设置为true
3 创建一个此控件的控件型对象
4 将控件的控件类型修改为创建的派生类对象
CButtonEx button;
5 在派生类中重写DrawItem函数,在此函数中绘制控件的属性
void CButtonEx::DrawItem(LPDRAWITEMSTRUCT lpDraw)
{
CDC dc;//创建一个CDC对象
dc.Attach(lpDraw->hDC);//将LPDRAWITEMSTRUCT中的结构体hDC 转化成 CDC
dc.SelectStockObject(LTGRAY_BRUSH);//选择一个背景刷
dc.FillSolidRect(&lpDraw->rcItem, GetSysColor(COLOR_BTNFACE));//
dc.Rectangle(&lpDraw->rcItem);//获取控件的范围
if (lpDraw->itemState & ODS_SELECTED)
dc.DrawEdge(&lpDraw->rcItem, BDR_SUNKENINNER, BF_LEFT | BF_BOTTOM | BF_TOP | BF_RIGHT);
else
dc.DrawEdge(&lpDraw->rcItem, BDR_RAISEDINNER, BF_LEFT | BF_BOTTOM | BF_TOP | BF_RIGHT);
//dc.DrawIcon(lpDraw->rcItem.left,lpDraw->rcItem.top,hIcon);
//DrawIconEx(lpDraw->hDC, 0, 0, hIcon, 16, 16, 0, 0, DI_NORMAL);
CString str;
GetWindowText(str); //获取系统文字
dc.SetBkMode(TRANSPARENT);//SetBkMode函数设置指定设备上下文的背景混合模式。
//背景混合模式用于非实线的文本,阴影画笔和笔样式。
dc.DrawText(_T("控件的二次开发"), &lpDraw->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//设置控件字体
// 调用此成员函数以格式化给定矩形中的文本。它通过将制表符扩展到适当的空间,
// 将文本与给定矩形的左,
// 右或中心对齐以及将文本分成适合给定矩形的行来格式化文本。格式化的类型由nFormat指定。
if (lpDraw->itemState & ODS_FOCUS)
{
CRect rect;
rect = lpDraw->rcItem;
rect.DeflateRect(2, 2);
dc.DrawFocusRect(&rect);
}
dc.Detach();
}
效果:
以下拉框为例:
组合控件包括三种自绘状态
- NO 不自绘,由系统提供列表文字显示
- Fixed 每个列表项的高度是固定的
- Variable 每项的高度是不固定的,由MeasureItem函数设定
1 创建一个CComBoxEx类继承CComBox类
2 向窗口拖一个想要自绘控件,将控件的Owner Draw的属性设置为Variable
3 创建一个此控件的控件型对象
4 将控件的控件类型修改为创建的派生类对象
CComBoxEx m_Combox;
5 在派生类中重写DrawItem函数,在此函数中绘制控件的属性
void CComBoxEx::DrawItem(LPDRAWITEMSTRUCT lpDraw)
{
//lpDraw->itemID 这个是下拉框中元素的ID
CDC *pDC = CDC::FromHandle(lpDraw->hDC); //将hDC句柄转换为CDC句柄
CRect rect = lpDraw->rcItem;//获取下拉框的范围
COLORREF cs[] = { RGB(0, 255, 0), RGB(0, 0, 255), RGB(255, 255, 0), RGB(255, 0, 255), 0 };
pDC->FillSolidRect(rect, cs[lpDraw->itemID]);
CString str;
if (lpDraw->itemID == -1)
{
lpDraw->itemID = 0;
}
GetLBText(lpDraw->itemID, str);
if (ODS_SELECTED & lpDraw->itemState)
{
pDC->SetTextColor(RGB(255, 0, 0));
pDC->FillSolidRect(rect, RGB(255, 255, 255));
if (lpDraw->itemAction & ODS_FOCUS)
{
pDC->DrawFocusRect(rect);
}
}
else
{
pDC->SetTextColor(RGB(0, 0, 0));
//pDC->FillSolidRect(rect,RGB(255,255,255));
}
pDC->DrawText(_T("追日"), rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
}
效果:
在主窗口中直接对空间进行二次开发
1 实现WM_DRAWITEM消息映射函数,一般是用于在父窗口中建立消息映射函数
2 如果父窗口内有一个或者多个具有自绘属性的控件,对所具有自绘属性的子窗口进行更新在WM_PAINT消息映射函数后执行,就会立即执行WM_REAWITEM消息映射函数OnDrawItem)
3 在消息映射函数OnDrawItem中,执行基类函数CWnd::OnDrawItem将回调,由进入的每一个自绘子窗口关联的派生类的虚函数DrawItem中
void CMyDlg::OnDrawItem(int nID, LPDRAWITEMSTRUCT lpDraw)
{
TRACE("nID = %d\n",nID);
switch (lpDraw->CtlType)
{
case ODT_BUTTON: //自绘按钮
SetButtonType(lpDraw);
break;
case ODT_COMBOBOX: //自绘下拉框
SetConBoBoxType(lpDraw);
break;
case ODT_LISTBOX: //自绘列表
SetCtrlListType(lpDraw);
break;
}
//CDialog::OnDrawItem(nID, lpDrawItemStruct);
}