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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2004-2005 Novell, Inc.
23 // Jackson Harper (jackson@ximian.com)
24 // Kazuki Oikawa (kazuki@panicode.com)
27 using System.Collections;
28 using System.ComponentModel;
29 using System.ComponentModel.Design;
31 using System.Drawing.Drawing2D;
32 using System.Runtime.InteropServices;
34 namespace System.Windows.Forms {
35 [DefaultProperty("Nodes")]
36 [DefaultEvent("AfterSelect")]
37 [Designer("System.Windows.Forms.Design.TreeViewDesigner, " + Consts.AssemblySystem_Design)]
38 public class TreeView : Control {
40 private string path_separator = "\\";
41 private int item_height = -1;
43 private TreeNode top_node;
44 internal TreeNode root_node;
45 private TreeNodeCollection nodes;
46 private int total_node_count;
48 private TreeNode selected_node = null;
49 private TreeNode focused_node = null;
50 private bool select_mmove = false;
52 private ImageList image_list;
53 private int image_index = -1;
54 private int selected_image_index = -1;
56 private bool full_row_select;
57 private bool hot_tracking;
58 private int indent = 19;
60 private TextBox edit_text_box;
61 private TreeNode edit_node;
63 private bool checkboxes;
64 private bool label_edit;
65 private bool scrollable;
66 private bool show_lines = true;
67 private bool show_root_lines = true;
68 private bool show_plus_minus = true;
69 private bool hide_selection = true;
71 private bool add_hscroll;
72 private bool add_vscroll;
73 private int max_node_width;
74 private VScrollBar vbar;
75 private bool vbar_added;
76 private int skipped_nodes;
77 private HScrollBar hbar;
78 private bool hbar_added;
79 private int hbar_offset;
81 private int update_stack;
83 private TreeViewEventHandler on_after_check;
84 private TreeViewEventHandler on_after_collapse;
85 private TreeViewEventHandler on_after_expand;
86 private NodeLabelEditEventHandler on_after_label_edit;
87 private TreeViewEventHandler on_after_select;
88 private TreeViewCancelEventHandler on_before_check;
89 private TreeViewCancelEventHandler on_before_collapse;
90 private TreeViewCancelEventHandler on_before_expand;
91 private NodeLabelEditEventHandler on_before_label_edit;
92 private TreeViewCancelEventHandler on_before_select;
95 private int open_node_count = -1;
98 #region Public Constructors
101 base.background_color = ThemeEngine.Current.ColorWindow;
102 base.foreground_color = ThemeEngine.Current.ColorWindowText;
104 root_node = new TreeNode (this);
105 root_node.Text = "ROOT NODE";
106 nodes = new TreeNodeCollection (root_node);
107 root_node.SetNodes (nodes);
109 MouseDown += new MouseEventHandler (MouseDownHandler);
110 MouseUp += new MouseEventHandler(MouseUpHandler);
111 MouseMove += new MouseEventHandler(MouseMoveHandler);
112 SizeChanged += new EventHandler (SizeChangedHandler);
114 SetStyle (ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true);
115 SetStyle (ControlStyles.UserPaint | ControlStyles.Selectable, true);
117 dash = new Pen (SystemColors.ControlLight, 1);
120 #endregion // Public Constructors
122 #region Public Instance Properties
123 public override Color BackColor {
124 get { return base.BackColor;}
125 set { base.BackColor = value; }
130 [EditorBrowsable(EditorBrowsableState.Never)]
131 public override Image BackgroundImage {
132 get { return base.BackgroundImage; }
133 set { base.BackgroundImage = value; }
136 [DefaultValue(BorderStyle.Fixed3D)]
138 public BorderStyle BorderStyle {
139 get { return InternalBorderStyle; }
140 set { InternalBorderStyle = value; }
143 [DefaultValue(false)]
144 public bool CheckBoxes {
145 get { return checkboxes; }
147 if (value == checkboxes)
151 // Match a "bug" in the MS implementation where disabling checkboxes
152 // collapses the entire tree, but enabling them does not affect the
153 // state of the tree.
155 root_node.CollapseAllUncheck ();
161 public override Color ForeColor {
162 get { return base.ForeColor; }
163 set { base.ForeColor = value; }
165 [DefaultValue(false)]
166 public bool FullRowSelect {
167 get { return full_row_select; }
169 if (value == full_row_select)
171 full_row_select = value;
176 public bool HideSelection {
177 get { return hide_selection; }
179 if (hide_selection == value)
181 hide_selection = value;
186 [DefaultValue(false)]
187 public bool HotTracking {
188 get { return hot_tracking; }
189 set { hot_tracking = value; }
193 [Editor("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
195 [TypeConverter(typeof(TreeViewImageIndexConverter))]
196 public int ImageIndex {
197 get { return image_index; }
200 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
201 "'value' must be greater than or equal to 0.");
207 [MonoTODO ("Anything special need to be done here?")]
209 public ImageList ImageList {
210 get { return image_list; }
211 set { image_list = value; }
216 get { return indent; }
221 throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
222 "'Indent' must be less than or equal to 32000");
225 throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
226 "'Indent' must be greater than or equal to 0.");
234 public int ItemHeight {
236 if (item_height == -1)
237 return FontHeight + 3;
241 if (value == item_height)
248 [DefaultValue(false)]
249 public bool LabelEdit {
250 get { return label_edit; }
251 set { label_edit = value; }
254 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
255 [MergableProperty(false)]
257 public TreeNodeCollection Nodes {
258 get { return nodes; }
262 public string PathSeparator {
263 get { return path_separator; }
264 set { path_separator = value; }
268 public bool Scrollable {
269 get { return scrollable; }
271 if (scrollable == value)
277 [Editor("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))]
278 [TypeConverter(typeof(TreeViewImageIndexConverter))]
281 public int SelectedImageIndex {
282 get { return selected_image_index; }
285 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
286 "'value' must be greater than or equal to 0.");
292 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
293 public TreeNode SelectedNode {
294 get { return selected_node; }
296 if (selected_node == value)
299 TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (value, false, TreeViewAction.Unknown);
305 Rectangle invalid = Rectangle.Empty;
307 if (selected_node != null)
308 invalid = selected_node.Bounds;
309 if (focused_node != null)
310 invalid = Rectangle.Union (focused_node.Bounds, invalid);
311 invalid = Rectangle.Union (invalid, value.Bounds);
313 selected_node = value;
314 focused_node = value;
316 Invalidate (invalid);
318 OnAfterSelect (new TreeViewEventArgs (value, TreeViewAction.Unknown));
323 public bool ShowLines {
324 get { return show_lines; }
326 if (show_lines == value)
334 public bool ShowPlusMinus {
335 get { return show_plus_minus; }
337 if (show_plus_minus == value)
339 show_plus_minus = value;
345 public bool ShowRootLines {
346 get { return show_root_lines; }
348 if (show_root_lines == value)
350 show_root_lines = value;
355 [DefaultValue(false)]
357 get { return sorted; }
369 [EditorBrowsable(EditorBrowsableState.Never)]
371 public override string Text {
372 get { return base.Text; }
373 set { base.Text = value; }
377 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
378 public TreeNode TopNode {
379 get { return top_node; }
383 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
384 public int VisibleCount {
386 return ClientRectangle.Height / ItemHeight;
390 #endregion // Public Instance Properties
392 #region Protected Instance Properties
393 [MonoTODO ("Anything extra needed here?")]
394 protected override CreateParams CreateParams {
396 CreateParams cp = base.CreateParams;
401 protected override Size DefaultSize {
402 get { return new Size (121, 97); }
405 #endregion // Protected Instance Properties
407 #region Public Instance Methods
408 public void BeginUpdate () {
409 if (!IsHandleCreated)
414 public void CollapseAll () {
415 root_node.CollapseAll ();
418 public void EndUpdate () {
419 if (!IsHandleCreated)
422 if (update_stack > 1) {
430 public void ExpandAll () {
431 root_node.ExpandAll ();
434 public TreeNode GetNodeAt (Point pt) {
435 return GetNodeAt (pt.X, pt.Y);
438 public TreeNode GetNodeAt (int x, int y) {
439 TreeNode node = GetNodeAt (y);
440 if (node == null || !IsTextArea (node, x))
446 public int GetNodeCount (bool include_subtrees) {
447 return root_node.GetNodeCount (include_subtrees);
450 public override string ToString () {
451 int count = Nodes.Count;
453 return String.Concat (base.ToString (), "Node Count: 0");
454 return String.Concat (base.ToString (), "Node Count: ", count, " Nodes[0]: ", Nodes [0]);
458 #endregion // Public Instance Methods
460 #region Protected Instance Methods
461 protected override void CreateHandle () {
462 base.CreateHandle ();
465 protected override void Dispose (bool disposing) {
467 if (image_list != null)
468 image_list.Dispose ();
470 base.Dispose (disposing);
473 [MonoTODO ("What does the state effect?")]
474 protected OwnerDrawPropertyBag GetItemRenderStyles (TreeNode node, int state) {
475 return node.prop_bag;
478 protected override bool IsInputKey (Keys key_data) {
479 if (label_edit && (key_data & Keys.Alt) == 0) {
480 switch (key_data & Keys.KeyCode) {
494 return base.IsInputKey (key_data);
497 protected virtual void OnAfterCheck (TreeViewEventArgs e) {
498 if (on_after_check != null)
499 on_after_check (this, e);
502 protected internal virtual void OnAfterCollapse (TreeViewEventArgs e) {
503 if (on_after_collapse != null)
504 on_after_collapse (this, e);
507 protected internal virtual void OnAfterExpand (TreeViewEventArgs e) {
508 if (on_after_expand != null)
509 on_after_expand (this, e);
512 protected virtual void OnAfterLabelEdit (NodeLabelEditEventArgs e) {
513 if (on_after_label_edit != null)
514 on_after_label_edit (this, e);
517 protected virtual void OnAfterSelect (TreeViewEventArgs e) {
518 if (on_after_select != null)
519 on_after_select (this, e);
522 protected virtual void OnBeforeCheck (TreeViewCancelEventArgs e) {
523 if (on_before_check != null)
524 on_before_check (this, e);
527 protected internal virtual void OnBeforeCollapse (TreeViewCancelEventArgs e) {
528 if (on_before_collapse != null)
529 on_before_collapse (this, e);
532 protected internal virtual void OnBeforeExpand (TreeViewCancelEventArgs e) {
533 if (on_before_expand != null)
534 on_before_expand (this, e);
537 protected virtual void OnBeforeLabelEdit (NodeLabelEditEventArgs e) {
538 if (on_before_label_edit != null)
539 on_before_label_edit (this, e);
542 protected virtual void OnBeforeSelect (TreeViewCancelEventArgs e) {
543 if (on_before_select != null)
544 on_before_select (this, e);
547 protected override void OnHandleCreated (EventArgs e) {
548 base.OnHandleCreated (e);
551 protected override void OnHandleDestroyed (EventArgs e) {
552 base.OnHandleDestroyed (e);
555 protected override void WndProc(ref Message m) {
556 switch ((Msg) m.Msg) {
558 PaintEventArgs paint_event;
560 paint_event = XplatUI.PaintEventStart (Handle);
561 DoPaint (paint_event);
562 XplatUI.PaintEventEnd (Handle);
565 case Msg.WM_LBUTTONDBLCLK:
566 int val = m.LParam.ToInt32();
567 DoubleClickHandler (null, new MouseEventArgs (MouseButtons.Left, 2, val & 0xffff, (val>>16) & 0xffff, 0));
570 base.WndProc (ref m);
573 #endregion // Protected Instance Methods
575 #region Internal & Private Methods and Properties
576 internal string LabelEditText {
578 if (edit_text_box == null)
580 return edit_text_box.Text;
584 internal int TotalNodeCount {
585 get { return total_node_count; }
586 set { total_node_count = value; }
589 // TODO: we shouldn't have to compute this on the fly
590 private Rectangle ViewportRectangle {
592 Rectangle res = ClientRectangle;
594 if (vbar != null && vbar.Visible)
595 res.Width -= vbar.Width;
596 if (hbar != null && hbar.Visible)
597 res.Height -= hbar.Height;
602 [MonoTODO ("Need to know if we are editing, not if editing is enabled")]
603 private TreeNode GetNodeAt (int y) {
605 if (top_node == null)
606 top_node = nodes [0];
608 OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (TopNode);
609 int move = y / ItemHeight + skipped_nodes;
611 for (int i = -1; i < move; i++) {
616 return o.CurrentNode;
619 private bool IsTextArea (TreeNode node, int x) {
620 return node != null && node.Bounds.Left <= x && node.Bounds.Right >= x;
624 // TODO: Update from supplied node down
625 internal void UpdateBelow (TreeNode node)
627 // We need to update the current node so the plus/minus block gets update too
628 Rectangle invalid = new Rectangle (0, node.Bounds.Top, Width, Height - node.Bounds.Top);
629 Invalidate (invalid);
632 internal void UpdateNode (TreeNode node)
634 Rectangle invalid = new Rectangle (0, node.Bounds.Top, Width, node.Bounds.Height);
635 Invalidate (invalid);
638 private void DoPaint (PaintEventArgs pe)
640 if (Width <= 0 || Height <= 0 || Visible == false)
643 Draw (pe.ClipRectangle);
645 pe.Graphics.DrawImage (ImageBuffer, pe.ClipRectangle, pe.ClipRectangle, GraphicsUnit.Pixel);
648 private void Draw (Rectangle clip)
650 if (top_node == null && Nodes.Count > 0)
651 top_node = nodes [0];
652 // Decide if we need a scrollbar
653 int old_open_node_count = open_node_count;
655 Rectangle fill = ClientRectangle;
659 DeviceContext.FillRectangle (new SolidBrush (BackColor), fill);
662 int item_height = ItemHeight;
664 int height = ClientRectangle.Height;
667 foreach (TreeNode node in nodes) {
668 DrawNode (node, clip, ref depth, item_height, font, height);
672 add_vscroll = (open_node_count * ItemHeight) > ClientRectangle.Height;
674 if (max_node_width > ClientRectangle.Width)
678 add_hscroll = max_node_width > ClientRectangle.Width - ThemeEngine.Current.VScrollBarDefaultSize.Width;
680 add_vscroll = (open_node_count * ItemHeight) > ClientRectangle.Height - ThemeEngine.Current.HScrollBarDefaultSize.Width;
683 AddHorizontalScrollBar ();
684 } else if (hbar != null) {
686 hbar.Visible = false;
690 AddVerticalScrollBar (open_node_count, old_open_node_count != open_node_count);
691 } else if (vbar != null) {
692 vbar.Visible = false;
696 if (add_hscroll && add_vscroll) {
697 Rectangle corner = new Rectangle (hbar.Right, vbar.Bottom, vbar.Width, hbar.Height);
698 if (clip.IntersectsWith (corner))
699 DeviceContext.FillRectangle (new SolidBrush (ThemeEngine.Current.ColorButtonFace), corner);
703 private void DrawNodePlusMinus (TreeNode node, Rectangle clip, int x, int y, int middle)
705 node.UpdatePlusMinusBounds (x, middle - 4, 8, 8);
707 if (!clip.IntersectsWith (node.PlusMinusBounds))
710 DeviceContext.DrawRectangle (SystemPens.ControlDark, node.PlusMinusBounds);
712 if (node.IsExpanded) {
713 DeviceContext.DrawLine (SystemPens.ControlDarkDark, x + 2, middle, x + 6, middle);
715 DeviceContext.DrawLine (SystemPens.ControlDarkDark, x + 2, middle, x + 6, middle);
716 DeviceContext.DrawLine (SystemPens.ControlDarkDark, x + 4, middle - 2, x + 4, middle + 2);
720 private void DrawNodeCheckBox (TreeNode node, Rectangle clip, int x, int y)
722 int offset = (ItemHeight - 13);
724 node.UpdateCheckBoxBounds (x + 3, y + offset, 10, 10);
726 // new rectangle that factors in line width
727 if (!RectsIntersect (clip, x + 3, y + offset, 12, 12))
730 DeviceContext.DrawRectangle (new Pen (Color.Black, 2), x + 0.5F + 3, y + 0.5F + offset, 11, 11);
733 Pen check_pen = new Pen (Color.Black, 1);
735 DeviceContext.DrawLine (check_pen, x + 6, y + offset + 5, x + 8, y + offset + 8);
736 DeviceContext.DrawLine (check_pen, x + 6, y + offset + 6, x + 8, y + offset + 9);
738 DeviceContext.DrawLine (check_pen, x + 7, y + offset + 8, x + 13, y + offset + 3);
739 DeviceContext.DrawLine (check_pen, x + 7, y + offset + 9, x + 13, y + offset + 4);
743 private void DrawNodeLines (TreeNode node, bool visible, Pen dash, int x, int y, int middle, int item_height, int node_count)
745 int ladjust = 9; // left adjust
746 int radjust = 0; // right adjust
748 if (node_count > 0 && show_plus_minus)
753 DeviceContext.DrawLine (dash, x - indent + ladjust, middle, x + radjust, middle);
759 if (node.PrevNode != null) {
760 int prevadjust = (node.Nodes.Count > 0 && show_plus_minus ? (node.PrevNode.Nodes.Count == 0 ? 0 : 4) :
761 (node.PrevNode.Nodes.Count == 0 ? 0 : 4));
762 int myadjust = (node.Nodes.Count > 0 && show_plus_minus ? 4 : 0);
763 ly = node.PrevNode.Bounds.Bottom - (item_height / 2) + prevadjust;
764 DeviceContext.DrawLine (dash, x - indent + 9, middle - myadjust, x - indent + 9, ly);
765 } else if (node.Parent != null) {
766 int myadjust = (node.Nodes.Count > 0 && show_plus_minus ? 4 : 0);
767 ly = node.Parent.Bounds.Bottom - 1;
768 DeviceContext.DrawLine (dash, x - indent + 9, middle - myadjust, x - indent + 9, ly);
772 private void DrawNodeImage (TreeNode node, Rectangle clip, int x, int y)
774 Rectangle r = new Rectangle (x, y + 2, ImageList.ImageSize.Width,
775 ImageList.ImageSize.Height);
776 if (!RectsIntersect (r, x, y + 2, ImageList.ImageSize.Width, ImageList.ImageSize.Height))
779 if (node.ImageIndex > -1 && ImageList != null && node.ImageIndex < ImageList.Images.Count) {
780 ImageList.Draw (DeviceContext, x, y + 2, ImageList.ImageSize.Width,
781 ImageList.ImageSize.Height, node.ImageIndex);
782 } else if (ImageIndex > -1 && ImageList != null && ImageIndex < ImageList.Images.Count) {
783 ImageList.Draw (DeviceContext, x, y + 2, ImageList.ImageSize.Width,
784 ImageList.ImageSize.Height, ImageIndex);
788 private void DrawEditNode (TreeNode node)
792 if (edit_text_box == null) {
793 edit_text_box = new TextBox ();
794 edit_text_box.BorderStyle = BorderStyle.FixedSingle;
795 edit_text_box.KeyUp += new KeyEventHandler (EditTextBoxKeyDown);
796 edit_text_box.Leave += new EventHandler (EditTextBoxLeave);
797 Controls.Add (edit_text_box);
800 edit_text_box.Bounds = node.Bounds;
801 edit_text_box.Width += 4;
803 edit_text_box.Text = node.Text;
804 edit_text_box.Visible = true;
805 edit_text_box.Focus ();
806 edit_text_box.SelectAll ();
811 private void EditTextBoxKeyDown (object sender, KeyEventArgs e)
813 if (e.KeyCode == Keys.Return)
817 private void EditTextBoxLeave (object sender, EventArgs e)
822 private void EndEdit ()
824 edit_text_box.Visible = false;
825 edit_node.EndEdit (false);
826 Invalidate (edit_node.Bounds);
829 private void UpdateNodeBounds (TreeNode node, int x, int y, int item_height)
831 int width = (int) (node.Text.Length * Font.Size);
832 node.UpdateBounds (x, y, width, item_height);
835 private void DrawNode (TreeNode node, Rectangle clip, ref int depth, int item_height,
836 Font font, int max_height)
839 int x = (!show_root_lines && node.Parent != null ? depth - 1 : depth) * indent - hbar_offset;
840 int y = item_height * (open_node_count - skipped_nodes - 1);
841 bool visible = (y >= 0 && y < max_height);
842 int _n_count = node.nodes.Count;
843 int middle = y + (item_height / 2);
845 // The thing is totally out of the clipping rectangle
846 if (clip.Top > y + ItemHeight || clip.Bottom < y)
849 if (show_root_lines || node.Parent != null) {
852 if (show_plus_minus && visible) {
853 DrawNodePlusMinus (node, clip, x, y, middle);
861 if (visible && checkboxes) {
862 DrawNodeCheckBox (node, clip, ox, y);
867 DrawNodeLines (node, visible, dash, x, y, middle, item_height, _n_count);
869 if (visible && ImageList != null) {
871 DrawNodeImage (node, clip, ox, y);
872 // MS leaves the space for the image if the ImageList is
873 // non null regardless of whether or not an image is drawn
874 ox += ImageList.ImageSize.Width + 3; // leave a little space so the text isn't against the image
877 UpdateNodeBounds (node, ox, y, item_height);
879 bool bounds_in_clip = clip.IntersectsWith (node.Bounds);
880 if (visible && bounds_in_clip && !node.IsEditing) {
881 Rectangle r = node.Bounds;
882 StringFormat format = new StringFormat ();
883 format.LineAlignment = StringAlignment.Center;
885 r.Y += 2; // we have to adjust this to get nice middle alignment
887 Color text_color = (Focused && SelectedNode == node ? ThemeEngine.Current.ColorHilightText : node.ForeColor);
889 if (SelectedNode == node)
890 DeviceContext.FillRectangle (new SolidBrush (ThemeEngine.Current.ColorHilight), r);
891 if (focused_node == node) {
892 Pen dot_pen = new Pen (ThemeEngine.Current.ColorButtonHilight, 1);
893 dot_pen.DashStyle = DashStyle.Dot;
894 DeviceContext.DrawRectangle (new Pen (ThemeEngine.Current.ColorButtonDkShadow),
895 node.Bounds.X, node.Bounds.Y, node.Bounds.Width - 1, node.Bounds.Height - 1);
896 DeviceContext.DrawRectangle (dot_pen, node.Bounds.X, node.Bounds.Y, node.Bounds.Width - 1, node.Bounds.Height - 1);
899 if (!HideSelection && SelectedNode == node)
900 DeviceContext.FillRectangle (new SolidBrush (ThemeEngine.Current.ColorButtonFace), node.Bounds);
902 DeviceContext.DrawString (node.Text, font, new SolidBrush (text_color), r, format);
903 y += item_height + 1;
904 } else if (visible && bounds_in_clip) {
908 if (node.Bounds.Right > max_node_width) {
909 max_node_width = node.Bounds.Right;
910 if (max_node_width > ClientRectangle.Width && !add_hscroll) {
911 max_height -= ItemHeight;
917 if (node.IsExpanded) {
918 for (int i = 0; i < _n_count; i++) {
920 DrawNode (node.nodes [i], clip, ref tdepth, item_height, font, max_height);
926 private void AddVerticalScrollBar (int total_nodes, bool count_changed)
929 vbar = new VScrollBar ();
930 count_changed = true;
933 vbar.Bounds = new Rectangle (ClientRectangle.Width - vbar.Width,
934 0, vbar.Width, (add_hscroll ? Height - ThemeEngine.Current.HScrollBarDefaultSize.Height : Height));
937 vbar.Maximum = total_nodes;
938 int height = ClientRectangle.Height;
939 vbar.LargeChange = height / ItemHeight;
944 vbar.ValueChanged += new EventHandler (VScrollBarValueChanged);
951 private void AddHorizontalScrollBar ()
954 hbar = new HScrollBar ();
956 hbar.Bounds = new Rectangle (ClientRectangle.Left, ClientRectangle.Bottom - hbar.Height,
957 (add_vscroll ? Width - ThemeEngine.Current.VScrollBarDefaultSize.Width : Width), hbar.Height);
961 hbar.ValueChanged += new EventHandler (HScrollBarValueChanged);
968 private void SizeChangedHandler (object sender, EventArgs e)
972 if (max_node_width > ClientRectangle.Width) {
974 AddHorizontalScrollBar ();
978 int height = (hbar != null && hbar.Visible ? Height - hbar.Height : Height);
979 vbar.SetBounds (Right - vbar.Width, 0, 0, height, BoundsSpecified.X | BoundsSpecified.Height);
983 int width = (vbar != null && vbar.Visible ? Width - vbar.Width : Width);
984 hbar.SetBounds (0, Bottom - hbar.Height, width, 0, BoundsSpecified.Y | BoundsSpecified.Width);
990 private void VScrollBarValueChanged (object sender, EventArgs e)
992 int old_skip = skipped_nodes;
993 skipped_nodes = vbar.Value;
995 int y_move = (old_skip - skipped_nodes) * ItemHeight;
996 XplatUI.ScrollWindow (Handle, ViewportRectangle, 0, y_move, false);
999 private void HScrollBarValueChanged(object sender, EventArgs e)
1001 int old_offset = hbar_offset;
1002 hbar_offset = hbar.Value;
1004 XplatUI.ScrollWindow (Handle, ViewportRectangle, old_offset - hbar_offset, 0, false);
1007 private int GetOpenNodeCount ()
1010 if (Nodes.Count < 1)
1013 OpenTreeNodeEnumerator e = new OpenTreeNodeEnumerator (root_node.Nodes [0]);
1016 while (e.MoveNext ()) {
1023 private void MouseDownHandler (object sender, MouseEventArgs e)
1025 if (!show_plus_minus)
1028 TreeNode node = GetNodeAt (e.Y);
1031 if (IsTextArea (node, e.X)) {
1032 TreeNode old_selected = selected_node;
1033 selected_node = node;
1034 if (label_edit && e.Clicks == 1 && selected_node == old_selected) {
1035 Rectangle invalid = node.Bounds;
1037 if (edit_node != null) {
1038 invalid = Rectangle.Union (invalid, edit_node.Bounds);
1039 edit_node.EndEdit (false);
1042 Invalidate (selected_node.Bounds);
1043 } else if (selected_node != focused_node) {
1044 select_mmove = true;
1045 Rectangle invalid = (old_selected == null ? Rectangle.Empty : old_selected.Bounds);
1046 invalid = Rectangle.Union (invalid, selected_node.Bounds);
1047 Invalidate (invalid);
1049 } else if (node.PlusMinusBounds.Contains (e.X, e.Y)) {
1052 } else if (node.CheckBoxBounds.Contains (e.X, e.Y)) {
1053 node.Checked = !node.Checked;
1058 private void MouseUpHandler (object sender, MouseEventArgs e) {
1062 select_mmove = false;
1064 TreeViewCancelEventArgs ce = new TreeViewCancelEventArgs (selected_node, false, TreeViewAction.ByMouse);
1065 OnBeforeSelect (ce);
1069 if (focused_node != null)
1070 invalid = Rectangle.Union (focused_node.Bounds, selected_node.Bounds);
1072 invalid = selected_node.Bounds;
1073 focused_node = selected_node;
1074 OnAfterSelect (new TreeViewEventArgs (selected_node, TreeViewAction.ByMouse));
1075 Invalidate (invalid);
1077 selected_node = focused_node;
1083 private void MouseMoveHandler (object sender, MouseEventArgs e) {
1086 TreeNode node = GetNodeAt(e.X,e.Y);
1087 if(node == selected_node)
1090 selected_node = focused_node;
1091 select_mmove = false;
1095 private void DoubleClickHandler (object sender, MouseEventArgs e) {
1096 TreeNode node = GetNodeAt(e.X,e.Y);
1103 private bool RectsIntersect (Rectangle r, int left, int top, int width, int height)
1105 return !((r.Left > left + width) || (r.Right < left) ||
1106 (r.Top > top + height) || (r.Bottom < top));
1109 #endregion // Internal & Private Methods and Properties
1112 public event TreeViewEventHandler AfterCheck {
1113 add { on_after_check += value; }
1114 remove { on_after_check -= value; }
1117 public event TreeViewEventHandler AfterCollapse {
1118 add { on_after_collapse += value; }
1119 remove { on_after_collapse -= value; }
1122 public event TreeViewEventHandler AfterExpand {
1123 add { on_after_expand += value; }
1124 remove { on_after_expand -= value; }
1127 public event NodeLabelEditEventHandler AfterLabelEdit {
1128 add { on_after_label_edit += value; }
1129 remove { on_after_label_edit -= value; }
1132 public event TreeViewEventHandler AfterSelect {
1133 add { on_after_select += value; }
1134 remove { on_after_select -= value; }
1137 public event TreeViewCancelEventHandler BeforeCheck {
1138 add { on_before_check += value; }
1139 remove { on_before_check -= value; }
1142 public event TreeViewCancelEventHandler BeforeCollapse {
1143 add { on_before_collapse += value; }
1144 remove { on_before_collapse -= value; }
1147 public event TreeViewCancelEventHandler BeforeExpand {
1148 add { on_before_expand += value; }
1149 remove { on_before_expand -= value; }
1152 public event NodeLabelEditEventHandler BeforeLabelEdit {
1153 add { on_before_label_edit += value; }
1154 remove { on_before_label_edit -= value; }
1157 public event TreeViewCancelEventHandler BeforeSelect {
1158 add { on_before_select += value; }
1159 remove { on_before_select -= value; }
1162 [EditorBrowsable (EditorBrowsableState.Never)]
1164 public new event PaintEventHandler Paint {
1165 add { base.Paint += value; }
1166 remove { base.Paint -= value; }
1169 [EditorBrowsable (EditorBrowsableState.Never)]
1171 public new event EventHandler TextChanged {
1172 add { base.TextChanged += value; }
1173 remove { base.TextChanged -= value; }
1175 #endregion // Events