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
{
36 [Docking (DockingBehavior
.Ask
)]
37 [ClassInterface (ClassInterfaceType
.AutoDispatch
)]
38 [DefaultProperty("Nodes")]
39 [DefaultEvent("AfterSelect")]
40 [Designer("System.Windows.Forms.Design.TreeViewDesigner, " + Consts
.AssemblySystem_Design
, "System.ComponentModel.Design.IDesigner")]
41 public class TreeView
: Control
{
43 private string path_separator
= "\\";
44 private int item_height
= -1;
46 internal TreeNode root_node
;
47 internal bool nodes_added
;
48 private TreeNodeCollection nodes
;
50 private TreeViewAction selection_action
;
51 internal TreeNode selected_node
;
52 private TreeNode pre_selected_node
;
53 private TreeNode focused_node
;
54 internal TreeNode highlighted_node
;
55 private Rectangle mouse_rect
;
56 private bool select_mmove
;
58 private ImageList image_list
;
59 private int image_index
= -1;
60 private int selected_image_index
= -1;
62 private string image_key
;
63 private bool is_hovering
;
64 private TreeNode mouse_click_node
;
65 private bool right_to_left_layout
;
66 private string selected_image_key
;
67 private bool show_node_tool_tips
;
68 private ImageList state_image_list
;
69 private TreeNode tooltip_currently_showing
;
70 private ToolTip tooltip_window
;
71 private bool full_row_select
;
72 private bool hot_tracking
;
73 private int indent
= 19;
75 private NodeLabelEditEventArgs edit_args
;
76 private LabelEditTextBox edit_text_box
;
77 internal TreeNode edit_node
;
79 private bool checkboxes
;
80 private bool label_edit
;
81 private bool scrollable
= true;
82 private bool show_lines
= true;
83 private bool show_root_lines
= true;
84 private bool show_plus_minus
= true;
85 private bool hide_selection
= true;
87 private int max_visible_order
= -1;
88 internal VScrollBar vbar
;
89 internal HScrollBar hbar
;
90 private bool vbar_bounds_set
;
91 private bool hbar_bounds_set
;
92 internal int skipped_nodes
;
93 internal int hbar_offset
;
95 private int update_stack
;
96 private bool update_needed
;
99 private Color line_color
;
100 private StringFormat string_format
;
102 private int drag_begin_x
= -1;
103 private int drag_begin_y
= -1;
104 private long handle_count
= 1;
106 private TreeViewDrawMode draw_mode
;
108 IComparer tree_view_node_sorter
;
111 #region Public Constructors
114 vbar
= new ImplicitVScrollBar ();
115 hbar
= new ImplicitHScrollBar ();
117 InternalBorderStyle
= BorderStyle
.Fixed3D
;
118 base.background_color
= ThemeEngine
.Current
.ColorWindow
;
119 base.foreground_color
= ThemeEngine
.Current
.ColorWindowText
;
120 draw_mode
= TreeViewDrawMode
.Normal
;
122 root_node
= new TreeNode (this);
123 root_node
.Text
= "ROOT NODE";
124 nodes
= new TreeNodeCollection (root_node
);
125 root_node
.SetNodes (nodes
);
127 MouseDown
+= new MouseEventHandler (MouseDownHandler
);
128 MouseUp
+= new MouseEventHandler(MouseUpHandler
);
129 MouseMove
+= new MouseEventHandler(MouseMoveHandler
);
130 SizeChanged
+= new EventHandler (SizeChangedHandler
);
131 FontChanged
+= new EventHandler (FontChangedHandler
);
132 LostFocus
+= new EventHandler (LostFocusHandler
);
133 GotFocus
+= new EventHandler (GotFocusHandler
);
134 MouseWheel
+= new MouseEventHandler(MouseWheelHandler
);
135 VisibleChanged
+= new EventHandler (VisibleChangedHandler
);
137 SetStyle (ControlStyles
.UserPaint
| ControlStyles
.StandardClick
| ControlStyles
.UseTextForAccessibility
, false);
139 string_format
= new StringFormat ();
140 string_format
.LineAlignment
= StringAlignment
.Center
;
141 string_format
.Alignment
= StringAlignment
.Center
;
143 vbar
.Visible
= false;
144 hbar
.Visible
= false;
145 vbar
.ValueChanged
+= new EventHandler (VScrollBarValueChanged
);
146 hbar
.ValueChanged
+= new EventHandler (HScrollBarValueChanged
);
149 Controls
.AddImplicit (vbar
);
150 Controls
.AddImplicit (hbar
);
153 #endregion // Public Constructors
155 #region Public Instance Properties
156 public override Color BackColor
{
157 get { return base.BackColor;}
159 base.BackColor
= value;
168 [EditorBrowsable(EditorBrowsableState
.Never
)]
169 public override Image BackgroundImage
{
170 get { return base.BackgroundImage; }
171 set { base.BackgroundImage = value; }
174 [DefaultValue(BorderStyle
.Fixed3D
)]
176 public BorderStyle BorderStyle
{
177 get { return InternalBorderStyle; }
178 set { InternalBorderStyle = value; }
181 [DefaultValue(false)]
182 public bool CheckBoxes
{
183 get { return checkboxes; }
185 if (value == checkboxes
)
189 // Match a "bug" in the MS implementation where disabling checkboxes
190 // collapses the entire tree, but enabling them does not affect the
191 // state of the tree.
193 root_node
.CollapseAllUncheck ();
197 // UIA Framework Event: CheckBoxes Changed
198 OnUIACheckBoxesChanged (EventArgs
.Empty
);
202 public override Color ForeColor
{
203 get { return base.ForeColor; }
204 set { base.ForeColor = value; }
206 [DefaultValue(false)]
207 public bool FullRowSelect
{
208 get { return full_row_select; }
210 if (value == full_row_select
)
212 full_row_select
= value;
217 public bool HideSelection
{
218 get { return hide_selection; }
220 if (hide_selection
== value)
222 hide_selection
= value;
227 [DefaultValue(false)]
228 public bool HotTracking
{
229 get { return hot_tracking; }
230 set { hot_tracking = value; }
234 [RelatedImageList ("ImageList")]
235 [RefreshProperties (RefreshProperties
.Repaint
)]
236 [TypeConverter (typeof (NoneExcludedImageIndexConverter
))]
237 [Editor("System.Windows.Forms.Design.ImageIndexEditor, " + Consts
.AssemblySystem_Design
, typeof(System
.Drawing
.Design
.UITypeEditor
))]
239 public int ImageIndex
{
240 get { return image_index; }
243 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
244 "'value' must be greater than or equal to 0.");
246 if (image_index
== value)
253 [RefreshProperties (RefreshProperties
.Repaint
)]
255 public ImageList ImageList
{
256 get { return image_list; }
265 get { return indent; }
270 throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
271 "'Indent' must be less than or equal to 32000");
274 throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
275 "'Indent' must be greater than or equal to 0.");
282 public int ItemHeight
{
284 if (item_height
== -1)
285 return FontHeight
+ 3;
289 if (value == item_height
)
296 internal int ActualItemHeight
{
298 int res
= ItemHeight
;
299 if (ImageList
!= null && ImageList
.ImageSize
.Height
> res
)
300 res
= ImageList
.ImageSize
.Height
;
305 [DefaultValue(false)]
306 public bool LabelEdit
{
307 get { return label_edit; }
311 // UIA Framework Event: LabelEdit Changed
312 OnUIALabelEditChanged (EventArgs
.Empty
);
316 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Content
)]
317 [MergableProperty(false)]
319 public TreeNodeCollection Nodes
{
320 get { return nodes; }
324 [EditorBrowsable (EditorBrowsableState
.Never
)]
325 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
326 public new Padding Padding
{
327 get { return base.Padding; }
328 set { base.Padding = value; }
332 public string PathSeparator
{
333 get { return path_separator; }
334 set { path_separator = value; }
338 [DefaultValue (false)]
339 public virtual bool RightToLeftLayout
{
340 get { return right_to_left_layout; }
342 if (right_to_left_layout
!= value) {
343 right_to_left_layout
= value;
344 OnRightToLeftLayoutChanged (EventArgs
.Empty
);
350 public bool Scrollable
{
351 get { return scrollable; }
353 if (scrollable
== value)
356 UpdateScrollBars (false);
361 [RelatedImageList ("ImageList")]
362 [TypeConverter (typeof (NoneExcludedImageIndexConverter
))]
363 [Editor("System.Windows.Forms.Design.ImageIndexEditor, " + Consts
.AssemblySystem_Design
, typeof(System
.Drawing
.Design
.UITypeEditor
))]
365 public int SelectedImageIndex
{
366 get { return selected_image_index; }
369 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
370 "'value' must be greater than or equal to 0.");
372 UpdateNode (SelectedNode
);
377 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
378 public TreeNode SelectedNode
{
380 if (!IsHandleCreated
)
381 return pre_selected_node
;
382 return selected_node
;
385 if (!IsHandleCreated
) {
386 pre_selected_node
= value;
390 if (selected_node
== value) {
391 selection_action
= TreeViewAction
.Unknown
;
396 TreeViewCancelEventArgs e
= new TreeViewCancelEventArgs (value, false, selection_action
);
403 Rectangle invalid
= Rectangle
.Empty
;
405 if (selected_node
!= null) {
406 invalid
= Bloat (selected_node
.Bounds
);
408 if (focused_node
!= null) {
409 invalid
= Rectangle
.Union (invalid
,
410 Bloat (focused_node
.Bounds
));
414 invalid
= Rectangle
.Union (invalid
, Bloat (value.Bounds
));
416 highlighted_node
= value;
417 selected_node
= value;
418 focused_node
= value;
420 if (full_row_select
|| draw_mode
!= TreeViewDrawMode
.Normal
) {
422 invalid
.Width
= ViewportRectangle
.Width
;
425 if (invalid
!= Rectangle
.Empty
)
426 Invalidate (invalid
);
428 // We ensure its visible after we update because
429 // scrolling is used for insure visible
430 if (selected_node
!= null)
431 selected_node
.EnsureVisible ();
434 OnAfterSelect (new TreeViewEventArgs (value, TreeViewAction
.Unknown
));
436 selection_action
= TreeViewAction
.Unknown
;
440 private Rectangle
Bloat (Rectangle rect
)
450 public bool ShowLines
{
451 get { return show_lines; }
453 if (show_lines
== value)
460 [DefaultValue (false)]
461 public bool ShowNodeToolTips
{
462 get { return show_node_tool_tips; }
463 set { show_node_tool_tips = value; }
467 public bool ShowPlusMinus
{
468 get { return show_plus_minus; }
470 if (show_plus_minus
== value)
472 show_plus_minus
= value;
478 public bool ShowRootLines
{
479 get { return show_root_lines; }
481 if (show_root_lines
== value)
483 show_root_lines
= value;
489 [EditorBrowsable (EditorBrowsableState
.Never
)]
490 [DefaultValue(false)]
492 get { return sorted; }
497 //LAMESPEC: The documentation says that setting this to true should sort alphabetically if TreeViewNodeSorter is set.
498 // There seems to be a bug in the Microsoft implementation.
499 if (sorted
&& tree_view_node_sorter
== null) {
505 [DefaultValue (null)]
506 public ImageList StateImageList
{
507 get { return state_image_list; }
509 state_image_list
= value;
515 [EditorBrowsable(EditorBrowsableState
.Never
)]
517 public override string Text
{
518 get { return base.Text; }
519 set { base.Text = value; }
523 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
524 public TreeNode TopNode
{
526 if (root_node
.FirstNode
== null)
528 OpenTreeNodeEnumerator one
= new OpenTreeNodeEnumerator (root_node
.FirstNode
);
530 for (int i
= 0; i
< skipped_nodes
; i
++)
532 return one
.CurrentNode
;
540 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
541 public IComparer TreeViewNodeSorter
{
543 return tree_view_node_sorter
;
546 tree_view_node_sorter
= value;
547 if (tree_view_node_sorter
!= null) {
549 //LAMESPEC: The documentation says that setting this should set Sorted to false.
550 // There seems to be a bug in the Microsoft implementation.
557 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
558 public int VisibleCount
{
560 return ViewportRectangle
.Height
/ ActualItemHeight
;
564 /// According to MSDN this property has no effect on the treeview
565 [EditorBrowsable (EditorBrowsableState
.Never
)]
566 protected override bool DoubleBuffered
{
567 get { return base.DoubleBuffered; }
568 set { base.DoubleBuffered = value; }
571 [DefaultValue ("Color [Black]")]
572 public Color LineColor
{
574 if (line_color
== Color
.Empty
) {
575 Color res
= ControlPaint
.Dark (BackColor
);
576 if (res
== BackColor
)
577 res
= ControlPaint
.Light (BackColor
);
593 [RelatedImageList ("ImageList")]
594 [RefreshProperties (RefreshProperties
.Repaint
)]
595 [TypeConverter (typeof (ImageKeyConverter
))]
596 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts
.AssemblySystem_Design
, typeof (System
.Drawing
.Design
.UITypeEditor
))]
597 public string ImageKey
{
598 get { return image_key; }
600 if (image_key
== value)
610 [RelatedImageList ("ImageList")]
611 [RefreshProperties (RefreshProperties
.Repaint
)]
612 [TypeConverter (typeof (ImageKeyConverter
))]
613 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts
.AssemblySystem_Design
, typeof (System
.Drawing
.Design
.UITypeEditor
))]
614 public string SelectedImageKey
{
615 get { return selected_image_key; }
617 if (selected_image_key
== value)
619 selected_image_index
= -1;
620 selected_image_key
= value;
621 UpdateNode (SelectedNode
);
626 [EditorBrowsable (EditorBrowsableState
.Never
)]
627 public override ImageLayout BackgroundImageLayout
{
628 get { return base.BackgroundImageLayout; }
629 set { base.BackgroundImageLayout = value; }
632 [DefaultValue (TreeViewDrawMode
.Normal
)]
633 public TreeViewDrawMode DrawMode
{
634 get { return draw_mode; }
635 set { draw_mode = value; }
637 #endregion // Public Instance Properties
639 #region UIA Framework Properties
640 internal ScrollBar UIAHScrollBar
{
644 internal ScrollBar UIAVScrollBar
{
647 #endregion // UIA Framework Properties
649 #region Protected Instance Properties
650 protected override CreateParams CreateParams
{
652 CreateParams cp
= base.CreateParams
;
657 protected override Size DefaultSize
{
658 get { return new Size (121, 97); }
661 #endregion // Protected Instance Properties
663 #region Public Instance Methods
664 public void BeginUpdate ()
669 public void EndUpdate ()
671 if (update_stack
> 1) {
676 RecalculateVisibleOrder (root_node
);
677 UpdateScrollBars (false);
678 // if (SelectedNode != null)
679 // SelectedNode.EnsureVisible ();
680 Invalidate (ViewportRectangle
);
681 update_needed
= false;
688 Sort (tree_view_node_sorter
);
691 void Sort (IComparer sorter
)
695 RecalculateVisibleOrder (root_node
);
696 UpdateScrollBars (false);
700 void SetVScrollValue (int value)
702 if (value > vbar
.Maximum
)
703 value = vbar
.Maximum
;
704 else if (value < vbar
.Minimum
)
705 value = vbar
.Minimum
;
710 public void ExpandAll ()
713 root_node
.ExpandAll ();
718 /// Everything below this is basically an emulation of a strange bug on MS
719 /// where they don't always scroll to the last node on ExpandAll
721 if (!IsHandleCreated
)
725 foreach (TreeNode child
in Nodes
) {
726 if (child
.Nodes
.Count
> 0)
733 if (IsHandleCreated
&& vbar
.VisibleInternal
) {
734 SetVScrollValue (vbar
.Maximum
- VisibleCount
+ 1);
736 RecalculateVisibleOrder (root_node
);
737 UpdateScrollBars (true);
739 // Only move the top node if we now have a scrollbar
740 if (vbar
.VisibleInternal
) {
741 SetTop (Nodes
[Nodes
.Count
- 1]);
742 SelectedNode
= Nodes
[Nodes
.Count
- 1];
747 public void CollapseAll ()
750 root_node
.CollapseAll ();
753 if (vbar
.VisibleInternal
)
754 SetVScrollValue (vbar
.Maximum
- VisibleCount
+ 1);
757 public TreeNode
GetNodeAt (Point pt
) {
758 return GetNodeAt (pt
.Y
);
761 public TreeNode
GetNodeAt (int x
, int y
)
763 return GetNodeAt (y
);
766 private TreeNode
GetNodeAtUseX (int x
, int y
) {
767 TreeNode node
= GetNodeAt (y
);
768 if (node
== null || !(IsTextArea (node
, x
) || full_row_select
))
774 public int GetNodeCount (bool includeSubTrees
) {
775 return root_node
.GetNodeCount (includeSubTrees
);
778 public TreeViewHitTestInfo
HitTest (Point pt
)
780 return HitTest (pt
.X
, pt
.Y
);
783 public TreeViewHitTestInfo
HitTest (int x
, int y
)
785 TreeNode n
= GetNodeAt (y
);
788 return new TreeViewHitTestInfo (null, TreeViewHitTestLocations
.None
);
790 if (IsTextArea (n
, x
))
791 return new TreeViewHitTestInfo (n
, TreeViewHitTestLocations
.Label
);
792 else if (IsPlusMinusArea (n
, x
))
793 return new TreeViewHitTestInfo (n
, TreeViewHitTestLocations
.PlusMinus
);
794 else if ((checkboxes
|| n
.StateImage
!= null) && IsCheckboxArea (n
, x
))
795 return new TreeViewHitTestInfo (n
, TreeViewHitTestLocations
.StateImage
);
796 else if (x
> n
.Bounds
.Right
)
797 return new TreeViewHitTestInfo (n
, TreeViewHitTestLocations
.RightOfLabel
);
798 else if (IsImage (n
, x
))
799 return new TreeViewHitTestInfo (n
, TreeViewHitTestLocations
.Image
);
801 return new TreeViewHitTestInfo (null, TreeViewHitTestLocations
.Indent
);
804 public override string ToString () {
805 int count
= Nodes
.Count
;
807 return String
.Concat (base.ToString (), ", Nodes.Count: 0");
808 return String
.Concat (base.ToString (), ", Nodes.Count: ", count
, ", Nodes[0]: ", Nodes
[0]);
811 #endregion // Public Instance Methods
813 #region Protected Instance Methods
814 protected override void CreateHandle () {
815 base.CreateHandle ();
816 RecalculateVisibleOrder (root_node
);
817 UpdateScrollBars (false);
819 if (pre_selected_node
!= null)
820 SelectedNode
= pre_selected_node
;
823 protected override void Dispose (bool disposing
) {
828 if (tooltip_window
!= null)
829 tooltip_window
.Dispose();
832 base.Dispose (disposing
);
835 protected OwnerDrawPropertyBag
GetItemRenderStyles (TreeNode node
, int state
) {
836 return node
.prop_bag
;
839 protected override bool IsInputKey (Keys keyData
)
841 if (IsHandleCreated
&& (keyData
& Keys
.Alt
) == 0) {
842 switch (keyData
& Keys
.KeyCode
) {
854 if (edit_node
!= null)
860 return base.IsInputKey (keyData
);
863 protected override void OnKeyDown (KeyEventArgs e
)
865 OpenTreeNodeEnumerator ne
;
867 switch (e
.KeyData
& Keys
.KeyCode
) {
869 if (selected_node
!= null && selected_node
.IsExpanded
)
870 selected_node
.Expand ();
873 if (selected_node
!= null && selected_node
.IsExpanded
)
874 selected_node
.Collapse ();
877 if (selected_node
!= null) {
878 if (selected_node
.IsExpanded
&& selected_node
.Nodes
.Count
> 0)
879 selected_node
.Collapse ();
881 TreeNode parent
= selected_node
.Parent
;
882 if (parent
!= null) {
883 selection_action
= TreeViewAction
.ByKeyboard
;
884 SelectedNode
= parent
;
890 if (selected_node
!= null) {
891 if (!selected_node
.IsExpanded
)
892 selected_node
.Expand ();
894 TreeNode child
= selected_node
.FirstNode
;
896 SelectedNode
= child
;
901 if (selected_node
!= null) {
902 ne
= new OpenTreeNodeEnumerator (selected_node
);
903 if (ne
.MovePrevious () && ne
.MovePrevious ()) {
904 selection_action
= TreeViewAction
.ByKeyboard
;
905 SelectedNode
= ne
.CurrentNode
;
910 if (selected_node
!= null) {
911 ne
= new OpenTreeNodeEnumerator (selected_node
);
912 if (ne
.MoveNext () && ne
.MoveNext ()) {
913 selection_action
= TreeViewAction
.ByKeyboard
;
914 SelectedNode
= ne
.CurrentNode
;
919 if (root_node
.Nodes
.Count
> 0) {
920 ne
= new OpenTreeNodeEnumerator (root_node
.Nodes
[0]);
921 if (ne
.MoveNext ()) {
922 selection_action
= TreeViewAction
.ByKeyboard
;
923 SelectedNode
= ne
.CurrentNode
;
928 if (root_node
.Nodes
.Count
> 0) {
929 ne
= new OpenTreeNodeEnumerator (root_node
.Nodes
[0]);
930 while (ne
.MoveNext ())
932 selection_action
= TreeViewAction
.ByKeyboard
;
933 SelectedNode
= ne
.CurrentNode
;
937 if (selected_node
!= null) {
938 ne
= new OpenTreeNodeEnumerator (selected_node
);
939 int move
= VisibleCount
;
940 for (int i
= 0; i
< move
&& ne
.MoveNext (); i
++) {
943 selection_action
= TreeViewAction
.ByKeyboard
;
944 SelectedNode
= ne
.CurrentNode
;
948 if (selected_node
!= null) {
949 ne
= new OpenTreeNodeEnumerator (selected_node
);
950 int move
= VisibleCount
;
951 for (int i
= 0; i
< move
&& ne
.MovePrevious (); i
++)
953 selection_action
= TreeViewAction
.ByKeyboard
;
954 SelectedNode
= ne
.CurrentNode
;
958 if (selected_node
!= null)
959 selected_node
.ExpandAll ();
964 if (!e
.Handled
&& checkboxes
&&
965 selected_node
!= null &&
966 (e
.KeyData
& Keys
.KeyCode
) == Keys
.Space
) {
967 selected_node
.check_reason
= TreeViewAction
.ByKeyboard
;
968 selected_node
.Checked
= !selected_node
.Checked
;
973 protected override void OnKeyPress (KeyPressEventArgs e
)
976 if (e
.KeyChar
== ' ')
980 protected override void OnKeyUp (KeyEventArgs e
)
983 if ((e
.KeyData
& Keys
.KeyCode
) == Keys
.Space
)
987 protected override void OnMouseHover (EventArgs e
)
989 base.OnMouseHover (e
);
993 TreeNode tn
= GetNodeAt (PointToClient (MousePosition
));
996 MouseEnteredItem (tn
);
999 protected override void OnMouseLeave (EventArgs e
)
1001 base.OnMouseLeave (e
);
1003 is_hovering
= false;
1005 if (tooltip_currently_showing
!= null)
1006 MouseLeftItem (tooltip_currently_showing
);
1009 protected virtual void OnNodeMouseClick (TreeNodeMouseClickEventArgs e
)
1011 TreeNodeMouseClickEventHandler eh
= (TreeNodeMouseClickEventHandler
)(Events
[NodeMouseClickEvent
]);
1016 protected virtual void OnNodeMouseDoubleClick (TreeNodeMouseClickEventArgs e
)
1018 TreeNodeMouseClickEventHandler eh
= (TreeNodeMouseClickEventHandler
)(Events
[NodeMouseDoubleClickEvent
]);
1023 protected virtual void OnNodeMouseHover (TreeNodeMouseHoverEventArgs e
)
1025 TreeNodeMouseHoverEventHandler eh
= (TreeNodeMouseHoverEventHandler
)(Events
[NodeMouseHoverEvent
]);
1030 protected virtual void OnItemDrag (ItemDragEventArgs e
)
1032 ItemDragEventHandler eh
= (ItemDragEventHandler
)(Events
[ItemDragEvent
]);
1037 protected virtual void OnDrawNode(DrawTreeNodeEventArgs e
) {
1038 DrawTreeNodeEventHandler eh
= (DrawTreeNodeEventHandler
)(Events
[DrawNodeEvent
]);
1043 [EditorBrowsable (EditorBrowsableState
.Advanced
)]
1044 protected virtual void OnRightToLeftLayoutChanged (EventArgs e
) {
1045 EventHandler eh
= (EventHandler
)(Events
[RightToLeftLayoutChangedEvent
]);
1050 protected internal virtual void OnAfterCheck (TreeViewEventArgs e
) {
1051 TreeViewEventHandler eh
= (TreeViewEventHandler
)(Events
[AfterCheckEvent
]);
1056 protected internal virtual void OnAfterCollapse (TreeViewEventArgs e
) {
1057 TreeViewEventHandler eh
= (TreeViewEventHandler
)(Events
[AfterCollapseEvent
]);
1062 protected internal virtual void OnAfterExpand (TreeViewEventArgs e
) {
1063 TreeViewEventHandler eh
= (TreeViewEventHandler
)(Events
[AfterExpandEvent
]);
1068 protected virtual void OnAfterLabelEdit (NodeLabelEditEventArgs e
) {
1069 NodeLabelEditEventHandler eh
= (NodeLabelEditEventHandler
)(Events
[AfterLabelEditEvent
]);
1074 protected virtual void OnAfterSelect (TreeViewEventArgs e
) {
1075 TreeViewEventHandler eh
= (TreeViewEventHandler
)(Events
[AfterSelectEvent
]);
1080 protected internal virtual void OnBeforeCheck (TreeViewCancelEventArgs e
) {
1081 TreeViewCancelEventHandler eh
= (TreeViewCancelEventHandler
)(Events
[BeforeCheckEvent
]);
1086 protected internal virtual void OnBeforeCollapse (TreeViewCancelEventArgs e
) {
1087 TreeViewCancelEventHandler eh
= (TreeViewCancelEventHandler
)(Events
[BeforeCollapseEvent
]);
1092 protected internal virtual void OnBeforeExpand (TreeViewCancelEventArgs e
) {
1093 TreeViewCancelEventHandler eh
= (TreeViewCancelEventHandler
)(Events
[BeforeExpandEvent
]);
1098 protected virtual void OnBeforeLabelEdit (NodeLabelEditEventArgs e
) {
1099 NodeLabelEditEventHandler eh
= (NodeLabelEditEventHandler
)(Events
[BeforeLabelEditEvent
]);
1104 protected virtual void OnBeforeSelect (TreeViewCancelEventArgs e
) {
1105 TreeViewCancelEventHandler eh
= (TreeViewCancelEventHandler
)(Events
[BeforeSelectEvent
]);
1110 protected override void OnHandleCreated (EventArgs e
) {
1111 base.OnHandleCreated (e
);
1114 protected override void OnHandleDestroyed (EventArgs e
) {
1115 base.OnHandleDestroyed (e
);
1118 protected override void WndProc(ref Message m
) {
1119 switch ((Msg
) m
.Msg
) {
1121 case Msg
.WM_LBUTTONDBLCLK
:
1122 int val
= m
.LParam
.ToInt32();
1123 DoubleClickHandler (null, new
1124 MouseEventArgs (MouseButtons
.Left
,
1126 (val
>>16) & 0xffff, 0));
1128 case Msg
.WM_CONTEXTMENU
:
1129 if (WmContextMenu (ref m
))
1134 base.WndProc (ref m
);
1137 #endregion // Protected Instance Methods
1139 #region Internal & Private Methods and Properties
1140 internal override bool ScaleChildrenInternal
{
1141 get { return false; }
1144 internal IntPtr
CreateNodeHandle ()
1146 return (IntPtr
) handle_count
++;
1149 // According to MSDN docs, for these to be raised,
1150 // the click must occur over a TreeNode
1151 internal override void HandleClick (int clicks
, MouseEventArgs me
)
1153 if (GetNodeAt (me
.Location
) != null) {
1154 if ((clicks
> 1) && GetStyle (ControlStyles
.StandardDoubleClick
)) {
1156 OnMouseDoubleClick (me
);
1164 internal override bool IsInputCharInternal (char charCode
)
1169 internal TreeNode
NodeFromHandle (IntPtr handle
)
1171 // This method is called rarely, so instead of maintaining a table
1172 // we just walk the tree nodes to find the matching handle
1173 return NodeFromHandleRecursive (root_node
, handle
);
1176 private TreeNode
NodeFromHandleRecursive (TreeNode node
, IntPtr handle
)
1178 if (node
.handle
== handle
)
1180 foreach (TreeNode child
in node
.Nodes
) {
1181 TreeNode match
= NodeFromHandleRecursive (child
, handle
);
1188 internal Rectangle ViewportRectangle
{
1190 Rectangle res
= ClientRectangle
;
1192 if (vbar
!= null && vbar
.Visible
)
1193 res
.Width
-= vbar
.Width
;
1194 if (hbar
!= null && hbar
.Visible
)
1195 res
.Height
-= hbar
.Height
;
1200 private TreeNode
GetNodeAt (int y
)
1202 if (nodes
.Count
<= 0)
1205 OpenTreeNodeEnumerator o
= new OpenTreeNodeEnumerator (TopNode
);
1206 int move
= y
/ ActualItemHeight
;
1207 for (int i
= -1; i
< move
; i
++) {
1212 return o
.CurrentNode
;
1215 private bool IsTextArea (TreeNode node
, int x
)
1217 return node
!= null && node
.Bounds
.Left
<= x
&& node
.Bounds
.Right
>= x
;
1220 private bool IsSelectableArea (TreeNode node
, int x
)
1224 int l
= node
.Bounds
.Left
;
1225 if (ImageList
!= null)
1226 l
-= ImageList
.ImageSize
.Width
;
1227 return l
<= x
&& node
.Bounds
.Right
>= x
;
1231 private bool IsPlusMinusArea (TreeNode node
, int x
)
1233 if (node
.Nodes
.Count
== 0 || (node
.parent
== root_node
&& !show_root_lines
))
1236 int l
= node
.Bounds
.Left
+ 5;
1238 if (show_root_lines
|| node
.Parent
!= null)
1240 if (ImageList
!= null)
1241 l
-= ImageList
.ImageSize
.Width
+ 3;
1244 // StateImage is basically a custom checkbox
1245 else if (node
.StateImage
!= null)
1247 return (x
> l
&& x
< l
+ 8);
1250 private bool IsCheckboxArea (TreeNode node
, int x
)
1252 int l
= CheckBoxLeft (node
);
1253 return (x
> l
&& x
< l
+ 10);
1256 private bool IsImage (TreeNode node
, int x
)
1258 if (ImageList
== null)
1261 int l
= node
.Bounds
.Left
;
1263 l
-= ImageList
.ImageSize
.Width
+ 5;
1265 if (x
>= l
&& x
<= (l
+ ImageList
.ImageSize
.Width
+ 5))
1271 private int CheckBoxLeft (TreeNode node
)
1273 int l
= node
.Bounds
.Left
+ 5;
1275 if (show_root_lines
|| node
.Parent
!= null)
1278 if (!show_root_lines
&& node
.Parent
== null)
1281 if (ImageList
!= null)
1282 l
-= ImageList
.ImageSize
.Width
+ 3;
1287 internal void RecalculateVisibleOrder (TreeNode start
)
1289 if (update_stack
> 0)
1293 if (start
== null) {
1297 order
= start
.visible_order
;
1301 OpenTreeNodeEnumerator walk
= new OpenTreeNodeEnumerator (start
);
1302 while (walk
.MoveNext ()) {
1303 walk
.CurrentNode
.visible_order
= order
;
1307 max_visible_order
= order
;
1310 internal void SetTop (TreeNode node
)
1314 order
= Math
.Max (0, node
.visible_order
- 1);
1316 if (!vbar
.is_visible
) {
1317 skipped_nodes
= order
;
1321 SetVScrollValue (Math
.Min (order
, vbar
.Maximum
- VisibleCount
+ 1));
1324 internal void SetBottom (TreeNode node
)
1326 if (!vbar
.is_visible
)
1329 OpenTreeNodeEnumerator walk
= new OpenTreeNodeEnumerator (node
);
1331 int bottom
= ViewportRectangle
.Bottom
;
1333 while (walk
.MovePrevious ()) {
1334 if (walk
.CurrentNode
.Bounds
.Bottom
<= bottom
)
1339 int nv
= vbar
.Value
+ offset
;
1340 if (vbar
.Value
+ offset
< vbar
.Maximum
) {
1341 SetVScrollValue (nv
);
1344 Console
.Error
.WriteLine ("setting bottom to value greater then maximum ({0}, {1})",
1351 internal void UpdateBelow (TreeNode node
)
1353 if (update_stack
> 0) {
1354 update_needed
= true;
1358 if (node
== root_node
) {
1359 Invalidate (ViewportRectangle
);
1363 // We need to update the current node so the plus/minus block gets update too
1364 int top
= Math
.Max (node
.Bounds
.Top
- 1, 0);
1365 Rectangle invalid
= new Rectangle (0, top
,
1366 Width
, Height
- top
);
1367 Invalidate (invalid
);
1370 internal void UpdateNode (TreeNode node
)
1375 if (update_stack
> 0) {
1376 update_needed
= true;
1380 if (node
== root_node
) {
1385 Rectangle invalid
= new Rectangle (0, node
.Bounds
.Top
- 1, Width
,
1386 node
.Bounds
.Height
+ 1);
1387 Invalidate (invalid
);
1390 internal void UpdateNodePlusMinus (TreeNode node
)
1392 if (update_stack
> 0) {
1393 update_needed
= true;
1397 int l
= node
.Bounds
.Left
+ 5;
1399 if (show_root_lines
|| node
.Parent
!= null)
1401 if (ImageList
!= null)
1402 l
-= ImageList
.ImageSize
.Width
+ 3;
1406 Invalidate (new Rectangle (l
, node
.Bounds
.Top
, 8, node
.Bounds
.Height
));
1409 internal override void OnPaintInternal (PaintEventArgs pe
)
1411 Draw (pe
.ClipRectangle
, pe
.Graphics
);
1414 internal void CreateDashPen ()
1416 dash
= new Pen (LineColor
, 1);
1417 dash
.DashStyle
= DashStyle
.Dot
;
1420 private void Draw (Rectangle clip
, Graphics dc
)
1422 dc
.FillRectangle (BackColorBrush
, clip
);
1427 Rectangle viewport
= ViewportRectangle
;
1428 Rectangle original_clip
= clip
;
1429 if (clip
.Bottom
> viewport
.Bottom
)
1430 clip
.Height
= viewport
.Bottom
- clip
.Top
;
1432 OpenTreeNodeEnumerator walk
= new OpenTreeNodeEnumerator (TopNode
);
1433 while (walk
.MoveNext ()) {
1434 TreeNode current
= walk
.CurrentNode
;
1436 // Haven't gotten to visible nodes yet
1437 if (current
.GetY () + ActualItemHeight
< clip
.Top
)
1440 // Past the visible nodes
1441 if (current
.GetY () > clip
.Bottom
)
1444 DrawTreeNode (current
, dc
, clip
);
1447 if (hbar
.Visible
&& vbar
.Visible
) {
1448 Rectangle corner
= new Rectangle (hbar
.Right
, vbar
.Bottom
, vbar
.Width
, hbar
.Height
);
1449 if (original_clip
.IntersectsWith (corner
))
1450 dc
.FillRectangle (ThemeEngine
.Current
.ResPool
.GetSolidBrush (ThemeEngine
.Current
.ColorControl
),
1455 private void DrawNodeState (TreeNode node
, Graphics dc
, int x
, int y
)
1458 if (StateImageList
.Images
[1] != null)
1459 dc
.DrawImage (StateImageList
.Images
[1], new Rectangle (x
, y
, 16, 16));
1461 if (StateImageList
.Images
[0] != null)
1462 dc
.DrawImage (StateImageList
.Images
[0], new Rectangle (x
, y
, 16, 16));
1466 private void DrawNodeCheckBox (TreeNode node
, Graphics dc
, int x
, int middle
)
1468 Pen pen
= ThemeEngine
.Current
.ResPool
.GetSizedPen(Color
.Black
, 2);
1469 dc
.DrawRectangle (pen
, x
+ 3, middle
- 4, 11, 11);
1472 Pen check_pen
= ThemeEngine
.Current
.ResPool
.GetPen(Color
.Black
);
1477 Rectangle rect
= new Rectangle (x
+ 4, middle
- 3, check_size
, check_size
);
1479 for (int i
= 0; i
< lineWidth
; i
++) {
1480 dc
.DrawLine (check_pen
, rect
.Left
+ 1, rect
.Top
+ lineWidth
+ i
, rect
.Left
+ 3, rect
.Top
+ 5 + i
);
1481 dc
.DrawLine (check_pen
, rect
.Left
+ 3, rect
.Top
+ 5 + i
, rect
.Left
+ 7, rect
.Top
+ 1 + i
);
1486 private void DrawNodeLines (TreeNode node
, Graphics dc
, Rectangle clip
, Pen dash
, int x
, int y
, int middle
)
1491 if (node
.nodes
.Count
> 0 && show_plus_minus
)
1496 if (show_root_lines
|| node
.Parent
!= null)
1497 dc
.DrawLine (dash
, x
- indent
+ ladjust
, middle
, x
+ radjust
, middle
);
1499 if (node
.PrevNode
!= null || node
.Parent
!= null) {
1501 dc
.DrawLine (dash
, x
- indent
+ ladjust
, node
.Bounds
.Top
,
1502 x
- indent
+ ladjust
, middle
- (show_plus_minus
&& node
.Nodes
.Count
> 0 ? 4 : 0));
1505 if (node
.NextNode
!= null) {
1507 dc
.DrawLine (dash
, x
- indent
+ ladjust
, middle
+ (show_plus_minus
&& node
.Nodes
.Count
> 0 ? 4 : 0),
1508 x
- indent
+ ladjust
, node
.Bounds
.Bottom
);
1513 if (show_plus_minus
)
1515 TreeNode parent
= node
.Parent
;
1516 while (parent
!= null) {
1517 if (parent
.NextNode
!= null) {
1518 int px
= parent
.GetLinesX () - indent
+ ladjust
;
1519 dc
.DrawLine (dash
, px
, node
.Bounds
.Top
, px
, node
.Bounds
.Bottom
);
1521 parent
= parent
.Parent
;
1525 private void DrawNodeImage (TreeNode node
, Graphics dc
, Rectangle clip
, int x
, int y
)
1527 if (!RectsIntersect (clip
, x
, y
, ImageList
.ImageSize
.Width
, ImageList
.ImageSize
.Height
))
1530 int use_index
= node
.Image
;
1532 if (use_index
> -1 && use_index
< ImageList
.Images
.Count
)
1533 ImageList
.Draw (dc
, x
, y
, ImageList
.ImageSize
.Width
, ImageList
.ImageSize
.Height
, use_index
);
1536 private void LabelEditFinished (object sender
, EventArgs e
)
1538 EndEdit (edit_node
);
1541 internal void BeginEdit (TreeNode node
)
1543 if (edit_node
!= null)
1544 EndEdit (edit_node
);
1546 if (edit_text_box
== null) {
1547 edit_text_box
= new LabelEditTextBox ();
1548 edit_text_box
.BorderStyle
= BorderStyle
.FixedSingle
;
1549 edit_text_box
.Visible
= false;
1550 edit_text_box
.EditingCancelled
+= new EventHandler (LabelEditCancelled
);
1551 edit_text_box
.EditingFinished
+= new EventHandler (LabelEditFinished
);
1552 edit_text_box
.TextChanged
+= new EventHandler (LabelTextChanged
);
1553 Controls
.Add (edit_text_box
);
1556 node
.EnsureVisible ();
1558 edit_text_box
.Bounds
= node
.Bounds
;
1559 edit_text_box
.Text
= node
.Text
;
1560 edit_text_box
.Visible
= true;
1561 edit_text_box
.Focus ();
1562 edit_text_box
.SelectAll ();
1564 edit_args
= new NodeLabelEditEventArgs (node
);
1565 OnBeforeLabelEdit (edit_args
);
1569 if (edit_args
.CancelEdit
) {
1575 private void LabelEditCancelled (object sender
, EventArgs e
)
1577 edit_args
.SetLabel (null);
1578 EndEdit (edit_node
);
1581 private void LabelTextChanged (object sender
, EventArgs e
)
1583 int width
= TextRenderer
.MeasureTextInternal (edit_text_box
.Text
, edit_text_box
.Font
, false).Width
+ 4;
1584 edit_text_box
.Width
= width
;
1586 if (edit_args
!= null)
1587 edit_args
.SetLabel (edit_text_box
.Text
);
1590 internal void EndEdit (TreeNode node
)
1592 if (edit_text_box
!= null && edit_text_box
.Visible
) {
1593 edit_text_box
.Visible
= false;
1598 // If we get a call to BeginEdit from any AfterLabelEdit handler,
1599 // the focus seems to always remain in the TreeView. This call seems
1600 // to synchronize the focus events - I don't like it but it works
1602 Application
.DoEvents ();
1604 if (edit_node
!= null && edit_node
== node
) {
1607 NodeLabelEditEventArgs e
= new NodeLabelEditEventArgs (edit_args
.Node
, edit_args
.Label
);
1609 OnAfterLabelEdit (e
);
1614 if (e
.Label
!= null)
1615 e
.Node
.Text
= e
.Label
;
1618 // EndEdit ends editing even if not called on the editing node
1623 internal void CancelEdit (TreeNode node
)
1625 edit_args
.SetLabel (null);
1627 if (edit_text_box
!= null && edit_text_box
.Visible
) {
1628 edit_text_box
.Visible
= false;
1636 internal int GetNodeWidth (TreeNode node
)
1638 Font font
= node
.NodeFont
;
1639 if (node
.NodeFont
== null)
1641 return (int)TextRenderer
.MeasureString (node
.Text
, font
, 0, string_format
).Width
+ 3;
1644 private void DrawSelectionAndFocus(TreeNode node
, Graphics dc
, Rectangle r
)
1646 if (Focused
&& focused_node
== node
&& !full_row_select
) {
1647 ControlPaint
.DrawFocusRectangle (dc
, r
, ForeColor
, BackColor
);
1649 if (draw_mode
!= TreeViewDrawMode
.Normal
)
1654 if (Focused
&& node
== highlighted_node
) {
1655 // Use the node's BackColor if is not empty, and is not actually the selected one (yet)
1656 Color back_color
= node
!= selected_node
&& node
.BackColor
!= Color
.Empty
? node
.BackColor
:
1657 ThemeEngine
.Current
.ColorHighlight
;
1658 dc
.FillRectangle (ThemeEngine
.Current
.ResPool
.GetSolidBrush (back_color
), r
);
1660 } else if (!hide_selection
&& node
== highlighted_node
) {
1661 dc
.FillRectangle (SystemBrushes
.Control
, r
);
1663 // If selected_node is not the current highlighted one, use the color of the TreeView
1664 Color back_color
= node
== selected_node
? BackColor
: node
.BackColor
;
1665 dc
.FillRectangle (ThemeEngine
.Current
.ResPool
.GetSolidBrush (back_color
), r
);
1669 private void DrawStaticNode (TreeNode node
, Graphics dc
)
1671 if (!full_row_select
|| show_lines
)
1672 DrawSelectionAndFocus(node
, dc
, node
.Bounds
);
1674 Font font
= node
.NodeFont
;
1675 if (node
.NodeFont
== null)
1677 Color text_color
= (Focused
&& node
== highlighted_node
?
1678 ThemeEngine
.Current
.ColorHighlightText
: node
.ForeColor
);
1679 if (text_color
.IsEmpty
)
1680 text_color
= ForeColor
;
1681 dc
.DrawString (node
.Text
, font
,
1682 ThemeEngine
.Current
.ResPool
.GetSolidBrush (text_color
),
1683 node
.Bounds
, string_format
);
1686 private void DrawTreeNode (TreeNode node
, Graphics dc
, Rectangle clip
)
1688 int child_count
= node
.nodes
.Count
;
1689 int y
= node
.GetY ();
1690 int middle
= y
+ (ActualItemHeight
/ 2);
1692 if (full_row_select
&& !show_lines
) {
1693 var r
= new Rectangle (1, y
, ViewportRectangle
.Width
- 2, ActualItemHeight
);
1694 DrawSelectionAndFocus (node
, dc
, r
);
1697 if (draw_mode
== TreeViewDrawMode
.Normal
|| draw_mode
== TreeViewDrawMode
.OwnerDrawText
) {
1698 if ((show_root_lines
|| node
.Parent
!= null) && show_plus_minus
&& child_count
> 0)
1699 ThemeEngine
.Current
.TreeViewDrawNodePlusMinus (this, node
, dc
, node
.GetLinesX () - Indent
+ 5, middle
);
1701 if (checkboxes
&& state_image_list
== null)
1702 DrawNodeCheckBox (node
, dc
, CheckBoxLeft (node
) - 3, middle
);
1704 if (checkboxes
&& state_image_list
!= null)
1705 DrawNodeState (node
, dc
, CheckBoxLeft (node
) - 3, y
);
1707 if (!checkboxes
&& node
.StateImage
!= null)
1708 dc
.DrawImage (node
.StateImage
, new Rectangle (CheckBoxLeft (node
) - 3, y
, 16, 16));
1711 DrawNodeLines (node
, dc
, clip
, dash
, node
.GetLinesX (), y
, middle
);
1713 if (ImageList
!= null)
1714 DrawNodeImage (node
, dc
, clip
, node
.GetImageX (), y
);
1717 if (draw_mode
!= TreeViewDrawMode
.Normal
) {
1718 dc
.FillRectangle (BackColorBrush
, node
.Bounds
);
1720 var tree_node_state
= TreeNodeStates
.Default
;;
1721 if (node
.IsSelected
)
1722 tree_node_state
= TreeNodeStates
.Selected
;
1724 tree_node_state
|= TreeNodeStates
.Checked
;
1725 if (node
== focused_node
)
1726 tree_node_state
|= TreeNodeStates
.Focused
;
1728 var node_bounds
= node
.Bounds
;
1729 if (draw_mode
!= TreeViewDrawMode
.OwnerDrawText
) {
1731 node_bounds
.Width
= Width
;
1734 var e
= new DrawTreeNodeEventArgs (dc
, node
, node_bounds
, tree_node_state
);
1741 if (!node
.IsEditing
)
1742 DrawStaticNode (node
, dc
);
1745 internal void UpdateScrollBars (bool force
)
1747 if (!force
&& (IsDisposed
|| update_stack
> 0 || !IsHandleCreated
|| !Visible
))
1755 int item_height
= ActualItemHeight
;
1757 OpenTreeNodeEnumerator walk
= new OpenTreeNodeEnumerator (root_node
);
1759 while (walk
.MoveNext ()) {
1760 int r
= walk
.CurrentNode
.Bounds
.Right
;
1764 height
+= item_height
;
1767 height
-= item_height
; // root_node adjustment
1768 width
+= hbar_offset
;
1770 if (height
> ClientRectangle
.Height
) {
1773 if (width
> ClientRectangle
.Width
- SystemInformation
.VerticalScrollBarWidth
)
1775 } else if (width
> ClientRectangle
.Width
) {
1779 if (!vert
&& horz
&& height
> ClientRectangle
.Height
- SystemInformation
.HorizontalScrollBarHeight
)
1784 int visible_height
= horz
? ClientRectangle
.Height
- hbar
.Height
: ClientRectangle
.Height
;
1785 vbar
.SetValues (Math
.Max (0, max_visible_order
- 2), visible_height
/ ActualItemHeight
);
1787 vbar.Maximum = max_visible_order;
1788 vbar.LargeChange = ClientRectangle.Height / ItemHeight;
1791 if (!vbar_bounds_set
) {
1792 vbar
.Bounds
= new Rectangle (ClientRectangle
.Width
- vbar
.Width
, 0, vbar
.Width
,
1793 ClientRectangle
.Height
-
1794 (horz
? SystemInformation
.VerticalScrollBarWidth
: 0));
1795 vbar_bounds_set
= true;
1797 // We need to recalc the hbar if the vbar is now visible
1798 hbar_bounds_set
= false;
1802 vbar
.Visible
= true;
1803 if (skipped_nodes
> 0) {
1804 int skip
= Math
.Min (skipped_nodes
, vbar
.Maximum
- VisibleCount
+ 1);
1806 vbar
.SafeValueSet (skip
);
1807 skipped_nodes
= skip
;
1811 RecalculateVisibleOrder (root_node
);
1812 vbar
.Visible
= false;
1813 SetVScrollValue (0);
1814 vbar_bounds_set
= false;
1818 hbar
.SetValues (width
+ 1, ClientRectangle
.Width
- (vert
? SystemInformation
.VerticalScrollBarWidth
: 0));
1820 hbar.LargeChange = ClientRectangle.Width;
1821 hbar.Maximum = width + 1;
1824 if (!hbar_bounds_set
) {
1825 hbar
.Bounds
= new Rectangle (0, ClientRectangle
.Height
- hbar
.Height
,
1826 ClientRectangle
.Width
- (vert
? SystemInformation
.VerticalScrollBarWidth
: 0),
1828 hbar_bounds_set
= true;
1830 hbar
.Visible
= true;
1833 hbar
.Visible
= false;
1834 hbar_bounds_set
= false;
1838 private void SizeChangedHandler (object sender
, EventArgs e
)
1840 if (IsHandleCreated
) {
1841 if (max_visible_order
== -1)
1842 RecalculateVisibleOrder (root_node
);
1843 UpdateScrollBars (false);
1847 vbar
.Bounds
= new Rectangle (ClientRectangle
.Width
- vbar
.Width
, 0, vbar
.Width
,
1848 ClientRectangle
.Height
- (hbar
.Visible
? SystemInformation
.HorizontalScrollBarHeight
: 0));
1852 hbar
.Bounds
= new Rectangle (0, ClientRectangle
.Height
- hbar
.Height
,
1853 ClientRectangle
.Width
- (vbar
.Visible
? SystemInformation
.VerticalScrollBarWidth
: 0), hbar
.Height
);
1857 private void VScrollBarValueChanged (object sender
, EventArgs e
)
1859 if (edit_node
!= null)
1860 EndEdit (edit_node
);
1862 SetVScrollPos (vbar
.Value
, null);
1865 private void SetVScrollPos (int pos
, TreeNode new_top
)
1867 if (!vbar
.VisibleInternal
)
1873 if (skipped_nodes
== pos
)
1876 int diff
= skipped_nodes
- pos
;
1877 skipped_nodes
= pos
;
1879 if (!IsHandleCreated
)
1882 int y_move
= diff
* ActualItemHeight
;
1883 XplatUI
.ScrollWindow (Handle
, ViewportRectangle
, 0, y_move
, false);
1886 /*private void SetVScrollTop (TreeNode new_top)
1888 vbar.Value = new_top.visible_order - VisibleCount;
1891 private void HScrollBarValueChanged(object sender
, EventArgs e
)
1893 if (edit_node
!= null)
1894 EndEdit (edit_node
);
1896 int old_offset
= hbar_offset
;
1897 hbar_offset
= hbar
.Value
;
1899 if (hbar_offset
< 0) {
1903 XplatUI
.ScrollWindow (Handle
, ViewportRectangle
, old_offset
- hbar_offset
, 0, false);
1906 internal void ExpandBelow (TreeNode node
, int count_to_next
)
1908 if (update_stack
> 0) {
1909 update_needed
= true;
1913 // If node Bottom is less than 0, the node is above and not visible,
1914 // and we need to scroll the entire viewport
1915 int node_bottom
= node
.Bounds
.Bottom
>= 0 ? node
.Bounds
.Bottom
: 0;
1916 Rectangle below
= new Rectangle (0, node_bottom
, ViewportRectangle
.Width
,
1917 ViewportRectangle
.Height
- node_bottom
);
1919 int amount
= count_to_next
* ActualItemHeight
;
1922 XplatUI
.ScrollWindow (Handle
, below
, 0, amount
, false);
1924 if (show_plus_minus
) {
1925 Invalidate (new Rectangle (0, node
.GetY (), Width
, ActualItemHeight
));
1929 internal void CollapseBelow (TreeNode node
, int count_to_next
)
1931 if (update_stack
> 0) {
1932 update_needed
= true;
1936 Rectangle below
= new Rectangle (0, node
.Bounds
.Bottom
, ViewportRectangle
.Width
,
1937 ViewportRectangle
.Height
- node
.Bounds
.Bottom
);
1939 int amount
= count_to_next
* ActualItemHeight
;
1942 XplatUI
.ScrollWindow (Handle
, below
, 0, -amount
, false);
1944 if (show_plus_minus
) {
1945 Invalidate (new Rectangle (0, node
.GetY (), Width
, ActualItemHeight
));
1949 private void MouseWheelHandler(object sender
, MouseEventArgs e
) {
1951 if (vbar
== null || !vbar
.is_visible
) {
1956 SetVScrollValue (Math
.Min (vbar
.Value
+ SystemInformation
.MouseWheelScrollLines
, vbar
.Maximum
- VisibleCount
+ 1));
1958 SetVScrollValue (Math
.Max (0, vbar
.Value
- SystemInformation
.MouseWheelScrollLines
));
1962 private void VisibleChangedHandler (object sender
, EventArgs e
)
1965 UpdateScrollBars (false);
1969 private void FontChangedHandler (object sender
, EventArgs e
)
1971 if (IsHandleCreated
) {
1972 TreeNode top
= TopNode
;
1973 InvalidateNodeWidthRecursive (root_node
);
1979 private void InvalidateNodeWidthRecursive (TreeNode node
)
1981 node
.InvalidateWidth ();
1982 foreach (TreeNode child
in node
.Nodes
) {
1983 InvalidateNodeWidthRecursive (child
);
1987 private void GotFocusHandler (object sender
, EventArgs e
)
1989 if (selected_node
== null) {
1990 if (pre_selected_node
!= null) {
1991 SelectedNode
= pre_selected_node
;
1995 SelectedNode
= TopNode
;
1997 } else if (selected_node
!= null)
1998 UpdateNode (selected_node
);
2001 private void LostFocusHandler (object sender
, EventArgs e
)
2003 UpdateNode (SelectedNode
);
2006 private void MouseDownHandler (object sender
, MouseEventArgs e
)
2008 if (e
.Button
== MouseButtons
.Right
)
2011 TreeNode node
= GetNodeAt (e
.Y
);
2015 mouse_click_node
= node
;
2017 if (show_plus_minus
&& IsPlusMinusArea (node
, e
.X
) && e
.Button
== MouseButtons
.Left
) {
2020 } else if (checkboxes
&& IsCheckboxArea (node
, e
.X
) && e
.Button
== MouseButtons
.Left
) {
2021 node
.check_reason
= TreeViewAction
.ByMouse
;
2022 node
.Checked
= !node
.Checked
;
2025 } else if (IsSelectableArea (node
, e
.X
) || full_row_select
) {
2026 TreeNode old_highlighted
= highlighted_node
;
2027 highlighted_node
= node
;
2028 if (label_edit
&& e
.Clicks
== 1 && highlighted_node
== old_highlighted
&& e
.Button
== MouseButtons
.Left
) {
2030 } else if (highlighted_node
!= focused_node
) {
2031 Size ds
= SystemInformation
.DragSize
;
2032 mouse_rect
.X
= e
.X
- ds
.Width
;
2033 mouse_rect
.Y
= e
.Y
- ds
.Height
;
2034 mouse_rect
.Width
= ds
.Width
* 2;
2035 mouse_rect
.Height
= ds
.Height
* 2;
2037 select_mmove
= true;
2040 Invalidate (highlighted_node
.Bounds
);
2041 if (old_highlighted
!= null)
2042 Invalidate (Bloat (old_highlighted
.Bounds
));
2049 private void MouseUpHandler (object sender
, MouseEventArgs e
) {
2050 TreeNode node
= GetNodeAt (e
.Y
);
2052 if (node
!= null && node
== mouse_click_node
) {
2054 OnNodeMouseDoubleClick (new TreeNodeMouseClickEventArgs (node
, e
.Button
, e
.Clicks
, e
.X
, e
.Y
));
2056 OnNodeMouseClick (new TreeNodeMouseClickEventArgs (node
, e
.Button
, e
.Clicks
, e
.X
, e
.Y
));
2059 mouse_click_node
= null;
2067 select_mmove
= false;
2069 if (e
.Button
== MouseButtons
.Right
&& selected_node
!= null) {
2070 Invalidate (highlighted_node
.Bounds
);
2071 highlighted_node
= selected_node
;
2072 Invalidate (selected_node
.Bounds
);
2076 TreeViewCancelEventArgs ce
= new TreeViewCancelEventArgs (highlighted_node
, false, TreeViewAction
.ByMouse
);
2077 OnBeforeSelect (ce
);
2081 TreeNode prev_focused_node
= focused_node
;
2082 TreeNode prev_highlighted_node
= highlighted_node
;
2084 selected_node
= highlighted_node
;
2085 focused_node
= highlighted_node
;
2086 OnAfterSelect (new TreeViewEventArgs (selected_node
, TreeViewAction
.ByMouse
));
2088 if (prev_highlighted_node
!= null) {
2089 if (prev_focused_node
!= null) {
2090 invalid
= Rectangle
.Union (Bloat (prev_focused_node
.Bounds
),
2091 Bloat (prev_highlighted_node
.Bounds
));
2093 invalid
= Bloat (prev_highlighted_node
.Bounds
);
2097 invalid
.Width
= ViewportRectangle
.Width
;
2099 Invalidate (invalid
);
2103 if (highlighted_node
!= null)
2104 Invalidate (highlighted_node
.Bounds
);
2106 highlighted_node
= focused_node
;
2107 selected_node
= focused_node
;
2108 if (selected_node
!= null)
2109 Invalidate (selected_node
.Bounds
);
2113 private void MouseMoveHandler (object sender
, MouseEventArgs e
) {
2114 // XXX - This should use HitTest and only fire when we are over
2115 // the important parts of a node, not things like gridlines or
2117 TreeNode tn
= GetNodeAt (e
.Location
);
2119 if (tn
!= tooltip_currently_showing
)
2120 MouseLeftItem (tooltip_currently_showing
);
2122 if (tn
!= null && tn
!= tooltip_currently_showing
)
2123 MouseEnteredItem (tn
);
2125 if (e
.Button
== MouseButtons
.Left
|| e
.Button
== MouseButtons
.Right
) {
2126 if (drag_begin_x
!= -1 && drag_begin_y
!= -1) {
2127 double rise
= Math
.Pow (drag_begin_x
- e
.X
, 2);
2128 double run
= Math
.Pow (drag_begin_y
- e
.Y
, 2);
2129 double move
= Math
.Sqrt (rise
+ run
);
2131 TreeNode drag
= GetNodeAtUseX (e
.X
, e
.Y
);
2134 OnItemDrag (new ItemDragEventArgs (e
.Button
, drag
));
2143 // If there is enough movement before the mouse comes up,
2144 // selection is reverted back to the originally selected node
2145 if (!select_mmove
|| mouse_rect
.Contains (e
.X
, e
.Y
))
2148 Invalidate (highlighted_node
.Bounds
);
2149 if (selected_node
!= null)
2150 Invalidate (selected_node
.Bounds
);
2151 if (focused_node
!= null)
2152 Invalidate (focused_node
.Bounds
);
2154 highlighted_node
= selected_node
;
2155 focused_node
= selected_node
;
2157 select_mmove
= false;
2160 private void DoubleClickHandler (object sender
, MouseEventArgs e
) {
2161 TreeNode node
= GetNodeAtUseX (e
.X
,e
.Y
);
2162 if(node
!= null && node
.Nodes
.Count
> 0) {
2168 private bool RectsIntersect (Rectangle r
, int left
, int top
, int width
, int height
)
2170 return !((r
.Left
> left
+ width
) || (r
.Right
< left
) ||
2171 (r
.Top
> top
+ height
) || (r
.Bottom
< top
));
2174 // Return true if message was handled, false to send it to base
2175 private bool WmContextMenu (ref Message m
)
2180 pt
= new Point (LowOrder ((int)m
.LParam
.ToInt32 ()), HighOrder ((int)m
.LParam
.ToInt32 ()));
2182 // This means it's a keyboard menu request
2183 if (pt
.X
== -1 || pt
.Y
== -1) {
2189 pt
= new Point (tn
.Bounds
.Left
, tn
.Bounds
.Top
+ (tn
.Bounds
.Height
/ 2));
2191 pt
= PointToClient (pt
);
2193 tn
= GetNodeAt (pt
);
2199 // At this point, we have a valid TreeNode
2200 if (tn
.ContextMenu
!= null) {
2201 tn
.ContextMenu
.Show (this, pt
);
2203 } else if (tn
.ContextMenuStrip
!= null) {
2204 tn
.ContextMenuStrip
.Show (this, pt
);
2208 // The node we found did not have its own menu, let the parent try to display its menu
2212 #region Stuff for ToolTips
2213 private void MouseEnteredItem (TreeNode item
)
2215 tooltip_currently_showing
= item
;
2220 if (ShowNodeToolTips
&& !string.IsNullOrEmpty (tooltip_currently_showing
.ToolTipText
))
2221 ToolTipWindow
.Present (this, tooltip_currently_showing
.ToolTipText
);
2223 OnNodeMouseHover (new TreeNodeMouseHoverEventArgs (tooltip_currently_showing
));
2226 private void MouseLeftItem (TreeNode item
)
2228 ToolTipWindow
.Hide (this);
2229 tooltip_currently_showing
= null;
2232 private ToolTip ToolTipWindow
{
2234 if (tooltip_window
== null)
2235 tooltip_window
= new ToolTip ();
2237 return tooltip_window
;
2242 #endregion // Internal & Private Methods and Properties
2245 static object ItemDragEvent
= new object ();
2246 static object AfterCheckEvent
= new object ();
2247 static object AfterCollapseEvent
= new object ();
2248 static object AfterExpandEvent
= new object ();
2249 static object AfterLabelEditEvent
= new object ();
2250 static object AfterSelectEvent
= new object ();
2251 static object BeforeCheckEvent
= new object ();
2252 static object BeforeCollapseEvent
= new object ();
2253 static object BeforeExpandEvent
= new object ();
2254 static object BeforeLabelEditEvent
= new object ();
2255 static object BeforeSelectEvent
= new object ();
2256 static object DrawNodeEvent
= new object ();
2257 static object NodeMouseClickEvent
= new object ();
2258 static object NodeMouseDoubleClickEvent
= new object();
2259 static object NodeMouseHoverEvent
= new object ();
2260 static object RightToLeftLayoutChangedEvent
= new object ();
2262 public event ItemDragEventHandler ItemDrag
{
2263 add { Events.AddHandler (ItemDragEvent, value); }
2264 remove { Events.RemoveHandler (ItemDragEvent, value); }
2267 public event TreeViewEventHandler AfterCheck
{
2268 add { Events.AddHandler (AfterCheckEvent, value); }
2269 remove { Events.RemoveHandler (AfterCheckEvent, value); }
2272 public event TreeViewEventHandler AfterCollapse
{
2273 add { Events.AddHandler (AfterCollapseEvent, value); }
2274 remove { Events.RemoveHandler (AfterCollapseEvent, value); }
2277 public event TreeViewEventHandler AfterExpand
{
2278 add { Events.AddHandler (AfterExpandEvent, value); }
2279 remove { Events.RemoveHandler (AfterExpandEvent, value); }
2282 public event NodeLabelEditEventHandler AfterLabelEdit
{
2283 add { Events.AddHandler (AfterLabelEditEvent, value); }
2284 remove { Events.RemoveHandler (AfterLabelEditEvent, value); }
2287 public event TreeViewEventHandler AfterSelect
{
2288 add { Events.AddHandler (AfterSelectEvent, value); }
2289 remove { Events.RemoveHandler (AfterSelectEvent, value); }
2292 public event TreeViewCancelEventHandler BeforeCheck
{
2293 add { Events.AddHandler (BeforeCheckEvent, value); }
2294 remove { Events.RemoveHandler (BeforeCheckEvent, value); }
2297 public event TreeViewCancelEventHandler BeforeCollapse
{
2298 add { Events.AddHandler (BeforeCollapseEvent, value); }
2299 remove { Events.RemoveHandler (BeforeCollapseEvent, value); }
2302 public event TreeViewCancelEventHandler BeforeExpand
{
2303 add { Events.AddHandler (BeforeExpandEvent, value); }
2304 remove { Events.RemoveHandler (BeforeExpandEvent, value); }
2307 public event NodeLabelEditEventHandler BeforeLabelEdit
{
2308 add { Events.AddHandler (BeforeLabelEditEvent, value); }
2309 remove { Events.RemoveHandler (BeforeLabelEditEvent, value); }
2312 public event TreeViewCancelEventHandler BeforeSelect
{
2313 add { Events.AddHandler (BeforeSelectEvent, value); }
2314 remove { Events.RemoveHandler (BeforeSelectEvent, value); }
2317 public event DrawTreeNodeEventHandler DrawNode
{
2318 add { Events.AddHandler (DrawNodeEvent, value); }
2319 remove { Events.RemoveHandler (DrawNodeEvent, value); }
2322 public event TreeNodeMouseClickEventHandler NodeMouseClick
{
2323 add { Events.AddHandler (NodeMouseClickEvent, value); }
2324 remove { Events.RemoveHandler (NodeMouseClickEvent, value); }
2328 public event TreeNodeMouseClickEventHandler NodeMouseDoubleClick
{
2329 add { Events.AddHandler (NodeMouseDoubleClickEvent, value); }
2330 remove { Events.RemoveHandler (NodeMouseDoubleClickEvent, value); }
2333 public event TreeNodeMouseHoverEventHandler NodeMouseHover
{
2334 add { Events.AddHandler (NodeMouseHoverEvent, value); }
2335 remove { Events.RemoveHandler (NodeMouseHoverEvent, value); }
2338 public event EventHandler RightToLeftLayoutChanged
{
2339 add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
2340 remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
2344 [EditorBrowsable (EditorBrowsableState
.Never
)]
2345 public new event EventHandler BackgroundImageChanged
{
2346 add { base.BackgroundImageChanged += value; }
2347 remove { base.BackgroundImageChanged -= value; }
2351 [EditorBrowsable (EditorBrowsableState
.Never
)]
2352 public new event EventHandler BackgroundImageLayoutChanged
{
2353 add { base.BackgroundImageLayoutChanged += value; }
2354 remove { base.BackgroundImageLayoutChanged -= value; }
2358 [EditorBrowsable (EditorBrowsableState
.Never
)]
2359 public new event EventHandler PaddingChanged
{
2360 add { base.PaddingChanged += value; }
2361 remove { base.PaddingChanged -= value; }
2364 [EditorBrowsable (EditorBrowsableState
.Never
)]
2366 public new event PaintEventHandler Paint
{
2367 add { base.Paint += value; }
2368 remove { base.Paint -= value; }
2371 [EditorBrowsable (EditorBrowsableState
.Never
)]
2373 public new event EventHandler TextChanged
{
2374 add { base.TextChanged += value; }
2375 remove { base.TextChanged -= value; }
2378 #region UIA Framework Events
2379 static object UIACheckBoxesChangedEvent
= new object ();
2381 internal event EventHandler UIACheckBoxesChanged
{
2382 add { Events.AddHandler (UIACheckBoxesChangedEvent, value); }
2383 remove { Events.RemoveHandler (UIACheckBoxesChangedEvent, value); }
2386 internal void OnUIACheckBoxesChanged (EventArgs e
)
2388 EventHandler eh
= (EventHandler
) Events
[UIACheckBoxesChangedEvent
];
2393 static object UIALabelEditChangedEvent
= new object ();
2395 internal event EventHandler UIALabelEditChanged
{
2396 add { Events.AddHandler (UIALabelEditChangedEvent, value); }
2397 remove { Events.RemoveHandler (UIALabelEditChangedEvent, value); }
2400 internal void OnUIALabelEditChanged (EventArgs e
)
2402 EventHandler eh
= (EventHandler
) Events
[UIALabelEditChangedEvent
];
2407 static object UIANodeTextChangedEvent
= new object ();
2409 internal event TreeViewEventHandler UIANodeTextChanged
{
2410 add { Events.AddHandler (UIANodeTextChangedEvent, value); }
2411 remove { Events.RemoveHandler (UIANodeTextChangedEvent, value); }
2414 internal void OnUIANodeTextChanged (TreeViewEventArgs e
)
2416 TreeViewEventHandler eh
=
2417 (TreeViewEventHandler
) Events
[UIANodeTextChangedEvent
];
2422 static object UIACollectionChangedEvent
= new object ();
2424 internal event CollectionChangeEventHandler UIACollectionChanged
{
2425 add { Events.AddHandler (UIACollectionChangedEvent, value); }
2426 remove { Events.RemoveHandler (UIACollectionChangedEvent, value); }
2429 internal void OnUIACollectionChanged (object sender
, CollectionChangeEventArgs e
)
2431 CollectionChangeEventHandler eh
=
2432 (CollectionChangeEventHandler
) Events
[UIACollectionChangedEvent
];
2434 if (sender
== root_node
)
2439 #endregion // UIA Framework Events
2440 #endregion // Events