TreeNode.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. // Permission is hereby granted, free of charge, to any person obtaining
  2. // a copy of this software and associated documentation files (the
  3. // "Software"), to deal in the Software without restriction, including
  4. // without limitation the rights to use, copy, modify, merge, publish,
  5. // distribute, sublicense, and/or sell copies of the Software, and to
  6. // permit persons to whom the Software is furnished to do so, subject to
  7. // the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be
  10. // included in all copies or substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  13. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  14. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  15. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  16. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  17. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  18. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. //
  20. // Copyright (c) 2004-2005 Novell, Inc.
  21. //
  22. // Authors:
  23. // Jackson Harper ([email protected])
  24. // Kazuki Oikawa ([email protected])
  25. using System;
  26. using System.ComponentModel;
  27. using System.Drawing;
  28. using System.Runtime.Serialization;
  29. using System.Text;
  30. namespace System.Windows.Forms {
  31. [TypeConverter(typeof(TreeNodeConverter))]
  32. [Serializable]
  33. public class TreeNode : MarshalByRefObject, ICloneable, ISerializable {
  34. #region Fields
  35. private TreeView tree_view;
  36. internal TreeNode parent;
  37. private int index;
  38. private string text;
  39. private int image_index = -1;
  40. private int selected_image_index = -1;
  41. internal TreeNodeCollection nodes;
  42. private bool is_expanded = false;
  43. private Rectangle bounds = Rectangle.Empty;
  44. private bool check;
  45. private bool is_editing;
  46. internal OwnerDrawPropertyBag prop_bag;
  47. private object tag;
  48. internal IntPtr handle;
  49. #endregion // Fields
  50. #region Internal Constructors
  51. internal TreeNode (TreeView tree_view) : this ()
  52. {
  53. this.tree_view = tree_view;
  54. is_expanded = true;
  55. }
  56. #endregion // Internal Constructors
  57. #region Public Constructors
  58. public TreeNode ()
  59. {
  60. nodes = new TreeNodeCollection (this);
  61. }
  62. public TreeNode (string text) : this ()
  63. {
  64. Text = text;
  65. }
  66. public TreeNode (string text, TreeNode [] children) : this (text)
  67. {
  68. Nodes.AddRange (children);
  69. }
  70. public TreeNode (string text, int image_index, int selected_image_index) : this (text)
  71. {
  72. this.image_index = image_index;
  73. this.selected_image_index = selected_image_index;
  74. }
  75. public TreeNode (string text, int image_index, int selected_image_index,
  76. TreeNode [] children) : this (text, image_index, selected_image_index)
  77. {
  78. Nodes.AddRange (children);
  79. }
  80. #endregion // Public Constructors
  81. #region ICloneable Members
  82. public object Clone()
  83. {
  84. TreeNode tn = new TreeNode (text, image_index, selected_image_index);
  85. if (nodes != null) {
  86. foreach (TreeNode child in nodes)
  87. tn.Nodes.Add ((TreeNode)child.Clone ());
  88. }
  89. tn.Tag = tag;
  90. tn.Checked = Checked;
  91. if (prop_bag != null)
  92. tn.prop_bag = OwnerDrawPropertyBag.Copy (prop_bag);
  93. return tn;
  94. }
  95. #endregion // ICloneable Members
  96. #region ISerializable Members
  97. [MonoTODO]
  98. void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
  99. throw new NotImplementedException();
  100. }
  101. #endregion // ISerializable Members
  102. #region Public Instance Properties
  103. public Color BackColor {
  104. get {
  105. if (prop_bag != null)
  106. return prop_bag.BackColor;
  107. if (TreeView != null)
  108. return TreeView.BackColor;
  109. return Color.Empty;
  110. }
  111. set {
  112. if (prop_bag == null)
  113. prop_bag = new OwnerDrawPropertyBag ();
  114. prop_bag.BackColor = value;
  115. }
  116. }
  117. public Rectangle Bounds {
  118. get { return bounds; }
  119. }
  120. public bool Checked {
  121. get { return check; }
  122. set {
  123. if (check == value)
  124. return;
  125. check = value;
  126. if (TreeView != null)
  127. TreeView.UpdateNode (this);
  128. }
  129. }
  130. public TreeNode FirstNode {
  131. get {
  132. if (nodes.Count > 0)
  133. return nodes [0];
  134. return null;
  135. }
  136. }
  137. public Color ForeColor {
  138. get {
  139. if (prop_bag != null)
  140. return prop_bag.ForeColor;
  141. if (TreeView != null)
  142. return TreeView.ForeColor;
  143. return Color.Empty;
  144. }
  145. set {
  146. if (prop_bag == null)
  147. prop_bag = new OwnerDrawPropertyBag ();
  148. prop_bag.ForeColor = value;
  149. }
  150. }
  151. public string FullPath {
  152. get {
  153. if (tree_view == null)
  154. throw new Exception ("No TreeView associated");
  155. StringBuilder builder = new StringBuilder ();
  156. BuildFullPath (builder);
  157. return builder.ToString ();
  158. }
  159. }
  160. [Localizable(true)]
  161. public int ImageIndex {
  162. get { return image_index; }
  163. set { image_index = value; }
  164. }
  165. public bool IsEditing {
  166. get { return is_editing; }
  167. }
  168. public bool IsExpanded {
  169. get { return is_expanded; }
  170. }
  171. public bool IsSelected {
  172. get {
  173. if (TreeView == null)
  174. return false;
  175. return TreeView.SelectedNode == this;
  176. }
  177. }
  178. public bool IsVisible {
  179. get {
  180. if (TreeView == null)
  181. return false;
  182. if (bounds.Y < 0 && bounds.Y > TreeView.ClientRectangle.Height)
  183. return false;
  184. TreeNode parent = Parent;
  185. while (parent != null) {
  186. if (!parent.IsExpanded)
  187. return false;
  188. parent = parent.Parent;
  189. }
  190. return true;
  191. }
  192. }
  193. public TreeNode LastNode {
  194. get {
  195. return (nodes == null || nodes.Count == 0) ? null : nodes [nodes.Count - 1];
  196. }
  197. }
  198. public TreeNode NextNode {
  199. get {
  200. if (parent == null)
  201. return null;
  202. if (parent.Nodes.Count > index + 1)
  203. return parent.Nodes [index + 1];
  204. return null;
  205. }
  206. }
  207. public TreeNode NextVisibleNode {
  208. get {
  209. OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (this);
  210. if (!o.MoveNext ())
  211. return null;
  212. TreeNode c = (TreeNode) o.Current;
  213. if (!c.IsInClippingRect)
  214. return null;
  215. return c;
  216. }
  217. }
  218. [Localizable(true)]
  219. public Font NodeFont {
  220. get {
  221. if (prop_bag != null)
  222. return prop_bag.Font;
  223. if (TreeView != null)
  224. return TreeView.Font;
  225. return null;
  226. }
  227. set {
  228. if (prop_bag == null)
  229. prop_bag = new OwnerDrawPropertyBag ();
  230. prop_bag.Font = value;
  231. InvalidateWidth ();
  232. }
  233. }
  234. [ListBindable(false)]
  235. public TreeNodeCollection Nodes {
  236. get {
  237. if (nodes == null)
  238. nodes = new TreeNodeCollection (this);
  239. return nodes;
  240. }
  241. }
  242. public TreeNode Parent {
  243. get {
  244. if (tree_view != null && tree_view.root_node == parent)
  245. return null;
  246. return parent;
  247. }
  248. }
  249. public TreeNode PrevNode {
  250. get {
  251. if (parent == null)
  252. return null;
  253. if (index == 0 || index > parent.Nodes.Count)
  254. return null;
  255. return parent.Nodes [index - 1];
  256. }
  257. }
  258. public TreeNode PrevVisibleNode {
  259. get {
  260. OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (this);
  261. if (!o.MovePrevious ())
  262. return null;
  263. TreeNode c = (TreeNode) o.Current;
  264. if (!c.IsInClippingRect)
  265. return null;
  266. return c;
  267. }
  268. }
  269. [Localizable(true)]
  270. public int SelectedImageIndex {
  271. get { return selected_image_index; }
  272. set { selected_image_index = value; }
  273. }
  274. [Bindable(true)]
  275. [Localizable(false)]
  276. [TypeConverter(typeof(System.ComponentModel.StringConverter))]
  277. [DefaultValue(null)]
  278. public object Tag {
  279. get { return tag; }
  280. set { tag = value; }
  281. }
  282. [Localizable(true)]
  283. public string Text {
  284. get {
  285. if (text == null)
  286. return String.Empty;
  287. return text;
  288. }
  289. set {
  290. if (text == value)
  291. return;
  292. text = value;
  293. bounds.Width = 0;
  294. }
  295. }
  296. public TreeView TreeView {
  297. get {
  298. if (tree_view != null)
  299. return tree_view;
  300. TreeNode walk = parent;
  301. while (walk != null) {
  302. if (walk.TreeView != null)
  303. tree_view = walk.TreeView;
  304. walk = walk.parent;
  305. }
  306. return tree_view;
  307. }
  308. }
  309. public IntPtr Handle {
  310. get {
  311. // MS throws a NullReferenceException if the TreeView isn't set...
  312. if (handle == IntPtr.Zero)
  313. handle = TreeView.CreateNodeHandle ();
  314. return handle;
  315. }
  316. }
  317. #endregion // Public Instance Properties
  318. public static TreeNode FromHandle (TreeView tree, IntPtr handle)
  319. {
  320. if (handle == IntPtr.Zero)
  321. return null;
  322. // No arg checking on MS it just throws a NullRef if treeview is null
  323. return tree.NodeFromHandle (handle);
  324. }
  325. #region Public Instance Methods
  326. public void BeginEdit () {
  327. is_editing = true;
  328. }
  329. public void Collapse () {
  330. Collapse(false);
  331. }
  332. public void EndEdit (bool cancel) {
  333. is_editing = false;
  334. if (!cancel && TreeView != null)
  335. Text = TreeView.LabelEditText;
  336. }
  337. public void Expand () {
  338. Expand(false);
  339. }
  340. public void ExpandAll () {
  341. ExpandRecursive (this);
  342. if(TreeView != null)
  343. TreeView.Refresh();
  344. }
  345. public void EnsureVisible ()
  346. {
  347. if (TreeView == null)
  348. return;
  349. TreeView.BeginUpdate ();
  350. if (this.Parent != null)
  351. ExpandParentRecursive (this.Parent);
  352. if (bounds.Y < 0) {
  353. TreeView.SetTop (this);
  354. } else if (bounds.Y > TreeView.ClientRectangle.Height) {
  355. TreeView.SetBottom (this);
  356. }
  357. TreeView.EndUpdate ();
  358. }
  359. public int GetNodeCount (bool include_subtrees) {
  360. if (!include_subtrees)
  361. return Nodes.Count;
  362. int count = 0;
  363. GetNodeCountRecursive (this, ref count);
  364. return count;
  365. }
  366. public void Remove () {
  367. if (parent == null)
  368. return;
  369. parent.Nodes.RemoveAt (Index);
  370. }
  371. public void Toggle () {
  372. if (is_expanded)
  373. Collapse ();
  374. else
  375. Expand ();
  376. }
  377. public override String ToString () {
  378. return String.Concat ("TreeNode: ", Text);
  379. }
  380. #endregion // Public Instance Methods
  381. #region Internal & Private Methods and Properties
  382. internal bool IsRoot {
  383. get {
  384. if (tree_view == null)
  385. return false;
  386. if (tree_view.root_node == this)
  387. return true;
  388. return false;
  389. }
  390. }
  391. bool BuildFullPath (StringBuilder path)
  392. {
  393. if (parent == null)
  394. return false;
  395. if (parent.BuildFullPath (path))
  396. path.Append (tree_view.PathSeparator);
  397. path.Append (text);
  398. return true;
  399. }
  400. public int Index {
  401. get { return index; }
  402. }
  403. private void Expand (bool byInternal)
  404. {
  405. if (is_expanded)
  406. return;
  407. bool cancel = false;
  408. if (TreeView != null) {
  409. TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (this, false, TreeViewAction.Expand);
  410. TreeView.OnBeforeExpand (e);
  411. cancel = e.Cancel;
  412. }
  413. if (!cancel) {
  414. is_expanded = true;
  415. if (TreeView != null)
  416. TreeView.OnAfterExpand (new TreeViewEventArgs (this));
  417. if (IsVisible && TreeView != null)
  418. TreeView.UpdateBelow (this);
  419. }
  420. }
  421. private void Collapse (bool byInternal)
  422. {
  423. if (!is_expanded)
  424. return;
  425. if (tree_view != null && tree_view.root_node == this)
  426. return;
  427. bool cancel = false;
  428. if (TreeView != null) {
  429. TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (this, false, TreeViewAction.Collapse);
  430. TreeView.OnBeforeCollapse (e);
  431. cancel = e.Cancel;
  432. }
  433. if (!cancel) {
  434. is_expanded = false;
  435. if (TreeView != null)
  436. TreeView.OnAfterCollapse (new TreeViewEventArgs (this));
  437. if (IsVisible && TreeView != null)
  438. TreeView.UpdateBelow (this);
  439. if(!byInternal && TreeView != null && HasFocusInChildren ())
  440. TreeView.SelectedNode = this;
  441. }
  442. }
  443. private bool HasFocusInChildren()
  444. {
  445. if(TreeView == null) return false;
  446. foreach(TreeNode node in nodes) {
  447. if(node == TreeView.SelectedNode) return true;
  448. if(node.HasFocusInChildren())
  449. return true;
  450. }
  451. return false;
  452. }
  453. private void ExpandRecursive (TreeNode node)
  454. {
  455. node.Expand (true);
  456. foreach (TreeNode child in node.Nodes) {
  457. ExpandRecursive (child);
  458. }
  459. }
  460. private void ExpandParentRecursive (TreeNode node)
  461. {
  462. node.Expand (true);
  463. if (node.Parent != null)
  464. ExpandParentRecursive (node.Parent);
  465. }
  466. internal void CollapseAll ()
  467. {
  468. CollapseRecursive (this);
  469. }
  470. internal void CollapseAllUncheck ()
  471. {
  472. CollapseUncheckRecursive (this);
  473. }
  474. private void CollapseRecursive (TreeNode node)
  475. {
  476. node.Collapse ();
  477. foreach (TreeNode child in node.Nodes) {
  478. CollapseRecursive (child);
  479. }
  480. }
  481. private void CollapseUncheckRecursive (TreeNode node)
  482. {
  483. node.Collapse ();
  484. node.Checked = false;
  485. foreach (TreeNode child in node.Nodes) {
  486. CollapseUncheckRecursive (child);
  487. }
  488. }
  489. internal void SetNodes (TreeNodeCollection nodes)
  490. {
  491. this.nodes = nodes;
  492. }
  493. private void GetNodeCountRecursive (TreeNode node, ref int count)
  494. {
  495. count += node.Nodes.Count;
  496. foreach (TreeNode child in node.Nodes) {
  497. GetNodeCountRecursive (child, ref count);
  498. }
  499. }
  500. internal bool NeedsWidth {
  501. get { return bounds.Width == 0; }
  502. }
  503. internal void InvalidateWidth ()
  504. {
  505. bounds.Width = 0;
  506. }
  507. internal void SetWidth (int width)
  508. {
  509. bounds.Width = width;
  510. }
  511. internal void SetHeight (int height)
  512. {
  513. bounds.Height = height;
  514. }
  515. internal void SetPosition (int x, int y)
  516. {
  517. bounds.X = x;
  518. bounds.Y = y;
  519. }
  520. internal void SetAddedData (TreeView tree_view, TreeNode parent, int index)
  521. {
  522. this.tree_view = tree_view;
  523. this.parent = parent;
  524. this.index = index;
  525. }
  526. internal void SetIndex (int index)
  527. {
  528. this.index = index;
  529. }
  530. private bool IsInClippingRect
  531. {
  532. get {
  533. if (TreeView == null)
  534. return false;
  535. if (bounds.Y < 0 && bounds.Y > tree_view.ClientRectangle.Height)
  536. return false;
  537. return true;
  538. }
  539. }
  540. #endregion // Internal & Private Methods and Properties
  541. }
  542. }