atconway:
This JavaScript works perfectly for me as far as forcing a postback when a treeview's checkbox is checked, but I am having one small issue. The postback is causing validation and my AJAX contol validator extenders are poping up on unrelated controls. I did not see any 'CausesValidation' property on the Treeview control.
Is there a way to still force the postback when checking the checkbox in the tree, but preventing page validation?
I've been experimenting with the TreeView and UpdatePanel, I added a Button to the page to see if I could reproduce your situation, but I can't. However, here is my code:
function TreeNodeCheckChanged(event, control) {
// Valid for IE and Firefox/Safari/Chrome.
var obj = window.event ? window.event.srcElement : event.target;
var source = window.event ? window.event.srcElement.id : event.target.id;
source = source.replace(control.id + "t", control.id + "n");
source = source.replace("CheckBox", "");
var checkbox = document.getElementById(source);
if (checkbox != null &&
obj.tagName == "INPUT" &&
obj.type == "checkbox") {
__doPostBack(checkbox.id, "");
}
}
I ended up changing the JavaScript posted above a little bit. First, to add some cross-browser compatibility, this is why the event gets passed. If someone has a cleaner method, please let me know. I suck at JavaScript for the most part. Second, I also pass the control so it is a bit more extensible. Finally, I added the CheckBox's ID to the __doPostBack() call as per the following link:
http://weblogs.asp.net/jeffreyzhao/archive/2008/04/26/refresh-the-updatepanel-using-javascript-code.aspx
FWIW, I didn't need to do anything to the ScriptManager in my little demo. Here is the rest of my HTML:
<asp:ScriptManager ID="ScriptManager" runat="server" />
<div>
<asp:UpdatePanel id="UpdatePanel" runat="server">
<ContentTemplate>
<asp:TextBox ID="TextBox1" runat="server" />
<asp:RequiredFieldValidator ID="rfvTextBox1" runat="server" ControlToValidate="TextBox1" ErrorMessage="Please enter some text."/><br />
<asp:TreeView ID="treeTest" runat="server"
NodeStyle-Font-Names="Consolas"
NodeStyle-Font-Size="12px"
ShowCheckBoxes="All"
ShowExpandCollapse="true"
EnableClientScript="true"
OnSelectedNodeChanged="OnSelectedNodeChanged"
OnTreeNodeCheckChanged="OnTreeNodeCheckChanged"
OnAdaptedTreeNodeCheckChanged="OnTreeNodeCheckChanged">
</asp:TreeView>
<br /><br />
</ContentTemplate>
</asp:UpdatePanel>
<asp:Button ID="btnTest" runat="server" Text="Click" />
And here is my code-behind:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadTreeView();
}
/// <summary>
/// Populates the TreeView control.
/// </summary>
private void LoadTreeView()
{
NorthwindDataContext db = new NorthwindDataContext();
// Query a list of Products and their Categories, excluding Products with no Category.
var products = from p in db.Products
where p.Category.CategoryName != string.Empty
orderby p.Category.CategoryName
select new
{
p.Category.CategoryName,
p.ProductName
};
string currentCategory = string.Empty;
int catNum = 0;
// Add nodes to the tree.
foreach (var cat in products)
{
if (!currentCategory.Equals(cat.CategoryName))
{
// Add Category TreeNode.
currentCategory = cat.CategoryName;
treeTest.Nodes.Add(new TreeNode(currentCategory));
treeTest.Nodes[catNum].Collapse();
// Add Product TreeNodes under each Category.
foreach (var prod in products)
{
if (prod.CategoryName.Equals(currentCategory))
{
treeTest.Nodes[catNum].ChildNodes.Add(new TreeNode(prod.ProductName));
}
}
catNum++;
}
}
// Add the onclick event handler so checking a parent node fires the OnTreeNodeCheckChanged event.
treeTest.Attributes.Add("onclick", "TreeNodeCheckChanged(event, this)");
}
/// <summary>
/// Expands/Collapses a node when clicked.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void OnSelectedNodeChanged(object sender, EventArgs e)
{
if (((TreeView)sender).SelectedNode.ChildNodes.Count > 0)
{
if ((bool)((TreeView)sender).SelectedNode.Expanded)
{
((TreeView)sender).SelectedNode.Collapse();
}
else
{
((TreeView)sender).SelectedNode.Expand();
}
}
// Deselects the SelectedNode so it can be toggled without clicking on another node first.
((TreeView)sender).SelectedNode.Selected = false;
}
/// <summary>
/// Checks or unchecks child nodes when a parent node is checked or unchecked.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void OnTreeNodeCheckChanged(object sender, TreeNodeEventArgs e)
{
// Determine if checked Node is a root node.
if (e.Node.ChildNodes.Count > 0)
{
// Check or uncheck all of the child nodes based on status of parent node.
if (e.Node.Checked)
ChangeChecked(e.Node, true);
else
ChangeChecked(e.Node, false);
}
}
/// <summary>
/// Recursively checks or unchecks all child nodes for a given TreeNode.
/// </summary>
/// <param name="node">TreeNode to check or uncheck.</param>
/// <param name="check">Desired value of TreeNode.Checked.</param>
private void ChangeChecked(TreeNode node, bool check)
{
// "Queue" up child nodes to be checked or unchecked.
if (node.ChildNodes.Count > 0)
{
for (int i = 0; i < node.ChildNodes.Count; i++)
ChangeChecked(node.ChildNodes[i], check);
}
node.Checked = check;
}
So, that kinda works for me. Let me know if I am missing something. BTW, if anyone has any suggestions to improve my LINQ section (or anything else), please let me know. My loop feels kinda inefficient.