分享
 
 
 

三态选择树实现终结者

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

三态选择树实现终结者

作者:南京银山网络有限公司 王佳豪

下载本文示例源代码

示例代码运行效果图如下:

在很多情况下,我们经常需要实现树的多态选择,如上图所示,当全部子节点选中的情况下,当前节点才被选中(如图示[荆门市]节点),当子节点部分选中时,当前节点处于第三态(如图示[湖北省]节点)当全部子节点未选中时,当前节点处于未选中的状态(如图示[江苏省]节点)。本文就介绍这种三态选择树的具体实现方法。

VC知识库第十九期中河南科技大学丛雷朋友也介绍了一种实现方法,两种方法比较,本文介绍的方法实现简单,兼容原CTreeCtrl的全部操作,CheckBox也是采用控件本身的CheckBox,只是在状态显示时重画而已。因此,本方法可以实现表示三态的情况下同时显示节点ICON图标,另增加了对CheckBox在某些节点是否显示的控制,同时增加了对键盘空格键选中、取消选中的控制。具体遍历父、子节点的方法同丛雷朋友朋友的方法类似,也是递归实现全部节点的遍历,只是优化了一些,效率更高。

下面介绍具体使用方法:

步骤一:生成一个对话框工程(示例工程CMutiTree)。

步骤二:添加树控件,按照实际需要设置所需的属性。

步骤三:做节点图标和三态选择框图标

一般情况下节点图标采用16×16,三态选择图标采用13×13大小比较合适。

三态选择图标对应: 0->无选择钮 1->没有选择 2->部分选择 3->全部选择

步骤四:将两个文件[MutiTreeCtrl.cpp

,MutiTreeCtrl.h]添加到步骤一创建的对话框

工程中,在CMutiTreeDlg类的头文件中增加对[MutiTreeCtrl.h]的包含,此时工程中增加了CMutiTreeCtrl类。

#include "MutiTreeCtrl.h"

步骤五:用ClassWizard在CmutiTreeDlg中创建一个树控件CTreeCtrl的对象m_TripleTree,更改该对象为上面步骤四加入的CMutiTreeCtrl类的对象。

步骤六:在CMutiTreeDlg类中定义两个CImageList

类的对象,用于加载CMutiTreeCtrl所需要的节点图标列表和三态选择框图标列表。

在CMutiTreeDlg类的头文件中:

CImageList m_imgList;

CImageList m_imgState;

在对话框的初始化函数中:m_imgState.Create(IDB_BITMAP_STATE,13, 1, RGB(255,255,255));

m_imgList.Create(IDB_BITMAP_LIST,16, 1, RGB(255,255,255));

m_TripleTree.SetImageList(&m_imgList,TVSIL_NORMAL);

m_TripleTree.SetImageList(&m_imgState,TVSIL_STATE);

完成以上六步操作后,编译、运行,用键盘空格键或鼠标单击CheckBox改变其状态,您将看到不需要再增加任何代码,已经实现了三态选择树的功能。如果需要隐藏某些选择框,如根节点的选择框,只需要设置对应的节点状态为0即可:

m_TripleTree.SetItemState( hRoot, INDEXTOSTATEIMAGEMASK(0),

TVIS_STATEIMAGEMASK );

上述代码将设置根节点不显示三态选择框。

我具体实现的思想是以Windows标准的CTreeCtrl类为基类派生一个类CMutiTreeCtrl,截获键盘和鼠标点击CheckBox的事件,在此消息响应函数中,更改CheckBox的状态,并搜索子节点、兄弟节点和父节点,更改其状态与上述逻辑一致。方法如下介绍:

一、 CTreeCtrl类为基类派生CMutiTreeCtrl类

class CMutiTreeCtrl : public CTreeCtrl

{

// Construction

public:

CMutiTreeCtrl();

// Attributes

public:

// Operations

public:

// Overrides

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CMutiTreeCtrl)

//}}AFX_VIRTUAL

// Implementation

public:

BOOL SetItemState( HTREEITEM hItem, UINT nState, UINT nStateMask, BOOL bSearch=TRUE);

virtual ~CMutiTreeCtrl();

// Generated message map functions

protected:

//{{AFX_MSG(CMutiTreeCtrl)

afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

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

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

afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

private:

UINT m_uFlags;

void TravelSiblingAndParent(HTREEITEM hItem, int nState);

void TravelChild(HTREEITEM hItem,int nState);

};

二、重载CTreeCtrl的SetItemState()函数,在调用了基类的SetItemState()函数修改了节点状态以后,遍历一遍当前节点子节点、兄弟节点、父节点,按照上述逻辑修改为相应的状态,实现三态显示。调用此函数有二种情况:

①键盘或鼠标输入修改节点状态,此时要遍历全部父、兄、子节点;

②程序根据实际情况调用修改节点状态,因为修改节点状态时是判断了全部子节点的状态后得出了状态,所以此时仅需要遍历全部的兄、父节点,更改其状态符合逻辑。故在重载的函数后面加了一个缺省为TRUE的bSearch变量,当程序修改节点时请置此标志为FALSE。

BOOL CMutiTreeCtrl::SetItemState(HTREEITEM hItem, UINT nState,

UINT nStateMask, BOOL bSearch)

{

BOOL bReturn=CTreeCtrl::SetItemState( hItem, nState, nStateMask );

UINT iState = nState 12;

if(iState!=0)

{

if(bSearch) TravelChild(hItem, iState);

TravelSiblingAndParent(hItem,iState);

}

return bReturn;

}

三、检测鼠标单击节点CHeckBox的事件,更改对应的节点状态并遍历树的其他节点。

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

{

HTREEITEM hItem =HitTest(point, &m_uFlags);

if ( (m_uFlags&TVHT_ONITEMSTATEICON ))

{

//nState: 0-无选择钮 1-没有选择 2-部分选择 3-全部选择

UINT nState = GetItemState( hItem, TVIS_STATEIMAGEMASK ) 12;

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

SetItemState(hItem,INDEXTOSTATEIMAGEMASK(nState),TVIS_STATEIMAGEMASK);

}

CTreeCtrl::OnLButtonDown(nFlags, point);

}

void CMutiTreeCtrl::OnStateIconClick(NMHDR* pNMHDR, LRESULT* pResult)

{

if(m_uFlags&TVHT_ONITEMSTATEICON) *pResult=1;

else *pResult = 0;

}

四、检测键盘按空格键的事件,更改对应的节点状态并遍历树的其他节点。

void CMutiTreeCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)

{

//处理空格键

if(nChar==0x20)

{

HTREEITEM hItem =GetSelectedItem();

UINT nState = GetItemState( hItem, TVIS_STATEIMAGEMASK ) 12;

if(nState!=0)

{

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

SetItemState( hItem, INDEXTOSTATEIMAGEMASK(nState),

TVIS_STATEIMAGEMASK );

}

}

else CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);

}

五、树的遍历用递归的方法搜索当前节点的父、兄、子节点

①递归搜索子节点void CMutiTreeCtrl::TravelChild(HTREEITEM hItem, int nState)

{

HTREEITEM hChildItem,hBrotherItem;

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

hChildItem=GetChildItem(hItem);

if(hChildItem!=NULL)

{

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

CTreeCtrl::SetItemState(hChildItem,INDEXTOSTATEIMAGEMASK(nState),

TVIS_STATEIMAGEMASK );

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

TravelChild(hChildItem, nState);

//处理子节点的兄弟节点和其子节点

hBrotherItem=GetNextSiblingItem(hChildItem);

while (hBrotherItem)

{

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

int nState1 = GetItemState( hBrotherItem, TVIS_STATEIMAGEMASK ) 12;

if(nState1!=0)

{

CTreeCtrl::SetItemState( hBrotherItem,

INDEXTOSTATEIMAGEMASK(nState),TVIS_STATEIMAGEMASK );

}

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

TravelChild(hBrotherItem, nState);

hBrotherItem=GetNextSiblingItem(hBrotherItem);

}

}

}

②递归搜索兄、父节点

void CMutiTreeCtrl::TravelSiblingAndParent(HTREEITEM hItem, int nState)

{

HTREEITEM hNextSiblingItem,hPrevSiblingItem,hParentItem;

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

hParentItem=GetParentItem(hItem);

if(hParentItem!=NULL)

{

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

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

hNextSiblingItem=GetNextSiblingItem(hItem);

while(hNextSiblingItem!=NULL)

{

nState1 = GetItemState( hNextSiblingItem, TVIS_STATEIMAGEMASK ) 12;

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

else hNextSiblingItem=GetNextSiblingItem(hNextSiblingItem);

}

if(nState1==nState)

{

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

hPrevSiblingItem=GetPrevSiblingItem(hItem);

while(hPrevSiblingItem!=NULL)

{

nState1 = GetItemState(hPrevSiblingItem,TVIS_STATEIMAGEMASK) 12;

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

else hPrevSiblingItem=GetPrevSiblingItem(hPrevSiblingItem);

}

}

if(nState1==nState || nState1==0)

{

nState1 = GetItemState( hParentItem, TVIS_STATEIMAGEMASK ) 12;

if(nState1!=0)

{

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

CTreeCtrl::SetItemState( hParentItem,

INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK );

}

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

TravelSiblingAndParent(hParentItem,nState);

}

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);

}

}

}

}

好了,一切就是这么简单,如果你还不清楚的话,那就打开工程看看吧,如你有什么问题也不要忘记来信告诉我哦!最后祝大家学习愉快,多多交流,多多进步,一切顺利!

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有