/// <summary> /// This method is only called when a node's state has been changed through the /// Checked property /// </summary> /// <param name="oldState">Previous state</param> /// <param name="newState">New state</param> private void CheckStateChanged(CheckState oldState, CheckState newState) { // States are equal? if (newState != oldState) { // not equal. // modify state this._nodeCheckState = newState; // change state of the children if (this.Nodes != null && this.Nodes.Count > 0) { foreach (System.Windows.Forms.TreeNode node in this.Nodes) { TriStateTreeNode tsNode = node as TriStateTreeNode; if (tsNode != null) { tsNode.ChangeChildState(newState); } } } // notify the parent of the changed state. if (this.Parent != null) { TriStateTreeNode parentNode = this.Parent as TriStateTreeNode; if (parentNode != null) { parentNode.ChildCheckStateChanged(this._nodeCheckState); } } } }
/// <summary> /// Toggles node state between checked & unchecked /// </summary> /// <param name="node"></param> private void ToggleNodeState(TriStateTreeNode node) { // no need to toggle state for non-existing node ( or non-tristatetreenode! ) if (null == node) { return; } // toggle state CheckState nextState; if (node.CheckState == CheckState.Unchecked) { nextState = CheckState.Checked; } else { nextState = CheckState.Unchecked; } // notify the treeview that an update is about to take place BeginUpdate(); // update the node state, and dependend nodes node.SetCheckedState(nextState); // force a redraw EndUpdate(); }
/// <summary> /// override the aftercheck event in order to get the node beeing checked/unchecked /// </summary> /// <param name="e"></param> protected override void OnAfterCheck(TreeViewEventArgs e) { base.OnAfterCheck(e); System.Windows.Forms.TreeNode node = e.Node; if (node != null) { TriStateTreeNode clickedNode = node as TriStateTreeNode; if (clickedNode.CheckboxVisible) { ToggleNodeState(clickedNode); } } }
/// <summary> /// This method is only called by other nodes so it can be private. /// Changes state of the node to the state provided. /// </summary> /// <param name="newState"></param> private void ChangeChildState(CheckState newState) { // change state this._nodeCheckState = newState; // change state on the children if (this.Nodes != null && this.Nodes.Count > 0) { foreach (System.Windows.Forms.TreeNode node in this.Nodes) { TriStateTreeNode tsNode = node as TriStateTreeNode; if (tsNode != null) { tsNode.ChangeChildState(newState); } } } }
/// <summary> /// Draws thee node lines before the checkboxes are drawn /// </summary> /// <param name="gx">Graphics context</param> private void DrawNodeLines(TriStateTreeNode node, Rectangle bounds, Graphics gx) { // determine type of line to draw NodeLineType lineType = node.NodeLineType; if (lineType == NodeLineType.None) { return; } using (Pen pen = new Pen(SystemColors.ControlDark, 1)) { pen.DashStyle = DashStyle.Dot; gx.DrawLine(pen, new Point(bounds.X, bounds.Y + 8), new Point(bounds.X + 15, bounds.Y + 8)); if (lineType == NodeLineType.WithChildren && node.IsExpanded) { gx.DrawLine(pen, new Point(bounds.X + 8, bounds.Y + 8), new Point(bounds.X + 8, bounds.Y + 16)); } } }
/// <summary> /// Paints the node specified. /// </summary> /// <param name="node"></param> /// <param name="gx"></param> private void PaintTreeNode(TriStateTreeNode node, Graphics gx) { if (this.CheckBoxes) { // calculate boundaries Rectangle ncRect = new Rectangle(node.Bounds.X - 35, node.Bounds.Y, 15, 15); // make sure the default checkboxes are gone ClearCheckbox(ncRect, gx); // draw lines, if we are supposed to if (this.ShowLines) { DrawNodeLines(node, ncRect, gx); } if (node.CheckboxVisible) { // now draw the checkboxes switch (node.CheckState) { case CheckState.Unchecked: // Normal DrawCheckbox(ncRect, gx, ButtonState.Normal | ButtonState.Flat); break; case CheckState.Checked: // Checked DrawCheckbox(ncRect, gx, ButtonState.Checked | ButtonState.Flat); break; case CheckState.Indeterminate: // Pushed DrawCheckbox(ncRect, gx, ButtonState.Pushed | ButtonState.Flat); break; } } } }
/// <summary> /// Private method that is called by one of the childnodes in order to report a /// change in state. /// </summary> /// <param name="childNewState">New state of the childnode in question</param> private void ChildCheckStateChanged(CheckState childNewState) { bool notifyParent = false; CheckState currentState = this._nodeCheckState; CheckState newState = this._nodeCheckState; // take action based on the child's new state switch (childNewState) { case CheckState.Indeterminate: // child state changed to indeterminate // if one of the children's state changes to indeterminate, // it's parent should do the same, if it is a container too!. if (IsContainer) { newState = CheckState.Indeterminate; // the same is valid for this node's parent. // check if this node's state has changed and inform the parent // if this is the case notifyParent = (newState != currentState); } break; case CheckState.Checked: // One of the child nodes was checked so we must check: // 1) if the child node is the only child node, our state becomes checked too. // 2) if there are children with a state other then checked, our state // must become indeterminate if this is a container. if (this.Nodes.Count == 1) // if there is only one child, our state changes too! { // set our state to checked too and set the flag for // parent notification. newState = CheckState.Checked; notifyParent = true; break; } // set to checked by default // if this is not a container, there is no need to check further newState = CheckState.Checked; if (!IsContainer) { notifyParent = (newState != currentState); break; } // traverse all child nodes to see if there are any with a state other then // checked. if so, change state to indeterminate. foreach (System.Windows.Forms.TreeNode node in this.Nodes) { TriStateTreeNode checkedNode = node as TriStateTreeNode; if (checkedNode != null && checkedNode.CheckState != CheckState.Checked) { newState = CheckState.Indeterminate; break; } } // set notification flag if our state has to be changed too notifyParent = (newState != currentState); break; case CheckState.Unchecked: // For nodes that are no containers, a child being unchecked is not relevant. // so we can exit at this point. if (!IsContainer) { break; } // A child's state has changed to unchecked so check: // 1) if this is the only child. if so, uncheck this node too, if it is a container, and set // notification flag for the parent. // 2) Check if there are child nodes with a state other then unchecked. // if so, change our state to indeterminate. if (this.Nodes.Count == 1) { // synchronize state with only child. // set notification flag newState = CheckState.Unchecked; notifyParent = true; break; } // set to unchecked by default newState = CheckState.Unchecked; // if there is a child with a state other then unchecked, // our state must become indeterminate. foreach (System.Windows.Forms.TreeNode node in this.Nodes) { TriStateTreeNode checkedNode = node as TriStateTreeNode; if (checkedNode != null && checkedNode.CheckState != CheckState.Unchecked) { newState = CheckState.Indeterminate; break; } } // notify the parent only if our state is about to be changed too. notifyParent = (newState != currentState); break; } // should we notify the parent? ( has our state changed? ) if (notifyParent) { // change state this._nodeCheckState = newState; // notify parent if (this.Parent != null) { TriStateTreeNode parentNode = this.Parent as TriStateTreeNode; if (parentNode != null) { // call the same method on the parent. parentNode.ChildCheckStateChanged(this._nodeCheckState); } } } }