| |
| 统计
|
- 随笔 - 5
- 文章 - 6
- 评论 - 58
- Trackbacks - 0
|
|
|
|
阅读此文前请注意:文中是以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.
Comments
-
日期: 2007-08-17 12:34
不错!有内容
-
日期: 2007-08-17 12:38
补充一下,其实应该很容易知道下拉的listbox不是comboBox的child,因为listBox比ComboBox大,而child是不可能比parent大的。
-
日期: 2007-08-18 08:39
ComboBox的大小不能依据Edit的大小来定, 它本身可以使用MoveWindow来调节大小,当然也可以对其子窗口进行更改大小。即可以使ComboBox变的较大,Edit变的较小,ListBox变的比Edit大很多,这时从界面上看不出ComboBox的实际大小。
-
日期: 2007-08-22 16:17
研究得有深度,
努力学习中....
-
日期: 2007-09-03 13:01
vc资料站:http://www.vcmsdn.com/ 对学习很有帮助的,可以上去
看看,或加群46138350,里面有高手可以请教的。
-
日期: 2008-12-25 14:07
值得学习,收藏了!
-
日期: 2009-03-31 13:59
谢谢了,可帮了我的大忙了.受益匪浅.
|
|