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
{
37 [Docking (DockingBehavior
.Ask
)]
38 [ClassInterface (ClassInterfaceType
.AutoDispatch
)]
40 [DefaultProperty("Nodes")]
41 [DefaultEvent("AfterSelect")]
42 [Designer("System.Windows.Forms.Design.TreeViewDesigner, " + Consts
.AssemblySystem_Design
, "System.ComponentModel.Design.IDesigner")]
43 public class TreeView
: Control
{
45 private string path_separator
= "\\";
46 private int item_height
= -1;
48 internal TreeNode root_node
;
49 internal bool nodes_added
;
50 private TreeNodeCollection nodes
;
52 private TreeViewAction selection_action
;
53 internal TreeNode selected_node
;
54 private TreeNode pre_selected_node
;
55 private TreeNode focused_node
;
56 internal TreeNode highlighted_node
;
57 private Rectangle mouse_rect
;
58 private bool select_mmove
;
60 private ImageList image_list
;
61 private int image_index
= -1;
62 private int selected_image_index
= -1;
65 private string image_key
;
66 private bool is_hovering
;
67 private TreeNode mouse_click_node
;
68 private bool right_to_left_layout
;
69 private string selected_image_key
;
70 private bool show_node_tool_tips
;
71 private ImageList state_image_list
;
72 private TreeNode tooltip_currently_showing
;
73 private ToolTip tooltip_window
;
75 private bool full_row_select
;
76 private bool hot_tracking
;
77 private int indent
= 19;
79 private NodeLabelEditEventArgs edit_args
;
80 private LabelEditTextBox edit_text_box
;
81 internal TreeNode edit_node
;
83 private bool checkboxes
;
84 private bool label_edit
;
85 private bool scrollable
= true;
86 private bool show_lines
= true;
87 private bool show_root_lines
= true;
88 private bool show_plus_minus
= true;
89 private bool hide_selection
= true;
91 private int max_visible_order
= -1;
92 internal VScrollBar vbar
;
93 internal HScrollBar hbar
;
94 private bool vbar_bounds_set
;
95 private bool hbar_bounds_set
;
96 internal int skipped_nodes
;
97 internal int hbar_offset
;
99 private int update_stack
;
100 private bool update_needed
;
103 private Color line_color
;
104 private StringFormat string_format
;
106 private int drag_begin_x
= -1;
107 private int drag_begin_y
= -1;
108 private long handle_count
= 1;
110 private TreeViewDrawMode draw_mode
;
113 IComparer tree_view_node_sorter
;
117 #region Public Constructors
120 vbar
= new ImplicitVScrollBar ();
121 hbar
= new ImplicitHScrollBar ();
123 InternalBorderStyle
= BorderStyle
.Fixed3D
;
124 base.background_color
= ThemeEngine
.Current
.ColorWindow
;
125 base.foreground_color
= ThemeEngine
.Current
.ColorWindowText
;
127 root_node
= new TreeNode (this);
128 root_node
.Text
= "ROOT NODE";
129 nodes
= new TreeNodeCollection (root_node
);
130 root_node
.SetNodes (nodes
);
132 MouseDown
+= new MouseEventHandler (MouseDownHandler
);
133 MouseUp
+= new MouseEventHandler(MouseUpHandler
);
134 MouseMove
+= new MouseEventHandler(MouseMoveHandler
);
135 SizeChanged
+= new EventHandler (SizeChangedHandler
);
136 FontChanged
+= new EventHandler (FontChangedHandler
);
137 LostFocus
+= new EventHandler (LostFocusHandler
);
138 GotFocus
+= new EventHandler (GotFocusHandler
);
139 MouseWheel
+= new MouseEventHandler(MouseWheelHandler
);
140 VisibleChanged
+= new EventHandler (VisibleChangedHandler
);
142 SetStyle (ControlStyles
.UserPaint
| ControlStyles
.StandardClick
144 | ControlStyles
.UseTextForAccessibility
148 string_format
= new StringFormat ();
149 string_format
.LineAlignment
= StringAlignment
.Center
;
150 string_format
.Alignment
= StringAlignment
.Center
;
152 vbar
.Visible
= false;
153 hbar
.Visible
= false;
154 vbar
.ValueChanged
+= new EventHandler (VScrollBarValueChanged
);
155 hbar
.ValueChanged
+= new EventHandler (HScrollBarValueChanged
);
158 Controls
.AddImplicit (vbar
);
159 Controls
.AddImplicit (hbar
);
163 #endregion // Public Constructors
165 #region Public Instance Properties
166 public override Color BackColor
{
167 get { return base.BackColor;}
169 base.BackColor
= value;
178 [EditorBrowsable(EditorBrowsableState
.Never
)]
179 public override Image BackgroundImage
{
180 get { return base.BackgroundImage; }
181 set { base.BackgroundImage = value; }
184 [DefaultValue(BorderStyle
.Fixed3D
)]
186 public BorderStyle BorderStyle
{
187 get { return InternalBorderStyle; }
188 set { InternalBorderStyle = value; }
191 [DefaultValue(false)]
192 public bool CheckBoxes
{
193 get { return checkboxes; }
195 if (value == checkboxes
)
199 // Match a "bug" in the MS implementation where disabling checkboxes
200 // collapses the entire tree, but enabling them does not affect the
201 // state of the tree.
203 root_node
.CollapseAllUncheck ();
209 public override Color ForeColor
{
210 get { return base.ForeColor; }
211 set { base.ForeColor = value; }
213 [DefaultValue(false)]
214 public bool FullRowSelect
{
215 get { return full_row_select; }
217 if (value == full_row_select
)
219 full_row_select
= value;
224 public bool HideSelection
{
225 get { return hide_selection; }
227 if (hide_selection
== value)
229 hide_selection
= value;
234 [DefaultValue(false)]
235 public bool HotTracking
{
236 get { return hot_tracking; }
237 set { hot_tracking = value; }
242 [RelatedImageList ("ImageList")]
243 [RefreshProperties (RefreshProperties
.Repaint
)]
244 [TypeConverter (typeof (NoneExcludedImageIndexConverter
))]
247 [TypeConverter (typeof (TreeViewImageIndexConverter
))]
249 [Editor("System.Windows.Forms.Design.ImageIndexEditor, " + Consts
.AssemblySystem_Design
, typeof(System
.Drawing
.Design
.UITypeEditor
))]
251 public int ImageIndex
{
252 get { return image_index; }
255 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
256 "'value' must be greater than or equal to 0.");
258 if (image_index
== value)
266 [RefreshProperties (RefreshProperties
.Repaint
)]
269 public ImageList ImageList
{
270 get { return image_list; }
279 get { return indent; }
284 throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
285 "'Indent' must be less than or equal to 32000");
288 throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
289 "'Indent' must be greater than or equal to 0.");
299 public int ItemHeight
{
301 if (item_height
== -1)
302 return FontHeight
+ 3;
306 if (value == item_height
)
313 internal int ActualItemHeight
{
315 int res
= ItemHeight
;
316 if (ImageList
!= null && ImageList
.ImageSize
.Height
> res
)
317 res
= ImageList
.ImageSize
.Height
;
322 [DefaultValue(false)]
323 public bool LabelEdit
{
324 get { return label_edit; }
325 set { label_edit = value; }
328 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
)]
329 [MergableProperty(false)]
331 public TreeNodeCollection Nodes
{
332 get { return nodes; }
337 [EditorBrowsable (EditorBrowsableState
.Never
)]
338 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
339 public new Padding Padding
{
340 get { return base.Padding; }
341 set { base.Padding = value; }
346 public string PathSeparator
{
347 get { return path_separator; }
348 set { path_separator = value; }
353 [DefaultValue (false)]
354 public virtual bool RightToLeftLayout
{
355 get { return right_to_left_layout; }
357 if (right_to_left_layout
!= value) {
358 right_to_left_layout
= value;
359 OnRightToLeftLayoutChanged (EventArgs
.Empty
);
366 public bool Scrollable
{
367 get { return scrollable; }
369 if (scrollable
== value)
372 UpdateScrollBars (false);
378 [RelatedImageList ("ImageList")]
379 [TypeConverter (typeof (NoneExcludedImageIndexConverter
))]
382 [TypeConverter (typeof (TreeViewImageIndexConverter
))]
384 [Editor("System.Windows.Forms.Design.ImageIndexEditor, " + Consts
.AssemblySystem_Design
, typeof(System
.Drawing
.Design
.UITypeEditor
))]
386 public int SelectedImageIndex
{
387 get { return selected_image_index; }
390 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
391 "'value' must be greater than or equal to 0.");
393 UpdateNode (SelectedNode
);
398 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
399 public TreeNode SelectedNode
{
401 if (!IsHandleCreated
)
402 return pre_selected_node
;
403 return selected_node
;
406 if (!IsHandleCreated
) {
407 pre_selected_node
= value;
411 if (selected_node
== value) {
412 selection_action
= TreeViewAction
.Unknown
;
417 TreeViewCancelEventArgs e
= new TreeViewCancelEventArgs (value, false, selection_action
);
424 Rectangle invalid
= Rectangle
.Empty
;
426 if (selected_node
!= null) {
427 invalid
= Bloat (selected_node
.Bounds
);
429 if (focused_node
!= null) {
430 invalid
= Rectangle
.Union (invalid
,
431 Bloat (focused_node
.Bounds
));
435 invalid
= Rectangle
.Union (invalid
, Bloat (value.Bounds
));
437 highlighted_node
= value;
438 selected_node
= value;
439 focused_node
= value;
441 if (full_row_select
|| draw_mode
!= TreeViewDrawMode
.Normal
) {
443 invalid
.Width
= ViewportRectangle
.Width
;
446 if (invalid
!= Rectangle
.Empty
)
447 Invalidate (invalid
);
449 // We ensure its visible after we update because
450 // scrolling is used for insure visible
451 if (selected_node
!= null)
452 selected_node
.EnsureVisible ();
455 OnAfterSelect (new TreeViewEventArgs (value, TreeViewAction
.Unknown
));
457 selection_action
= TreeViewAction
.Unknown
;
461 private Rectangle
Bloat (Rectangle rect
)
471 public bool ShowLines
{
472 get { return show_lines; }
474 if (show_lines
== value)
482 [DefaultValue (false)]
483 public bool ShowNodeToolTips
{
484 get { return show_node_tool_tips; }
485 set { show_node_tool_tips = value; }
490 public bool ShowPlusMinus
{
491 get { return show_plus_minus; }
493 if (show_plus_minus
== value)
495 show_plus_minus
= value;
501 public bool ShowRootLines
{
502 get { return show_root_lines; }
504 if (show_root_lines
== value)
506 show_root_lines
= value;
513 [EditorBrowsable (EditorBrowsableState
.Never
)]
515 [DefaultValue(false)]
517 get { return sorted; }
523 //LAMESPEC: The documentation says that setting this to true should sort alphabetically if TreeViewNodeSorter is set.
524 // There seems to be a bug in the Microsoft implementation.
525 if (sorted
&& tree_view_node_sorter
== null) {
539 [DefaultValue (null)]
540 public ImageList StateImageList
{
541 get { return state_image_list; }
543 state_image_list
= value;
550 [EditorBrowsable(EditorBrowsableState
.Never
)]
552 public override string Text
{
553 get { return base.Text; }
554 set { base.Text = value; }
558 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
559 public TreeNode TopNode
{
561 if (root_node
.FirstNode
== null)
563 OpenTreeNodeEnumerator one
= new OpenTreeNodeEnumerator (root_node
.FirstNode
);
565 for (int i
= 0; i
< skipped_nodes
; i
++)
567 return one
.CurrentNode
;
578 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
579 public IComparer TreeViewNodeSorter
{
581 return tree_view_node_sorter
;
584 tree_view_node_sorter
= value;
585 if (tree_view_node_sorter
!= null) {
587 //LAMESPEC: The documentation says that setting this should set Sorted to false.
588 // There seems to be a bug in the Microsoft implementation.
596 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
597 public int VisibleCount
{
599 return ViewportRectangle
.Height
/ ActualItemHeight
;
604 /// According to MSDN this property has no effect on the treeview
605 [EditorBrowsable (EditorBrowsableState
.Never
)]
606 protected override bool DoubleBuffered
{
607 get { return base.DoubleBuffered; }
608 set { base.DoubleBuffered = value; }
613 [DefaultValue ("Color [Black]")]
620 if (line_color
== Color
.Empty
) {
621 Color res
= ControlPaint
.Dark (BackColor
);
622 if (res
== BackColor
)
623 res
= ControlPaint
.Light (BackColor
);
639 [RelatedImageList ("ImageList")]
640 [RefreshProperties (RefreshProperties
.Repaint
)]
641 [TypeConverter (typeof (ImageKeyConverter
))]
642 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts
.AssemblySystem_Design
, typeof (System
.Drawing
.Design
.UITypeEditor
))]
643 public string ImageKey
{
644 get { return image_key; }
646 if (image_key
== value)
656 [RelatedImageList ("ImageList")]
657 [RefreshProperties (RefreshProperties
.Repaint
)]
658 [TypeConverter (typeof (ImageKeyConverter
))]
659 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts
.AssemblySystem_Design
, typeof (System
.Drawing
.Design
.UITypeEditor
))]
660 public string SelectedImageKey
{
661 get { return selected_image_key; }
663 if (selected_image_key
== value)
665 selected_image_index
= -1;
666 selected_image_key
= value;
667 UpdateNode (SelectedNode
);
674 [EditorBrowsable (EditorBrowsableState
.Never
)]
675 public override ImageLayout BackgroundImageLayout
{
676 get { return base.BackgroundImageLayout; }
677 set { base.BackgroundImageLayout = value; }
682 [DefaultValue (TreeViewDrawMode
.Normal
)]
683 public TreeViewDrawMode DrawMode
{
684 get { return draw_mode; }
685 set { draw_mode = value; }
688 #endregion // Public Instance Properties
690 #region Protected Instance Properties
691 protected override CreateParams CreateParams
{
693 CreateParams cp
= base.CreateParams
;
698 protected override Size DefaultSize
{
699 get { return new Size (121, 97); }
702 #endregion // Protected Instance Properties
704 #region Public Instance Methods
705 public void BeginUpdate ()
710 public void EndUpdate ()
712 if (update_stack
> 1) {
717 RecalculateVisibleOrder (root_node
);
718 UpdateScrollBars (false);
719 // if (SelectedNode != null)
720 // SelectedNode.EnsureVisible ();
721 Invalidate (ViewportRectangle
);
722 update_needed
= false;
730 Sort (Nodes
.Count
>= 2 ? tree_view_node_sorter
: null);
734 void Sort (IComparer sorter
)
738 RecalculateVisibleOrder (root_node
);
739 UpdateScrollBars (false);
743 public void ExpandAll ()
746 root_node
.ExpandAll ();
751 /// Everything below this is basically an emulation of a strange bug on MS
752 /// where they don't always scroll to the last node on ExpandAll
755 if (!IsHandleCreated
)
760 foreach (TreeNode child
in Nodes
) {
761 if (child
.Nodes
.Count
> 0)
768 if (IsHandleCreated
&& vbar
.VisibleInternal
) {
769 vbar
.Value
= vbar
.Maximum
- VisibleCount
+ 1;
771 RecalculateVisibleOrder (root_node
);
772 UpdateScrollBars (true);
773 SetTop (Nodes
[Nodes
.Count
- 1]);
774 SelectedNode
= Nodes
[Nodes
.Count
- 1];
779 public void CollapseAll ()
781 TreeNode walk
= TopNode
;
786 while (walk
.parent
!= root_node
)
790 root_node
.CollapseAll ();
796 public TreeNode
GetNodeAt (Point pt
) {
797 return GetNodeAt (pt
.Y
);
800 public TreeNode
GetNodeAt (int x
, int y
)
802 return GetNodeAt (y
);
805 private TreeNode
GetNodeAtUseX (int x
, int y
) {
806 TreeNode node
= GetNodeAt (y
);
807 if (node
== null || !(IsTextArea (node
, x
) || full_row_select
))
813 public int GetNodeCount (bool includeSubTrees
) {
814 return root_node
.GetNodeCount (includeSubTrees
);
818 public TreeViewHitTestInfo
HitTest (Point pt
)
820 return HitTest (pt
.X
, pt
.Y
);
823 public TreeViewHitTestInfo
HitTest (int x
, int y
)
825 TreeNode n
= GetNodeAt (y
);
828 return new TreeViewHitTestInfo (null, TreeViewHitTestLocations
.None
);
830 if (IsTextArea (n
, x
))
831 return new TreeViewHitTestInfo (n
, TreeViewHitTestLocations
.Label
);
832 else if (IsPlusMinusArea (n
, x
))
833 return new TreeViewHitTestInfo (n
, TreeViewHitTestLocations
.PlusMinus
);
834 else if ((checkboxes
|| n
.StateImage
!= null) && IsCheckboxArea (n
, x
))
835 return new TreeViewHitTestInfo (n
, TreeViewHitTestLocations
.StateImage
);
836 else if (x
> n
.Bounds
.Right
)
837 return new TreeViewHitTestInfo (n
, TreeViewHitTestLocations
.RightOfLabel
);
838 else if (IsImage (n
, x
))
839 return new TreeViewHitTestInfo (n
, TreeViewHitTestLocations
.Image
);
841 return new TreeViewHitTestInfo (null, TreeViewHitTestLocations
.Indent
);
845 public override string ToString () {
846 int count
= Nodes
.Count
;
848 return String
.Concat (base.ToString (), ", Nodes.Count: 0");
849 return String
.Concat (base.ToString (), ", Nodes.Count: ", count
, ", Nodes[0]: ", Nodes
[0]);
853 #endregion // Public Instance Methods
855 #region Protected Instance Methods
856 protected override void CreateHandle () {
857 base.CreateHandle ();
858 RecalculateVisibleOrder (root_node
);
859 UpdateScrollBars (false);
861 if (pre_selected_node
!= null)
862 SelectedNode
= pre_selected_node
;
865 protected override void Dispose (bool disposing
) {
869 base.Dispose (disposing
);
872 protected OwnerDrawPropertyBag
GetItemRenderStyles (TreeNode node
, int state
) {
873 return node
.prop_bag
;
876 protected override bool IsInputKey (Keys keyData
)
878 if (IsHandleCreated
&& (keyData
& Keys
.Alt
) == 0) {
879 switch (keyData
& Keys
.KeyCode
) {
891 if (edit_node
!= null)
897 return base.IsInputKey (keyData
);
900 protected override void OnKeyDown (KeyEventArgs e
)
902 OpenTreeNodeEnumerator ne
;
904 switch (e
.KeyData
& Keys
.KeyCode
) {
906 if (selected_node
!= null && selected_node
.IsExpanded
)
907 selected_node
.Expand ();
910 if (selected_node
!= null && selected_node
.IsExpanded
)
911 selected_node
.Collapse ();
914 if (selected_node
!= null) {
915 if (selected_node
.IsExpanded
&& selected_node
.Nodes
.Count
> 0)
916 selected_node
.Collapse ();
918 TreeNode parent
= selected_node
.Parent
;
919 if (parent
!= null) {
920 selection_action
= TreeViewAction
.ByKeyboard
;
921 SelectedNode
= parent
;
927 if (selected_node
!= null) {
928 if (!selected_node
.IsExpanded
)
929 selected_node
.Expand ();
931 TreeNode child
= selected_node
.FirstNode
;
933 SelectedNode
= child
;
938 if (selected_node
!= null) {
939 ne
= new OpenTreeNodeEnumerator (selected_node
);
940 if (ne
.MovePrevious () && ne
.MovePrevious ()) {
941 selection_action
= TreeViewAction
.ByKeyboard
;
942 SelectedNode
= ne
.CurrentNode
;
947 if (selected_node
!= null) {
948 ne
= new OpenTreeNodeEnumerator (selected_node
);
949 if (ne
.MoveNext () && ne
.MoveNext ()) {
950 selection_action
= TreeViewAction
.ByKeyboard
;
951 SelectedNode
= ne
.CurrentNode
;
956 if (root_node
.Nodes
.Count
> 0) {
957 ne
= new OpenTreeNodeEnumerator (root_node
.Nodes
[0]);
958 if (ne
.MoveNext ()) {
959 selection_action
= TreeViewAction
.ByKeyboard
;
960 SelectedNode
= ne
.CurrentNode
;
965 if (root_node
.Nodes
.Count
> 0) {
966 ne
= new OpenTreeNodeEnumerator (root_node
.Nodes
[0]);
967 while (ne
.MoveNext ())
969 selection_action
= TreeViewAction
.ByKeyboard
;
970 SelectedNode
= ne
.CurrentNode
;
974 if (selected_node
!= null) {
975 ne
= new OpenTreeNodeEnumerator (selected_node
);
976 int move
= VisibleCount
;
977 for (int i
= 0; i
< move
&& ne
.MoveNext (); i
++) {
980 selection_action
= TreeViewAction
.ByKeyboard
;
981 SelectedNode
= ne
.CurrentNode
;
985 if (selected_node
!= null) {
986 ne
= new OpenTreeNodeEnumerator (selected_node
);
987 int move
= VisibleCount
;
988 for (int i
= 0; i
< move
&& ne
.MovePrevious (); i
++)
990 selection_action
= TreeViewAction
.ByKeyboard
;
991 SelectedNode
= ne
.CurrentNode
;
995 if (selected_node
!= null)
996 selected_node
.ExpandAll ();
1001 if (!e
.Handled
&& checkboxes
&&
1002 selected_node
!= null &&
1003 (e
.KeyData
& Keys
.KeyCode
) == Keys
.Space
) {
1004 selected_node
.check_reason
= TreeViewAction
.ByKeyboard
;
1005 selected_node
.Checked
= !selected_node
.Checked
;
1010 protected override void OnKeyPress (KeyPressEventArgs e
)
1012 base.OnKeyPress (e
);
1013 if (e
.KeyChar
== ' ')
1017 protected override void OnKeyUp (KeyEventArgs e
)
1020 if ((e
.KeyData
& Keys
.KeyCode
) == Keys
.Space
)
1025 protected override void OnMouseHover (EventArgs e
)
1027 base.OnMouseHover (e
);
1031 TreeNode tn
= GetNodeAt (PointToClient (MousePosition
));
1034 MouseEnteredItem (tn
);
1037 protected override void OnMouseLeave (EventArgs e
)
1039 base.OnMouseLeave (e
);
1041 is_hovering
= false;
1043 if (tooltip_currently_showing
!= null)
1044 MouseLeftItem (tooltip_currently_showing
);
1047 protected virtual void OnNodeMouseClick (TreeNodeMouseClickEventArgs e
)
1049 TreeNodeMouseClickEventHandler eh
= (TreeNodeMouseClickEventHandler
)(Events
[NodeMouseClickEvent
]);
1054 protected virtual void OnNodeMouseDoubleClick (TreeNodeMouseClickEventArgs e
)
1056 TreeNodeMouseClickEventHandler eh
= (TreeNodeMouseClickEventHandler
)(Events
[NodeMouseDoubleClickEvent
]);
1061 protected virtual void OnNodeMouseHover (TreeNodeMouseHoverEventArgs e
)
1063 TreeNodeMouseHoverEventHandler eh
= (TreeNodeMouseHoverEventHandler
)(Events
[NodeMouseHoverEvent
]);
1069 protected virtual void OnItemDrag (ItemDragEventArgs e
)
1071 ItemDragEventHandler eh
= (ItemDragEventHandler
)(Events
[ItemDragEvent
]);
1077 protected virtual void OnDrawNode(DrawTreeNodeEventArgs e
) {
1078 DrawTreeNodeEventHandler eh
= (DrawTreeNodeEventHandler
)(Events
[DrawNodeEvent
]);
1083 [EditorBrowsable (EditorBrowsableState
.Advanced
)]
1084 protected virtual void OnRightToLeftLayoutChanged (EventArgs e
) {
1085 EventHandler eh
= (EventHandler
)(Events
[RightToLeftLayoutChangedEvent
]);
1091 protected internal virtual void OnAfterCheck (TreeViewEventArgs e
) {
1092 TreeViewEventHandler eh
= (TreeViewEventHandler
)(Events
[AfterCheckEvent
]);
1097 protected internal virtual void OnAfterCollapse (TreeViewEventArgs e
) {
1098 TreeViewEventHandler eh
= (TreeViewEventHandler
)(Events
[AfterCollapseEvent
]);
1103 protected internal virtual void OnAfterExpand (TreeViewEventArgs e
) {
1104 TreeViewEventHandler eh
= (TreeViewEventHandler
)(Events
[AfterExpandEvent
]);
1109 protected virtual void OnAfterLabelEdit (NodeLabelEditEventArgs e
) {
1110 NodeLabelEditEventHandler eh
= (NodeLabelEditEventHandler
)(Events
[AfterLabelEditEvent
]);
1115 protected virtual void OnAfterSelect (TreeViewEventArgs e
) {
1116 TreeViewEventHandler eh
= (TreeViewEventHandler
)(Events
[AfterSelectEvent
]);
1121 protected internal virtual void OnBeforeCheck (TreeViewCancelEventArgs e
) {
1122 TreeViewCancelEventHandler eh
= (TreeViewCancelEventHandler
)(Events
[BeforeCheckEvent
]);
1127 protected internal virtual void OnBeforeCollapse (TreeViewCancelEventArgs e
) {
1128 TreeViewCancelEventHandler eh
= (TreeViewCancelEventHandler
)(Events
[BeforeCollapseEvent
]);
1133 protected internal virtual void OnBeforeExpand (TreeViewCancelEventArgs e
) {
1134 TreeViewCancelEventHandler eh
= (TreeViewCancelEventHandler
)(Events
[BeforeExpandEvent
]);
1139 protected virtual void OnBeforeLabelEdit (NodeLabelEditEventArgs e
) {
1140 NodeLabelEditEventHandler eh
= (NodeLabelEditEventHandler
)(Events
[BeforeLabelEditEvent
]);
1145 protected virtual void OnBeforeSelect (TreeViewCancelEventArgs e
) {
1146 TreeViewCancelEventHandler eh
= (TreeViewCancelEventHandler
)(Events
[BeforeSelectEvent
]);
1151 protected override void OnHandleCreated (EventArgs e
) {
1152 base.OnHandleCreated (e
);
1155 protected override void OnHandleDestroyed (EventArgs e
) {
1156 base.OnHandleDestroyed (e
);
1159 protected override void WndProc(ref Message m
) {
1160 switch ((Msg
) m
.Msg
) {
1162 case Msg
.WM_LBUTTONDBLCLK
:
1163 int val
= m
.LParam
.ToInt32();
1164 DoubleClickHandler (null, new
1165 MouseEventArgs (MouseButtons
.Left
,
1167 (val
>>16) & 0xffff, 0));
1170 case Msg
.WM_CONTEXTMENU
:
1171 if (WmContextMenu (ref m
))
1177 base.WndProc (ref m
);
1180 #endregion // Protected Instance Methods
1182 #region Internal & Private Methods and Properties
1183 internal override bool ScaleChildrenInternal
{
1184 get { return false; }
1187 internal IntPtr
CreateNodeHandle ()
1189 return (IntPtr
) handle_count
++;
1192 // According to MSDN docs, for these to be raised,
1193 // the click must occur over a TreeNode
1194 internal override void HandleClick (int clicks
, MouseEventArgs me
)
1196 if (GetNodeAt (me
.Location
) != null) {
1197 if ((clicks
> 1) && GetStyle (ControlStyles
.StandardDoubleClick
)) {
1199 OnDoubleClick(EventArgs
.Empty
);
1201 OnClick(EventArgs
.Empty
);
1204 OnMouseDoubleClick (me
);
1213 internal override bool IsInputCharInternal (char charCode
)
1218 internal TreeNode
NodeFromHandle (IntPtr handle
)
1220 // This method is called rarely, so instead of maintaining a table
1221 // we just walk the tree nodes to find the matching handle
1222 return NodeFromHandleRecursive (root_node
, handle
);
1225 private TreeNode
NodeFromHandleRecursive (TreeNode node
, IntPtr handle
)
1227 if (node
.handle
== handle
)
1229 foreach (TreeNode child
in node
.Nodes
) {
1230 TreeNode match
= NodeFromHandleRecursive (child
, handle
);
1237 internal Rectangle ViewportRectangle
{
1239 Rectangle res
= ClientRectangle
;
1241 if (vbar
!= null && vbar
.Visible
)
1242 res
.Width
-= vbar
.Width
;
1243 if (hbar
!= null && hbar
.Visible
)
1244 res
.Height
-= hbar
.Height
;
1249 private TreeNode
GetNodeAt (int y
)
1251 if (nodes
.Count
<= 0)
1254 OpenTreeNodeEnumerator o
= new OpenTreeNodeEnumerator (TopNode
);
1255 int move
= y
/ ActualItemHeight
;
1256 for (int i
= -1; i
< move
; i
++) {
1261 return o
.CurrentNode
;
1264 private bool IsTextArea (TreeNode node
, int x
)
1266 return node
!= null && node
.Bounds
.Left
<= x
&& node
.Bounds
.Right
>= x
;
1269 private bool IsSelectableArea (TreeNode node
, int x
)
1273 int l
= node
.Bounds
.Left
;
1274 if (ImageList
!= null)
1275 l
-= ImageList
.ImageSize
.Width
;
1276 return l
<= x
&& node
.Bounds
.Right
>= x
;
1280 private bool IsPlusMinusArea (TreeNode node
, int x
)
1282 if (node
.Nodes
.Count
== 0 || (node
.parent
== root_node
&& !show_root_lines
))
1285 int l
= node
.Bounds
.Left
+ 5;
1287 if (show_root_lines
|| node
.Parent
!= null)
1289 if (ImageList
!= null)
1290 l
-= ImageList
.ImageSize
.Width
+ 3;
1294 // StateImage is basically a custom checkbox
1295 else if (node
.StateImage
!= null)
1298 return (x
> l
&& x
< l
+ 8);
1301 private bool IsCheckboxArea (TreeNode node
, int x
)
1303 int l
= CheckBoxLeft (node
);
1304 return (x
> l
&& x
< l
+ 10);
1308 private bool IsImage (TreeNode node
, int x
)
1310 if (ImageList
== null)
1313 int l
= node
.Bounds
.Left
;
1315 l
-= ImageList
.ImageSize
.Width
+ 5;
1317 if (x
>= l
&& x
<= (l
+ ImageList
.ImageSize
.Width
+ 5))
1324 private int CheckBoxLeft (TreeNode node
)
1326 int l
= node
.Bounds
.Left
+ 5;
1328 if (show_root_lines
|| node
.Parent
!= null)
1331 if (!show_root_lines
&& node
.Parent
== null)
1334 if (ImageList
!= null)
1335 l
-= ImageList
.ImageSize
.Width
+ 3;
1340 internal void RecalculateVisibleOrder (TreeNode start
)
1342 if (update_stack
> 0)
1346 if (start
== null) {
1350 order
= start
.visible_order
;
1354 OpenTreeNodeEnumerator walk
= new OpenTreeNodeEnumerator (start
);
1355 while (walk
.MoveNext ()) {
1356 walk
.CurrentNode
.visible_order
= order
;
1360 max_visible_order
= order
;
1363 internal void SetTop (TreeNode node
)
1367 order
= Math
.Max (0, node
.visible_order
- 1);
1369 if (!vbar
.is_visible
) {
1370 skipped_nodes
= order
;
1374 vbar
.Value
= Math
.Min (order
, vbar
.Maximum
- VisibleCount
+ 1);
1377 internal void SetBottom (TreeNode node
)
1379 if (!vbar
.is_visible
)
1382 OpenTreeNodeEnumerator walk
= new OpenTreeNodeEnumerator (node
);
1384 int bottom
= ViewportRectangle
.Bottom
;
1386 while (walk
.MovePrevious ()) {
1387 if (walk
.CurrentNode
.Bounds
.Bottom
<= bottom
)
1392 int nv
= vbar
.Value
+ offset
;
1393 if (vbar
.Value
+ offset
< vbar
.Maximum
) {
1397 Console
.Error
.WriteLine ("setting bottom to value greater then maximum ({0}, {1})",
1404 internal void UpdateBelow (TreeNode node
)
1406 if (update_stack
> 0) {
1407 update_needed
= true;
1411 if (node
== root_node
) {
1412 Invalidate (ViewportRectangle
);
1416 // We need to update the current node so the plus/minus block gets update too
1417 int top
= Math
.Max (node
.Bounds
.Top
- 1, 0);
1418 Rectangle invalid
= new Rectangle (0, top
,
1419 Width
, Height
- top
);
1420 Invalidate (invalid
);
1423 internal void UpdateNode (TreeNode node
)
1428 if (update_stack
> 0) {
1429 update_needed
= true;
1433 if (node
== root_node
) {
1438 Rectangle invalid
= new Rectangle (0, node
.Bounds
.Top
- 1, Width
,
1439 node
.Bounds
.Height
+ 1);
1440 Invalidate (invalid
);
1443 internal void UpdateNodePlusMinus (TreeNode node
)
1445 if (update_stack
> 0) {
1446 update_needed
= true;
1450 int l
= node
.Bounds
.Left
+ 5;
1452 if (show_root_lines
|| node
.Parent
!= null)
1454 if (ImageList
!= null)
1455 l
-= ImageList
.ImageSize
.Width
+ 3;
1459 Invalidate (new Rectangle (l
, node
.Bounds
.Top
, 8, node
.Bounds
.Height
));
1462 internal override void OnPaintInternal (PaintEventArgs pe
)
1464 Draw (pe
.ClipRectangle
, pe
.Graphics
);
1467 internal void CreateDashPen ()
1469 dash
= new Pen (LineColor
, 1);
1470 dash
.DashStyle
= DashStyle
.Dot
;
1473 private void Draw (Rectangle clip
, Graphics dc
)
1475 dc
.FillRectangle (ThemeEngine
.Current
.ResPool
.GetSolidBrush (BackColor
), clip
);
1480 Rectangle viewport
= ViewportRectangle
;
1481 Rectangle original_clip
= clip
;
1482 if (clip
.Bottom
> viewport
.Bottom
)
1483 clip
.Height
= viewport
.Bottom
- clip
.Top
;
1485 OpenTreeNodeEnumerator walk
= new OpenTreeNodeEnumerator (TopNode
);
1486 while (walk
.MoveNext ()) {
1487 TreeNode current
= walk
.CurrentNode
;
1489 // Haven't gotten to visible nodes yet
1490 if (current
.GetY () + ActualItemHeight
< clip
.Top
)
1493 // Past the visible nodes
1494 if (current
.GetY () > clip
.Bottom
)
1497 DrawTreeNode (current
, dc
, clip
);
1500 if (hbar
.Visible
&& vbar
.Visible
) {
1501 Rectangle corner
= new Rectangle (hbar
.Right
, vbar
.Bottom
, vbar
.Width
, hbar
.Height
);
1502 if (original_clip
.IntersectsWith (corner
))
1503 dc
.FillRectangle (ThemeEngine
.Current
.ResPool
.GetSolidBrush (ThemeEngine
.Current
.ColorControl
),
1509 private void DrawNodeState (TreeNode node
, Graphics dc
, int x
, int y
)
1512 if (StateImageList
.Images
[1] != null)
1513 dc
.DrawImage (StateImageList
.Images
[1], new Rectangle (x
, y
, 16, 16));
1515 if (StateImageList
.Images
[0] != null)
1516 dc
.DrawImage (StateImageList
.Images
[0], new Rectangle (x
, y
, 16, 16));
1521 private void DrawNodeCheckBox (TreeNode node
, Graphics dc
, int x
, int middle
)
1523 Pen pen
= ThemeEngine
.Current
.ResPool
.GetSizedPen(Color
.Black
, 2);
1524 dc
.DrawRectangle (pen
, x
+ 3, middle
- 4, 11, 11);
1527 Pen check_pen
= ThemeEngine
.Current
.ResPool
.GetPen(Color
.Black
);
1532 Rectangle rect
= new Rectangle (x
+ 4, middle
- 3, check_size
, check_size
);
1534 for (int i
= 0; i
< lineWidth
; i
++) {
1535 dc
.DrawLine (check_pen
, rect
.Left
+ 1, rect
.Top
+ lineWidth
+ i
, rect
.Left
+ 3, rect
.Top
+ 5 + i
);
1536 dc
.DrawLine (check_pen
, rect
.Left
+ 3, rect
.Top
+ 5 + i
, rect
.Left
+ 7, rect
.Top
+ 1 + i
);
1541 private void DrawNodeLines (TreeNode node
, Graphics dc
, Rectangle clip
, Pen dash
, int x
, int y
, int middle
)
1546 if (node
.nodes
.Count
> 0 && show_plus_minus
)
1551 if (show_root_lines
|| node
.Parent
!= null)
1552 dc
.DrawLine (dash
, x
- indent
+ ladjust
, middle
, x
+ radjust
, middle
);
1554 if (node
.PrevNode
!= null || node
.Parent
!= null) {
1556 dc
.DrawLine (dash
, x
- indent
+ ladjust
, node
.Bounds
.Top
,
1557 x
- indent
+ ladjust
, middle
- (show_plus_minus
&& node
.Nodes
.Count
> 0 ? 4 : 0));
1560 if (node
.NextNode
!= null) {
1562 dc
.DrawLine (dash
, x
- indent
+ ladjust
, middle
+ (show_plus_minus
&& node
.Nodes
.Count
> 0 ? 4 : 0),
1563 x
- indent
+ ladjust
, node
.Bounds
.Bottom
);
1568 if (show_plus_minus
)
1570 TreeNode parent
= node
.Parent
;
1571 while (parent
!= null) {
1572 if (parent
.NextNode
!= null) {
1573 int px
= parent
.GetLinesX () - indent
+ ladjust
;
1574 dc
.DrawLine (dash
, px
, node
.Bounds
.Top
, px
, node
.Bounds
.Bottom
);
1576 parent
= parent
.Parent
;
1580 private void DrawNodeImage (TreeNode node
, Graphics dc
, Rectangle clip
, int x
, int y
)
1582 // Rectangle r = new Rectangle (x, y + 2, ImageList.ImageSize.Width, ImageList.ImageSize.Height);
1584 if (!RectsIntersect (clip
, x
, y
, ImageList
.ImageSize
.Width
, ImageList
.ImageSize
.Height
))
1587 if (ImageList
== null)
1592 if (!node
.IsSelected
) {
1593 if (node
.ImageIndex
> -1 && node
.ImageIndex
< ImageList
.Images
.Count
) {
1594 use_index
= node
.ImageIndex
;
1595 } else if (ImageIndex
> -1 && ImageIndex
< ImageList
.Images
.Count
) {
1596 use_index
= ImageIndex
;
1599 if (node
.SelectedImageIndex
> -1 && node
.SelectedImageIndex
< ImageList
.Images
.Count
) {
1600 use_index
= node
.SelectedImageIndex
;
1601 } else if (SelectedImageIndex
> -1 && SelectedImageIndex
< ImageList
.Images
.Count
) {
1602 use_index
= SelectedImageIndex
;
1608 if (!node
.IsSelected
) {
1609 if (use_index
== -1 && !String
.IsNullOrEmpty (node
.ImageKey
)) {
1610 use_index
= image_list
.Images
.IndexOfKey (node
.ImageKey
);
1613 if (use_index
== -1 && !String
.IsNullOrEmpty (ImageKey
)) {
1614 use_index
= image_list
.Images
.IndexOfKey (ImageKey
);
1617 if (use_index
== -1 && !String
.IsNullOrEmpty (node
.SelectedImageKey
)) {
1618 use_index
= image_list
.Images
.IndexOfKey (node
.SelectedImageKey
);
1621 if (use_index
== -1 && !String
.IsNullOrEmpty (SelectedImageKey
)) {
1622 use_index
= image_list
.Images
.IndexOfKey (SelectedImageKey
);
1627 if (use_index
== -1 && ImageList
.Images
.Count
> 0) {
1631 if (use_index
!= -1) {
1632 ImageList
.Draw (dc
, x
, y
, ImageList
.ImageSize
.Width
,
1633 ImageList
.ImageSize
.Height
, use_index
);
1637 private void LabelEditFinished (object sender
, EventArgs e
)
1639 EndEdit (edit_node
);
1642 internal void BeginEdit (TreeNode node
)
1644 if (edit_node
!= null)
1645 EndEdit (edit_node
);
1647 if (edit_text_box
== null) {
1648 edit_text_box
= new LabelEditTextBox ();
1649 edit_text_box
.BorderStyle
= BorderStyle
.FixedSingle
;
1650 edit_text_box
.Visible
= false;
1651 edit_text_box
.EditingCancelled
+= new EventHandler (LabelEditCancelled
);
1652 edit_text_box
.EditingFinished
+= new EventHandler (LabelEditFinished
);
1653 edit_text_box
.TextChanged
+= new EventHandler (LabelTextChanged
);
1654 Controls
.Add (edit_text_box
);
1657 node
.EnsureVisible ();
1659 edit_text_box
.Bounds
= node
.Bounds
;
1660 edit_text_box
.Text
= node
.Text
;
1661 edit_text_box
.Visible
= true;
1662 edit_text_box
.Focus ();
1663 edit_text_box
.SelectAll ();
1665 edit_args
= new NodeLabelEditEventArgs (node
);
1666 OnBeforeLabelEdit (edit_args
);
1670 if (edit_args
.CancelEdit
) {
1676 private void LabelEditCancelled (object sender
, EventArgs e
)
1678 edit_args
.SetLabel (null);
1679 EndEdit (edit_node
);
1682 private void LabelTextChanged (object sender
, EventArgs e
)
1684 int width
= TextRenderer
.MeasureTextInternal (edit_text_box
.Text
, edit_text_box
.Font
, false).Width
+ 4;
1685 edit_text_box
.Width
= width
;
1687 if (edit_args
!= null)
1688 edit_args
.SetLabel (edit_text_box
.Text
);
1691 internal void EndEdit (TreeNode node
)
1693 if (edit_text_box
!= null && edit_text_box
.Visible
) {
1694 edit_text_box
.Visible
= false;
1699 // If we get a call to BeginEdit from any AfterLabelEdit handler,
1700 // the focus seems to always remain in the TreeView. This call seems
1701 // to synchronize the focus events - I don't like it but it works
1703 Application
.DoEvents ();
1705 if (edit_node
!= null && edit_node
== node
) {
1708 NodeLabelEditEventArgs e
= new NodeLabelEditEventArgs (edit_args
.Node
, edit_args
.Label
);
1710 OnAfterLabelEdit (e
);
1715 if (e
.Label
!= null)
1716 e
.Node
.Text
= e
.Label
;
1719 // EndEdit ends editing even if not called on the editing node
1724 internal void CancelEdit (TreeNode node
)
1726 edit_args
.SetLabel (null);
1728 if (edit_text_box
!= null && edit_text_box
.Visible
) {
1729 edit_text_box
.Visible
= false;
1737 internal int GetNodeWidth (TreeNode node
)
1739 Font font
= node
.NodeFont
;
1740 if (node
.NodeFont
== null)
1742 return (int)TextRenderer
.MeasureString (node
.Text
, font
, 0, string_format
).Width
+ 3;
1745 private void DrawSelectionAndFocus(TreeNode node
, Graphics dc
, Rectangle r
)
1747 if (Focused
&& focused_node
== node
&& !full_row_select
) {
1748 ControlPaint
.DrawFocusRectangle (dc
, r
, ForeColor
, BackColor
);
1750 if (draw_mode
!= TreeViewDrawMode
.Normal
)
1754 if (Focused
&& node
== highlighted_node
) {
1755 dc
.FillRectangle (ThemeEngine
.Current
.ResPool
.GetSolidBrush (ThemeEngine
.Current
.ColorHighlight
), r
);
1756 } else if (!hide_selection
&& node
== highlighted_node
) {
1757 dc
.FillRectangle (SystemBrushes
.Control
, r
);
1759 dc
.FillRectangle (ThemeEngine
.Current
.ResPool
.GetSolidBrush (node
.BackColor
), r
);
1763 private void DrawStaticNode (TreeNode node
, Graphics dc
)
1765 if (!full_row_select
|| show_lines
)
1766 DrawSelectionAndFocus(node
, dc
, node
.Bounds
);
1769 Font font
= node
.NodeFont
;
1770 if (node
.NodeFont
== null)
1772 Color text_color
= (Focused
&& node
== highlighted_node
?
1773 ThemeEngine
.Current
.ColorHighlightText
: node
.ForeColor
);
1774 if (text_color
.IsEmpty
)
1775 text_color
= ForeColor
;
1776 dc
.DrawString (node
.Text
, font
,
1777 ThemeEngine
.Current
.ResPool
.GetSolidBrush (text_color
),
1778 node
.Bounds
, string_format
);
1781 private void DrawTreeNode (TreeNode node
, Graphics dc
, Rectangle clip
)
1783 int child_count
= node
.nodes
.Count
;
1784 int y
= node
.GetY ();
1785 int middle
= y
+ (ActualItemHeight
/ 2);
1787 if (full_row_select
&& !show_lines
) {
1788 Rectangle r
= new Rectangle (1, y
, ViewportRectangle
.Width
- 2, ActualItemHeight
);
1789 DrawSelectionAndFocus (node
, dc
, r
);
1792 if (draw_mode
== TreeViewDrawMode
.Normal
|| draw_mode
== TreeViewDrawMode
.OwnerDrawText
) {
1793 if ((show_root_lines
|| node
.Parent
!= null) && show_plus_minus
&& child_count
> 0)
1794 ThemeEngine
.Current
.TreeViewDrawNodePlusMinus (this, node
, dc
, node
.GetLinesX () - Indent
+ 5, middle
);
1797 if (checkboxes
&& state_image_list
== null)
1798 DrawNodeCheckBox (node
, dc
, CheckBoxLeft (node
) - 3, middle
);
1800 if (checkboxes
&& state_image_list
!= null)
1801 DrawNodeState (node
, dc
, CheckBoxLeft (node
) - 3, y
);
1803 if (!checkboxes
&& node
.StateImage
!= null)
1804 dc
.DrawImage (node
.StateImage
, new Rectangle (CheckBoxLeft (node
) - 3, y
, 16, 16));
1807 DrawNodeCheckBox (node
, dc
, CheckBoxLeft (node
) - 3, middle
);
1811 DrawNodeLines (node
, dc
, clip
, dash
, node
.GetLinesX (), y
, middle
);
1813 if (ImageList
!= null)
1814 DrawNodeImage (node
, dc
, clip
, node
.GetImageX (), y
);
1819 if (draw_mode
!= TreeViewDrawMode
.Normal
) {
1820 dc
.FillRectangle (Brushes
.White
, node
.Bounds
);
1821 TreeNodeStates tree_node_state
= TreeNodeStates
.Default
;;
1822 if (node
.IsSelected
)
1823 tree_node_state
= TreeNodeStates
.Selected
;
1825 tree_node_state
|= TreeNodeStates
.Checked
;
1826 if (node
== focused_node
)
1827 tree_node_state
|= TreeNodeStates
.Focused
;
1828 Rectangle node_bounds
= node
.Bounds
;
1829 if (draw_mode
== TreeViewDrawMode
.OwnerDrawText
) {
1834 node_bounds
.Width
= Width
;
1837 DrawTreeNodeEventArgs e
= new DrawTreeNodeEventArgs (dc
, node
, node_bounds
, tree_node_state
);
1845 if (!node
.IsEditing
)
1846 DrawStaticNode (node
, dc
);
1849 internal void UpdateScrollBars (bool force
)
1851 if (!force
&& (IsDisposed
|| update_stack
> 0 || !IsHandleCreated
|| !Visible
))
1860 OpenTreeNodeEnumerator walk
= new OpenTreeNodeEnumerator (root_node
);
1862 while (walk
.MoveNext ()) {
1863 int r
= walk
.CurrentNode
.Bounds
.Right
;
1864 int b
= walk
.CurrentNode
.Bounds
.Bottom
;
1872 // Remove scroll adjustments
1873 if (nodes
.Count
> 0)
1874 height
-= nodes
[0].Bounds
.Top
;
1875 width
+= hbar_offset
;
1877 if (height
> ClientRectangle
.Height
) {
1880 if (width
> ClientRectangle
.Width
- SystemInformation
.VerticalScrollBarWidth
)
1882 } else if (width
> ClientRectangle
.Width
) {
1886 if (!vert
&& horz
&& height
> ClientRectangle
.Height
- SystemInformation
.HorizontalScrollBarHeight
)
1891 int visible_height
= horz
? ClientRectangle
.Height
- hbar
.Height
: ClientRectangle
.Height
;
1892 vbar
.SetValues (Math
.Max (0, max_visible_order
- 2), visible_height
/ ActualItemHeight
);
1894 vbar.Maximum = max_visible_order;
1895 vbar.LargeChange = ClientRectangle.Height / ItemHeight;
1898 if (!vbar_bounds_set
) {
1899 vbar
.Bounds
= new Rectangle (ClientRectangle
.Width
- vbar
.Width
, 0, vbar
.Width
,
1900 ClientRectangle
.Height
-
1901 (horz
? SystemInformation
.VerticalScrollBarWidth
: 0));
1902 vbar_bounds_set
= true;
1904 // We need to recalc the hbar if the vbar is now visible
1905 hbar_bounds_set
= false;
1909 vbar
.Visible
= true;
1910 if (skipped_nodes
> 0) {
1911 int skip
= Math
.Min (skipped_nodes
, vbar
.Maximum
- VisibleCount
+ 1);
1913 vbar
.SafeValueSet (skip
);
1914 skipped_nodes
= skip
;
1918 RecalculateVisibleOrder (root_node
);
1919 vbar
.Visible
= false;
1920 vbar_bounds_set
= false;
1924 hbar
.SetValues (width
+ 1, ClientRectangle
.Width
- (vert
? SystemInformation
.VerticalScrollBarWidth
: 0));
1926 hbar.LargeChange = ClientRectangle.Width;
1927 hbar.Maximum = width + 1;
1930 if (!hbar_bounds_set
) {
1931 hbar
.Bounds
= new Rectangle (0, ClientRectangle
.Height
- hbar
.Height
,
1932 ClientRectangle
.Width
- (vert
? SystemInformation
.VerticalScrollBarWidth
: 0),
1934 hbar_bounds_set
= true;
1936 hbar
.Visible
= true;
1939 hbar
.Visible
= false;
1940 hbar_bounds_set
= false;
1944 private void SizeChangedHandler (object sender
, EventArgs e
)
1946 if (IsHandleCreated
) {
1947 if (max_visible_order
== -1)
1948 RecalculateVisibleOrder (root_node
);
1949 UpdateScrollBars (false);
1953 vbar
.Bounds
= new Rectangle (ClientRectangle
.Width
- vbar
.Width
, 0, vbar
.Width
,
1954 ClientRectangle
.Height
- (hbar
.Visible
? SystemInformation
.HorizontalScrollBarHeight
: 0));
1958 hbar
.Bounds
= new Rectangle (0, ClientRectangle
.Height
- hbar
.Height
,
1959 ClientRectangle
.Width
- (vbar
.Visible
? SystemInformation
.VerticalScrollBarWidth
: 0), hbar
.Height
);
1963 private void VScrollBarValueChanged (object sender
, EventArgs e
)
1965 EndEdit (edit_node
);
1967 SetVScrollPos (vbar
.Value
, null);
1970 private void SetVScrollPos (int pos
, TreeNode new_top
)
1975 if (skipped_nodes
== pos
)
1978 int diff
= skipped_nodes
- pos
;
1979 skipped_nodes
= pos
;
1981 if (!IsHandleCreated
)
1984 int y_move
= diff
* ActualItemHeight
;
1985 XplatUI
.ScrollWindow (Handle
, ViewportRectangle
, 0, y_move
, false);
1988 /*private void SetVScrollTop (TreeNode new_top)
1990 vbar.Value = new_top.visible_order - VisibleCount;
1993 private void HScrollBarValueChanged(object sender
, EventArgs e
)
1995 EndEdit (edit_node
);
1997 int old_offset
= hbar_offset
;
1998 hbar_offset
= hbar
.Value
;
2000 if (hbar_offset
< 0) {
2004 XplatUI
.ScrollWindow (Handle
, ViewportRectangle
, old_offset
- hbar_offset
, 0, false);
2007 internal void ExpandBelow (TreeNode node
, int count_to_next
)
2009 if (update_stack
> 0) {
2010 update_needed
= true;
2014 // If node Bottom is less than 0, the node is above and not visible,
2015 // and we need to scroll the entire viewport
2016 int node_bottom
= node
.Bounds
.Bottom
>= 0 ? node
.Bounds
.Bottom
: 0;
2017 Rectangle below
= new Rectangle (0, node_bottom
, ViewportRectangle
.Width
,
2018 ViewportRectangle
.Height
- node_bottom
);
2020 int amount
= count_to_next
* ActualItemHeight
;
2023 XplatUI
.ScrollWindow (Handle
, below
, 0, amount
, false);
2025 if (show_plus_minus
) {
2026 Invalidate (new Rectangle (0, node
.GetY (), Width
, ActualItemHeight
));
2030 internal void CollapseBelow (TreeNode node
, int count_to_next
)
2032 if (update_stack
> 0) {
2033 update_needed
= true;
2037 Rectangle below
= new Rectangle (0, node
.Bounds
.Bottom
, ViewportRectangle
.Width
,
2038 ViewportRectangle
.Height
- node
.Bounds
.Bottom
);
2040 int amount
= count_to_next
* ActualItemHeight
;
2043 XplatUI
.ScrollWindow (Handle
, below
, 0, -amount
, false);
2045 if (show_plus_minus
) {
2046 Invalidate (new Rectangle (0, node
.GetY (), Width
, ActualItemHeight
));
2050 private void MouseWheelHandler(object sender
, MouseEventArgs e
) {
2052 if (vbar
== null || !vbar
.is_visible
) {
2057 vbar
.Value
= Math
.Min(vbar
.Value
+ SystemInformation
.MouseWheelScrollLines
, vbar
.Maximum
- VisibleCount
+ 1);
2059 vbar
.Value
= Math
.Max(0, vbar
.Value
- SystemInformation
.MouseWheelScrollLines
);
2063 private void VisibleChangedHandler (object sender
, EventArgs e
)
2066 UpdateScrollBars (false);
2070 private void FontChangedHandler (object sender
, EventArgs e
)
2072 if (IsHandleCreated
) {
2073 TreeNode top
= TopNode
;
2074 InvalidateNodeWidthRecursive (root_node
);
2080 private void InvalidateNodeWidthRecursive (TreeNode node
)
2082 node
.InvalidateWidth ();
2083 foreach (TreeNode child
in node
.Nodes
) {
2084 InvalidateNodeWidthRecursive (child
);
2088 private void GotFocusHandler (object sender
, EventArgs e
)
2090 if (selected_node
== null) {
2091 if (pre_selected_node
!= null) {
2092 SelectedNode
= pre_selected_node
;
2096 SelectedNode
= TopNode
;
2098 } else if (selected_node
!= null)
2099 UpdateNode (selected_node
);
2102 private void LostFocusHandler (object sender
, EventArgs e
)
2104 UpdateNode (SelectedNode
);
2107 private void MouseDownHandler (object sender
, MouseEventArgs e
)
2109 if (e
.Button
== MouseButtons
.Right
)
2112 TreeNode node
= GetNodeAt (e
.Y
);
2117 mouse_click_node
= node
;
2120 if (show_plus_minus
&& IsPlusMinusArea (node
, e
.X
) && e
.Button
== MouseButtons
.Left
) {
2123 } else if (checkboxes
&& IsCheckboxArea (node
, e
.X
) && e
.Button
== MouseButtons
.Left
) {
2124 node
.check_reason
= TreeViewAction
.ByMouse
;
2125 node
.Checked
= !node
.Checked
;
2128 } else if (IsSelectableArea (node
, e
.X
) || full_row_select
) {
2129 TreeNode old_highlighted
= highlighted_node
;
2130 highlighted_node
= node
;
2131 if (label_edit
&& e
.Clicks
== 1 && highlighted_node
== old_highlighted
&& e
.Button
== MouseButtons
.Left
) {
2133 } else if (highlighted_node
!= focused_node
) {
2134 Size ds
= SystemInformation
.DragSize
;
2135 mouse_rect
.X
= e
.X
- ds
.Width
;
2136 mouse_rect
.Y
= e
.Y
- ds
.Height
;
2137 mouse_rect
.Width
= ds
.Width
* 2;
2138 mouse_rect
.Height
= ds
.Height
* 2;
2140 select_mmove
= true;
2143 Invalidate (highlighted_node
.Bounds
);
2144 if (old_highlighted
!= null)
2145 Invalidate (Bloat (old_highlighted
.Bounds
));
2149 private void MouseUpHandler (object sender
, MouseEventArgs e
) {
2151 TreeNode node
= GetNodeAt (e
.Y
);
2153 if (node
!= null && node
== mouse_click_node
) {
2155 OnNodeMouseDoubleClick (new TreeNodeMouseClickEventArgs (node
, e
.Button
, e
.Clicks
, e
.X
, e
.Y
));
2157 OnNodeMouseClick (new TreeNodeMouseClickEventArgs (node
, e
.Button
, e
.Clicks
, e
.X
, e
.Y
));
2160 mouse_click_node
= null;
2169 select_mmove
= false;
2171 if (e
.Button
== MouseButtons
.Right
&& selected_node
!= null) {
2172 Invalidate (highlighted_node
.Bounds
);
2173 highlighted_node
= selected_node
;
2174 Invalidate (selected_node
.Bounds
);
2178 TreeViewCancelEventArgs ce
= new TreeViewCancelEventArgs (highlighted_node
, false, TreeViewAction
.ByMouse
);
2179 OnBeforeSelect (ce
);
2183 TreeNode prev_focused_node
= focused_node
;
2184 TreeNode prev_highlighted_node
= highlighted_node
;
2186 selected_node
= highlighted_node
;
2187 focused_node
= highlighted_node
;
2188 OnAfterSelect (new TreeViewEventArgs (selected_node
, TreeViewAction
.ByMouse
));
2190 if (prev_focused_node
!= null) {
2191 invalid
= Rectangle
.Union (Bloat (prev_focused_node
.Bounds
),
2192 Bloat (prev_highlighted_node
.Bounds
));
2194 invalid
= Bloat (prev_highlighted_node
.Bounds
);
2198 invalid
.Width
= ViewportRectangle
.Width
;
2200 Invalidate (invalid
);
2202 highlighted_node
= focused_node
;
2203 selected_node
= focused_node
;
2207 private void MouseMoveHandler (object sender
, MouseEventArgs e
) {
2209 // XXX - This should use HitTest and only fire when we are over
2210 // the important parts of a node, not things like gridlines or
2212 TreeNode tn
= GetNodeAt (e
.Location
);
2214 if (tn
!= tooltip_currently_showing
)
2215 MouseLeftItem (tooltip_currently_showing
);
2217 if (tn
!= null && tn
!= tooltip_currently_showing
)
2218 MouseEnteredItem (tn
);
2221 if (e
.Button
== MouseButtons
.Left
|| e
.Button
== MouseButtons
.Right
) {
2222 if (drag_begin_x
== -1 && drag_begin_y
== -1) {
2226 double rise
= Math
.Pow (drag_begin_x
- e
.X
, 2);
2227 double run
= Math
.Pow (drag_begin_y
- e
.Y
, 2);
2228 double move
= Math
.Sqrt (rise
+ run
);
2230 TreeNode drag
= GetNodeAtUseX (e
.X
, e
.Y
);
2233 OnItemDrag (new ItemDragEventArgs (e
.Button
, drag
));
2242 // If there is enough movement before the mouse comes up,
2243 // selection is reverted back to the originally selected node
2244 if (!select_mmove
|| mouse_rect
.Contains (e
.X
, e
.Y
))
2247 Invalidate (highlighted_node
.Bounds
);
2248 Invalidate (selected_node
.Bounds
);
2249 Invalidate (focused_node
.Bounds
);
2251 highlighted_node
= selected_node
;
2252 focused_node
= selected_node
;
2254 select_mmove
= false;
2257 private void DoubleClickHandler (object sender
, MouseEventArgs e
) {
2258 TreeNode node
= GetNodeAtUseX (e
.X
,e
.Y
);
2265 private bool RectsIntersect (Rectangle r
, int left
, int top
, int width
, int height
)
2267 return !((r
.Left
> left
+ width
) || (r
.Right
< left
) ||
2268 (r
.Top
> top
+ height
) || (r
.Bottom
< top
));
2272 // Return true if message was handled, false to send it to base
2273 private bool WmContextMenu (ref Message m
)
2278 pt
= new Point (LowOrder ((int)m
.LParam
.ToInt32 ()), HighOrder ((int)m
.LParam
.ToInt32 ()));
2280 // This means it's a keyboard menu request
2281 if (pt
.X
== -1 || pt
.Y
== -1) {
2287 pt
= new Point (tn
.Bounds
.Left
, tn
.Bounds
.Top
+ (tn
.Bounds
.Height
/ 2));
2289 pt
= PointToClient (pt
);
2291 tn
= GetNodeAt (pt
);
2297 // At this point, we have a valid TreeNode
2298 if (tn
.ContextMenu
!= null) {
2299 tn
.ContextMenu
.Show (this, pt
);
2301 } else if (tn
.ContextMenuStrip
!= null) {
2302 tn
.ContextMenuStrip
.Show (this, pt
);
2306 // The node we found did not have its own menu, let the parent try to display its menu
2311 #region Stuff for ToolTips
2313 private void MouseEnteredItem (TreeNode item
)
2315 tooltip_currently_showing
= item
;
2320 if (ShowNodeToolTips
&& !string.IsNullOrEmpty (tooltip_currently_showing
.ToolTipText
))
2321 ToolTipWindow
.Present (this, tooltip_currently_showing
.ToolTipText
);
2323 OnNodeMouseHover (new TreeNodeMouseHoverEventArgs (tooltip_currently_showing
));
2326 private void MouseLeftItem (TreeNode item
)
2328 ToolTipWindow
.Hide (this);
2329 tooltip_currently_showing
= null;
2332 private ToolTip ToolTipWindow
{
2334 if (tooltip_window
== null)
2335 tooltip_window
= new ToolTip ();
2337 return tooltip_window
;
2343 #endregion // Internal & Private Methods and Properties
2346 static object ItemDragEvent
= new object ();
2347 static object AfterCheckEvent
= new object ();
2348 static object AfterCollapseEvent
= new object ();
2349 static object AfterExpandEvent
= new object ();
2350 static object AfterLabelEditEvent
= new object ();
2351 static object AfterSelectEvent
= new object ();
2352 static object BeforeCheckEvent
= new object ();
2353 static object BeforeCollapseEvent
= new object ();
2354 static object BeforeExpandEvent
= new object ();
2355 static object BeforeLabelEditEvent
= new object ();
2356 static object BeforeSelectEvent
= new object ();
2358 static object DrawNodeEvent
= new object ();
2359 static object NodeMouseClickEvent
= new object ();
2360 static object NodeMouseDoubleClickEvent
= new object();
2361 static object NodeMouseHoverEvent
= new object ();
2362 static object RightToLeftLayoutChangedEvent
= new object ();
2365 public event ItemDragEventHandler ItemDrag
{
2366 add { Events.AddHandler (ItemDragEvent, value); }
2367 remove { Events.RemoveHandler (ItemDragEvent, value); }
2370 public event TreeViewEventHandler AfterCheck
{
2371 add { Events.AddHandler (AfterCheckEvent, value); }
2372 remove { Events.RemoveHandler (AfterCheckEvent, value); }
2375 public event TreeViewEventHandler AfterCollapse
{
2376 add { Events.AddHandler (AfterCollapseEvent, value); }
2377 remove { Events.RemoveHandler (AfterCollapseEvent, value); }
2380 public event TreeViewEventHandler AfterExpand
{
2381 add { Events.AddHandler (AfterExpandEvent, value); }
2382 remove { Events.RemoveHandler (AfterExpandEvent, value); }
2385 public event NodeLabelEditEventHandler AfterLabelEdit
{
2386 add { Events.AddHandler (AfterLabelEditEvent, value); }
2387 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
2390 public event TreeViewEventHandler AfterSelect
{
2391 add { Events.AddHandler (AfterSelectEvent, value); }
2392 remove { Events.RemoveHandler (AfterSelectEvent, value); }
2395 public event TreeViewCancelEventHandler BeforeCheck
{
2396 add { Events.AddHandler (BeforeCheckEvent, value); }
2397 remove { Events.RemoveHandler (BeforeCheckEvent, value); }
2400 public event TreeViewCancelEventHandler BeforeCollapse
{
2401 add { Events.AddHandler (BeforeCollapseEvent, value); }
2402 remove { Events.RemoveHandler (BeforeCollapseEvent, value); }
2405 public event TreeViewCancelEventHandler BeforeExpand
{
2406 add { Events.AddHandler (BeforeExpandEvent, value); }
2407 remove { Events.RemoveHandler (BeforeExpandEvent, value); }
2410 public event NodeLabelEditEventHandler BeforeLabelEdit
{
2411 add { Events.AddHandler (BeforeLabelEditEvent, value); }
2412 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
2415 public event TreeViewCancelEventHandler BeforeSelect
{
2416 add { Events.AddHandler (BeforeSelectEvent, value); }
2417 remove { Events.RemoveHandler (BeforeSelectEvent, value); }
2421 public event DrawTreeNodeEventHandler DrawNode
{
2422 add { Events.AddHandler (DrawNodeEvent, value); }
2423 remove { Events.RemoveHandler (DrawNodeEvent, value); }
2426 public event TreeNodeMouseClickEventHandler NodeMouseClick
{
2427 add { Events.AddHandler (NodeMouseClickEvent, value); }
2428 remove { Events.RemoveHandler (NodeMouseClickEvent, value); }
2432 public event TreeNodeMouseClickEventHandler NodeMouseDoubleClick
{
2433 add { Events.AddHandler (NodeMouseDoubleClickEvent, value); }
2434 remove { Events.RemoveHandler (NodeMouseDoubleClickEvent, value); }
2437 public event TreeNodeMouseHoverEventHandler NodeMouseHover
{
2438 add { Events.AddHandler (NodeMouseHoverEvent, value); }
2439 remove { Events.RemoveHandler (NodeMouseHoverEvent, value); }
2442 public event EventHandler RightToLeftLayoutChanged
{
2443 add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
2444 remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
2449 [EditorBrowsable (EditorBrowsableState
.Never
)]
2450 public new event EventHandler BackgroundImageChanged
{
2451 add { base.BackgroundImageChanged += value; }
2452 remove { base.BackgroundImageChanged -= value; }
2457 [EditorBrowsable (EditorBrowsableState
.Never
)]
2458 public new event EventHandler BackgroundImageLayoutChanged
{
2459 add { base.BackgroundImageLayoutChanged += value; }
2460 remove { base.BackgroundImageLayoutChanged -= value; }
2464 [EditorBrowsable (EditorBrowsableState
.Never
)]
2465 public new event EventHandler PaddingChanged
{
2466 add { base.PaddingChanged += value; }
2467 remove { base.PaddingChanged -= value; }
2471 [EditorBrowsable (EditorBrowsableState
.Never
)]
2473 public new event PaintEventHandler Paint
{
2474 add { base.Paint += value; }
2475 remove { base.Paint -= value; }
2478 [EditorBrowsable (EditorBrowsableState
.Never
)]
2480 public new event EventHandler TextChanged
{
2481 add { base.TextChanged += value; }
2482 remove { base.TextChanged -= value; }
2484 #endregion // Events