最近在做一个关于绩效考核问卷填报的项目(注:这是关于人力资源项目中的一个模块),在发布考核问卷前要设定好填报人员的范围,即这个问卷需要由哪些人来填报,因此也就用到了TreeView控件来展现公司组织结构中的员工。效果如下图所示:

本来以为可以轻松搞定,没想到还是遇到了几个问题,下面我把遇到的几个问题描述一下,并写出我是如何解决的。
问题一、TreeNodeCheckChanged事件无法正常响应的问题
功能描述:勾选父节点时,希望将父节点本身和其下所有子节点全选或全清。
问题描述: TreeNodeCheckChanged事件压根就没有响应。
问题原因:MSDN原文如下:当 TreeView 控件的复选框在两次向服务器发送之间更改状态时,会引发TreeNodeCheckChanged 事件。尽管 TreeNodeCheckChanged 事件在回发时激发,但更改复选框不会导致回发。
也就是说,更改复选框并不能导致回发,而此事件激发的条件是只有回发页面时才被激发,这样说是不是很饶舌,呵呵。
解决办法:我们自己来制造一个页面回发。这样不就可以激发TreeNodeCheckChanged事件了吗,说做就做。
第一步:写一个javascript方法,在TreeView的客户端单击事件中调用。
<script language="javascript" type="text/javascript">
function postBackByObject() {
var o = window.event.srcElement;
if (o.tagName == "INPUT" && o.type == "checkbox") {
//回发到树形控件,即只部分刷新TreeView
__doPostBack("<%= tvRange.ClientID%>", "");
}
}
</script>
第二步:加入客户端单击事件:onclick="javascript:postBackByObject()"
并加入服务器端的TreeNodeCheckChanged事件。
TreeView控件的HTML如下:
<asp:TreeView ID="tvRange" runat="server" ShowCheckBoxes="All" ShowLines="true"
onclick="javascript:postBackByObject()"
ontreenodecheckchanged="tvRange_TreeNodeCheckChanged">
</asp:TreeView>
第三步:在TreeNodeCheckChanged中调用全选/全清函数。
protected void tvRange_TreeNodeCheckChanged(object sender, TreeNodeEventArgs e)
{
this.CheckTreeNode(e.Node, e.Node.Checked);
}
/// <summary>
/// 勾选节点(全选/全清)
/// </summary>
/// <param name="node"></param>
/// <param name="bChecked"></param>
private void CheckTreeNode(TreeNode node, bool bChecked)
{
node.Checked = bChecked;
foreach (TreeNode childNode in node.ChildNodes)
{
childNode.Checked = bChecked;
CheckTreeNode(childNode, bChecked);
}
}
好了,一切大功告成,让我再来理一下思路。我们知道,激发TreeNodeCheckChanged的条件是页面回发,那么我们通过TreeView的单击事件就人为制造一次页面回发,这时页面回发后就会激发TreeNodeCheckChanged事件,接下来自然就会调用CheckTreeNode函数。此方法很笨,响应速度有点慢,至于慢的原因自己想想。
问题二、TreeNodeCollection莫名地删除节点的问题
这个问题是由于我对TreeNodeCollection认知不足引起的。下面我来描述一下事情的经过。用户勾选好填报人员的范围后,我想把选中的岗位节点保存到数据库中,那么自然而然地就会要写个函数来获取这些被选中的岗位节点,函数最初写法如下:
/// <summary>
/// 获取选中的岗位节点
/// </summary>
/// <returns></returns>
private TreeNodeCollection GetCheckedPositionNodes()
{
TreeNodeCollection checkedNodes = new TreeNodeCollection();
foreach (TreeNode node in this.tvRange.CheckedNodes)
{
/*PositionEmps是岗位员工关系表,意思是只要关系表中
有对应的选中节点,我就会把它记录在选中节点的集合中*/
if (this.PositionEmps.Select(string.Format("OUID={0}", node.Value)).Length > 0)
checkedNodes.Add(node);
}
return checkedNodes;
}
看出问题所在了吗?
调试过程中,发现我往TreeNodeCollection中添加的这些岗位节点不翼而飞,重新刷新一下TreeView,这些节点就没了,真的很莫名奇妙。于是带着这个问题查了下关于TreeNodeCollection的MSDN文档。
关于TreeNodeCollection类的解释原文如下:
TreeNodeCollection 类用于存储和管理 TreeView 控件中的 TreeNode 对象的集合。TreeView 控件在其两个属性中使用 TreeNodeCollection 类。在 Nodes 属性中存储其根节点,在 CheckedNodes 属性中存储其选定的节点。TreeNodeCollection 集合也用于 ChildNodes 属性来存储子节点(如果有的话)。
关于TreeNodeCollection的构造函数有两个:
即:(1)public TreeNodeCollection()
此构造函数用于创建根节点集合
(2)public TreeNodeCollection(TreeNode owner)
此构造函数用于创建指定父节点的非根节点集合。
如上信息告诉我TreeNodeCollection类是给“根节点Nodes”、“选中节点CheckedNodes”和“子节点ChildNodes(如果有的话)”专用的。可是这并不能解释我往TreeNodeCollection实例添加的选中节点不翼而飞的原因,于是我用Reflector工具看了一下TreeNodeCollection的Add方法,方法如下:
public void Add(TreeNode child)
{
this.AddAt(this.Count, child);
}
public void AddAt(int index, TreeNode child)
{
if (child == null)
{
throw new ArgumentNullException("child");
}
if (this._updateParent)
{
if ((child.Owner != null) && (child.Parent == null))
{
child.Owner.Nodes.Remove(child);
}
if (child.Parent != null)
{
child.Parent.ChildNodes.Remove(child);
}
if (this._owner != null)
{
child.SetParent(this._owner);
child.SetOwner(this._owner.Owner);
}
}
this._list.Insert(index, child);
this._version++;
if (this._isTrackingViewState)
{
((IStateManager) child).TrackViewState();
child.SetDirty();
}
this.Log.Add(new TreeNodeCollection.LogItem(TreeNodeCollection.LogItemType.Insert, index, this._isTrackingViewState));
}
看到这里我才有所顿悟,原来添加指定父节点的TreeNode时被清了,即如下这句话导致的。
if(child.Parent != null){child.Parent.ChildNodes.Remove(child); }
没办法,只好将获取选中的岗位节点的函数改为如下方法:
/// <summary>
/// 获取选中的岗位节点
/// </summary>
/// <returns></returns>
private List<TreeNode> GetCheckedPositionNodes()
{
List<TreeNode> checkedNodes = new List<TreeNode>();
foreach (TreeNode node in this.tvRange.CheckedNodes)
{
if (this.PositionEmps.Select(string.Format("OUID={0}", node.Value)).Length > 0)
checkedNodes.Add(node);
}
return checkedNodes;
}
经调试,一切恢复正常。看到了吗?我将
TreeNodeCollection checkedNodes = new TreeNodeCollection();改为
List<TreeNode> checkedNodes = new List<TreeNode>();就没事了。

浙公网安备 33010602011771号