生殖器去医院看哪科:5.5 ListControl控件、树控件和标签控件 - 《Visual C++开发经验技...

来源:百度文库 编辑:偶看新闻 时间:2024/04/27 15:21:26

5.5  ListControl控件、树控件和标签控件

0282  设置ListControl控件的显示风格

ListControl控件可在窗体中管理和显示列表项。可控制列表内容的显示方式,能够以图标和表格的形式显示数据。打开ListControl控件的属性窗口,在Styles选项卡中的View属性中可以设置显示风格,包括:Icon表示图标视图;Small Icon表示小图标视图;List表示列表视图;Report表示报表视图。

0283  为ListControl控件设置列标题

在使用ListControl控件Report显示风格时,需要设置列标题信息,否则不能向控件中添加数据信息,编辑列标题需要使用InsertColumn方法。该方法的语法如下:

int InsertColumn( int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT,int nWidth = -1, int nSubItem = -1 );

参数说明如下。

l     nCol:标识新列的索引。

l     lpszColumnHeading:标识列标题。

l     nFormat:标识列的对齐方式。

l     nWidth:标识列宽度。

l     nSubItem:标识关联当前列的子视图项索引。

程序代码如下:

    m_Grid.InsertColumn(0,"姓名",LVCFMT_LEFT,150,0);

    m_Grid.InsertColumn(1,"联系电话",LVCFMT_LEFT,150,1);

0284  为ListControl控件添加行

在ListControl控件中添加信息时不能直接向控件中添加列信息,需要先为控件添加行,使用InsertItem方法,该方法用于向ListControl控件中添加行。语法如下:

int InsertItem( int nItem, LPCTSTR lpszItem );

参数说明如下。

l     nItem:表示被插入的行索引。

l     lpszItem:表示行文本。

程序代码如下:

m_Grid.InsertItem(0,"");

0285  为ListControl控件添加列

通过SetItemText方法可以为任意行的任意列添加数据,该方法用于设置ListControl控件中的文本。语法如下:

BOOL SetItemText( int nItem, int nSubItem, LPTSTR lpszText );

参数说明如下。

l     nItem:标识行索引。

l     nSubItem:标识列索引。

l     lpszText:标识设置的显示文本。

程序代码如下:

    m_Grid.SetItemText(0,0,"周X");

    m_Grid.SetItemText(0,1,"12345XXXXXX");

0286  设置ListControl控件的扩展风格

在使用ListControl控件的Report显示风格时,需要设置一些常用的扩展风格。如图5.33所示。

图5.33  设置ListControl控件的扩展风格

程序代码如下:

    m_Grid.SetExtendedStyle(

        LVS_EX_FLATSB               //扁平风格滚动条

        |LVS_EX_FULLROWSELECT     //允许整行选中

        |LVS_EX_HEADERDRAGDROP    //允许标题拖曳

        |LVS_EX_ONECLICKACTIVATE  //高亮显示

        |LVS_EX_GRIDLINES          //画出网格线

        );

    m_Grid.InsertColumn(0,"姓名",LVCFMT_LEFT,150,0);

    m_Grid.InsertColumn(1,"联系电话",LVCFMT_LEFT,150,1);

    m_Grid.InsertItem(0,"");

    m_Grid.SetItemText(0,0,"周X");

    m_Grid.SetItemText(0,1,"12345XXXXXX");

    m_Grid.InsertItem(1,"");

    m_Grid.SetItemText(1,0,"诸葛X");

    m_Grid.SetItemText(1,1,"67890XXXXXX");

0287  设置ListControl控件数据的排列顺序

通过ListControl控件的属性窗口可以设置控件中数据的排列顺序,在Styles选项卡中的Sort属性中可以对控件中的数据进行排序,包括:None表示不进行排序;Ascending表示升序排列;Decending表示降序排列。

0288  单击ListControl控件列标题进行排序

在使用ListControl控件的Report显示风格时,要实现单击列标题进行排序需要在控件的LVN_COLUMNCLICK消息的处理函数中添加SortItems函数,SortItems函数实现了对列表项排序的功能,语法如下:

BOOL SortItems( PFNLVCOMPARE pfnCompare, DWORD dwData );

参数说明如下。

l     pfnCompare:排序时所使用的回调函数,如果不进行排序就让回调函数返回-1。

l     dwData:指定列表控件对象指针。

程序代码如下:

void CCompositorDlg::OnColumnclickList1(NMHDR* pNMHDR, LRESULT* pResult)

{

    NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

    m_Grid.SortItems(Compositor,(DWORD)reinterpret_cast(this));

    *pResult = 0;

}

int CALLBACK CCompositorDlg::Compositor(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)

{

    CListCtrl* pListCtrl=reinterpret_cast(lParamSort);   

    return 1;

}

0289  具有热点效果的视图控件

在使用列表视图控件时,如果列表视图以表格形式显示数据,并且包含有LVS_EX_TWOCLICKACTIVATE扩展风格,那么,当鼠标移动到某一行时,整行数据会出现热点效果。如何将某一个单元格设置为热点效果呢?本例实现了该功能,效果如图5.34所示。

本例是通过绘制列表视图控件实现的。首先从CListCtrl派生一个子类,然后添加ON_NOTIFY_REFLECT_EX(NM_CUSTOMDRAW,OnCustomDraw)消息映射实现CListCtrl的绘制。最后处理鼠标移动时的事件,判断鼠标在移动过程中,单元格是否发生了改变,如果发生了改变,重绘窗口。程序主要代码如下:

BOOL CCustomView::OnCustomDraw(NMHDR *pNotifyStruct, LRESULT *lResult)

{

    NMLVCUSTOMDRAW * nmDraw = (NMLVCUSTOMDRAW *)pNotifyStruct;

    NMCUSTOMDRAW & pDraw = nmDraw->nmcd;

        UINT flag =  pDraw.uItemState;

    switch (pDraw.dwDrawStage)

    {

    case  CDDS_PREPAINT :

        {

            *lResult =CDRF_NOTIFYITEMDRAW;

            break;

        }

    case CDDS_ITEMPREPAINT :

        {

            *lResult =CDRF_NOTIFYSUBITEMDRAW ;

            break;   

        }

    case (CDDS_ITEMPREPAINT | CDDS_SUBITEM) :

        {

            int row = pDraw.dwItemSpec;

            int col = nmDraw->iSubItem;

            CPoint point;

            GetCursorPos(&point);

            ScreenToClient(&point);

            CRect rect,secRC;

            this->GetSubItemRect(row,col,LVIR_BOUNDS ,rect);

           

            int cWidth =  this->GetColumnWidth(0); //获取第一列的宽度

            nmDraw->clrText = m_FontColor;

            if (col !=0)

            {

                if (rect.PtInRect(point))

                    nmDraw->clrText = m_HotFontColor;   

            }

            else

            {

                secRC.CopyRect(CRect(0,rect.top,cWidth,rect.bottom));   

                if (secRC.PtInRect(point))

                    nmDraw->clrText = m_HotFontColor;               

            }

           

            break;       

        }

    default:

        {

            *lResult = CDRF_DODEFAULT;

            break;

        }

    }

    return TRUE;

}

void CCustomView::OnMouseMove(UINT nFlags, CPoint point)

{   

    CListCtrl::OnMouseMove(nFlags, point);

    LVHITTESTINFO pos;

    pos.pt = point;

    pos.flags = LVHT_ABOVE;

   

    int curcol = -1;

    int currow = -1;

    if (this->SubItemHitTest(&pos)>=0)

    {       

        curcol = pos.iSubItem;

        currow = pos.iItem;

        if (m_PreCol ==-1)

            m_PreCol = curcol;

        if (m_PreRow== -1)

            m_PreRow = currow;

        if (currow==0 && m_Valid == FALSE )

        {

            m_Valid = TRUE;

            CRect rect;

            GetSubItemRect(pos.iItem,pos.iSubItem,LVIR_BOUNDS ,rect);

            int cWidth = rect.right;

            if (curcol==0)

            {

                cWidth =  this->GetColumnWidth(0); //获取第一列的宽度

            }

            RedrawWindow(CRect(rect.left,rect.top,cWidth,rect.bottom));   

            GetSubItemRect(m_PreRow,m_PreCol,LVIR_BOUNDS ,rect);

            cWidth = rect.right;

            if (m_PreCol==0)

            {

                cWidth =  this->GetColumnWidth(0); //获取第一列的宽度

            }

            RedrawWindow(CRect(rect.left,rect.top,cWidth,rect.bottom));           

        }

        else if (m_PreCol != curcol ||(m_PreRow != currow))  //用户移动鼠标时列发生了改变//或行发生了改变

        {

            CRect rect;

            GetSubItemRect(pos.iItem,pos.iSubItem,LVIR_BOUNDS ,rect);

            int cWidth = rect.right;

            if (curcol==0)

            {

                cWidth =  this->GetColumnWidth(0); //获取第一列的宽度

            }

            RedrawWindow(CRect(rect.left,rect.top,cWidth,rect.bottom));    

            GetSubItemRect(m_PreRow,m_PreCol,LVIR_BOUNDS ,rect);

            cWidth = rect.right;

            if (m_PreCol==0)

            {

                cWidth =  this->GetColumnWidth(0); //获取第一列的宽度

            }

            RedrawWindow(CRect(rect.left,rect.top,cWidth,rect.bottom));   

        }

        m_PreCol = curcol;

        m_PreRow = currow;

    }

}

0290  具有背景的列表视图控件

在使用列表视图控件时,默认情况下,列表视图控件具有白色的背景,如果能够在列表视图中以图片为背景,界面效果会好很多。本例实现了一个具有背景的列表视图控件,如图5.35所示。

图5.35  具有背景的列表视图控件

有些用户可能认为只要从CListCtrl派生一个子类,然后在WM_PAINT消息处理函数中绘制一幅图片就可以了。但是,这样会导致列表视图中的数据被背景图片覆盖。其实,实现具有背景的列表视图控件并不复杂,首先在程序初始化时调用AfxOleInit()函数初始化Com;然后调用CListCtrl的SetBkImage方法设置背景位图;最后调用SetTextBkColor方法将文本背景透明。程序主要代码如下:

    m_List.SetBkImage("c:\\background2.bmp");

    m_List.SetTextBkColor(CLR_NONE);

0291  设计类似OutLook的导航列表控件

许多人都使用OutLook发送过电子邮件。在OutLook界面的左边有一个导航列表。用户单击不同的按钮,列表中将显示相应的数据,这是如何实现的呢?本例设计了一个类似OutLook的导航列表控件,如图5.36所示。

图5.36  设计类似OutLook的导航列表控件

本例中的导航列表控件由两部分组成,一个是由按钮构成的按钮组,另一个是由CListCtrl控件构成的客户区域。当用户单击按钮,将调整按钮的位置,同时计算客户区域的位置,将CListCtrl控件显示在客户区域中。导航列表控件主要代码如下:

//定义双击列表视图项的回调函数

typedef void(ItemDlbFun)(const CListCtrl* pListCtrl,int nItemIndex);

class COutlookList : public CListCtrl

{

// Construction

public:

    COutlookList();

    CPtrArray m_pButton; //按钮数组

    UINT m_ButtonCount ; //按钮数量

    UINT m_LeftMargin; //图像列表显示的左边距

    CListCtrl m_ClientList; //在客户区域显示视图项

    ItemDlbFun* pItemDlbFun; //视图项的双击事件

// Attributes

public:

// Operations

private:

    UINT m_ButtonHeight; //按钮高度

// Overrides

    // ClassWizard generated virtual function overrides

    //{{AFX_VIRTUAL(COutlookList)

    public:

    virtual BOOL PreTranslateMessage(MSG* pMsg);

    virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo);

    protected:

    virtual void PreSubclassWindow();

    //}}AFX_VIRTUAL

// Implementation

public:

    void OnItemDblClick();

    void SetImageLists(CImageList* pImagelist);

    void ShowButtonItems(UINT nIndex);

    void AddButtonItems(UINT nIndex,CString& strItem);

    UINT GetBtmBtnHeight();

    CRect GetListClientRect();

    int GetBtmTopIndex();

    UINT GetNumCurToBtmBtn(UINT nIndex);

    UINT GetTopButtonHeight();

    UINT CommandToIndex(UINT nID);

    void OnButtonDown(UINT bIndex, UINT nID);

    CRect GetAddButtonRect();

    void AddButton(LPCSTR text,UINT uID);

    virtual ~COutlookList();

    // Generated message map functions

protected:

    //{{AFX_MSG(COutlookList)

    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

    afx_msg void OnDblclk(NMHDR* pNMHDR, LRESULT* pResult);

    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()

};

COutlookList::COutlookList()

    : m_ButtonHeight (30),m_ButtonCount(0)

{

    m_LeftMargin = 70;

    pItemDlbFun = NULL;

}

COutlookList::~COutlookList()

{

    //m_pButton.RemoveAll();

    int m = m_pButton.GetSize();

    for (int i = 0; i

    {

        ((CButton*)m_pButton[i])->DestroyWindow();

        delete ((CButton*)m_pButton[i]);

    }

}

BEGIN_MESSAGE_MAP(COutlookList, CListCtrl)

    //{{AFX_MSG_MAP(COutlookList)

    ON_WM_CREATE()

    ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk)

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

// COutlookList message handlers

void COutlookList::AddButton(LPCSTR Btntext,UINT uID)

{

    int index =  m_pButton.Add(new CListButton);

    ((CListButton*)m_pButton[m_pButton.GetSize()-1])->Create(Btntext,WS_CHILD,GetAddButtonRect(),this,uID);

    ((CListButton*)m_pButton[m_pButton.GetSize()-1])->m_Index = index;

    ((CListButton*)m_pButton[m_pButton.GetSize()-1])->ShowWindow(SW_SHOW);

    m_ButtonCount+=1;

}

//获取将要添加按钮的客户区域

CRect COutlookList::GetAddButtonRect()

{

    int count = m_pButton.GetSize();

    int sumheight = 0;

    CRect rect;

    GetClientRect(rect);

    if (count>1)

    {

        CRect rect1;

        for (int m=0; m

        {

            ((CListButton*)m_pButton[m])->GetClientRect(rect1);

            sumheight+=rect1.Height();

        }

        return CRect(0,sumheight,rect.Width(),sumheight+m_ButtonHeight);       

    }

    else

        return CRect(0,0,rect.Width(),m_ButtonHeight);

}

int COutlookList::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

    if (CListCtrl::OnCreate(lpCreateStruct) == -1)

    {

        return -1;

    }       

    return 0;

}

void COutlookList::PreSubclassWindow()

{

    CListCtrl::PreSubclassWindow();

    m_ClientList.Create(LVS_ICON,CRect(0,0,0,0),this,101);

   

    SetBkColor(RGB(192,192,192));

    SetTextBkColor(RGB(192,192,192));

    SetTextColor(RGB(0,0,255));

    m_ClientList.SetExtendedStyle(LVS_EX_FLATSB);

    m_ClientList.SetBkColor(RGB(192,192,192));

    m_ClientList.SetTextBkColor(RGB(192,192,192));

    m_ClientList.SetTextColor(RGB(0,0,255));

    m_ClientList.Arrange(LVA_ALIGNLEFT );

    m_ClientList.ShowWindow(SW_SHOW);

}

BOOL COutlookList::PreTranslateMessage(MSG* pMsg)

{   

    if ((pMsg->hwnd==m_ClientList.m_hWnd)&&(pMsg->message==WM_LBUTTONDBLCLK))

    {

        int index;

        index = m_ClientList.GetSelectionMark();

        if (pItemDlbFun!= NULL)

            pItemDlbFun(&m_ClientList,index);

    }

    return CListCtrl::PreTranslateMessage(pMsg);

}

BOOL COutlookList::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)

{

    int index =  CommandToIndex(nID);

    if (index != -1)

    {

        OnButtonDown(index,nID);

    }

    m_ClientList.OnCmdMsg(nID,nCode,pExtra,pHandlerInfo);

    return CListCtrl::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);

}

//按钮的单击处理

void COutlookList::OnButtonDown(UINT bIndex, UINT nID)

{

    CRect listrect,buttonrect;

    int height = 0;

   

    CListButton* temp;

    GetClientRect(listrect);

    if (m_ButtonCount!=1)

    {   

        temp = (CListButton*)m_pButton[bIndex];

        if (temp->m_Toped) //用户单击顶层按钮

        {

            for (int i= m_ButtonCount-1; i>bIndex;i--)

            {

                temp = (CListButton*)m_pButton[i];

                temp->GetClientRect(buttonrect);           

                CRect rect(0,listrect.bottom-buttonrect.Height()-height,buttonrect.Width(),listrect.bottom-height);               

                height+= buttonrect.Height();

                temp->MoveWindow(rect);

                temp->m_Toped = FALSE;

            }           

        }

        else //用于单击底层按钮

        {

            int bottomindex = GetBtmTopIndex();

            for (int i = bottomindex; i <=bIndex;i++)

            {

                int topheight = GetTopButtonHeight();//获取顶层按钮高度

                temp = (CListButton*)m_pButton[i];

                temp->GetClientRect(buttonrect);

                CRect rect(0,topheight,buttonrect.Width(),topheight+buttonrect.Height());temp->

MoveWindow(rect);

                temp->m_Toped = TRUE;           

            }

        }

        CRect rectclt =GetListClientRect();

        m_ClientList.MoveWindow(rectclt);

        ShowButtonItems(bIndex);

    }   

}

//根据按钮命令返回索引

UINT COutlookList::CommandToIndex(UINT nID)

{

    for (int i = 0;i< m_pButton.GetSize();i++)

    {

        if ( ((CListButton*)m_pButton[i])->GetDlgCtrlID()==nID)

        {

            return ((CListButton*)m_pButton[i])->m_Index;

        }

    }

    return -1;

}

//返回顶层按钮的高度

UINT COutlookList::GetTopButtonHeight()

{

    UINT result = 0;

    CListButton* temp;

    CRect rect;

    for (int i = 0; i< m_ButtonCount;i++)

    {

        temp = (CListButton*)m_pButton[i];

        if (temp->m_Toped)

        {

            temp->GetClientRect(rect);

            result += rect.Height();

        }

    }

    return result;

}

UINT COutlookList::GetBtmBtnHeight()

{

    UINT result = 0;

    CListButton* temp;

    CRect rect;

    for (int i = 0; i< m_ButtonCount;i++)

    {

        temp = (CListButton*)m_pButton[i];

        if (temp->m_Toped==FALSE)

        {

            temp->GetClientRect(rect);

            result += rect.Height();

        }

    }

    return result;

}

//获取当前底层按钮到其上方的底层按钮的数量

UINT COutlookList::GetNumCurToBtmBtn(UINT nIndex)

{

    UINT result = 0;

    CListButton* temp;

    for (int i = 0; i

    {

        temp = (CListButton*)m_pButton[i];       

        if (temp->m_Toped==FALSE)

            result+=1;

    }

    return (result);

}

//获取下方按钮的顶层索引

int COutlookList::GetBtmTopIndex()

{

    CListButton* temp;

    for (int i = 0; i

    {

        temp = (CListButton*)m_pButton[i];       

        if (temp->m_Toped==FALSE)

            return i;

    }

    return -1;   

}

CRect COutlookList::GetListClientRect()

{

    int TopHeight = GetTopButtonHeight(); //获取上方按钮的高度

    int BtmHeight = GetBtmBtnHeight();    //获取下方按钮的高度

   

    CRect rect;

    GetClientRect(rect); //获取列表总的客户区域

    CRect ClientRect(0,TopHeight,rect.Width(),rect.Height()-BtmHeight);

    return ClientRect;

}

void COutlookList::AddButtonItems(UINT nIndex, CString &strItem)

{

    CListButton* temp;

    temp = (CListButton*)m_pButton[nIndex];

    temp->m_ButtonItems.AddTail(strItem);

}

//显示指定按钮关联的列表视图项

void COutlookList::ShowButtonItems(UINT nIndex)

{

    CListButton* temp;

    temp = (CListButton*)m_pButton[nIndex];

    m_ClientList.DeleteAllItems();

    CRect showrect = GetListClientRect();

    if (temp->m_ButtonItems.GetCount()>0)

    {

        POSITION pos;

        pos = temp->m_ButtonItems.GetHeadPosition();

        CString str= temp->m_ButtonItems.GetHead();

        CRect ClientRect;

        ClientRect= GetListClientRect();

        int m = 0;

        m_ClientList.InsertItem(m,str,m);

        m_ClientList.SetItemPosition(m,CPoint(m_LeftMargin,20+m*48));

        while (pos != temp->m_ButtonItems.GetTailPosition())

        {

            str = temp->m_ButtonItems.GetNext(pos);

            m_ClientList.InsertItem(m,str,m);

            m_ClientList.SetItemPosition(m,CPoint(m_LeftMargin,20+m*48));

            m+=1;

        }

        str = temp->m_ButtonItems.GetAt(pos);

        m_ClientList.InsertItem(m,str,m);

        m_ClientList.SetItemPosition(m,CPoint(m_LeftMargin,20+m*48));

    }

}

void COutlookList::SetImageLists(CImageList *pImagelist)

{

    if (pImagelist)

        m_ClientList.SetImageList(pImagelist,LVSIL_NORMAL);

}

0292  以+、−号和线条形式显示树控件节点层级关系

可以通过树控件的属性窗口设置是否以+、−号和线条形式显示,打开树控件的属性窗口,在Styles选项卡中Has buttons属性用于在父节点旁边显示+或−按钮,Has lines属性以线条形式显示节点层级关系,Lines at root属性将+、−号和线条连接至根节点。

0293  如何在程序运行时展开根节点

在程序运行时展开根节点可以通过在程序的OnInitDialog函数中调用树控件的Expand方法实现,该方法用于展开或收缩节点。语法如下:

BOOL Expand( HTREEITEM hItem, UINT nCode );

参数说明如下。

l     hItem:标识展开的节点句柄。

l     nCode:确定展开的动作,可选值如下。

l          TVE_COLLAPSE:收缩所有节点。

l          TVE_COLLAPSERESET:收缩节点,移除子节点。

l          TVE_EXPAND:展开所有节点。

l          TVE_TOGGLE:展开或收缩当前节点。

程序代码如下:

m_Tree.Expand(m_Root,TVE_EXPAND); // m_Root是根节点

0294  动态编辑树控件的节点

在使用树控件时,如果要直接在节点上进行编辑可以通过树控件的TVN_ENDLABELEDIT事件和TVN_SELCHANGED事件来实现,当编辑节点结束时产生TVN_ENDLABELEDIT事件,当选择节点改变后产生TVN_SELCHANGED事件。如图5.37所示。

图5.37  动态编辑树控件的节点

程序代码如下:

void CTreeItemDlg::OnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult)

{

    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

   

    m_Root = m_Tree.GetSelectedItem();

    CString text;

    text = m_Tree.GetItemText(m_Root);

    *pResult = 0;

}

void CTreeItemDlg::OnEndlabeleditTree1(NMHDR* pNMHDR, LRESULT* pResult)

{

    TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;

   

    m_Tree.SetItemText(pTVDispInfo->item.hItem,pTVDispInfo->item.pszText);

    *pResult = 0;

}

0295  带复选功能的树控件

要实现带复选功能的树控件需要选择控件的Check Boxes属性,这样在树控件的每个节点前就添加了一个复选框,如图5.38所示。

图5.38  带复选功能的树控件

程序代码如下:

void CCheckTreeDlg::OuttoTree(HTREEITEM m_node)

{

    HTREEITEM m_child;

    CString str;

    m_node = m_Tree.GetChildItem(m_node);

    if (m_node != NULL)

    {

        while(m_node!= NULL)

        {

            if(m_Tree.GetCheck(m_node))

            {

                str=m_Tree.GetItemText(m_node);

                m_List.AddString(str);

            }

            m_child = m_node;

            OuttoTree(m_child);

            m_node = m_Tree.GetNextItem(m_node,TVGN_NEXT);

        }

    }

}

void CCheckTreeDlg::OnButton1()

{

    m_List.ResetContent();

    HTREEITEM item;

    CString str;

    item = m_Tree.GetRootItem();

    if(m_Tree.GetCheck(item))

    {

        str = m_Tree.GetItemText(item);

        m_List.AddString(str);

    }

    OuttoTree(item);

}

0296  使用树控件显示磁盘目录

要实现使用树控件显示磁盘目录,可以通过GetLogicalDriveStrings函数先获取磁盘分区,然后处理树型控件的双击消息,双击某个目录就用循环查找该目录下的全部子目录。如图5.39所示。

图5.39  使用树控件显示磁盘目录

程序代码如下:

BOOL CTreeCatalogDlg::OnInitDialog()

{

    ……  //此处代码省略

    m_ImageList.Create(16,16,ILC_COLOR32|ILC_MASK,0,0);

    m_Tree.SetImageList(&m_ImageList,TVSIL_NORMAL);

    m_Tree.ModifyStyle(0L,TVS_HASLINES|TVS_LINESATROOT);

    size_t alldriver = ::GetLogicalDriveStrings(0,NULL);

    _TCHAR *driverstr;

    driverstr = new _TCHAR[alldriver + sizeof(_T(""))];

    if(GetLogicalDriveStrings(alldriver,driverstr) != alldriver-1)

        return FALSE;

    _TCHAR *pdriverstr = driverstr;

    size_t driversize = strlen(pdriverstr);

    HTREEITEM disktree;

    while(driversize>0)

    {

        SHGetFileInfo(pdriverstr,0,&fileinfo,sizeof(fileinfo),

            SHGFI_ICON);

        imindex = m_ImageList.Add(fileinfo.hIcon);

        disktree = m_Tree.InsertItem(pdriverstr,imindex,imindex,

            TVI_ROOT,TVI_LAST);

        pdriverstr += driversize+1;

        driversize = strlen(pdriverstr);

    }

    return TRUE;

}

void CTreeCatalogDlg::OnDblclkTree1(NMHDR* pNMHDR, LRESULT* pResult)

{

    CFileFind filefd;

    HTREEITEM parent;

    HTREEITEM item = m_Tree.GetSelectedItem();

    if(m_Tree.GetChildItem(item))return;

    parent = item;

    CString rootstr = m_Tree.GetItemText(item);

    CString temp;

    CString lstr;

    if(rootstr.Find("\\") == 2)

    {

        lstr.Format("%s*.*",rootstr);

    }

    else

    {

       

        CString strparent;

        while(1)

        {

            parent = m_Tree.GetParentItem(parent);

            strparent = m_Tree.GetItemText(parent);

            if(strparent.Find("\\") == 2)

                goto end;

            temp += strparent;

            temp += "\\";

        }

    end:

        CString root = m_Tree.GetItemText(parent);

        lstr.Format("%s%s%s\\*.*",root,temp,rootstr);

    }

    BOOL bfinded = filefd.FindFile(lstr);

    while(bfinded)

    {

        bfinded = filefd.FindNextFile();

        CString filepath;

        if(filefd.IsDirectory()&&!filefd.IsDots())

        {

            SHGetFileInfo(filefd.GetFilePath(),0,&fileinfo,sizeof(fileinfo),

                SHGFI_ICON);

            imindex = m_ImageList.Add(fileinfo.hIcon);

            m_Tree.InsertItem(filefd.GetFileName(),imindex,imindex,item);

        }

    }

    *pResult = 0;

}

0297  向TXT文件中保存并提取树控件中数据

在向TXT文件中保存树控件中数据时,可以根据数据所在的层次向TXT文件中插入不同的数字,提取时则根据这些数字判断数据在树控件中的节点位置。

程序代码如下:

void CTreetxtDlg::OnButton1()

{

    HTREEITEM item;

    item = m_Tree1.GetRootItem();

    CString str;

    str = m_Tree1.GetItemText(item);

    num = 0;

    strText.Format("%d~",num);

    strText += str;

    strText += "~";

    OutToTree(item,num+1);

    CFile file;

    char buf[256];

    ::GetCurrentDirectory(256,buf);

    strcat(buf,"\\aaa.txt");

    file.Open(_T(buf),CFile::modeCreate | CFile::modeWrite);

    file.Write(strText,strText.GetLength());

    file.Close();

    MessageBox("完成保存");

}

void CTreetxtDlg::OutToTree(HTREEITEM m_node, int pp)

{

    HTREEITEM m_child;

    m_child = m_Tree1.GetChildItem(m_node);

    if(m_child != NULL)

    {

        while(m_child != NULL)

        {

            CString str,strp;

            str = m_Tree1.GetItemText(m_child);

            strp.Format("%d~",pp);

            strText += strp;

            strText += str;

            strText += "~";

            OutToTree(m_child,pp+1);

            m_child = m_Tree1.GetNextItem(m_child,TVGN_NEXT);

        }

    }

}

void CTreetxtDlg::OnButton2()

{

    CFile file;

    char buf[256];

    ::GetCurrentDirectory(256,buf);

    strcat(buf,"\\aaa.txt");

    file.Open(_T(buf),CFile::modeRead);

    char read[128];

    file.Read(read,file.GetLength());

    int n=0;

    HTREEITEM m_root,m_child;

    CString str="",str1;

    for(int i=0;i

    {

       

        if(read[i]!='~')

        {

            if(n%2==0)

            {

                str1 = read[i];

            }

            else

            {

                str += read[i];

            }

        }

        else

        {

       

            if(n%2!=0)

            switch(atoi(str1))

            {

            case 0:

                m_root = m_Tree2.InsertItem(str,0,0);

                str="";

                break;

            case 1:

                m_child = m_Tree2.InsertItem(str,1,1,m_root);

                str="";

                break;

            case 2:

                m_Tree2.InsertItem(str,2,2,m_child);

                str="";

                break;

            }

                n++;

        }

    }

    file.Close();

}

0298  具有背景的树形控件

在使用树形控件时,如何能够在树形控件中显示背景图片呢?本例实现了该功能,效果如图5.40所示。

图5.40  具有背景的树形控件

在设计具有背景的树形控件时,由于CTreeCtrl没有SetBkImage方法设置背景图像,因此只能重新设计一个CTreeCtrl控件了。采用如下的设计思路:首先获得树形控件原始图像,将其绘制在一个画布对象上,然后定义一个画布对象,将背景图片绘制在画布对象上,最后将两个画布对象进行与运算绘制在树形控件上。程序主要代码如下:

void CBKTree::OnPaint()

{

    CPaintDC dc(this); // device context for painting

    CRect rcclient;

    GetClientRect(&rcclient);

    CDC memdc;

    memdc.CreateCompatibleDC(&dc);   

    CBitmap bitmap;

    bitmap.CreateCompatibleBitmap(&dc, rcclient.Width(), rcclient.Height());

    memdc.SelectObject( &bitmap );

    //获取原始画布

    CWnd::DefWindowProc(WM_PAINT, (WPARAM)memdc.m_hDC , 0);

    //绘制背景图片

    CMemDC tempDC( &dc,rcclient);

    CBrush brush;

    brush.CreatePatternBrush(&m_Bitmap);

    tempDC.FillRect(rcclient, &brush);   

   

    //将原始图片与背景进行组合

    tempDC.BitBlt(rcclient.left, rcclient.top, rcclient.Width(), rcclient.Height(),

           &memdc, rcclient.left, rcclient.top,SRCAND);

    brush.DeleteObject();

}

0299  在树形控件中利用位图添加复选框

许多应用软件的树形控件具有漂亮的复选框,如何实现呢?本例中,利用位图设计了一个具有复选框的树形控件,效果如图5.41所示。

图5.41  在树形控件中利用位图添加复选框

实际上,树形控件中的复选框是通过图像状态索引绘制的。首先设计一个位图,其中包含视图项的各个状态,将位图添加到CImageList中,然后调用CTreeCtrl的SetImageList方法设置图像状态索引。程序主要代码如下:

void CBitTreeCtl::OnLButtonDown(UINT nFlags, CPoint point)

{

    HTREEITEM hItemInfo =HitTest(point, &m_Flags);

    nFlags = m_Flags;

    //TVHT_ONITEMSTATEICON表示用户定义的视图项的图标状态

    if ( m_Flags &TVHT_ONITEMSTATEICON )

    {

        //State: 0无状态 1没有选择 2部分选择 3全部选择

        //12到15位表示视图项的图像状态索引

        UINT State = GetItemState( hItemInfo, TVIS_STATEIMAGEMASK ) >> 12;

        State=(State==3)?1:3;

        SetItemState( hItemInfo, INDEXTOSTATEIMAGEMASK(State), TVIS_STATEIMAGEMASK );

    }

    else

        CTreeCtrl::OnLButtonDown(nFlags, point);

}

BOOL CBitTreeCtl::SetItemState(HTREEITEM hItem, UINT State, UINT StateMask, BOOL IsSearch)

{

    BOOL ret=CTreeCtrl::SetItemState( hItem, State, StateMask );

    UINT nState =State >> 12;

    if(nState!=0)

    {

        if(IsSearch)

            RansackChild(hItem, nState);

        RansackParentAndChild(hItem,nState);

    }

    return ret;

}

//遍历子节点

void CBitTreeCtl::RansackChild(HTREEITEM hItem, UINT State)

{

    HTREEITEM hChildItem,hBrotherItem;

    //查找子节点

    hChildItem=GetChildItem(hItem);

    if(hChildItem!=NULL)

    {

        //将所有子节点的状态设置与父节点相同

        CTreeCtrl::SetItemState( hChildItem, INDEXTOSTATEIMAGEMASK(State), TVIS_STATEIMAGEMASK );

        //再递归处理子节点的子节点

        RansackChild(hChildItem, State);

       

        //搜索子节点的兄弟节点

        hBrotherItem=GetNextSiblingItem(hChildItem);

        while (hBrotherItem)

        {

            //设置子节点的兄弟节点状态与当前节点的状态一致

            int nState = GetItemState( hBrotherItem, TVIS_STATEIMAGEMASK ) >> 12;

            if(nState!=0)

            {

                CTreeCtrl::SetItemState( hBrotherItem, INDEXTOSTATEIMAGEMASK(State), TVIS_STATEIMAGEMASK );

            }

            //再递归处理兄弟节点

            RansackChild(hBrotherItem, State);

            hBrotherItem=GetNextSiblingItem(hBrotherItem);

        }

    }

}

void CBitTreeCtl::RansackParentAndChild(HTREEITEM hItem, UINT State)

{

    HTREEITEM hNextItem,hPrevItem,hParentItem;

   

    //查找父节点,没有就结束

    hParentItem=GetParentItem(hItem);

    if(hParentItem!=NULL)

    {

        UINT nState1=State;//设初始值,防止没有兄弟节点时出错

       

        //查找当前节点的所有兄弟节点,如果所有兄弟节点状态都相同,

        //设置父节点的状态

        //查找当前节点下面的兄弟节点的状态

        hNextItem=GetNextSiblingItem(hItem);

        while(hNextItem!=NULL)

        {

            nState1 = GetItemState( hNextItem, TVIS_STATEIMAGEMASK ) >> 12;

            if(nState1!=State && nState1!=0) break;

            else hNextItem=GetNextSiblingItem(hNextItem);

        }

       

        if(nState1==State)

        {

            //查找当前节点上面的兄弟节点的状态

            hPrevItem=GetPrevSiblingItem(hItem);

            while(hPrevItem!=NULL)

            {

                nState1 = GetItemState( hPrevItem, TVIS_STATEIMAGEMASK ) >> 12;

                if(nState1!=State && nState1!=0) break;

                else hPrevItem=GetPrevSiblingItem

                    (hPrevItem);

            }

        }   

        if(nState1==State || nState1==0)

        {

            nState1 = GetItemState( hParentItem, TVIS_STATEIMAGEMASK ) >> 12;

            if(nState1!=0)

            {

                //如果状态一致,则父节点的状态与当前节点的状态一致

                CTreeCtrl::SetItemState( hParentItem, INDEXTOSTATEIMAGEMASK(State), TVIS_STATEIMAGEMASK );

            }

            //再递归处理父节点的兄弟节点和其父节点

            RansackParentAndChild(hParentItem,State);

        }

        else

        {

            //状态不一致,则当前节点的父节点、父节点的父节点……状态均为第三态

            hParentItem=GetParentItem(hItem);

            while(hParentItem!=NULL)

            {

                nState1 = GetItemState( hParentItem, TVIS_STATEIMAGEMASK ) >> 12;

                if(nState1!=0)

                {

                    CTreeCtrl::SetItemState( hParentItem, INDEXTOSTATEIMAGEMASK(2), TVIS_STATEIMAGEMASK );

                }

                hParentItem=GetParentItem(hParentItem);

            }

        }

    }

}

0300  为标签控件添加标签

为标签控件添加标签需要使用InsertItem方法,该方法用于向标签控件中添加标签。语法如下:

BOOL InsertItem( int nItem, TCITEM* pTabCtrlItem );

BOOL InsertItem( int nItem, LPCTSTR lpszItem );

BOOL InsertItem( int nItem, LPCTSTR lpszItem, int nImage );

BOOL InsertItem( UINT nMask, int nItem, LPCTSTR lpszItem, int nImage, LPARAM lParam );

参数说明如下。

l     nMask:确定哪一项标签信息可用。

l     nItem:标识新的标签索引。

l     pTabCtrlItem:是TCITEM结构指针,TCITEM结构中包含了标签的详细信息。

l     lpszItem:标识被插入项的指针。

l     nImage:标识图像索引。

l     lParam:用于设置关联标签的附加信息。

程序代码如下:

    m_Tab.InsertItem(0,"操作员信息");

    m_Tab.InsertItem(1,"客户信息");

    m_Tab.InsertItem(2,"商品信息");

0301  带图标的标签控件

在使用标签控件时,如果感觉单调可以创建带图标的标签控件,这样可以使程序的界面更加美观,如图5.42所示。

图5.42  带图标的标签控件

程序代码如下:

BOOL CIconTabDlg::OnInitDialog()

{

    ……  //此处代码省略

    m_ImageList.Create(16,16,ILC_COLOR24|ILC_MASK,1,0);

    //向图像列表中添加图标

    m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON1));

    m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON2));

    m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON3));

    //将图像列表关联到标签控件中

    m_Tab.SetImageList(&m_ImageList);

    m_Tab.InsertItem(0,"操作员信息",0);

    m_Tab.InsertItem(1,"客户信息",1);

    m_Tab.InsertItem(2,"商品信息",2);

    return TRUE;

}

0302  设置按钮形状的标签

在使用标签控件时,除了使用默认的层叠显示的方式外,还可以将标签设置为按钮的形式。打开标签控件的属性窗口,在Styles选项卡中选择Buttons属性,该属性使标签以按钮形状显示,如图5.43所示。

图5.43  设置按钮形状的标签

0303  在控件底部显示标签选项

在使用标签控件时,还可以将标签控件的标签设置到控件的底部,打开标签控件的属性窗口,在More Styles选项卡中选择Bottom属性,该属性使标签在控件的底部显示,如图5.44所示。

图5.44  在控件底部显示标签选项

0304  CTabCtrl控件的使用方法

对于一些Visual C++的初学者来说,最头痛的是使用CTabCtrl控件。下面介绍一下CTabCtrl控件的使用方法。使用CTabCtrl控件,关键是如何向该控件中添加子控件。最简单的方法是在对话框中事先摆放好控件,然后处理标签控件的TCN_SELCHANGE消息,在当前标签改变时,显示和隐藏相应的控件。这样做会导致界面很零乱,而且不灵活。因此该方法不建议在程序中使用。第二种方法是使用子对话框实现。首先创建一个对话框,将对话框的风格设置为“WS_CHILD”,并且取消对话框的边框和标题栏;然后向对话框中添加控件;最后从CTabCtrl类派生一个子类,在该类中定义对话框指针,处理TCN_SELCHANGE消息,根据当前的标签索引将某个对话框显示在标签页中。