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<DWORD>(this));
*pResult = 0;
}
int CALLBACK CCompositorDlg::Compositor(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CListCtrl* pListCtrl=reinterpret_cast<CListCtrl*>(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<m_pButton.GetSize();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<count-1;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<nIndex;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<m_ButtonCount;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<file.GetLength();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消息,根据当前的标签索引将某个对话框显示在标签页中。
联系客服