改变ListBox中文本的颜色

     我们在编写基于对话框的程序的时候,常常会用到列表框,有的时候希望列表框中的某些数据能够醒目一些,最好的办法当然就是用不同的颜色来显示了,可惜listbox根本没有这个功能,listctrl倒是有,想改就得全改,其实和没改一样。那么怎么办呢,在论坛上搜一下,很简单,“重载drawitem函数”,但是怎么重载呢?晕,没人说。幸好有好心人提供了一个链接 http://www.pcvc.net/category/content.asp?sendid=41(好象是这个,最少是同一篇文章,其实用查一下就知道,整个网上中文的就只有这一篇文章)。
     说来惭愧,我照着做了一个下午,半个晚上,又加一个上午才搞定,下面就说说我的编写过程和一点体会。
     首先使用向导生成一个基于对话框的程序,我起了个名字叫test,然后把界面上的static控件删了,我要改的是列表框,可不是静态文本框,当然静态文本框要容易改一些,名字用默认的IDC_LIST1,放心,没有2了,我只是懒得改它。
    然后有一个很重要的工作要做,得把对话框的属性该一下,ower draw:选fixed。勾选下面的has string,只所以说它很重要,因为我的半个晚上,又加一个上午就是因为这里没有动,准确的说就是少选了一个勾(这里要感谢弱兔兄,是他指出了我的这个错误)。
    选fixed是因为如果不用自画风格的话,根本不会调用drawitem,你在里面写什么都没用,而如果不选has string的话,后面使用GetText(lpdis->itemID, itemString );根本得不到字符串,同时你只能向list中写入一个字符串,想再多写一个就会出错,除非我想把这个列表框当做static来用,不然根本没用。
    用classwizard继承一个类,我起名叫CMyListBox,基类当然用listbox。然后在test.h中包含这个类的定义文件,就是CMyListBox.h,这个文件其实可以包含在stdafx.h中,只不过我喜欢将自定义的类在test.h中包含,只是个习惯问题。
    一般的,我们使用控件对象的时候会定义一个该类型的变量,至少我习惯这样使用,所以classview中右击ctestdlg,选add member variable,类型填CMyListBox,变量名填m_list1,点确定。当然也可以在testdlg.h中直接添加,不过既然有向导,为什么不用呢?
    但是这个m_list1现在并没有与idc_list1绑定,所以还不能用,要在testdlg.cpp的oninitdialog()中写入下面一句:m_list1.SubclassDlgItem(IDC_LIST1, this);“SubclassDlgItem可以把对话框中已有的控件与某个窗口对象动态连接起来,该窗口对象将接管控件的消息处理,从而使控件具有新的特性.SubclassDlgItem函数的声明为BOOL SubclassDlgItem( UINT nID, CWnd* pParent ); 参数nID是控件的ID,pParent是指向父窗口的指针.若连接成功则函数返回TRUE,否则返回FALSE.”(引号部分为引用文献)好了,现在我们就完成了绑定,如果这时候将idc_list1的ower draw:选no,就可以正常使用m_list1对idc_list1进行操作了,但是如果不改风格就会出错,因为自画风格可见部件发生改变后会调用drawitem进行重绘,而我们现在还没有重载drawitem。

    将下面这三行代码加入到mylistbox.h中

 int AddString( LPCTSTR lpszItem);
 int AddString( LPCTSTR lpszItem, COLORREF rgb);
 int InsertString( int nIndex, LPCTSTR lpszItem, COLORREF rgb);


 这是重载三个函数的声明,重要的是后面的二个,因为我们必须能够使用我们想要的功能。在mylistbox.cpp中加入这三个函数的函数体:

    int CMyListBox::AddString( LPCTSTR lpszItem)
    {
        return (((CListBox*)this)->AddString(lpszItem);
    }
    int CMyListBox::AddString( LPCTSTR lpszItem,COLORREF rgb )
    {
        int item = AddString(lpszItem);
        if(item >=0)
        SetItemData(item,rgb);
        return item;}
    int CMyListBox::InsertString( int nIndex, LPCTSTR lpszItem, COLORREF rgb)
    {
        int item = ((CListBox*)this)->InsertString(nIndex,lpszItem);
        if(item >=0) SetItemData(item,rgb);
        return item;
    }


    我们发现一个参数的addstring只是调用了listbox原有的功能,可以与二个参数的addstring合并,但是考虑到我们可能会不需要指定颜色,那么总不能用((CListBox*)this)->AddString(CString)吧?所以这个函数的重载是必须的。同样的道理,我们也可以重载insertstring。最后,我们来进行最重要的事,重载drawitem函数,如果想方便一点,在CMyListBox.h中加入virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);从理论上说virtual很重要,他指定了对drawitem进行覆盖而不是重载,但是drawitem本身就是listbox的重载函数,所以也可以使用class wizard添加这个函数,要注意的是在messages中有三个drawitem,第一个drawitem不是,如果你重载了就会发现他有两个参数,这个实际上是listbox自己用的。后面还有两个,前面有wm字样,不同的是第二个前面没有“=”,这个很重要,有wm字样证明这个是消息映射 函数,有“=”证明这个函数可以被主窗体反射到控件,用哪个不用说了吧?(实际上我感觉这两个在定义方法上是没有区别的,不知道对不对。)这样就会在cmylistbox.h中添加afx_msg void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);从理论上说应该使用上面的第一种方法,当然第二种也可以,我的意思是说一样能用。
    好了,现在回到cmylistbox.cpp中为drawitem添加代码

    if (lpdis->itemID < 0)
        return;
    COLORREF cvText;
    COLORREF cvBack;
    CString itemString;
    if ((lpdis->itemState & ODS_SELECTED) &&      // if item has been selected
        (lpdis->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
        DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
    if (!(lpdis->itemState & ODS_SELECTED) &&     // if item has been deselected             
        (lpdis->itemAction & ODA_SELECT))
        DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
    if(lpdis->itemData) // if color information is present
        cvText = SetTextColor(lpdis->hDC, lpdis->itemData);
    else         // if no color information, use default system colors
        cvText = SetTextColor(lpdis->hDC, GetSysColor((lpdis->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));   
    // always use system colors for background
    cvBack = SetBkColor(lpdis->hDC, GetSysColor((lpdis->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHT : COLOR_WINDOW));    
    // get and display item text
    GetText(lpdis->itemID, itemString );
    DrawText(lpdis->hDC, itemString, -1, &lpdis->rcItem, DT_LEFT | DT_SINGLELINE);    // restore DC colors
    SetTextColor(lpdis->hDC, cvText);
    SetBkColor(lpdis->hDC, cvBack);

    好了,现在就可以使用了,我们重载一下test的onok,记得注释掉原来的代码。//CDialog::OnOK();m_list1.AddString("test",RGB(255,0,0));m_list1.AddString("test",RGB(0,255,0));m_list1.AddString("test",RGB(0,0,255));好了,一切ok,写得这么详细,主要是为了初学者,其实对高手来说,很多东西都是很平常的,但是我看过很多的技术文档,关键的地方说得很明白,就是不告诉前提,比如自定义对话框不告诉应该加入包含文件,使用recordset类不告诉包含afxdb.h等等,可能在高手眼中不值一提,但是我们这些高手就硬是不会。另外,这次虽然只重载了四个函数,但是用到了重载、覆盖、隐藏等多种技术,这在“高质量c++编程”中有详细的论述,这里就不说了,希望这篇文章能对初学者有帮助,同时感谢弱兔兄。


以上转自:  http://blog.sina.com.cn/s/blog_513abb890100bjax.html


将DrawItem中的LPDRAWITEMSTRUCT lpDrawItemStruct 改为LPDRAWITEMSTRUCT  lpdis编译能通过,但运行失败;

在testdlg.cpp的oninitdialog()中的m_list1.SubclassDlgItem(IDC_LIST1, this)之后加上m_list1.AddString(("test",RGB(255,0,0))后能运行起来,也能达到预期效果,原因待查,得解决才能用。。


之前程序崩溃以解决  将drawitem函数中的代码改为如下:

    if (lpdis->itemID < 0)
        return;
    COLORREF cvText;
    COLORREF cvBack;
    CString itemString;
    if ((lpdis->itemState & ODS_SELECTED) &&      // if item has been selected
        (lpdis->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
        DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
    if (!(lpdis->itemState & ODS_SELECTED) &&     // if item has been deselected             
        (lpdis->itemAction & ODA_SELECT))
        DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
    if(lpdis->itemData) // if color information is present
        cvText = SetTextColor(lpdis->hDC, lpdis->itemData);
    else         // if no color information, use default system colors
        cvText = SetTextColor(lpdis->hDC, GetSysColor((lpdis->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));   
    // always use system colors for background
    cvBack = SetBkColor(lpdis->hDC, GetSysColor((lpdis->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHT : COLOR_WINDOW));    
    // get and display item text
	if(lpdis->itemID != (UINT) -1)
	{
    	    GetText(lpdis->itemID, itemString );
    	    DrawText(lpdis->hDC, itemString, -1, &lpdis->rcItem, DT_LEFT | DT_SINGLELINE);    // restore DC colors
    	    SetTextColor(lpdis->hDC, cvText);
    	    SetBkColor(lpdis->hDC, cvBack);
	}

效果如下图:

改变ListBox中文本的颜色_第1张图片


可见效果不太好,需要修改DrawItem函数,具体见下一篇吧。


你可能感兴趣的:(vc)