Hi everyone,
Adapting Vikram Bhardwaj's original code for fixing various bugs in the TreeView control, I've written up a JScript library. This code is independant of a given TreeView control, so as long as you setup the event handlers up properly it should work for multiple TreeView controls on the same page.
Please let me know if you find any bugs. Instructions for using this script are found in the opening block comment. Note that the functionality I wanted is slightly different than Mr. Bhardwaj's, as explained in the comment.
If this doesn't format properly, just send me an e-mail and I'll forward you the code.
Thanks!
John LaRusic
/*
MODULE: MS IE TreeView Control client script
PURPOSE: This code fixes numerous bugs in the Microsoft TreeView
control, including the ability to allow postbacks
A TreeView object setup to use this library will have the
following functionality:
a) When a given checkbox is checked, the parent check
boxes will also be checked. *NOTE* This differs from
the functionality that Mr. Bhardwaj originally
supplied.
b) If a given checkbox is unchecked, then if all the
sibiling checkboxes are also not checked, the parent
checkbox will become unchecked.
There are still the occasional bug in this treeview, and I
think its tied to the behavior file for the treeview. If you
see a bug in this code, please bring my attention to it. My
e-mail is johnlr@gmail.com thanks!
AUTHOR: John LaRusic (with many thanks to Mr. Bhardwaj for the problem
definition and solution)
CREDIT: Vikram Bhardwaj wrote most of this code for a single TreeView
object. I simply adapted it by making it a bit more portable
(multiple TreeView objects can share this library) and fixing
a couple of bugs. I also formatted the code nicely and added
comments should anyone want to adapt this.
Mr. Bhardwaj's original post on this code can be found at:
http://www.asp.net/Forums/ShowPost.aspx?tabindex=1&PostID=301443
TO USE: Reference this script file on your page and add the following
event handlers (where "treeview1" is the ID of your tree view
object)
<script event="oncheck" for="treeview1">
TreeView_OnCheck(treeview1);
</script>
<script event="onhover" for="treeview1">
TreeView_OnHover(treeview1, event.treeNodeIndex);
</script>
<script event="onload" for="window">
TreeView_Setup(treeview1);
</script>
You also need to add an onclick event handler to your submit
button to call the TreeView_ReadyForPostback for the tree.
For example:
Button1.Attributes.Add("onclick", _
"TreeView_ReadyForPostback(treeview1)")
*/
/*
FUNCTION: TreeView_OnCheck
PURPOSE: Handles the OnCheck event for a TreeView control.
This will check all the parent nodes above the
"checked" node as well as check all the children
nodes below the "checked" node.
PARAMS: objTreeView - The TreeView control ID
*/
function TreeView_OnCheck(objTreeView){
var objNode = objTreeView.getTreeNode(objTreeView.clickedNodeIndex);
// If this node was initially checked, then we need to handle a bug
// in the TreeView control and do exactly the opposite. Once we do
// this once, then we want to continue as normal
if(TreeView_IsInitChecked(objNode)){
TreeView_SetChecked(objNode, !TreeView_IsChecked(objNode));
TreeView_SetInitChecked(objNode, false);
}
// Traverse the children and parents and refresh the tree
var bChecked = TreeView_IsChecked(objNode);
TreeView_TraverseChildren(objNode.getChildren(), bChecked);
TreeView_TraverseParents(objNode, bChecked);
//TreeView_RefreshTree(objTreeView.getChildren());
}
/*
FUNCTION: TreeView_OnHover
PURPOSE: Handles the OnHover event for a TreeView control
It selects the node the user hovered over. This is to help
the program of an expanded node not being the selected node
PARAMS: objTreeView - The TreeView control ID
strNodeIndex - The index of the node to select
*/
function TreeView_OnHover(objTreeView, strNodeIndex){
objTreeView.selectedNodeIndex = strNodeIndex;
}
/*
FUNCTION: TreeView_Setup
PURPOSE: This sets up the TreeView control by building its
StrVals property.
PARAMS: objTreeView - The TreeView control ID
*/
function TreeView_Setup(objTreeView){
TreeView_SetInitCheckedNodes(objTreeView.getChildren());
}
/*
FUNCTION: TreeView_SetInitCheckedNodes
PURPOSE: Sets which nodes in the tree are initially checked or not
PARAMS: arrChildren - An array of children for a node
RETURNS: The StrVal string for a given group of children
*/
function TreeView_SetInitCheckedNodes(arrChildren){
var objChild;
for(var i = 0; i < arrChildren.length; i++){
objChild = arrChildren[i];
// Set whether the nodes were initially checked or not as well
// as set their initial value... yes, they are the same thing,
// but we might change whether a node has been "initially
// checked" or not later to handle a bug.
var blnIsChecked = TreeView_IsChecked(objChild);
TreeView_SetInitChecked(objChild, blnIsChecked);
TreeView_SetInitCheckValue(objChild, blnIsChecked);
// Call this function recursively on the children
TreeView_SetInitCheckedNodes(objChild.getChildren());
}
}
/*
FUNCTION: TreeView_IsChecked
PURPOSE: Determines if a given tree node is checked or not
PARAMS: objNode - A tree node object
RETURNS: True if the node is checked, false if not
*/
function TreeView_IsChecked(objNode){
// This seems like it should be done in one line, but it helps deal with
// the case that the Checked attribute is equal to null
if(objNode.getAttribute("Checked"))
return true;
else
return false;
}
/*
FUNCTION: TreeView_SetChecked
PURPOSE: Sets if a tree node is checked or not
PARAMS: objNode - A tree node object
*/
function TreeView_SetChecked(objNode, blnChecked){
objNode.setAttribute("Checked", blnChecked);
}
/*
FUNCTION: TreeView_IsInitChecked
PURPOSE: Determines if a given tree node was initially checked
PARAMS: objNode - A tree node object
RETURNS: True if the node was initially checked, false if not
*/
function TreeView_IsInitChecked(objNode){
return objNode.getAttribute("InitChecked");
}
/*
FUNCTION: TreeView_SetInitChecked
PURPOSE: Sets if a tree node is initially checked or not
PARAMS: objNode - A tree node object
blnChecked - True if the node was initially checked,
false if not
*/
function TreeView_SetInitChecked(objNode, blnChecked){
objNode.setAttribute("InitChecked", blnChecked);
}
/*
FUNCTION: TreeView_IsInitChecked
PURPOSE: Determines if a given tree node was initially checked
PARAMS: objNode - A tree node object
RETURNS: True if the node was initially checked, false if not
*/
function TreeView_GetInitCheckValue(objNode){
return objNode.getAttribute("InitCheckValue");
}
/*
FUNCTION: TreeView_SetInitCheckValue
PURPOSE: Sets the initially checked value of a node
PARAMS: objNode - A tree node object
blnChecked - True if the node was originally checked,
false if not
*/
function TreeView_SetInitCheckValue(objNode, blnChecked){
objNode.setAttribute("InitCheckValue", blnChecked);
}
/*
FUNCTION: TreeView_TraverseChildren
PURPOSE: A recursive function that traverses through the tree and
checks or unchecks the nodes
PARAMS: arrChildren - An array of nodes
blnChecked - Determines whether to check the nodes or not
*/
function TreeView_TraverseChildren(arrChildren, blnChecked){
var objChild;
for(var i = 0; i < arrChildren.length; i++){
objChild = arrChildren[i];
// Set whether the node is checked or not
TreeView_SetChecked(objChild, blnChecked);
// Call this function recursively on the children of the node
TreeView_TraverseChildren(objChild.getChildren(), blnChecked);
}
}
/*
FUNCTION: TreeView_TraverseParents
PURPOSE: A recursive function that traverses through a node's
parents and checks them
PARAMS: arrChildren - An array of nodes
blnChecked - Determines whether to check the nodes or not
*/
function TreeView_TraverseParents(objNode, blnChecked){
var objParent = objNode.getParent();
if(objParent != null){
// If we are selecting a checkbox, then we want to check all the
// parent checkboxes as well
if(blnChecked){
TreeView_SetChecked(objParent, true);
}
// Otherwise, we want to check to see if any of the siblings of the
// original node are also checked. If they are all not checked,
// then we want to uncheck the parent.
else{
var blnFlag = true;
var arrSiblings = objParent.getChildren();
for(var i = 0; i < arrSiblings.length ; i++){
if(TreeView_IsChecked(arrSiblings[i]))
blnFlag = false;
}
if(blnFlag)
TreeView_SetChecked(objParent, false);
}
// Call this function recursively on the parent
TreeView_TraverseParents(objParent, blnChecked);
}
}
/*
FUNCTION: TreeView_ReadyForPostback
PURPOSE: Readies a tree for a postback
PARAMS: objTreeView - A TreeView control
*/
function TreeView_ReadyForPostback(objTreeView){
if(objTreeView != null)
TreeView_CheckVals(objTreeView, objTreeView.getChildren());
else
alert("ERROR: TreeView object is null");
}
/*
FUNCTION: TreeView_CheckVals
PURPOSE: Signals that nodes have been checked for the postback
PARAMS: objTreeView - A TreeView control
arrChildren - An array of child nodes
*/
function TreeView_CheckVals(objTreeView, arrChildren){
var objChild;
// Loop through all the nodes
for(var i = 0; i < arrChildren.length; i++){
objChild = arrChildren[i];
// If the current value of the node is different from its initial
// value, then we want to queue the oncheck event
if(TreeView_IsChecked(objChild) != TreeView_GetInitCheckValue(objChild))
objTreeView.queueEvent("oncheck", objChild.getNodeIndex());
// Call this function recursively the child nodes of the current
// node
TreeView_CheckVals(objTreeView, objChild.getChildren());
}
}
/*
FUNCTION: TreeView_RefreshTree
PURPOSE: ??? - This was in the Vikram Bhardwaj original code... not
sure if its necessary or not, but I'm leaving it here...
uncomment the call to it in the TreeView_OnCheck method if
you need it
PARAMS: objTreeView - A TreeView control
*/
/*
function TreeView_RefreshTree(arrChildren){
var objChild;
for(var i = 0; i < arrChildren.length; i++){
objChild = arrChildren[i];
TreeView_SetChecked(objChild, TreeView_IsChecked(objChild));
TreeView_RefreshTree(objChild.getChildren());
}
}
*/