PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/Windows/ThreeStateTreeNode.cs

#
C# | 271 lines | 158 code | 24 blank | 89 comment | 27 complexity | 05acd8f4fb36ae5237d30e2bb59560b4 MD5 | raw file
  1. using System;
  2. using System.ComponentModel;
  3. using System.Text;
  4. using System.Windows.Forms;
  5. namespace JoshuaOneEight.Windows
  6. {
  7. /// <summary>
  8. /// ThreeStateTreeNode inherits from <see cref="http://msdn2.microsoft.com/en-us/library/system.windows.forms.treenode.aspx">TreeNode</see>
  9. /// and adds the ability to support a third, indeterminate state as well as optionally cascading state changes to related nodes, i.e.
  10. /// child nodes and or parent nodes, as determined by this instance's related parent TreeView settings, CascadeNodeChecksToChildNodes and
  11. /// CascadeNodeChecksToParentNode.
  12. /// </summary>
  13. public class ThreeStateTreeNode : TreeNode
  14. {
  15. #region Constructors
  16. /// <summary>
  17. /// Initializes a new instance of the ThreeStateTreeNode class in addition to intializing
  18. /// the base class (<see cref="http://msdn2.microsoft.com/en-us/library/bk8h64c9.aspx">TreeNode Constructor</see>).
  19. /// </summary>
  20. public ThreeStateTreeNode() : base()
  21. {
  22. this.CommonConstructor();
  23. }
  24. /// <summary>
  25. /// Initializes a new instance of the ThreeStateTreeNode class with a string for the text label to display in addition to intializing
  26. /// the base class (<see cref="http://msdn2.microsoft.com/en-us/library/ytx906df.aspx">TreeNode Constructor</see>).
  27. /// </summary>
  28. /// <param name="text">The string for the label of the new tree node.</param>
  29. public ThreeStateTreeNode(string text) : base(text)
  30. {
  31. this.CommonConstructor();
  32. }
  33. /// <summary>
  34. /// Initializes a new instance of the ThreeStateTreeNode class with a string for the text label to display
  35. /// and an array of child ThreeStateTreeNodes in addition to intializing the base class
  36. /// (<see cref="http://msdn2.microsoft.com/en-us/library/774ty506.aspx">TreeNode Constructor</see>).
  37. /// </summary>
  38. /// <param name="text">The string for the label of the new tree node.</param>
  39. /// <param name="children">An array of child ThreeStateTreeNodes.</param>
  40. public ThreeStateTreeNode(string text, ThreeStateTreeNode[] children) : base(text, children)
  41. {
  42. this.CommonConstructor();
  43. }
  44. /// <summary>
  45. /// Initializes a new instance of the ThreeStateTreeNode class with a string for the text label to display
  46. /// and the selected and unselected image indexes in addition to intializing the base class
  47. /// (<see cref="http://msdn2.microsoft.com/en-us/library/8dfy3k5t.aspx">TreeNode Constructor</see>).
  48. /// </summary>
  49. /// <param name="text">The string for the label of the new tree node.</param>
  50. /// <param name="imageIndex">The image index of the unselected image in the parent TreeView's <see cref="http://msdn2.microsoft.com/en-us/library/system.windows.forms.treeview.imagelist.aspx">ImageList</see>.</param>
  51. /// <param name="selectedImageIndex">The image index of the selected image in the parent TreeView's <see cref="http://msdn2.microsoft.com/en-us/library/system.windows.forms.treeview.imagelist.aspx">ImageList</see>.</param>
  52. public ThreeStateTreeNode(string text, int imageIndex, int selectedImageIndex) : base(text, imageIndex, selectedImageIndex)
  53. {
  54. this.CommonConstructor();
  55. }
  56. /// <summary>
  57. /// Initializes a new instance of the ThreeStateTreeNode class with a string for the text label to display ,
  58. /// the selected and unselected image indexes, and an array of child ThreeStateTreeNodes in addition to intializing the base class
  59. /// (<see cref="http://msdn2.microsoft.com/en-us/library/8dfy3k5t.aspx">TreeNode Constructor</see>).
  60. /// </summary>
  61. /// <param name="text">The string for the label of the new tree node.</param>
  62. /// <param name="imageIndex">The image index of the unselected image in the parent TreeView's <see cref="http://msdn2.microsoft.com/en-us/library/system.windows.forms.treeview.imagelist.aspx">ImageList</see>.</param>
  63. /// <param name="selectedImageIndex">The image index of the selected image in the parent TreeView's <see cref="http://msdn2.microsoft.com/en-us/library/system.windows.forms.treeview.imagelist.aspx">ImageList</see>.</param>
  64. /// <param name="children">An array of child ThreeStateTreeNodes.</param>
  65. public ThreeStateTreeNode(string text, int imageIndex, int selectedImageIndex, ThreeStateTreeNode[] children) : base(text, imageIndex, selectedImageIndex, children)
  66. {
  67. this.CommonConstructor();
  68. }
  69. #endregion
  70. #region Initialization
  71. /// <summary>
  72. /// Performs common initialization to all constructors.
  73. /// </summary>
  74. private void CommonConstructor()
  75. {
  76. }
  77. #endregion
  78. #region Properties
  79. /// <summary>
  80. /// The current state of the checkbox.
  81. /// </summary>
  82. private CheckBoxState mState = CheckBoxState.Unchecked;
  83. [Category("Three State TreeView"),
  84. Description("The current state of the node's checkbox, Unchecked, Checked, or Indeterminate"),
  85. DefaultValue(CheckBoxState.Unchecked),
  86. TypeConverter(typeof(CheckBoxState)),
  87. Editor( "JoshuaOneEight.CheckBoxState", typeof( CheckBoxState ) )]
  88. public CheckBoxState State
  89. {
  90. get { return this.mState; }
  91. set
  92. {
  93. if (this.mState != value)
  94. {
  95. this.mState = value;
  96. switch( value )
  97. {
  98. case CheckBoxState.Unchecked:
  99. this.ImageIndex = 0;
  100. this.SelectedImageIndex = 0;
  101. break;
  102. case CheckBoxState.Checked:
  103. this.ImageIndex = 1;
  104. this.SelectedImageIndex = 1;
  105. break;
  106. default:
  107. this.ImageIndex = 2;
  108. this.SelectedImageIndex = 2;
  109. break;
  110. }
  111. }
  112. }
  113. }
  114. /// <summary>
  115. /// Returns the 'combined' state for all siblings of a node.
  116. /// </summary>
  117. private CheckBoxState SiblingsState
  118. {
  119. get
  120. {
  121. // If parent is null, cannot have any siblings or if the parent
  122. // has only one child (i.e. this node) then return the state of this
  123. // instance as the state.
  124. if ((this.Parent == null) || (this.Parent.Nodes.Count == 1))
  125. return this.State;
  126. // The parent has more than one child. Walk through parent's child
  127. // nodes to determine the state of all this node's siblings,
  128. // including this node.
  129. CheckBoxState state = 0;
  130. foreach (TreeNode node in this.Parent.Nodes)
  131. {
  132. ThreeStateTreeNode child = node as ThreeStateTreeNode;
  133. if (child != null)
  134. state |= child.State;
  135. // If the state is now indeterminate then know there
  136. // is a combination of checked and unchecked nodes
  137. // and no longer need to continue evaluating the rest
  138. // of the sibling nodes.
  139. if (state == CheckBoxState.Indeterminate)
  140. break;
  141. }
  142. return (state == 0) ? CheckBoxState.Unchecked : state;
  143. }
  144. }
  145. #endregion
  146. #region Methods
  147. /// <summary>
  148. /// Manages state changes from one state to the next.
  149. /// </summary>
  150. /// <param name="fromState">The state upon which to base the state change.</param>
  151. public void Toggle(CheckBoxState fromState)
  152. {
  153. switch (fromState)
  154. {
  155. case CheckBoxState.Unchecked:
  156. {
  157. this.State = CheckBoxState.Checked;
  158. break;
  159. }
  160. case CheckBoxState.Checked:
  161. case CheckBoxState.Indeterminate:
  162. default:
  163. {
  164. this.State = CheckBoxState.Unchecked;
  165. break;
  166. }
  167. }
  168. this.UpdateStateOfRelatedNodes();
  169. }
  170. /// <summary>
  171. /// Manages state changes from one state to the next.
  172. /// </summary>
  173. public new void Toggle()
  174. {
  175. this.Toggle(this.State);
  176. }
  177. /// <summary>
  178. /// Manages updating related child and parent nodes of this instance.
  179. /// </summary>
  180. public void UpdateStateOfRelatedNodes()
  181. {
  182. ThreeStateTreeView tv = this.TreeView as ThreeStateTreeView;
  183. if ((tv != null) && tv.UseThreeStateCheckBoxes)
  184. {
  185. tv.BeginUpdate();
  186. // If want to cascade checkbox state changes to child nodes of this node and
  187. // if the current state is not intermediate, update the state of child nodes.
  188. if (this.State != CheckBoxState.Indeterminate)
  189. this.UpdateChildNodeState();
  190. this.UpdateParentNodeState(true);
  191. tv.EndUpdate();
  192. }
  193. }
  194. /// <summary>
  195. /// Recursiveley update child node's state based on the state of this node.
  196. /// </summary>
  197. private void UpdateChildNodeState()
  198. {
  199. ThreeStateTreeNode child;
  200. foreach (TreeNode node in this.Nodes)
  201. {
  202. // It is possible node is not a ThreeStateTreeNode, so check first.
  203. if (node is ThreeStateTreeNode)
  204. {
  205. child = node as ThreeStateTreeNode;
  206. child.State = this.State;
  207. child.Checked = (this.State != CheckBoxState.Unchecked);
  208. child.UpdateChildNodeState();
  209. }
  210. }
  211. }
  212. /// <summary>
  213. /// Recursiveley update parent node state based on the current state of this node.
  214. /// </summary>
  215. private void UpdateParentNodeState(bool isStartingPoint)
  216. {
  217. // If isStartingPoint is false, then know this is not the initial call
  218. // to the recursive method as we want to force on the first time
  219. // this is called to set the instance's parent node state based on
  220. // the state of all the siblings of this node, including the state
  221. // of this node. So, if not the startpoint (!isStartingPoint) and
  222. // the state of this instance is indeterminate (Enumerations.CheckBoxState.Indeterminate)
  223. // then know to set all subsequent parents to the indeterminate
  224. // state. However, if not in an indeterminate state, then still need
  225. // to evaluate the state of all the siblings of this node, including the state
  226. // of this node before setting the state of the parent of this instance.
  227. ThreeStateTreeNode parent = this.Parent as ThreeStateTreeNode;
  228. if (parent != null)
  229. {
  230. CheckBoxState state = CheckBoxState.Unchecked;
  231. // Determine the new state
  232. if (!isStartingPoint && (this.State == CheckBoxState.Indeterminate))
  233. state = CheckBoxState.Indeterminate;
  234. else
  235. state = this.SiblingsState;
  236. // Update parent state if not the same.
  237. if (parent.State != state)
  238. {
  239. parent.State = state;
  240. parent.Checked = (state != CheckBoxState.Unchecked);
  241. parent.UpdateParentNodeState(false);
  242. }
  243. }
  244. }
  245. #endregion
  246. }
  247. }