生殖器去医院看哪科: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
*pResult = 0;
}
int CALLBACK CCompositorDlg::Compositor(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CListCtrl* pListCtrl=reinterpret_cast
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消息,根据当前的标签索引将某个对话框显示在标签页中。