關于Winform下,獲取Treeview中CheckBox選中項的技巧
背景
今天,在做一個指紋管理平臺的界面上,遇到了一些問題,因為公司的基礎庫中沒有針對Winform平臺的操作,所以在一個部門選擇的場景中,關于如何獲取部門TreeView中被選中的項目被難住了。所幸還有一個神器叫做搜索引擎,故谷歌一番,發現Asp.net下的這個問題很好解決,但是關于Winform平臺下的類似問題,如鳳毛麟角啊!
當然,今天我也是查了很久,終于發現了兩篇不錯的文章,幫助我解決了這個問題,不敢獨享,結合自己對使用其方法的心得,特意記錄下來,并通過一個小例子和大家一起分享。
問題是這樣的
有一個部門列表,多級的(或者說無限極),我們在Winform端列出來(這個很簡單)后,需要對其進行部分的選擇,然后通過一個按鈕,提交選擇好的部門,進入下一個業務操作場景,今天我遇到的問題就是,如何獲取這個部門TreeView中,各個級別所選取的項目呢?如下圖:
在這里只是用一個例子來說明,所以就簡單一點吧,如左圖所示,我們的任務就是通過程序獲取被選中的部門,將其信息送到下一個業務場景之中,在傳統的Asp.net中,Web.UI下的Treeview控件提供了很方便的接口方法,用于獲取被選中的項目,但是來到了Winform下,瞬間杯催……
發現不能用Asp.net的思路來做這個事情后,我立馬谷歌了一下,發現如下這篇文章,說的很不錯,基本上解決了我遇到的問題,原文地址如下:“時空隧道”
我總結了一下,原作者的大概思路應該如下:
圖1-簡單的流程圖示意
解決問題:
在頁面(窗體)申明兩個變量:
- 第一個是用于存放被選項目的,文中是StringBuilder對象。
- 第二個是一個Tag標簽,按照原文的話就是“我們在TreeView的結點中用Tag來附加了CheckBox是否選中的標識信息”,字符串對象。
用代碼來描述就是如下形式:
StringBuilder nodesTag;//容器
接下來,我們就開始去處理AfterCheck事件,讓程序能夠“記住”我們選擇過的項目。無論大家有沒有看過那篇文章,其中提到過“樹的遞歸算法”,關于算法,大家可以參考如下這個鏈接:點擊穿越
在了解完算法后,相信大家能離家如下一段的代碼了吧:這里就照搬這位博友的code了

{
TreeNode node = parent;
if (node != null)
{
if (node.Tag != null && node.Tag.ToString() == tag)
nodesTag.AppendFormat("node:{0} has checked\n", node.Text);
if (node.FirstNode != null)////如果node節點還有子節點則進入遍歷
{
TraversNodes(node.FirstNode);
}
if (node.NextNode != null)////如果node節點后面有同級節點則進入遍歷
{
TraversNodes(node.NextNode);
}
}
}
AfterCheck事件代碼:

{
if (e.Action != TreeViewAction.Unknown)
{
TreeNode node = e.Node;
if (node.Tag == null)
node.Tag = tag;//附加結點信息
else
node.Tag = null;
CheckAllChildNodes(e.Node, e.Node.Checked);
//選中父節點
bool bol = true;
if (e.Node.Parent != null)
{
for (int i = 0; i < e.Node.Parent.Nodes.Count; i++)
{
if (!e.Node.Parent.Nodes[i].Checked)
bol = false;
}
e.Node.Parent.Checked = bol;
////記得如果父節點被選中或取消,記得設置它的tag哦
if (bol)
{
e.Node.Parent.Tag = tag;
}
else
{
e.Node.Parent.Tag = null;
}
}
}
}
這個方法的調用時機應該是什么時候呢?對于我的場景來說,應該是再點擊那個業務按鈕之后,在之前,我們的AfterCheck事件代碼唯一要做的事情就是如下圖所示的一個流程:
當我們點擊業務按鈕之后,為其添加如下代碼即可:

{
nodesTag = new StringBuilder();
TreeNode node = treeView1.TopNode;//得到TreeView的根結點,注意根結點只有一個
//每次搜尋到根結點
while (node.PrevNode != null)
node = node.PrevNode;
TraversNodes(node);//遍歷根結點
MessageBox.Show(nodesTag.ToString());
}
再好一點:
到這里呢,按理說問題已經被解決完了。我們獲取到了選中的部門,但是,按照常規來說,應該“選中節點的時候,如果節點存在子節點,可以選中全部的子節點;如果取消一個節點的選中狀態,當前節點同時又存在父節點的話,就取消父節點的選中狀態”,怎么辦呢?
我覺得自己很懶,“搜”的一下,給大家鏈上一篇文章,我覺得寫的很不錯啊,如下:點此穿越
好啦,根據原文的意思,我們結合之前的獲取選中狀態的代碼,修改如下:

{
if (e.Action != TreeViewAction.Unknown)
{
TreeNode node = e.Node;
if (node.Tag == null)
node.Tag = tag;//附加結點信息
else
node.Tag = null;
CheckAllChildNodes(e.Node, e.Node.Checked);
//選中父節點
bool bol = true;
if (e.Node.Parent != null)
{
for (int i = 0; i < e.Node.Parent.Nodes.Count; i++)
{
if (!e.Node.Parent.Nodes[i].Checked)
bol = false;
}
e.Node.Parent.Checked = bol;
////記得如果父節點被選中或取消,記得設置它的tag哦
if (bol)
{
e.Node.Parent.Tag = tag;
}
else
{
e.Node.Parent.Tag = null;
}
}
}
}
新的CheckAllChildNodes方法:
根據博友Flynn2009的提示,應該注意代碼需要有修改,如下:
node.Tag = nodeChecked ? (bool)tag : null;

{
foreach (TreeNode node in treeNode.Nodes)
{
node.Checked = nodeChecked;
node.Tag = tag;////記得在這里為選中的項目設置tag屬性
if (node.Nodes.Count > 0)
{
this.CheckAllChildNodes(node, nodeChecked);
}
}
}
結束:
好啦,在這里呢,我們就完成了獲取Treeview中CheckBox選中狀態的小例子,并且呢,還人性化的設置了一個關于父子選項的效果。文章就到這里為止吧,希望能給大家帶來收獲。歡迎拍磚!!!
最后附上本文的示例項目代碼:Vs2008+.Net 2.0