导航
随笔档案
文章档案

统计
  • 随笔 - 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(
25500));                   
        //如果目前画的控件是IDC_COMBO1,即组合框,则将文字背景色改成红色。
        
if(pWnd->GetDlgCtrlID() == IDC_COMBO1 )
     {                                             
             pDC
->SetBkColor(RGB(25500));
                //设置成透明色,以免背景前景不协调。
             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(
25500));
        //如果目前画的控件是IDC_COMBO1,即组合框,则将文字背景色改成组色。
        //嘿嘿,如果目前所画的控件的父窗口是ComboBox框,那么也让它返回红色画刷。
     if(pWnd->GetDlgCtrlID() == IDC_COMBO1 ||  pWnd->GetParent()->GetDlgCtrlID() == IDC_COMBO1
     {
          pDC
->SetBkColor(RGB(25500)); 
             //设置成透明色,以免背景前景不协调。
          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(
25500));
        //嘿嘿,如果目前所画的控件的父窗口是ComboBox框,那么也让它返回红色画刷。
    
 if(pWnd->GetParent()->GetDlgCtrlID() == IDC_COMBO1) 
     {
            //没其它目的,我就是想看看改变颜色的ComboBox的子窗口的ID。
            TRACE1(
"%d  ", pWnd->GetDlgCtrlID());
            pDC->SetBkColor(RGB(25500)); 
            //设置成透明色,以免背景前景不协调。
         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(25500));
        //把IDC_COMBO1和1001即组合框的Edit的ID屏蔽,看看剩下什么。
     
if(pWnd->GetDlgCtrlID() != IDC_COMBO1 && pWnd->GetDlgCtrlID() != 1001 )
     {
         TRACE1(
"%d  ", pWnd->GetDlgCtrlID());
         pDC
->SetBkColor(RGB(25500));
         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(25500));
    
if(pWnd->GetDlgCtrlID() != IDC_COMBO1 && pWnd->GetDlgCtrlID() != 1001 )
    {
           //除了得到ID, 还要得到RuntimeClass的名字。
        TRACE2(
"%d %s  ", pWnd->GetDlgCtrlID(), pWnd->GetRuntimeClass()->m_lpszClassName); 
        pDC->SetBkColor(RGB(25500));
        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(25500));
    
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(25500));
         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(25500));
       //验证一下自己的想法,看是不是每个组合框的下拉列表都变颜色了。
    
if(pWnd->GetDlgCtrlID() == 1000 )
    {
        TRACE2(
"%d %s  ", pWnd->GetDlgCtrlID(), pWnd->GetRuntimeClass()->m_lpszClassName);
        pDC
->SetBkColor(RGB(25500));
        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(25500));
    
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(25500));
            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 水石 阅读(3022) 评论(7)  编辑 收藏
Comments
  • # re: CComboBox的研究
    codediscuss
    日期: 2007-08-17 12:34
    不错!有内容
  • # re: CComboBox的研究
    codediscuss
    日期: 2007-08-17 12:38
    补充一下,其实应该很容易知道下拉的listbox不是comboBox的child,因为listBox比ComboBox大,而child是不可能比parent大的。
  • # 不同意codediscuss的说法。
    水石
    日期: 2007-08-18 08:39
    ComboBox的大小不能依据Edit的大小来定, 它本身可以使用MoveWindow来调节大小,当然也可以对其子窗口进行更改大小。即可以使ComboBox变的较大,Edit变的较小,ListBox变的比Edit大很多,这时从界面上看不出ComboBox的实际大小。
  • # re: CComboBox的研究
    BOYLE9
    日期: 2007-08-22 16:17
    研究得有深度,
    努力学习中....
  • # re: CComboBox的研究
    maggie
    日期: 2007-09-03 13:01
    vc资料站:http://www.vcmsdn.com/     对学习很有帮助的,可以上去

    看看,或加群46138350,里面有高手可以请教的。
  • # re: CComboBox的研究
    zhgkun
    日期: 2008-12-25 14:07
    值得学习,收藏了!
  • # re: CComboBox的研究
    陈月
    日期: 2009-03-31 13:59
    谢谢了,可帮了我的大忙了.受益匪浅.
标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]
┋ 关于本站 ┋ 广告服务 ┋ 项目合作 ┋ 联系方式 ┋ 版权声明 ┋ 技术支持 ┋
Copyright © 2003-2004 All Rights Reserved
版权所有 © 2003-2004 (水石)