Chinaunix首页 | 论坛 | 博客
  • 博客访问: 468088
  • 博文数量: 724
  • 博客积分: 40000
  • 博客等级: 大将
  • 技术积分: 5010
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-13 14:47
文章分类

全部博文(724)

文章存档

2011年(1)

2008年(723)

我的朋友

分类:

2008-10-13 16:56:48

         阅读此文前请注意:文中是以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 水石 阅读(1722)   

--------------------next---------------------
<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;
}

--------------------next---------------------

阅读(433) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~