TreeView.cs 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276
  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.Collections;
  27. using System.ComponentModel;
  28. using System.ComponentModel.Design;
  29. using System.Drawing;
  30. using System.Drawing.Drawing2D;
  31. using System.Runtime.InteropServices;
  32. namespace System.Windows.Forms {
  33. [DefaultProperty("Nodes")]
  34. [DefaultEvent("AfterSelect")]
  35. [Designer("System.Windows.Forms.Design.TreeViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
  36. public class TreeView : Control {
  37. #region Fields
  38. private string path_separator = "\\";
  39. private int item_height = -1;
  40. private bool sorted;
  41. private TreeNode top_node;
  42. internal TreeNode root_node;
  43. private TreeNodeCollection nodes;
  44. private int total_node_count;
  45. private TreeNode selected_node = null;
  46. private TreeNode focused_node = null;
  47. private bool select_mmove = false;
  48. private ImageList image_list;
  49. private int image_index = -1;
  50. private int selected_image_index = -1;
  51. private bool full_row_select;
  52. private bool hot_tracking;
  53. private int indent = 19;
  54. private TextBox edit_text_box;
  55. private TreeNode edit_node;
  56. private bool checkboxes;
  57. private bool label_edit;
  58. private bool scrollable;
  59. private bool show_lines = true;
  60. private bool show_root_lines = true;
  61. private bool show_plus_minus = true;
  62. private bool hide_selection = true;
  63. private bool add_hscroll;
  64. private bool add_vscroll;
  65. private int max_node_width;
  66. private VScrollBar vbar;
  67. private bool vbar_added;
  68. private int skipped_nodes;
  69. private HScrollBar hbar;
  70. private bool hbar_added;
  71. private int hbar_offset;
  72. private int update_stack;
  73. private TreeViewEventHandler on_after_check;
  74. private TreeViewEventHandler on_after_collapse;
  75. private TreeViewEventHandler on_after_expand;
  76. private NodeLabelEditEventHandler on_after_label_edit;
  77. private TreeViewEventHandler on_after_select;
  78. private TreeViewCancelEventHandler on_before_check;
  79. private TreeViewCancelEventHandler on_before_collapse;
  80. private TreeViewCancelEventHandler on_before_expand;
  81. private NodeLabelEditEventHandler on_before_label_edit;
  82. private TreeViewCancelEventHandler on_before_select;
  83. private Pen dash;
  84. private int open_node_count = -1;
  85. private long handle_count = 1;
  86. #endregion // Fields
  87. #region Public Constructors
  88. public TreeView ()
  89. {
  90. base.background_color = ThemeEngine.Current.ColorWindow;
  91. base.foreground_color = ThemeEngine.Current.ColorWindowText;
  92. root_node = new TreeNode (this);
  93. root_node.Text = "ROOT NODE";
  94. nodes = new TreeNodeCollection (root_node);
  95. root_node.SetNodes (nodes);
  96. MouseDown += new MouseEventHandler (MouseDownHandler);
  97. MouseUp += new MouseEventHandler(MouseUpHandler);
  98. MouseMove += new MouseEventHandler(MouseMoveHandler);
  99. SizeChanged += new EventHandler (SizeChangedHandler);
  100. SetStyle (ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true);
  101. SetStyle (ControlStyles.UserPaint | ControlStyles.Selectable, true);
  102. dash = new Pen (SystemColors.ControlLight, 1);
  103. dash.DashStyle = DashStyle.Dash;
  104. }
  105. #endregion // Public Constructors
  106. #region Public Instance Properties
  107. public override Color BackColor {
  108. get { return base.BackColor;}
  109. set { base.BackColor = value; }
  110. }
  111. [Browsable(false)]
  112. [EditorBrowsable(EditorBrowsableState.Never)]
  113. public override Image BackgroundImage {
  114. get { return base.BackgroundImage; }
  115. set { base.BackgroundImage = value; }
  116. }
  117. [DefaultValue(BorderStyle.Fixed3D)]
  118. [DispId(-504)]
  119. public BorderStyle BorderStyle {
  120. get { return InternalBorderStyle; }
  121. set { InternalBorderStyle = value; }
  122. }
  123. [DefaultValue(false)]
  124. public bool CheckBoxes {
  125. get { return checkboxes; }
  126. set {
  127. if (value == checkboxes)
  128. return;
  129. checkboxes = value;
  130. // Match a "bug" in the MS implementation where disabling checkboxes
  131. // collapses the entire tree, but enabling them does not affect the
  132. // state of the tree.
  133. if (!checkboxes)
  134. root_node.CollapseAllUncheck ();
  135. Refresh ();
  136. }
  137. }
  138. public override Color ForeColor {
  139. get { return base.ForeColor; }
  140. set { base.ForeColor = value; }
  141. }
  142. [DefaultValue(false)]
  143. public bool FullRowSelect {
  144. get { return full_row_select; }
  145. set {
  146. if (value == full_row_select)
  147. return;
  148. full_row_select = value;
  149. Refresh ();
  150. }
  151. }
  152. [DefaultValue(true)]
  153. public bool HideSelection {
  154. get { return hide_selection; }
  155. set {
  156. if (hide_selection == value)
  157. return;
  158. hide_selection = value;
  159. this.Refresh ();
  160. }
  161. }
  162. [DefaultValue(false)]
  163. public bool HotTracking {
  164. get { return hot_tracking; }
  165. set { hot_tracking = value; }
  166. }
  167. [DefaultValue(0)]
  168. [Editor("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
  169. [Localizable(true)]
  170. [TypeConverter(typeof(TreeViewImageIndexConverter))]
  171. public int ImageIndex {
  172. get { return image_index; }
  173. set {
  174. if (value < -1) {
  175. throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
  176. "'value' must be greater than or equal to 0.");
  177. }
  178. if (image_index == value)
  179. return;
  180. image_index = value;
  181. Refresh ();
  182. }
  183. }
  184. [DefaultValue(null)]
  185. public ImageList ImageList {
  186. get { return image_list; }
  187. set {
  188. image_list = value;
  189. Refresh ();
  190. }
  191. }
  192. [Localizable(true)]
  193. public int Indent {
  194. get { return indent; }
  195. set {
  196. if (indent == value)
  197. return;
  198. if (value > 32000) {
  199. throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
  200. "'Indent' must be less than or equal to 32000");
  201. }
  202. if (value < 0) {
  203. throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
  204. "'Indent' must be greater than or equal to 0.");
  205. }
  206. indent = value;
  207. Refresh ();
  208. }
  209. }
  210. [Localizable(true)]
  211. public int ItemHeight {
  212. get {
  213. if (item_height == -1)
  214. return FontHeight + 3;
  215. return item_height;
  216. }
  217. set {
  218. if (value == item_height)
  219. return;
  220. item_height = value;
  221. Refresh ();
  222. }
  223. }
  224. [DefaultValue(false)]
  225. public bool LabelEdit {
  226. get { return label_edit; }
  227. set { label_edit = value; }
  228. }
  229. [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  230. [MergableProperty(false)]
  231. [Localizable(true)]
  232. public TreeNodeCollection Nodes {
  233. get { return nodes; }
  234. }
  235. [DefaultValue("\\")]
  236. public string PathSeparator {
  237. get { return path_separator; }
  238. set { path_separator = value; }
  239. }
  240. [DefaultValue(true)]
  241. public bool Scrollable {
  242. get { return scrollable; }
  243. set {
  244. if (scrollable == value)
  245. return;
  246. scrollable = value;
  247. }
  248. }
  249. [Editor("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
  250. [TypeConverter(typeof(TreeViewImageIndexConverter))]
  251. [Localizable(true)]
  252. [DefaultValue(0)]
  253. public int SelectedImageIndex {
  254. get { return selected_image_index; }
  255. set {
  256. if (value < -1) {
  257. throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
  258. "'value' must be greater than or equal to 0.");
  259. }
  260. }
  261. }
  262. [Browsable(false)]
  263. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  264. public TreeNode SelectedNode {
  265. get { return selected_node; }
  266. set {
  267. if (selected_node == value)
  268. return;
  269. TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (value, false, TreeViewAction.Unknown);
  270. OnBeforeSelect (e);
  271. if (e.Cancel)
  272. return;
  273. Rectangle invalid = Rectangle.Empty;
  274. if (selected_node != null)
  275. invalid = selected_node.Bounds;
  276. if (focused_node != null)
  277. invalid = Rectangle.Union (focused_node.Bounds, invalid);
  278. invalid = Rectangle.Union (invalid, value.Bounds);
  279. selected_node = value;
  280. focused_node = value;
  281. Invalidate (invalid);
  282. OnAfterSelect (new TreeViewEventArgs (value, TreeViewAction.Unknown));
  283. }
  284. }
  285. [DefaultValue(true)]
  286. public bool ShowLines {
  287. get { return show_lines; }
  288. set {
  289. if (show_lines == value)
  290. return;
  291. show_lines = value;
  292. Refresh ();
  293. }
  294. }
  295. [DefaultValue(true)]
  296. public bool ShowPlusMinus {
  297. get { return show_plus_minus; }
  298. set {
  299. if (show_plus_minus == value)
  300. return;
  301. show_plus_minus = value;
  302. Refresh ();
  303. }
  304. }
  305. [DefaultValue(true)]
  306. public bool ShowRootLines {
  307. get { return show_root_lines; }
  308. set {
  309. if (show_root_lines == value)
  310. return;
  311. show_root_lines = value;
  312. Refresh ();
  313. }
  314. }
  315. [DefaultValue(false)]
  316. public bool Sorted {
  317. get { return sorted; }
  318. set {
  319. if (sorted != value)
  320. sorted = value;
  321. if (sorted) {
  322. Nodes.Sort ();
  323. Refresh ();
  324. }
  325. }
  326. }
  327. [Browsable(false)]
  328. [EditorBrowsable(EditorBrowsableState.Never)]
  329. [Bindable(false)]
  330. public override string Text {
  331. get { return base.Text; }
  332. set { base.Text = value; }
  333. }
  334. [Browsable(false)]
  335. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  336. public TreeNode TopNode {
  337. get { return top_node; }
  338. }
  339. [Browsable(false)]
  340. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  341. public int VisibleCount {
  342. get {
  343. return ClientRectangle.Height / ItemHeight;
  344. }
  345. }
  346. #endregion // Public Instance Properties
  347. #region Protected Instance Properties
  348. protected override CreateParams CreateParams {
  349. get {
  350. CreateParams cp = base.CreateParams;
  351. return cp;
  352. }
  353. }
  354. protected override Size DefaultSize {
  355. get { return new Size (121, 97); }
  356. }
  357. #endregion // Protected Instance Properties
  358. #region Public Instance Methods
  359. public void BeginUpdate () {
  360. if (!IsHandleCreated)
  361. return;
  362. update_stack++;
  363. }
  364. public void CollapseAll () {
  365. root_node.CollapseAll ();
  366. }
  367. public void EndUpdate () {
  368. if (!IsHandleCreated)
  369. return;
  370. if (update_stack > 1) {
  371. update_stack--;
  372. } else {
  373. update_stack = 0;
  374. Refresh ();
  375. }
  376. }
  377. public void ExpandAll () {
  378. root_node.ExpandAll ();
  379. }
  380. public TreeNode GetNodeAt (Point pt) {
  381. return GetNodeAt (pt.X, pt.Y);
  382. }
  383. public TreeNode GetNodeAt (int x, int y) {
  384. TreeNode node = GetNodeAt (y);
  385. if (node == null || !IsTextArea (node, x))
  386. return null;
  387. return node;
  388. }
  389. public int GetNodeCount (bool include_subtrees) {
  390. return root_node.GetNodeCount (include_subtrees);
  391. }
  392. public override string ToString () {
  393. int count = Nodes.Count;
  394. if (count < 0)
  395. return String.Concat (base.ToString (), "Node Count: 0");
  396. return String.Concat (base.ToString (), "Node Count: ", count, " Nodes[0]: ", Nodes [0]);
  397. }
  398. #endregion // Public Instance Methods
  399. #region Protected Instance Methods
  400. protected override void CreateHandle () {
  401. base.CreateHandle ();
  402. }
  403. protected override void Dispose (bool disposing) {
  404. if (disposing) {
  405. if (image_list != null)
  406. image_list.Dispose ();
  407. }
  408. base.Dispose (disposing);
  409. }
  410. protected OwnerDrawPropertyBag GetItemRenderStyles (TreeNode node, int state) {
  411. return node.prop_bag;
  412. }
  413. protected override bool IsInputKey (Keys key_data) {
  414. if (label_edit && (key_data & Keys.Alt) == 0) {
  415. switch (key_data & Keys.KeyCode) {
  416. case Keys.Enter:
  417. case Keys.Escape:
  418. case Keys.Prior:
  419. case Keys.Next:
  420. case Keys.End:
  421. case Keys.Home:
  422. case Keys.Left:
  423. case Keys.Up:
  424. case Keys.Right:
  425. case Keys.Down:
  426. return true;
  427. }
  428. }
  429. return base.IsInputKey (key_data);
  430. }
  431. protected virtual void OnAfterCheck (TreeViewEventArgs e) {
  432. if (on_after_check != null)
  433. on_after_check (this, e);
  434. }
  435. protected internal virtual void OnAfterCollapse (TreeViewEventArgs e) {
  436. if (on_after_collapse != null)
  437. on_after_collapse (this, e);
  438. }
  439. protected internal virtual void OnAfterExpand (TreeViewEventArgs e) {
  440. if (on_after_expand != null)
  441. on_after_expand (this, e);
  442. }
  443. protected virtual void OnAfterLabelEdit (NodeLabelEditEventArgs e) {
  444. if (on_after_label_edit != null)
  445. on_after_label_edit (this, e);
  446. }
  447. protected virtual void OnAfterSelect (TreeViewEventArgs e) {
  448. if (on_after_select != null)
  449. on_after_select (this, e);
  450. }
  451. protected virtual void OnBeforeCheck (TreeViewCancelEventArgs e) {
  452. if (on_before_check != null)
  453. on_before_check (this, e);
  454. }
  455. protected internal virtual void OnBeforeCollapse (TreeViewCancelEventArgs e) {
  456. if (on_before_collapse != null)
  457. on_before_collapse (this, e);
  458. }
  459. protected internal virtual void OnBeforeExpand (TreeViewCancelEventArgs e) {
  460. if (on_before_expand != null)
  461. on_before_expand (this, e);
  462. }
  463. protected virtual void OnBeforeLabelEdit (NodeLabelEditEventArgs e) {
  464. if (on_before_label_edit != null)
  465. on_before_label_edit (this, e);
  466. }
  467. protected virtual void OnBeforeSelect (TreeViewCancelEventArgs e) {
  468. if (on_before_select != null)
  469. on_before_select (this, e);
  470. }
  471. protected override void OnHandleCreated (EventArgs e) {
  472. base.OnHandleCreated (e);
  473. }
  474. protected override void OnHandleDestroyed (EventArgs e) {
  475. base.OnHandleDestroyed (e);
  476. }
  477. protected override void WndProc(ref Message m) {
  478. switch ((Msg) m.Msg) {
  479. case Msg.WM_PAINT: {
  480. PaintEventArgs paint_event;
  481. paint_event = XplatUI.PaintEventStart (Handle);
  482. DoPaint (paint_event);
  483. XplatUI.PaintEventEnd (Handle);
  484. return;
  485. }
  486. case Msg.WM_LBUTTONDBLCLK:
  487. int val = m.LParam.ToInt32();
  488. DoubleClickHandler (null, new MouseEventArgs (MouseButtons.Left, 2, val & 0xffff, (val>>16) & 0xffff, 0));
  489. break;
  490. }
  491. base.WndProc (ref m);
  492. }
  493. #endregion // Protected Instance Methods
  494. #region Internal & Private Methods and Properties
  495. internal string LabelEditText {
  496. get {
  497. if (edit_text_box == null)
  498. return String.Empty;
  499. return edit_text_box.Text;
  500. }
  501. }
  502. internal int TotalNodeCount {
  503. get { return total_node_count; }
  504. set { total_node_count = value; }
  505. }
  506. internal IntPtr CreateNodeHandle ()
  507. {
  508. return (IntPtr) handle_count++;
  509. }
  510. internal TreeNode NodeFromHandle (IntPtr handle)
  511. {
  512. // This method is called rarely, so instead of maintaining a table
  513. // we just walk the tree nodes to find the matching handle
  514. return NodeFromHandleRecursive (root_node, handle);
  515. }
  516. private TreeNode NodeFromHandleRecursive (TreeNode node, IntPtr handle)
  517. {
  518. if (node.handle == handle)
  519. return node;
  520. foreach (TreeNode child in node.Nodes) {
  521. TreeNode match = NodeFromHandleRecursive (child, handle);
  522. if (match != null)
  523. return match;
  524. }
  525. return null;
  526. }
  527. // TODO: we shouldn't have to compute this on the fly
  528. private Rectangle ViewportRectangle {
  529. get {
  530. Rectangle res = ClientRectangle;
  531. if (vbar != null && vbar.Visible)
  532. res.Width -= vbar.Width;
  533. if (hbar != null && hbar.Visible)
  534. res.Height -= hbar.Height;
  535. return res;
  536. }
  537. }
  538. [MonoTODO ("Need to know if we are editing, not if editing is enabled")]
  539. private TreeNode GetNodeAt (int y) {
  540. if (top_node == null)
  541. top_node = nodes [0];
  542. OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (TopNode);
  543. int move = y / ItemHeight;
  544. for (int i = -1; i < move; i++) {
  545. if (!o.MoveNext ())
  546. return null;
  547. }
  548. return o.CurrentNode;
  549. }
  550. private bool IsTextArea (TreeNode node, int x) {
  551. return node != null && node.Bounds.Left <= x && node.Bounds.Right >= x;
  552. }
  553. private bool IsPlusMinusArea (TreeNode node, int x)
  554. {
  555. int l = node.Bounds.Left + 5;
  556. if (show_root_lines || node.Parent != null)
  557. l -= indent;
  558. if (ImageList != null)
  559. l -= ImageList.ImageSize.Width + 3;
  560. if (checkboxes)
  561. l -= 19;
  562. return (x > l && x < l + 8);
  563. }
  564. private bool IsCheckboxArea (TreeNode node, int x)
  565. {
  566. int l = node.Bounds.Left + 5;
  567. if (show_root_lines || node.Parent != null)
  568. l -= indent;
  569. if (ImageList != null)
  570. l -= ImageList.ImageSize.Width + 3;
  571. return (x > l && x < l + 10);
  572. }
  573. internal void SetTop (TreeNode node)
  574. {
  575. OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (root_node);
  576. int offset = 0;
  577. while (walk.CurrentNode != node && walk.MoveNext ())
  578. offset++;
  579. vbar.Value = offset;
  580. }
  581. internal void SetBottom (TreeNode node)
  582. {
  583. int visible = ClientRectangle.Height / ItemHeight;
  584. OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (node);
  585. TreeNode top = null;
  586. visible--;
  587. while (visible-- > 0 && walk.MovePrevious ())
  588. top = walk.CurrentNode;
  589. if (top != null)
  590. SetTop (top);
  591. }
  592. internal void UpdateBelow (TreeNode node)
  593. {
  594. // We need to update the current node so the plus/minus block gets update too
  595. Rectangle invalid = new Rectangle (0, node.Bounds.Top, Width, Height - node.Bounds.Top);
  596. Invalidate (invalid);
  597. }
  598. internal void UpdateNode (TreeNode node)
  599. {
  600. Rectangle invalid = new Rectangle (0, node.Bounds.Top, Width, node.Bounds.Height);
  601. Invalidate (invalid);
  602. }
  603. private void DoPaint (PaintEventArgs pe)
  604. {
  605. if (Width <= 0 || Height <= 0 || Visible == false)
  606. return;
  607. Draw (pe.ClipRectangle, pe.Graphics);
  608. }
  609. private void Draw (Rectangle clip, Graphics dc)
  610. {
  611. if (top_node == null && Nodes.Count > 0)
  612. top_node = nodes [0];
  613. // Decide if we need a scrollbar
  614. int old_open_node_count = open_node_count;
  615. //Rectangle fill = ClientRectangle;
  616. add_vscroll = false;
  617. add_hscroll = false;
  618. dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), clip);
  619. int depth = 0;
  620. int item_height = ItemHeight;
  621. Font font = Font;
  622. int height = ClientRectangle.Height;
  623. open_node_count = 0;
  624. foreach (TreeNode node in nodes) {
  625. DrawNode (node, dc, clip, ref depth, item_height, font, height);
  626. depth = 0;
  627. }
  628. add_vscroll = (open_node_count * ItemHeight) > ClientRectangle.Height;
  629. if (max_node_width > ClientRectangle.Width)
  630. add_hscroll = true;
  631. if (add_vscroll)
  632. add_hscroll = max_node_width > ClientRectangle.Width - ThemeEngine.Current.VScrollBarDefaultSize.Width;
  633. if (add_hscroll)
  634. add_vscroll = (open_node_count * ItemHeight) > ClientRectangle.Height - ThemeEngine.Current.HScrollBarDefaultSize.Height;
  635. if (add_hscroll) {
  636. AddHorizontalScrollBar ();
  637. } else if (hbar != null) {
  638. hbar_offset = 0;
  639. hbar.Visible = false;
  640. }
  641. if (add_vscroll) {
  642. AddVerticalScrollBar (open_node_count, old_open_node_count != open_node_count);
  643. } else if (vbar != null) {
  644. vbar.Visible = false;
  645. skipped_nodes = 0;
  646. }
  647. if (add_hscroll && add_vscroll) {
  648. Rectangle corner = new Rectangle (hbar.Right, vbar.Bottom, vbar.Width, hbar.Height);
  649. if (clip.IntersectsWith (corner))
  650. dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorButtonFace), corner);
  651. }
  652. }
  653. private void DrawNodePlusMinus (TreeNode node, Graphics dc, Rectangle clip, int x, int y, int middle)
  654. {
  655. if (!RectsIntersect (clip, x, middle - 4, 8, 8))
  656. return;
  657. dc.DrawRectangle (SystemPens.ControlDark, x, middle - 4, 8, 8);
  658. if (node.IsExpanded) {
  659. dc.DrawLine (SystemPens.ControlDarkDark, x + 2, middle, x + 6, middle);
  660. } else {
  661. dc.DrawLine (SystemPens.ControlDarkDark, x + 2, middle, x + 6, middle);
  662. dc.DrawLine (SystemPens.ControlDarkDark, x + 4, middle - 2, x + 4, middle + 2);
  663. }
  664. }
  665. private void DrawNodeCheckBox (TreeNode node, Graphics dc, Rectangle clip, int x, int y)
  666. {
  667. int offset = (ItemHeight - 13);
  668. // new rectangle that factors in line width
  669. if (!RectsIntersect (clip, x + 3, y + offset, 12, 12))
  670. return;
  671. dc.DrawRectangle (new Pen (Color.Black, 2), x + 0.5F + 3, y + 0.5F + offset, 11, 11);
  672. if (node.Checked) {
  673. Pen check_pen = new Pen (Color.Black, 1);
  674. dc.DrawLine (check_pen, x + 6, y + offset + 5, x + 8, y + offset + 8);
  675. dc.DrawLine (check_pen, x + 6, y + offset + 6, x + 8, y + offset + 9);
  676. dc.DrawLine (check_pen, x + 7, y + offset + 8, x + 13, y + offset + 3);
  677. dc.DrawLine (check_pen, x + 7, y + offset + 9, x + 13, y + offset + 4);
  678. }
  679. }
  680. private void DrawNodeLines (TreeNode node, Graphics dc, bool visible, Pen dash, int x, int y,
  681. int middle, int item_height, int node_count)
  682. {
  683. int ladjust = 9; // left adjust
  684. int radjust = 0; // right adjust
  685. if (node_count > 0 && show_plus_minus)
  686. ladjust = 13;
  687. if (checkboxes)
  688. radjust = 3;
  689. dc.DrawLine (dash, x - indent + ladjust, middle, x + radjust, middle);
  690. //if (!visible)
  691. // return;
  692. int ly = 0;
  693. if (node.PrevNode != null) {
  694. int prevadjust = (node.Nodes.Count > 0 && show_plus_minus ? (node.PrevNode.Nodes.Count == 0 ? 0 : 4) :
  695. (node.PrevNode.Nodes.Count == 0 ? 0 : 4));
  696. int myadjust = (node.Nodes.Count > 0 && show_plus_minus ? 4 : 0);
  697. ly = node.PrevNode.Bounds.Bottom - (item_height / 2) + prevadjust;
  698. dc.DrawLine (dash, x - indent + 9, middle - myadjust, x - indent + 9, ly);
  699. } else if (node.Parent != null) {
  700. int myadjust = (node.Nodes.Count > 0 && show_plus_minus ? 4 : 0);
  701. ly = node.Parent.Bounds.Bottom - 1;
  702. dc.DrawLine (dash, x - indent + 9, middle - myadjust, x - indent + 9, ly);
  703. }
  704. }
  705. private void DrawNodeImage (TreeNode node, Graphics dc, Rectangle clip, int x, int y)
  706. {
  707. // Rectangle r = new Rectangle (x, y + 2, ImageList.ImageSize.Width, ImageList.ImageSize.Height);
  708. if (!RectsIntersect (clip, x, y + 2, ImageList.ImageSize.Width, ImageList.ImageSize.Height))
  709. return;
  710. if (node.ImageIndex > -1 && ImageList != null && node.ImageIndex < ImageList.Images.Count) {
  711. ImageList.Draw (dc, x, y + 2, ImageList.ImageSize.Width,
  712. ImageList.ImageSize.Height, node.ImageIndex);
  713. } else if (ImageIndex > -1 && ImageList != null && ImageIndex < ImageList.Images.Count) {
  714. ImageList.Draw (dc, x, y + 2, ImageList.ImageSize.Width,
  715. ImageList.ImageSize.Height, ImageIndex);
  716. }
  717. }
  718. private void DrawEditNode (TreeNode node)
  719. {
  720. SuspendLayout ();
  721. if (edit_text_box == null) {
  722. edit_text_box = new TextBox ();
  723. edit_text_box.BorderStyle = BorderStyle.FixedSingle;
  724. edit_text_box.KeyUp += new KeyEventHandler (EditTextBoxKeyDown);
  725. edit_text_box.Leave += new EventHandler (EditTextBoxLeave);
  726. Controls.Add (edit_text_box);
  727. }
  728. edit_text_box.Bounds = node.Bounds;
  729. edit_text_box.Width += 4;
  730. edit_text_box.Text = node.Text;
  731. edit_text_box.Visible = true;
  732. edit_text_box.Focus ();
  733. edit_text_box.SelectAll ();
  734. ResumeLayout ();
  735. }
  736. private void EditTextBoxKeyDown (object sender, KeyEventArgs e)
  737. {
  738. if (e.KeyCode == Keys.Return)
  739. EndEdit ();
  740. }
  741. private void EditTextBoxLeave (object sender, EventArgs e)
  742. {
  743. EndEdit ();
  744. }
  745. private void EndEdit ()
  746. {
  747. edit_text_box.Visible = false;
  748. edit_node.EndEdit (false);
  749. Invalidate (edit_node.Bounds);
  750. }
  751. private void UpdateNodeBounds (TreeNode node, int x, int y, int item_height)
  752. {
  753. int width = (int) (node.Text.Length * Font.Size);
  754. node.UpdateBounds (x, y, width, item_height);
  755. }
  756. private void DrawNode (TreeNode node, Graphics dc, Rectangle clip, ref int depth, int item_height,
  757. Font font, int max_height)
  758. {
  759. open_node_count++;
  760. int x = (!show_root_lines && node.Parent != null ? depth - 1 : depth) * indent - hbar_offset;
  761. int y = item_height * (open_node_count - skipped_nodes - 1);
  762. bool visible = (y >= 0 && y < max_height);
  763. int _n_count = node.nodes.Count;
  764. int middle = y + (item_height / 2);
  765. // The thing is totally out of the clipping rectangle
  766. if (clip.Top > y + ItemHeight || clip.Bottom < y)
  767. visible = false;
  768. if (show_root_lines || node.Parent != null) {
  769. x += 5;
  770. if (_n_count > 0) {
  771. if (show_plus_minus && visible) {
  772. DrawNodePlusMinus (node, dc, clip, x, y, middle);
  773. }
  774. }
  775. x += indent - 5;
  776. }
  777. int ox = x;
  778. if (visible && checkboxes) {
  779. DrawNodeCheckBox (node, dc, clip, ox, y);
  780. ox += 19;
  781. }
  782. if (show_lines)
  783. DrawNodeLines (node, dc, visible, dash, x, y, middle, item_height, _n_count);
  784. if (visible && ImageList != null) {
  785. if (visible)
  786. DrawNodeImage (node, dc, clip, ox, y);
  787. // MS leaves the space for the image if the ImageList is
  788. // non null regardless of whether or not an image is drawn
  789. ox += ImageList.ImageSize.Width + 3; // leave a little space so the text isn't against the image
  790. }
  791. UpdateNodeBounds (node, ox, y, item_height);
  792. bool bounds_in_clip = clip.IntersectsWith (node.Bounds);
  793. if (visible && bounds_in_clip && !node.IsEditing) {
  794. Rectangle r = node.Bounds;
  795. StringFormat format = new StringFormat ();
  796. format.LineAlignment = StringAlignment.Center;
  797. r.Y += 2; // we have to adjust this to get nice middle alignment
  798. Color text_color = (Focused && SelectedNode == node ? ThemeEngine.Current.ColorHilightText : node.ForeColor);
  799. if (Focused) {
  800. if (SelectedNode == node)
  801. dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorHilight), r);
  802. if (focused_node == node)
  803. ControlPaint.DrawFocusRectangle (dc, r, ForeColor, BackColor);
  804. } else {
  805. if (!HideSelection && SelectedNode == node)
  806. dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorButtonFace), r);
  807. }
  808. dc.DrawString (node.Text, font, ThemeEngine.Current.ResPool.GetSolidBrush (text_color), r, format);
  809. y += item_height + 1;
  810. } else if (visible && bounds_in_clip) {
  811. DrawEditNode (node);
  812. }
  813. if (node.Bounds.Right > max_node_width) {
  814. max_node_width = node.Bounds.Right;
  815. if (max_node_width > ClientRectangle.Width && !add_hscroll) {
  816. max_height -= ItemHeight;
  817. add_hscroll = true;
  818. }
  819. }
  820. depth++;
  821. if (node.IsExpanded) {
  822. for (int i = 0; i < _n_count; i++) {
  823. int tdepth = depth;
  824. DrawNode (node.nodes [i], dc, clip, ref tdepth, item_height, font, max_height);
  825. }
  826. }
  827. }
  828. private void AddVerticalScrollBar (int total_nodes, bool count_changed)
  829. {
  830. if (vbar == null) {
  831. vbar = new VScrollBar ();
  832. count_changed = true;
  833. }
  834. vbar.Bounds = new Rectangle (ClientRectangle.Width - vbar.Width,
  835. 0, vbar.Width, (add_hscroll ? Height - ThemeEngine.Current.HScrollBarDefaultSize.Height : Height));
  836. if (count_changed) {
  837. vbar.Maximum = total_nodes;
  838. int height = ClientRectangle.Height;
  839. vbar.LargeChange = height / ItemHeight;
  840. }
  841. if (!vbar_added) {
  842. Controls.Add (vbar);
  843. vbar.ValueChanged += new EventHandler (VScrollBarValueChanged);
  844. vbar_added = true;
  845. }
  846. vbar.Visible = true;
  847. }
  848. private void AddHorizontalScrollBar ()
  849. {
  850. if (hbar == null)
  851. hbar = new HScrollBar ();
  852. hbar.Bounds = new Rectangle (ClientRectangle.Left, ClientRectangle.Bottom - hbar.Height,
  853. (add_vscroll ? Width - ThemeEngine.Current.VScrollBarDefaultSize.Width : Width), hbar.Height);
  854. if (!hbar_added) {
  855. Controls.Add (hbar);
  856. hbar.ValueChanged += new EventHandler (HScrollBarValueChanged);
  857. hbar_added = true;
  858. }
  859. hbar.Visible = true;
  860. }
  861. private void SizeChangedHandler (object sender, EventArgs e)
  862. {
  863. SuspendLayout ();
  864. if (max_node_width > ClientRectangle.Width) {
  865. add_hscroll = true;
  866. AddHorizontalScrollBar ();
  867. }
  868. if (vbar != null) {
  869. int height = (hbar != null && hbar.Visible ? Height - hbar.Height : Height);
  870. vbar.SetBounds (Right - vbar.Width, 0, 0, height, BoundsSpecified.X | BoundsSpecified.Height);
  871. }
  872. if (hbar != null) {
  873. int width = (vbar != null && vbar.Visible ? Width - vbar.Width : Width);
  874. hbar.SetBounds (0, Bottom - hbar.Height, width, 0, BoundsSpecified.Y | BoundsSpecified.Width);
  875. }
  876. ResumeLayout ();
  877. }
  878. private void VScrollBarValueChanged (object sender, EventArgs e)
  879. {
  880. SetVScrollPos (vbar.Value, null);
  881. }
  882. private void SetVScrollPos (int pos, TreeNode new_top)
  883. {
  884. if (skipped_nodes == pos)
  885. return;
  886. int old_skip = skipped_nodes;
  887. skipped_nodes = pos;
  888. int diff = old_skip - skipped_nodes;
  889. // Determine the new top node if we have to
  890. if (new_top == null) {
  891. if (top_node == null)
  892. top_node = nodes [0];
  893. OpenTreeNodeEnumerator walk = new OpenTreeNodeEnumerator (TopNode);
  894. if (diff < 0) {
  895. for (int i = diff; i <= 0; i++)
  896. walk.MoveNext ();
  897. new_top = walk.CurrentNode;
  898. } else {
  899. for (int i = 0; i <= diff; i++)
  900. walk.MovePrevious ();
  901. new_top = walk.CurrentNode;
  902. }
  903. }
  904. top_node = new_top;
  905. int y_move = diff * ItemHeight;
  906. XplatUI.ScrollWindow (Handle, ViewportRectangle, 0, y_move, false);
  907. }
  908. private void HScrollBarValueChanged(object sender, EventArgs e)
  909. {
  910. int old_offset = hbar_offset;
  911. hbar_offset = hbar.Value;
  912. XplatUI.ScrollWindow (Handle, ViewportRectangle, old_offset - hbar_offset, 0, false);
  913. }
  914. private int GetOpenNodeCount ()
  915. {
  916. if (Nodes.Count < 1)
  917. return 0;
  918. OpenTreeNodeEnumerator e = new OpenTreeNodeEnumerator (root_node.Nodes [0]);
  919. int count = 0;
  920. while (e.MoveNext ()) {
  921. count++;
  922. }
  923. return count;
  924. }
  925. private void MouseDownHandler (object sender, MouseEventArgs e)
  926. {
  927. TreeNode node = GetNodeAt (e.Y);
  928. if (node == null)
  929. return;
  930. if (IsTextArea (node, e.X)) {
  931. TreeNode old_selected = selected_node;
  932. selected_node = node;
  933. if (label_edit && e.Clicks == 1 && selected_node == old_selected) {
  934. Rectangle invalid = node.Bounds;
  935. node.BeginEdit ();
  936. if (edit_node != null) {
  937. invalid = Rectangle.Union (invalid, edit_node.Bounds);
  938. edit_node.EndEdit (false);
  939. }
  940. edit_node = node;
  941. Invalidate (selected_node.Bounds);
  942. } else if (selected_node != focused_node) {
  943. select_mmove = true;
  944. Rectangle invalid = (old_selected == null ? Rectangle.Empty : old_selected.Bounds);
  945. invalid = Rectangle.Union (invalid, selected_node.Bounds);
  946. Invalidate (invalid);
  947. }
  948. } else if (show_plus_minus && IsPlusMinusArea (node, e.X)) {
  949. node.Toggle ();
  950. return;
  951. } else if (checkboxes && IsCheckboxArea (node, e.X)) {
  952. node.Checked = !node.Checked;
  953. return;
  954. }
  955. }
  956. private void MouseUpHandler (object sender, MouseEventArgs e) {
  957. if (!select_mmove)
  958. return;
  959. select_mmove = false;
  960. TreeViewCancelEventArgs ce = new TreeViewCancelEventArgs (selected_node, false, TreeViewAction.ByMouse);
  961. OnBeforeSelect (ce);
  962. Rectangle invalid;
  963. if (!ce.Cancel) {
  964. if (focused_node != null)
  965. invalid = Rectangle.Union (focused_node.Bounds, selected_node.Bounds);
  966. else
  967. invalid = selected_node.Bounds;
  968. focused_node = selected_node;
  969. OnAfterSelect (new TreeViewEventArgs (selected_node, TreeViewAction.ByMouse));
  970. Invalidate (invalid);
  971. } else {
  972. selected_node = focused_node;
  973. }
  974. }
  975. private void MouseMoveHandler (object sender, MouseEventArgs e) {
  976. if(!select_mmove)
  977. return;
  978. TreeNode node = GetNodeAt(e.X,e.Y);
  979. if(node == selected_node)
  980. return;
  981. selected_node = focused_node;
  982. select_mmove = false;
  983. Refresh();
  984. }
  985. private void DoubleClickHandler (object sender, MouseEventArgs e) {
  986. TreeNode node = GetNodeAt(e.X,e.Y);
  987. if(node != null) {
  988. node.Toggle();
  989. }
  990. }
  991. private bool RectsIntersect (Rectangle r, int left, int top, int width, int height)
  992. {
  993. return !((r.Left > left + width) || (r.Right < left) ||
  994. (r.Top > top + height) || (r.Bottom < top));
  995. }
  996. #endregion // Internal & Private Methods and Properties
  997. #region Events
  998. public event TreeViewEventHandler AfterCheck {
  999. add { on_after_check += value; }
  1000. remove { on_after_check -= value; }
  1001. }
  1002. public event TreeViewEventHandler AfterCollapse {
  1003. add { on_after_collapse += value; }
  1004. remove { on_after_collapse -= value; }
  1005. }
  1006. public event TreeViewEventHandler AfterExpand {
  1007. add { on_after_expand += value; }
  1008. remove { on_after_expand -= value; }
  1009. }
  1010. public event NodeLabelEditEventHandler AfterLabelEdit {
  1011. add { on_after_label_edit += value; }
  1012. remove { on_after_label_edit -= value; }
  1013. }
  1014. public event TreeViewEventHandler AfterSelect {
  1015. add { on_after_select += value; }
  1016. remove { on_after_select -= value; }
  1017. }
  1018. public event EventHandler BackgroundImageChanged {
  1019. add { base.BackgroundImageChanged += value; }
  1020. remoave { base.BackgroundImageChanged -= value; }
  1021. }
  1022. public event TreeViewCancelEventHandler BeforeCheck {
  1023. add { on_before_check += value; }
  1024. remove { on_before_check -= value; }
  1025. }
  1026. public event TreeViewCancelEventHandler BeforeCollapse {
  1027. add { on_before_collapse += value; }
  1028. remove { on_before_collapse -= value; }
  1029. }
  1030. public event TreeViewCancelEventHandler BeforeExpand {
  1031. add { on_before_expand += value; }
  1032. remove { on_before_expand -= value; }
  1033. }
  1034. public event NodeLabelEditEventHandler BeforeLabelEdit {
  1035. add { on_before_label_edit += value; }
  1036. remove { on_before_label_edit -= value; }
  1037. }
  1038. public event TreeViewCancelEventHandler BeforeSelect {
  1039. add { on_before_select += value; }
  1040. remove { on_before_select -= value; }
  1041. }
  1042. [EditorBrowsable (EditorBrowsableState.Never)]
  1043. [Browsable (false)]
  1044. public new event PaintEventHandler Paint {
  1045. add { base.Paint += value; }
  1046. remove { base.Paint -= value; }
  1047. }
  1048. [EditorBrowsable (EditorBrowsableState.Never)]
  1049. [Browsable (false)]
  1050. public new event EventHandler TextChanged {
  1051. add { base.TextChanged += value; }
  1052. remove { base.TextChanged -= value; }
  1053. }
  1054. #endregion // Events
  1055. }
  1056. }