阅读此文前请注意:文中是以FormView来探究整个过程,如果读者使用的是基于对话框的应用程序,则视情况更改代码内容,不影响阅读。
程序设计中组合框使用相当多,但对于组合框的结构,大部分程序设计者只知其大概,即一般的组合框是由 Edit框+ListBox框组合而成。实际情况是这样吗? 如果上面观点正确,那么就能够通过Edit指针和ListBox指针直接操作CComboBox控件了,可如何获得组合框的Edit指针和ListBox指针呢,Edit框和ListBox框在CComboBox中是怎样结合在一起的呢? 本文将根据此一要点,进一步解析组合框的结构。
在开始探究之前,我们建立一对话框应用程序,在其上放几个ComboBox控件(IDC_COMBO1~IDC_COMBO7吧),放多个的目的是为了便于比较观察。然后在控件的Data属性中增加一些内容,以免点击下拉箭头空空如也。
程序设计中有可能会遇到更改控件颜色的问题。对于一般的控件,可以在对话框的WM_CTLCOLOR消息中设置,我们也以此方法做下实验。相关代码如下:
<1>.
HBRUSH CFormVView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
//构造一个红色画刷对像,将其背景用红色填充。静态可以避免对像失效。
static CBrush br(RGB(255, 0, 0));
//如果目前画的控件是IDC_COMBO1,即组合框,则将文字背景色改成红色。
if(pWnd->GetDlgCtrlID() == IDC_COMBO1 )
{
pDC->SetBkColor(RGB(255, 0, 0));
//设置成透明色,以免背景前景不协调。
pDC->SetBkMode(TRANSPARENT);
//返回我们自己的画刷,让系统画去吧。由于CBrush有重载HBRUSH运算符,所以我偷下懒,直接写成return br;
return br;
}
return hbr;
}
以上设置完毕,编译,运行,却发现并非想像那样整个框呈现红色背景,而是仅仅在Edit框周围产生一个红色的矩形,Edit框内部仍然是白色的。点击下拉箭头,下拉框中连个红色的矩形框都没有了,怎么回事?
哦,想起来了,想起来了,因为Edit框会把组合框给盖住,但没有全部盖住,所以,Edit框外边会有一个红色的矩形,但内部还是Edit的颜色,即白色。ListBox框根本就没有机会绘制红色边框,所以整个框仅仅是ListBox的颜色。我们想,Edit和ListBox肯定是CComBox的子控件。根据此想法将程序做下修改。如下:
<2>.
HBRUSH CFormVView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
//构造一个红色画刷对像,将其背景用红色填充。静态可以避免对像失效。
static CBrush br(RGB(255, 0, 0));
//如果目前画的控件是IDC_COMBO1,即组合框,则将文字背景色改成组色。
//嘿嘿,如果目前所画的控件的父窗口是ComboBox框,那么也让它返回红色画刷。
if(pWnd->GetDlgCtrlID() == IDC_COMBO1 || pWnd->GetParent()->GetDlgCtrlID() == IDC_COMBO1)
{
pDC->SetBkColor(RGB(255, 0, 0));
//设置成透明色,以免背景前景不协调。
pDC->SetBkMode(TRANSPARENT);
//返回我们自己的画刷,让系统画去吧。由于CBrush有重载HBRUSH运算符,所以我偷下懒,直接写成return br; return br;
}
return hbr;
}
带着疑惑并激动的心编译,运行,哈哈,果然如此也。成功了成功了。赶紧点击下拉箭头,看看奇迹的发生。结果
:(, 怎么回事。困惑,同样的ComboBox, 同样的Edit, 同样的CListBox, 为什么CListBox不改变颜色呢? 难道CListBox无法使用此方法改变颜色吗? 好,我先试试。
在对话框上加一个Listbox,按照上述改变控件颜色的方法,运行, 红色的ListBox映入眼睑,可以改变呀, 但 但ComboBox中的ListBox为什么改变不了呢? 又一次困惑。
别着急,先把程序改成如下:
<3>.
HBRUSH CFormVView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
//构造一个红色画刷对像,将其背景用红色填充。静态可以避免对像失效。
static CBrush br(RGB(255, 0, 0));
//嘿嘿,如果目前所画的控件的父窗口是ComboBox框,那么也让它返回红色画刷。
if(pWnd->GetParent()->GetDlgCtrlID() == IDC_COMBO1)
{
//没其它目的,我就是想看看改变颜色的ComboBox的子窗口的ID。
TRACE1("%d ", pWnd->GetDlgCtrlID());
pDC->SetBkColor(RGB(255, 0, 0));
//设置成透明色,以免背景前景不协调。
pDC->SetBkMode(TRANSPARENT);
返回我们自己的画刷,让系统画去吧。由于CBrush有重载HBRUSH运算符,所以我偷下懒,直接写成return br;
return br;
}
return hbr;
}
按F5运行, 因为TRACE1只有在调试模式下才会输出到输出窗口中。然后观察值,结果得出的全部是1001,并没有其它值,而我的IDC_COMBO1是1002。
好,我再改,将if(pWnd->GetParent()->GetDlgCtrlID() == IDC_COMBO1)改为if(pWnd->GetDlgCtrlID() == 1001), 再次编译,运行,可以看到所有的组合框中Edit框中颜色均变成红色,这说明一个问题:
CComboBox控件中的确包含一个Edit子控件,而且此控件的ID是1001。
为了验证这句话是正确的,我们在对话框上放一按钮控件IDC_BUTTON1,在其响应函数中增加如下代码。
<4>.
void CFormVView::OnButton1()
{
CComboBox* pCom = (CComboBox *)GetDlgItem(IDC_COMBO1);
CEdit *pEdit = (CEdit*)pCom->GetDlgItem(1001);
CString str;
pEdit->GetWindowText(str);
MessageBox(str);
}
运行,并在IDC_COMBO1组合框中随便输入一串字符, 按下Button1按钮, 正如我们所想,弹出来刚才输入的字符串,那么列表框呢?
我们再来看看程序段<2>, 这段程序中能改变组合框中Edit框的颜色却改变不了ListBox框的颜色,说明什么呢? 在对话框中的控件不可能不产生WM_CTLCOLOR呀,那好,我们一个一个的查。将对话框上的控件只留一个组合框IDC_COMBO1, 其它控件全部删除,并将程序改为如下。
<5>.
HBRUSH CFormVView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
static CBrush br(RGB(255, 0, 0));
//把IDC_COMBO1和1001即组合框的Edit的ID屏蔽,看看剩下什么。
if(pWnd->GetDlgCtrlID() != IDC_COMBO1 && pWnd->GetDlgCtrlID() != 1001 )
{
TRACE1("%d ", pWnd->GetDlgCtrlID());
pDC->SetBkColor(RGB(255, 0, 0));
pDC->SetBkMode(TRANSPARENT);
return br;
}
return hbr;
}
按F5调试运行, 在TRACE1("%d \n", pWnd->GetDlgCtrlID())下行设置断点,查看输出窗口,得出59648, 这个是什么,我没定义这个ID呀,莫非是对话框模板的? 打开Resource.h头文件,发现
#define IDD_FORMV_FORM 101
显然不是,在Resource.h中查找也没查到。反正它是一个控件,不然怎么能用pWnd->GetDlgCtrlID()这个函数呢? 嗯,有了,查看RuntimeClass的名字,就知道是什么了。于是继续更改上述程序<5>。
<6>
HBRUSH CFormVView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
static CBrush br(RGB(255, 0, 0));
if(pWnd->GetDlgCtrlID() != IDC_COMBO1 && pWnd->GetDlgCtrlID() != 1001 )
{
//除了得到ID, 还要得到RuntimeClass的名字。
TRACE2("%d %s ", pWnd->GetDlgCtrlID(), pWnd->GetRuntimeClass()->m_lpszClassName);
pDC->SetBkColor(RGB(255, 0, 0));
pDC->SetBkMode(TRANSPARENT);
return br;
}
// TODO: Return a different brush if the default is not desired
return hbr;
}
再次运行,发现每次在输出窗口中都是得到 59648 CFormVView, 原来59648是CFormVView的ID啊。其实如果对MFC的框架结构熟悉,就会知道,当前View的ID为AFX_IDW_PANE_FIRST, 它的值是(#define AFX_IDW_PANE_FIRST 0xE900 // first pane (256 max))转换成十进制就是59648。好了,再将这个ID也屏蔽掉,即:
<7>
HBRUSH CFormVView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
static CBrush br(RGB(255, 0, 0));
if(pWnd->GetDlgCtrlID() != IDC_COMBO1 &&
pWnd->GetDlgCtrlID() != 1001 &&
pWnd->GetDlgCtrlID() != AFX_IDW_PANE_FIRST)
{
TRACE2("%d %s ", pWnd->GetDlgCtrlID(), pWnd->GetRuntimeClass()->m_lpszClassName);
pDC->SetBkColor(RGB(255, 0, 0));
pDC->SetBkMode(TRANSPARENT);
return br;
}
return hbr;
}
按F5运行,发现再也得不到TRACE2的东东了,这说明此消息没有其它需要绘制的控件了。再次点击组合框的下拉箭头, 哈哈有了, 输出1000 CWnd, 并且组合框中列表呈现红色,想必这个1000一定是ListBox的控件ID了。为了说明问题,我再改程序,打破沙锅改到底。如下:
<8>
HBRUSH CFormVView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
static CBrush br(RGB(255, 0, 0));
//验证一下自己的想法,看是不是每个组合框的下拉列表都变颜色了。
if(pWnd->GetDlgCtrlID() == 1000 )
{
TRACE2("%d %s ", pWnd->GetDlgCtrlID(), pWnd->GetRuntimeClass()->m_lpszClassName);
pDC->SetBkColor(RGB(255, 0, 0));
pDC->SetBkMode(TRANSPARENT);
return br;
}
return hbr;
}
然后再添加几个组合框,运行。按下每个组合框的下拉箭头,终于将每个组合框的ListBox都改成了红色的了。可是,还有一个问题没弄明白,那就是运行第<2>个程序的时候为什么没能改变ListBox的颜色呢,我再修改程序跟踪一下。
<9>
HBRUSH CFormVView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
static CBrush br(RGB(255, 0, 0));
if( pWnd->GetDlgCtrlID() != IDC_COMBO1 &&
pWnd->GetDlgCtrlID() != 1001 &&
pWnd->GetDlgCtrlID() != AFX_IDW_PANE_FIRST)
{
//查查到底组合框中ListBox的父亲是谁。
CWnd *pWndParent = pWnd->GetParent();
TRACE1("%x ", pWndParent->m_hWnd);
pDC->SetBkColor(RGB(255, 0, 0));
pDC->SetBkMode(TRANSPARENT);
return br;
}
// TODO: Return a different brush if the default is not desired
return hbr;
}
这一次的调试是为了得到这个ListBox的父窗口的句柄,然后查看这个父窗口是什么,我的机器得到的结果是:10014。 打开Spy++工具(Tools->Spy++), 在菜单中点击Search->Find Window,在框中输入刚才的结果10014, OK, 结果,天啊,竟然是Desktop, 哈哈哈哈,谜底解开了,原来ComboBox的ListBox不是ComboBox的子窗口, Edit控件才是ComboBox的子窗口。
顺便说一下,组合框中的CListBox是在下拉时,也就是弹出CListBox时(即DropDown时)才创建的,等Listbox合起来时销毁。
今天先写到这里,如果读者有兴趣,可以做出如下功能。
代码<4>中,可以通过CEdit来获得组合框中Edit框的值, 读者可以模拟此段程序,实现使用CListBox类来更改组合框下拉列表的内容。
本文的调试环境是Windows Xp sp2, VC++ 6.0 Vs6sp6.
发表于 2007-08-06 09:20 水石 阅读(1722)
--------------------next---------------------
<3>.
HBRUSH CFormVView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
//构造一个红色画刷对像,将其背景用红色填充。静态可以避免对像失效。
static CBrush br(RGB(255, 0, 0));
//嘿嘿,如果目前所画的控件的父窗口是ComboBox框,那么也让它返回红色画刷。
if(pWnd->GetParent()->GetDlgCtrlID() == IDC_COMBO1)
{
//没其它目的,我就是想看看改变颜色的ComboBox的子窗口的ID。
TRACE1("%d ", pWnd->GetDlgCtrlID());
pDC->SetBkColor(RGB(255, 0, 0));
//设置成透明色,以免背景前景不协调。
pDC->SetBkMode(TRANSPARENT);
返回我们自己的画刷,让系统画去吧。由于CBrush有重载HBRUSH运算符,所以我偷下懒,直接写成return br;
return br;
}
return hbr;
}--------------------next---------------------